/**
* `@huaqiu/dsh-tool-symbol-footprint` — sidebar entry wiring over the shared
* family package `@huaqiu/dsh-sidebar-entry` (adapted from dsh-web's
* task-board / skill-explorer / ssh pattern).
*
* Injects two compact rows — 封装生成 and Symbol 生成 — between the shell's New
* Session button and the workspace browser, instead of the previous cramped
* `sidebar.footer.action` buttons. Each row toggles the shared
* `shell.overlay` workspace on its page. The row is plain DOM (no React tree);
* the overlay it toggles is the React workspace owned by `workspace.tsx`.
*
* The injection core now lives in `@huaqiu/dsh-sidebar-entry` so
* `dsh-tool-kicad-schematic-gen` can mount a sibling row with identical placement,
* ordering and self-healing behaviour instead of forking it.
*/
import {
HUAQIU_SIDEBAR_ENTRY_FAMILY,
mountSidebarEntry as mountSharedSidebarEntry,
} from '@huaqiu/dsh-sidebar-entry'
/** Stable data attributes identifying the injected entry rows. */
export const FOOTPRINT_ENTRY_SELECTOR = '[data-hqcg-footprint-entry]'
export const SYMBOL_ENTRY_SELECTOR = '[data-hqcg-symbol-entry]'
/** L2 semantic plugin id (issue #506): rows carry data-dsh-plugin + data-dsh-part. */
const PLUGIN_ID = 'huaqiu-component-gen'
/**
* Inline icons (footprint.svg / symbol.svg from the repo root). Both viewBoxes
* are cropped to the artwork's ink so the glyph fills the icon slot instead of
* canvas padding. The rendered size is pinned by CSS to 16×16 (the shell's
* wide nav-glyph size — same as the official Plugins entry) and grows to 18×18
* on the collapsed rail. Both use `currentColor` so they inherit the sidebar
* text/theme color.
*/
export const FOOTPRINT_ICON =
``
export const SYMBOL_ICON =
``
/** Class names used by the injected rows (defined by `injectSidebarEntryStyles`). */
const ENTRY_CSS: Record = {
entry: 'hqcg-sidebar-entry',
entryIcon: 'hqcg-sidebar-entry__icon',
entryLabel: 'hqcg-sidebar-entry__label',
}
/**
* Sidebar entry styles — matched to the official `sidebar.panellist` rows
* (ui-sidebar `.panelRow`): same 2px inset, 7px/8px padding, 8px gap, 12px
* radius and 16px glyph, so 封装生成 / Symbol 生成 sit flush with the shell's
* own Plugins entry instead of drifting right of it.
*/
const ENTRY_CSS_TEXT = `
.hqcg-sidebar-entry {
box-sizing: border-box;
display: flex;
align-items: center;
gap: 8px;
width: 100%;
margin: 0 2px;
min-height: 36px;
padding: 7px 8px;
background: transparent;
border: none;
border-radius: 12px;
color: var(--dsw-alias-label-primary);
cursor: pointer;
font: inherit;
line-height: 22px;
text-align: left;
white-space: nowrap;
}
.hqcg-sidebar-entry:hover {
background: var(--dsw-alias-interactive-bg-hover);
}
.hqcg-sidebar-entry[data-active] {
background: var(--dsw-alias-interactive-bg-hover);
color: var(--dsw-alias-label-primary);
}
.hqcg-sidebar-entry__icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
}
.hqcg-sidebar-entry__icon svg {
display: block;
width: 16px;
height: 16px;
}
.hqcg-sidebar-entry__label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
/* Collapsed rail: icon-only, centered — same 36px square the shell uses. */
[data-dsh-frame][data-sidebar-collapsed] .hqcg-sidebar-entry,
[data-sidebar-collapsed] .hqcg-sidebar-entry {
justify-content: center;
width: 36px;
height: 36px;
margin: 0;
padding: 0;
}
[data-dsh-frame][data-sidebar-collapsed] .hqcg-sidebar-entry__icon svg,
[data-sidebar-collapsed] .hqcg-sidebar-entry__icon svg {
width: 18px;
height: 18px;
}
[data-dsh-frame][data-sidebar-collapsed] .hqcg-sidebar-entry__label,
[data-sidebar-collapsed] .hqcg-sidebar-entry__label {
display: none;
}
`
const STYLE_TAG_ID = 'data-plugin-css="huaqiu-component-gen-sidebar"'
/** Inject the sidebar entry stylesheet once (idempotent). */
export function injectSidebarEntryStyles(): void {
if (typeof document === 'undefined') return
if (document.querySelector(`style[${STYLE_TAG_ID}]`) !== null) return
const tag = document.createElement('style')
tag.dataset.pluginCss = 'huaqiu-component-gen-sidebar'
tag.textContent = ENTRY_CSS_TEXT
document.head.appendChild(tag)
}
/** Per-row mounting options (wired to the workspace state by the caller). */
export interface SidebarRowOptions {
selector: string
attribute: string
icon: string
/** Localized label; re-read on every locale refresh (see `subscribe`). */
label: () => string
/** Localized tooltip; re-read on every locale refresh (see `subscribe`). */
tooltip: () => string
position: 'before' | 'after'
onToggle(): void
isOpen(): boolean
}
/**
* Mount the two component-gen sidebar rows (footprint + symbol). Rows are
* ordered deterministically: footprint first, symbol second — whatever the
* mount order, the family-positioning keeps the pair stable.
*
* @param rows - the two rows' wiring (footprint, then symbol).
* @param subscribe - subscription source for active-state + label refresh.
* @returns disposer removing both rows and their observers.
*/
export function mountComponentGenSidebarEntries(
rows: [SidebarRowOptions, SidebarRowOptions],
subscribe: (listener: () => void) => () => void,
): () => void {
if (typeof document === 'undefined') return () => {}
injectSidebarEntryStyles()
// Shared with dsh-tool-kicad-schematic-gen so the family block keeps one stable
// order whichever plugin mounts first.
const familySelectors = HUAQIU_SIDEBAR_ENTRY_FAMILY
const disposers = rows.map((row) =>
mountSharedSidebarEntry({
rowAttribute: row.attribute,
rowSelector: row.selector,
plugin: PLUGIN_ID,
icon: row.icon,
css: ENTRY_CSS,
label: row.label,
tooltip: row.tooltip,
refresh: { subscribe },
onToggle: row.onToggle,
position: row.position,
familySelectors,
active: {
subscribe,
isOpen: row.isOpen,
},
}),
)
return () => {
for (const dispose of disposers) {
try { dispose() } catch { /* already disposed */ }
}
}
}