@ds-mo/ui 1.10.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"names":["PANEL_TOOLS_PRIMARY_TOOL_ID","PANEL_TOOLS_LABELS","search","messages","stacks","activity","agents","parsePanelToolsItems","items","itemsJson","parseJsonArrayProp","panelToolsCss","PanelTools","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","open","activeTool","motion","readyForMotion","motionEnableGeneration","handleTransitionEnd","event","target","el","querySelector","propertyName","dsChromeTransitionEnd","emit","source","handleToolChange","id","selected","isRailSelected","dsToolChange","railItems","disconnectedCallback","removeEventListener","deferMotionEnable","generation","requestAnimationFrame","connectedCallback","addEventListener","openChanged","isOpen","wasOpen","undefined","dsChromeTransitionStart","activeToolChanged","componentDidLoad","itemsChanged","isDrawerPresent","isViewActive","headerLabel","renderRailAction","item","h","key","class","icon","dot","inactive","ariaLabel","onDsChange","render","headerItem","find","bodyItems","filter","showDrawerChrome","Host","role","hidden","name","map"],"sources":["src/wc/components/PanelTools/panel-tools-types.ts","src/wc/components/PanelTools/panel-tools-utils.ts","src/wc/components/PanelTools/PanelTools.css?tag=ds-panel-tools&encapsulation=scoped","src/wc/components/PanelTools/PanelTools.tsx"],"sourcesContent":["/** Tool ids for the `ds-panel-tools` rail and drawer views. */\nexport type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';\n\nexport const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [\n 'search',\n 'agents',\n 'messages',\n 'stacks',\n 'activity',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId = 'search';\n\nexport const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n","import { parseJsonArrayProp } from '../BarNav/bar-nav-utils';\nimport type { PanelToolsItem } from './panel-tools-types';\n\nexport function parsePanelToolsItems(\n items: PanelToolsItem[],\n itemsJson: string,\n): PanelToolsItem[] {\n if (itemsJson) {\n return parseJsonArrayProp<PanelToolsItem>(itemsJson, []);\n }\n return items ?? [];\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[],\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.inactive !== item.inactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n",":host {\n display: block;\n flex: 0 0 auto;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n --_panel-tools-rail-width: var(--dimension-size-600);\n --_panel-tools-drawer-width: var(--dimension-panel-width-sm);\n --_panel-tools-border: var(--dimension-stroke-width-012, 1px) solid var(--color-border-tertiary);\n /* Match panel-nav expand timing (medium-1); easing differs by direction. */\n --_panel-tools-expand-dur: var(--effect-animation-duration-medium-1, 300ms);\n}\n\n.panel-tools__layout {\n display: flex;\n flex-direction: row;\n align-items: stretch;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* Clip frame — only max-width animates (slide reveal, same as pre-rail :host). */\n.panel-tools__drawer {\n flex: 0 0 auto;\n max-width: 0;\n min-width: 0;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n box-sizing: border-box;\n transition: none;\n}\n\n:host(.panel-tools--ready.panel-tools--motion-opening) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-out, ease-out);\n}\n\n:host(.panel-tools--ready.panel-tools--motion-closing) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-in, ease-in);\n}\n\n:host(.panel-tools--open) .panel-tools__drawer {\n max-width: var(--_panel-tools-drawer-width);\n}\n\n.panel-tools__drawer--visible {\n border-left: var(--_panel-tools-border);\n}\n\n/* Fixed-width surface — stays 400px while the clip frame opens/closes. */\n.panel-tools__drawer-surface {\n display: flex;\n flex-direction: column;\n width: var(--_panel-tools-drawer-width);\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n}\n\n/* Shell-owned chrome (bg + wash + grid) — drawer is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .panel-tools__drawer-surface {\n background-color: transparent;\n}\n\n/* Aligns with ds-bar-nav row — same height, inset, border, and title token. */\n.panel-tools__header {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n border-bottom: var(--_panel-tools-border);\n color: var(--color-foreground-primary);\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.panel-tools__title {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.panel-tools__body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n position: relative;\n}\n\n.panel-tools__view {\n display: none;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n padding: var(--dimension-space-200);\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view ::slotted(*) {\n margin: 0;\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view--active {\n display: block;\n}\n\n/* Rail — mirrors collapsed panel-nav width, padding, and item rhythm. */\n.panel-tools__rail {\n flex: 0 0 var(--_panel-tools-rail-width);\n width: var(--_panel-tools-rail-width);\n min-width: var(--_panel-tools-rail-width);\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n border-left: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-header {\n display: flex;\n align-items: center;\n justify-content: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-tools__rail-body::-webkit-scrollbar {\n display: none;\n}\n\n.panel-tools__rail-action {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: var(--dimension-size-400);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Element, State, Watch, h, Host } from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport {\n PANEL_TOOLS_LABELS,\n PANEL_TOOLS_PRIMARY_TOOL_ID,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\nimport { parsePanelToolsItems } from './panel-tools-utils';\n\n@Component({\n tag: 'ds-panel-tools',\n styleUrl: 'PanelTools.css',\n scoped: true,\n})\nexport class PanelTools {\n @Element() el!: HTMLElement;\n\n /** When false, only the icon rail is shown. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n\n /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */\n @Prop({ mutable: true, attribute: 'active-tool', reflect: true })\n activeTool: PanelToolsToolId | '' = '';\n\n /**\n * Rail items rendered in the right column.\n * Set via JS property: `el.items = [...]`. Replace the array reference to update.\n */\n @Prop() items: PanelToolsItem[] = [];\n\n /** JSON fallback for `items` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'items-json' }) itemsJson: string = '';\n\n /** Emitted when a rail button is toggled. Detail = { id, selected }. */\n @Event({ bubbles: true, composed: true }) dsToolChange!: EventEmitter<{\n id: PanelToolsToolId;\n selected: boolean;\n }>;\n\n /** Bubbling lifecycle — `ds-bar-nav` defers overflow checks during drawer motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Arms open vs close easing for the in-flight width transition. */\n @State() private motion: 'opening' | 'closing' | 'idle' = 'idle';\n\n /** Suppresses width transition until the host has painted its initial open state. */\n @State() private readyForMotion = false;\n\n private motionEnableGeneration = 0;\n\n private get railItems(): PanelToolsItem[] {\n return parsePanelToolsItems(this.items, this.itemsJson);\n }\n\n disconnectedCallback() {\n this.el.removeEventListener('transitionend', this.handleTransitionEnd);\n this.motionEnableGeneration += 1;\n }\n\n private deferMotionEnable() {\n if (this.readyForMotion) return;\n const generation = ++this.motionEnableGeneration;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n this.readyForMotion = true;\n });\n });\n }\n\n connectedCallback() {\n this.el.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n @Watch('open')\n openChanged(isOpen: boolean, wasOpen?: boolean) {\n if (this.readyForMotion && wasOpen !== undefined && wasOpen !== isOpen) {\n this.motion = isOpen ? 'opening' : 'closing';\n this.dsChromeTransitionStart.emit({ source: 'panel-tools' });\n }\n this.deferMotionEnable();\n }\n\n @Watch('activeTool')\n activeToolChanged() {\n this.deferMotionEnable();\n }\n\n componentDidLoad() {\n this.deferMotionEnable();\n }\n\n @Watch('items')\n @Watch('itemsJson')\n itemsChanged() {\n this.deferMotionEnable();\n }\n\n private handleTransitionEnd = (event: TransitionEvent) => {\n if (event.target !== this.el.querySelector('.panel-tools__drawer')) return;\n if (event.propertyName !== 'max-width') return;\n this.motion = 'idle';\n this.dsChromeTransitionEnd.emit({ source: 'panel-tools' });\n };\n\n /** Rail selection follows `open` immediately — independent of the slide animation. */\n private isRailSelected(id: PanelToolsToolId): boolean {\n return this.open && this.activeTool === id;\n }\n\n /** Drawer body stays mounted while the clip frame animates closed. */\n private isDrawerPresent(): boolean {\n return this.open || this.motion === 'closing';\n }\n\n private isViewActive(id: PanelToolsToolId): boolean {\n return this.isDrawerPresent() && this.activeTool === id;\n }\n\n private headerLabel(): string {\n if (!this.isDrawerPresent() || !this.activeTool) return '';\n return PANEL_TOOLS_LABELS[this.activeTool as PanelToolsToolId] ?? '';\n }\n\n private handleToolChange = (id: PanelToolsToolId) => {\n const selected = !this.isRailSelected(id);\n if (selected) {\n this.open = true;\n this.activeTool = id;\n } else {\n this.open = false;\n }\n this.dsToolChange.emit({ id, selected });\n };\n\n private renderRailAction(item: PanelToolsItem) {\n return (\n <ds-bar-nav-action\n key={item.id}\n class=\"panel-tools__rail-action\"\n icon={item.icon}\n selected={this.isRailSelected(item.id)}\n dot={item.dot ?? false}\n inactive={item.inactive}\n aria-label={item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id]}\n onDsChange={() => this.handleToolChange(item.id)}\n />\n );\n }\n\n render() {\n const headerLabel = this.headerLabel();\n const railItems = this.railItems;\n const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);\n const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);\n const showDrawerChrome = this.isDrawerPresent();\n\n return (\n <Host\n class={{\n 'panel-tools': true,\n 'panel-tools--open': this.open,\n 'panel-tools--ready': this.readyForMotion,\n 'panel-tools--motion-opening': this.motion === 'opening',\n 'panel-tools--motion-closing': this.motion === 'closing',\n }}\n role=\"complementary\"\n aria-label=\"Tools\"\n >\n <div class=\"panel-tools__layout\">\n <div\n class={{\n 'panel-tools__drawer': true,\n 'panel-tools__drawer--visible': showDrawerChrome,\n }}\n aria-hidden={showDrawerChrome ? null : 'true'}\n >\n <div class=\"panel-tools__drawer-surface\">\n <header class=\"panel-tools__header\">\n <span class=\"panel-tools__title text-body-medium-emphasis\">{headerLabel}</span>\n </header>\n <div class=\"panel-tools__body\">\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('search'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('search')}\n >\n <slot name=\"search\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('messages'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('messages')}\n >\n <slot name=\"messages\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('stacks'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('stacks')}\n >\n <slot name=\"stacks\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('activity'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('activity')}\n >\n <slot name=\"activity\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('agents'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('agents')}\n >\n <slot name=\"agents\" />\n </div>\n </div>\n </div>\n </div>\n\n <nav class=\"panel-tools__rail\" aria-label=\"Tool shortcuts\">\n {headerItem ? (\n <div class=\"panel-tools__rail-header\">{this.renderRailAction(headerItem)}</div>\n ) : null}\n <div class=\"panel-tools__rail-body\">\n {bodyItems.map(item => this.renderRailAction(item))}\n </div>\n </nav>\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["PANEL_TOOLS_PRIMARY_TOOL_ID","PANEL_TOOLS_LABELS","search","messages","stacks","activity","agents","parsePanelToolsItems","items","itemsJson","parseJsonArrayProp","panelToolsCss","PanelTools","__stencil_proxyCustomElement","HTMLElement","constructor","registerHost","this","open","activeTool","motion","readyForMotion","motionEnableGeneration","handleTransitionEnd","event","target","el","querySelector","propertyName","dsChromeTransitionEnd","emit","source","handleToolChange","id","selected","isRailSelected","dsToolChange","railItems","disconnectedCallback","removeEventListener","deferMotionEnable","generation","requestAnimationFrame","connectedCallback","addEventListener","openChanged","isOpen","wasOpen","undefined","dsChromeTransitionStart","activeToolChanged","componentDidLoad","itemsChanged","isDrawerPresent","isViewActive","headerLabel","renderRailAction","item","h","key","class","icon","dot","inactive","ariaLabel","onDsChange","render","headerItem","find","bodyItems","filter","showDrawerChrome","Host","role","hidden","name","map"],"sources":["src/wc/components/PanelTools/panel-tools-types.ts","src/wc/components/PanelTools/panel-tools-utils.ts","src/wc/components/PanelTools/PanelTools.css?tag=ds-panel-tools&encapsulation=scoped","src/wc/components/PanelTools/PanelTools.tsx"],"sourcesContent":["/** Tool ids for the `ds-panel-tools` rail and drawer views. */\nexport type PanelToolsToolId = 'search' | 'agents' | 'messages' | 'stacks' | 'activity';\n\nexport const PANEL_TOOLS_TOOL_IDS: PanelToolsToolId[] = [\n 'search',\n 'agents',\n 'messages',\n 'stacks',\n 'activity',\n];\n\n/** Rail header slot — mirrors panel-nav M logo row. */\nexport const PANEL_TOOLS_PRIMARY_TOOL_ID: PanelToolsToolId = 'search';\n\nexport const PANEL_TOOLS_LABELS: Record<PanelToolsToolId, string> = {\n search: 'Search',\n messages: 'Messages',\n stacks: 'Stacks',\n activity: 'Activity',\n agents: 'Agents',\n};\n\nexport interface PanelToolsItem {\n id: PanelToolsToolId;\n /** Icon name for <ds-icon>. */\n icon: string;\n /** Whether this rail button is currently pressed/active. */\n selected?: boolean;\n /** Show a notification dot. */\n dot?: boolean;\n inactive?: boolean;\n ariaLabel?: string;\n}\n","import { parseJsonArrayProp } from '../BarNav/bar-nav-utils';\nimport type { PanelToolsItem } from './panel-tools-types';\n\nexport function parsePanelToolsItems(\n items: PanelToolsItem[],\n itemsJson: string,\n): PanelToolsItem[] {\n if (itemsJson) {\n return parseJsonArrayProp<PanelToolsItem>(itemsJson, []);\n }\n return items ?? [];\n}\n\nexport function shouldResyncPanelToolsItems(\n prev: PanelToolsItem[],\n next: PanelToolsItem[],\n): boolean {\n if (prev.length !== next.length) return true;\n return next.some((item, index) => {\n const prior = prev[index];\n if (!prior) return true;\n return (\n prior.id !== item.id ||\n prior.icon !== item.icon ||\n prior.selected !== item.selected ||\n prior.dot !== item.dot ||\n prior.inactive !== item.inactive ||\n prior.ariaLabel !== item.ariaLabel\n );\n });\n}\n",":host {\n display: block;\n flex: 0 0 auto;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n --_panel-tools-rail-width: var(--dimension-size-600);\n --_panel-tools-drawer-width: var(--dimension-panel-width-sm);\n --_panel-tools-border: var(--dimension-stroke-width-012, 1px) solid var(--color-border-tertiary);\n /* Match panel-nav expand timing (medium-1); easing differs by direction. */\n --_panel-tools-expand-dur: var(--effect-animation-duration-medium-1, 300ms);\n}\n\n.panel-tools__layout {\n display: flex;\n flex-direction: row;\n align-items: stretch;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n}\n\n/* Clip frame — only max-width animates (slide reveal, same as pre-rail :host). */\n.panel-tools__drawer {\n flex: 0 0 auto;\n max-width: 0;\n min-width: 0;\n height: 100%;\n min-height: 0;\n overflow: hidden;\n box-sizing: border-box;\n transition: none;\n}\n\n:host(.panel-tools--ready.panel-tools--motion-opening) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-out, ease-out);\n}\n\n:host(.panel-tools--ready.panel-tools--motion-closing) .panel-tools__drawer {\n transition: max-width var(--_panel-tools-expand-dur)\n var(--effect-animation-easing-ease-in, ease-in);\n}\n\n:host(.panel-tools--open) .panel-tools__drawer {\n max-width: var(--_panel-tools-drawer-width);\n}\n\n.panel-tools__drawer--visible {\n border-left: var(--_panel-tools-border);\n}\n\n/* Fixed-width surface — stays 400px while the clip frame opens/closes. */\n.panel-tools__drawer-surface {\n display: flex;\n flex-direction: column;\n width: var(--_panel-tools-drawer-width);\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n background-color: var(--color-background-primary);\n}\n\n/* Shell-owned chrome (bg + wash + grid) — drawer is a transparent frame. */\n:host-context(ds-app-shell[gradient]) .panel-tools__drawer-surface,\n:host-context(ds-app-shell[grid]) .panel-tools__drawer-surface {\n background-color: transparent;\n}\n\n/* Aligns with ds-bar-nav row — same height, inset, border, and title token. */\n.panel-tools__header {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n border-bottom: var(--_panel-tools-border);\n color: var(--color-foreground-primary);\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n}\n\n.panel-tools__title {\n display: inline-flex;\n align-items: center;\n height: var(--dimension-size-400);\n padding-inline: var(--dimension-space-100);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.panel-tools__body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n position: relative;\n}\n\n.panel-tools__view {\n display: none;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n padding: var(--dimension-space-200);\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view ::slotted(*) {\n margin: 0;\n color: var(--color-foreground-secondary);\n font-family: var(--typography-font-family);\n font-size: var(--typography-fontsize-md);\n line-height: var(--typography-lineheight-md);\n font-weight: var(--typography-weight-regular);\n letter-spacing: var(--typography-letterspacing-negative-half);\n}\n\n.panel-tools__view--active {\n display: block;\n}\n\n/* Rail — mirrors collapsed panel-nav width, padding, and item rhythm. */\n.panel-tools__rail {\n flex: 0 0 var(--_panel-tools-rail-width);\n width: var(--_panel-tools-rail-width);\n min-width: var(--_panel-tools-rail-width);\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n border-left: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-header {\n display: flex;\n align-items: center;\n justify-content: center;\n height: var(--dimension-size-600);\n padding: var(--dimension-space-100);\n flex-shrink: 0;\n box-sizing: border-box;\n border-bottom: var(--_panel-tools-border);\n}\n\n.panel-tools__rail-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n gap: var(--dimension-space-050);\n padding: var(--dimension-space-100);\n box-sizing: border-box;\n scrollbar-width: none;\n}\n\n.panel-tools__rail-body::-webkit-scrollbar {\n display: none;\n}\n\n.panel-tools__rail-action {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: var(--dimension-size-400);\n flex-shrink: 0;\n}\n","import { Component, Prop, Event, EventEmitter, Element, State, Watch, h, Host } from '@stencil/core';\nimport type { ChromeTransitionDetail } from '../../nav/chrome-transition';\nimport {\n PANEL_TOOLS_LABELS,\n PANEL_TOOLS_PRIMARY_TOOL_ID,\n type PanelToolsItem,\n type PanelToolsToolId,\n} from './panel-tools-types';\nimport { parsePanelToolsItems } from './panel-tools-utils';\n\n@Component({\n tag: 'ds-panel-tools',\n styleUrl: 'PanelTools.css',\n scoped: true,\n})\nexport class PanelTools {\n @Element() el!: HTMLElement;\n\n /** When false, only the icon rail is shown. */\n @Prop({ mutable: true, reflect: true }) open: boolean = false;\n\n /** Active tool view — `search`, `agents`, `messages`, `stacks`, or `activity`. */\n @Prop({ mutable: true, attribute: 'active-tool', reflect: true })\n activeTool: PanelToolsToolId | '' = '';\n\n /**\n * Rail items rendered in the right column.\n * Set via JS property: `el.items = [...]`. Replace the array reference to update.\n */\n @Prop() items: PanelToolsItem[] = [];\n\n /** JSON fallback for `items` — useful when framework bindings don't propagate arrays. */\n @Prop({ attribute: 'items-json' }) itemsJson: string = '';\n\n /** Emitted when a rail button is toggled. Detail = { id, selected }. */\n @Event({ bubbles: true, composed: true }) dsToolChange!: EventEmitter<{\n id: PanelToolsToolId;\n selected: boolean;\n }>;\n\n /** Bubbling lifecycle — `ds-bar-nav` defers overflow checks during drawer motion. */\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionStart!: EventEmitter<ChromeTransitionDetail>;\n\n @Event({ bubbles: true, composed: true })\n dsChromeTransitionEnd!: EventEmitter<ChromeTransitionDetail>;\n\n /** Arms open vs close easing for the in-flight width transition. */\n @State() private motion: 'opening' | 'closing' | 'idle' = 'idle';\n\n /** Suppresses width transition until the host has painted its initial open state. */\n @State() private readyForMotion = false;\n\n private motionEnableGeneration = 0;\n\n private get railItems(): PanelToolsItem[] {\n return parsePanelToolsItems(this.items, this.itemsJson);\n }\n\n disconnectedCallback() {\n this.el.removeEventListener('transitionend', this.handleTransitionEnd);\n this.motionEnableGeneration += 1;\n }\n\n private deferMotionEnable() {\n if (this.readyForMotion) return;\n const generation = ++this.motionEnableGeneration;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n requestAnimationFrame(() => {\n if (generation !== this.motionEnableGeneration) return;\n this.readyForMotion = true;\n });\n });\n }\n\n connectedCallback() {\n this.el.addEventListener('transitionend', this.handleTransitionEnd);\n }\n\n @Watch('open')\n openChanged(isOpen: boolean, wasOpen?: boolean) {\n if (this.readyForMotion && wasOpen !== undefined && wasOpen !== isOpen) {\n this.motion = isOpen ? 'opening' : 'closing';\n this.dsChromeTransitionStart.emit({ source: 'panel-tools' });\n }\n this.deferMotionEnable();\n }\n\n @Watch('activeTool')\n activeToolChanged() {\n this.deferMotionEnable();\n }\n\n componentDidLoad() {\n this.deferMotionEnable();\n }\n\n @Watch('items')\n @Watch('itemsJson')\n itemsChanged() {\n this.deferMotionEnable();\n }\n\n private handleTransitionEnd = (event: TransitionEvent) => {\n if (event.target !== this.el.querySelector('.panel-tools__drawer')) return;\n if (event.propertyName !== 'max-width') return;\n this.motion = 'idle';\n this.dsChromeTransitionEnd.emit({ source: 'panel-tools' });\n };\n\n /** Rail selection follows `open` immediately — independent of the slide animation. */\n private isRailSelected(id: PanelToolsToolId): boolean {\n return this.open && this.activeTool === id;\n }\n\n /** Drawer body stays mounted while the clip frame animates closed. */\n private isDrawerPresent(): boolean {\n return this.open || this.motion === 'closing';\n }\n\n private isViewActive(id: PanelToolsToolId): boolean {\n return this.isDrawerPresent() && this.activeTool === id;\n }\n\n private headerLabel(): string {\n if (!this.isDrawerPresent() || !this.activeTool) return '';\n return PANEL_TOOLS_LABELS[this.activeTool as PanelToolsToolId] ?? '';\n }\n\n private handleToolChange = (id: PanelToolsToolId) => {\n const selected = !this.isRailSelected(id);\n if (selected) {\n this.open = true;\n this.activeTool = id;\n } else {\n this.open = false;\n }\n this.dsToolChange.emit({ id, selected });\n };\n\n private renderRailAction(item: PanelToolsItem) {\n return (\n <ds-bar-nav-action\n key={item.id}\n class=\"panel-tools__rail-action\"\n icon={item.icon}\n selected={this.isRailSelected(item.id)}\n dot={item.dot ?? false}\n inactive={item.inactive}\n aria-label={item.ariaLabel ?? PANEL_TOOLS_LABELS[item.id]}\n onDsChange={() => this.handleToolChange(item.id)}\n />\n );\n }\n\n render() {\n const headerLabel = this.headerLabel();\n const railItems = this.railItems;\n const headerItem = railItems.find(item => item.id === PANEL_TOOLS_PRIMARY_TOOL_ID);\n const bodyItems = railItems.filter(item => item.id !== PANEL_TOOLS_PRIMARY_TOOL_ID);\n const showDrawerChrome = this.isDrawerPresent();\n\n return (\n <Host\n class={{\n 'panel-tools': true,\n 'panel-tools--open': this.open,\n 'panel-tools--ready': this.readyForMotion,\n 'panel-tools--motion-opening': this.motion === 'opening',\n 'panel-tools--motion-closing': this.motion === 'closing',\n }}\n role=\"complementary\"\n aria-label=\"Tools\"\n >\n <div class=\"panel-tools__layout\">\n <div\n class={{\n 'panel-tools__drawer': true,\n 'panel-tools__drawer--visible': showDrawerChrome,\n }}\n aria-hidden={showDrawerChrome ? null : 'true'}\n >\n <div class=\"panel-tools__drawer-surface\">\n <header class=\"panel-tools__header\">\n <span class=\"panel-tools__title text-body-medium-emphasis\">{headerLabel}</span>\n </header>\n <div class=\"panel-tools__body\">\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('search'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('search')}\n >\n <slot name=\"search\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('messages'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('messages')}\n >\n <slot name=\"messages\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('stacks'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('stacks')}\n >\n <slot name=\"stacks\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('activity'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('activity')}\n >\n <slot name=\"activity\" />\n </div>\n <div\n class={{\n 'panel-tools__view': true,\n 'panel-tools__view--active': this.isViewActive('agents'),\n 'text-body-medium': true,\n }}\n hidden={!this.isViewActive('agents')}\n >\n <slot name=\"agents\" />\n </div>\n </div>\n </div>\n </div>\n\n <nav class=\"panel-tools__rail\" aria-label=\"Tool shortcuts\">\n {headerItem ? (\n <div class=\"panel-tools__rail-header\">{this.renderRailAction(headerItem)}</div>\n ) : null}\n <div class=\"panel-tools__rail-body\">\n {bodyItems.map(item => this.renderRailAction(item))}\n </div>\n </nav>\n </div>\n </Host>\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -2,8 +2,10 @@ import type { NavChromeStyle } from '../../nav/nav-chrome';
2
2
  export declare class AppShell {
3
3
  /** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */
4
4
  navStyle: NavChromeStyle;
5
- /** When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools. */
5
+ /** When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout). */
6
6
  gradient: boolean;
7
+ /** When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`. */
8
+ grid: boolean;
7
9
  /**
8
10
  * Optional custom gradient for `background-image` (e.g. SVG URL).
9
11
  * When set, overrides the built-in radial wash.
@@ -32,6 +34,7 @@ export declare class AppShell {
32
34
  private syncChromeLayoutVars;
33
35
  private resolvePanelWidthPx;
34
36
  private resolveShellDimensions;
37
+ private chromeLayerActive;
35
38
  private syncChrome;
36
39
  render(): any;
37
40
  }
@@ -99,7 +99,7 @@ export namespace Components {
99
99
  }
100
100
  interface DsAppShell {
101
101
  /**
102
- * When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools.
102
+ * When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
103
103
  * @default false
104
104
  */
105
105
  "gradient": boolean;
@@ -108,6 +108,11 @@ export namespace Components {
108
108
  * @default ''
109
109
  */
110
110
  "gradientSrc": string;
111
+ /**
112
+ * When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
113
+ * @default false
114
+ */
115
+ "grid": boolean;
111
116
  /**
112
117
  * Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
113
118
  * @default 'dashboard'
@@ -1781,7 +1786,7 @@ declare namespace LocalJSX {
1781
1786
  }
1782
1787
  interface DsAppShell {
1783
1788
  /**
1784
- * When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools.
1789
+ * When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
1785
1790
  * @default false
1786
1791
  */
1787
1792
  "gradient"?: boolean;
@@ -1790,6 +1795,11 @@ declare namespace LocalJSX {
1790
1795
  * @default ''
1791
1796
  */
1792
1797
  "gradientSrc"?: string;
1798
+ /**
1799
+ * When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
1800
+ * @default false
1801
+ */
1802
+ "grid"?: boolean;
1793
1803
  /**
1794
1804
  * Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
1795
1805
  * @default 'dashboard'
@@ -2871,6 +2881,7 @@ declare namespace LocalJSX {
2871
2881
  interface DsAppShellAttributes {
2872
2882
  "navStyle": NavChromeStyle;
2873
2883
  "gradient": boolean;
2884
+ "grid": boolean;
2874
2885
  "gradientSrc": string;
2875
2886
  }
2876
2887
  interface DsBadgeAttributes {
@@ -2,7 +2,7 @@ export type { NavChromeStyle } from './nav-chrome';
2
2
  export { NAV_STYLE_HINT_ATTR, readNavStyleAttr, setNavStyleHint, clearNavStyleHint, resolveNavChromeStyle, shouldResyncNavChromeStyle, } from './nav-chrome';
3
3
  export { SHELL_BAR_NAV_VT_NAME, SHELL_NAV_REVEAL_DURATION_VAR, SHELL_NAV_REVEAL_EASING_VAR, parseCssTimeMs, ensureShellNavVtStyle, resolveShellNavRevealOrigin, setShellNavRevealOriginVars, animateShellNavRadialReveal, runShellNavStyleRevealOnReady, } from './shell-view-transition';
4
4
  export type { ShellNavRevealOrigin } from './shell-view-transition';
5
- export { SHELL_GRADIENT_IMAGE_VAR, SHELL_GRADIENT_SIZE_VAR, SHELL_GRADIENT_POSITION_PANEL_VAR, SHELL_GRADIENT_POSITION_BAR_VAR, SHELL_GRADIENT_OPACITY_VAR, SHELL_CHROME_SURFACE_POSITION_VAR, SHELL_GRADIENT_OPACITY, buildShellRadialGradient, shellGradientImage, shellGradientSize, shellGradientPositionPanel, shellGradientPositionBar, shellChromeSurfacePosition, } from './shell-gradient';
5
+ export { SHELL_GRADIENT_IMAGE_VAR, SHELL_GRADIENT_SIZE_VAR, SHELL_GRADIENT_POSITION_PANEL_VAR, SHELL_GRADIENT_POSITION_BAR_VAR, SHELL_GRADIENT_OPACITY_VAR, SHELL_CHROME_SURFACE_POSITION_VAR, SHELL_GRADIENT_OPACITY, buildShellRadialGradient, shellGradientImage, shellGradientSize, shellGradientPositionPanel, shellGradientPositionBar, shellChromeSurfacePosition, shellChromeLayerActive, } from './shell-gradient';
6
6
  export type { ShellGradientLayout } from './shell-gradient';
7
7
  export type { ChromeTransitionDetail, ChromeTransitionSource } from './chrome-transition';
8
8
  export { CHROME_TRANSITION_END, CHROME_TRANSITION_START, } from './chrome-transition';
@@ -8,6 +8,8 @@ export declare const SHELL_GRADIENT_OPACITY_VAR = "--ds-shell-gradient-opacity";
8
8
  export declare const SHELL_CHROME_SURFACE_POSITION_VAR = "--ds-shell-chrome-surface-position";
9
9
  /** Layer opacity for the nav gradient wash. */
10
10
  export declare const SHELL_GRADIENT_OPACITY = "0.1";
11
+ /** Whether the shared chrome layer (bg + optional wash/grid) should mount. */
12
+ export declare function shellChromeLayerActive(gradient: boolean, grid: boolean): boolean;
11
13
  /**
12
14
  * Shell radial wash — same for panel and bar nav.
13
15
  * Tokens follow `data-theme` (light/dark blue intent).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ds-mo/ui",
3
- "version": "1.10.1",
3
+ "version": "1.11.0",
4
4
  "description": "CompoMo — composable web components styled with TokoMo design tokens",
5
5
  "keywords": [
6
6
  "web-components",
@@ -69,7 +69,7 @@
69
69
  "pretest": "node scripts/generate-icon-catalog.mjs",
70
70
  "build": "stencil build && node scripts/patch-index-types.mjs && node scripts/verify-icons-externalized.mjs && node scripts/write-build-stamp.mjs",
71
71
  "verify:pack": "node scripts/verify-npm-pack.mjs && node scripts/verify-nav-import.mjs",
72
- "test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/panel-tools.test.ts tests/shell-view-transition.test.ts tests/shell-gradient.test.ts tests/shell-chrome-metrics.test.ts tests/bar-nav-overflow-hysteresis.test.ts tests/badge-gradient-ring.test.ts tests/scroll-edge-fade.test.ts tests/resolve-css-length-px.test.ts tests/resolve-css-time-ms.test.ts tests/overlay-positioning.test.ts tests/menu-position.test.ts tests/bar-nav-utils.test.ts tests/bar-nav-tabs-menu-utils.test.ts tests/bar-nav-dom-utils.test.ts tests/icon-catalog.test.ts",
72
+ "test": "node --import tsx/esm --test tests/panel-nav-utils.test.ts tests/panel-tools.test.ts tests/shell-view-transition.test.ts tests/shell-gradient.test.ts tests/shell-chrome-metrics.test.ts tests/shell-chrome-layer.test.ts tests/bar-nav-overflow-hysteresis.test.ts tests/badge-gradient-ring.test.ts tests/scroll-edge-fade.test.ts tests/resolve-css-length-px.test.ts tests/resolve-css-time-ms.test.ts tests/overlay-positioning.test.ts tests/menu-position.test.ts tests/bar-nav-utils.test.ts tests/bar-nav-tabs-menu-utils.test.ts tests/bar-nav-dom-utils.test.ts tests/icon-catalog.test.ts",
73
73
  "test:e2e": "npm run build && playwright test",
74
74
  "test:e2e:install": "playwright install chromium",
75
75
  "build:docs": "stencil build --docs",
@@ -36,14 +36,14 @@ export declare interface DsAccordion extends Components.DsAccordion {
36
36
 
37
37
 
38
38
  @ProxyCmp({
39
- inputs: ['gradient', 'gradientSrc', 'navStyle']
39
+ inputs: ['gradient', 'gradientSrc', 'grid', 'navStyle']
40
40
  })
41
41
  @Component({
42
42
  selector: 'ds-app-shell',
43
43
  changeDetection: ChangeDetectionStrategy.OnPush,
44
44
  template: '<ng-content></ng-content>',
45
45
  // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
46
- inputs: ['gradient', 'gradientSrc', 'navStyle'],
46
+ inputs: ['gradient', 'gradientSrc', 'grid', 'navStyle'],
47
47
  standalone: false
48
48
  })
49
49
  export class DsAppShell {
@@ -33,9 +33,12 @@ export class AppShell {
33
33
  /** Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`. */
34
34
  @Prop({ attribute: 'nav-style', reflect: true }) navStyle: NavChromeStyle = 'dashboard';
35
35
 
36
- /** When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools. */
36
+ /** When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout). */
37
37
  @Prop({ reflect: true }) gradient: boolean = false;
38
38
 
39
+ /** When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`. */
40
+ @Prop({ reflect: true }) grid: boolean = false;
41
+
39
42
  /**
40
43
  * Optional custom gradient for `background-image` (e.g. SVG URL).
41
44
  * When set, overrides the built-in radial wash.
@@ -75,6 +78,7 @@ export class AppShell {
75
78
 
76
79
  @Watch('navStyle')
77
80
  @Watch('gradient')
81
+ @Watch('grid')
78
82
  @Watch('gradientSrc')
79
83
  onShellPropsChange() {
80
84
  this.syncSlottedNavStyle();
@@ -205,11 +209,32 @@ export class AppShell {
205
209
  return { width: rect.width, height: rect.height };
206
210
  }
207
211
 
212
+ private chromeLayerActive(): boolean {
213
+ return this.gradient || this.grid;
214
+ }
215
+
208
216
  private syncChrome() {
209
217
  const panelNav = this.el.querySelector('ds-panel-nav') as HTMLElement | null;
210
218
  const bar = this.el.querySelector('ds-bar-nav') as HTMLElement | null;
211
219
  const targets = [this.el, panelNav, bar].filter((el): el is HTMLElement => el !== null);
212
220
 
221
+ const clearLayoutVars = () => {
222
+ if (panelNav) panelNav.style.removeProperty(SHELL_GRADIENT_POSITION_PANEL_VAR);
223
+ if (bar) bar.style.removeProperty(SHELL_GRADIENT_POSITION_BAR_VAR);
224
+ };
225
+
226
+ if (!this.chromeLayerActive()) {
227
+ this.clearGradientPaintVars(targets);
228
+ clearLayoutVars();
229
+ return;
230
+ }
231
+
232
+ if (!this.gradient) {
233
+ this.clearGradientPaintVars(targets);
234
+ clearLayoutVars();
235
+ return;
236
+ }
237
+
213
238
  const { width: shellWidth, height: shellHeight } = this.resolveShellDimensions();
214
239
  const panelWidth = this.resolvePanelWidthPx(panelNav);
215
240
 
@@ -218,11 +243,6 @@ export class AppShell {
218
243
 
219
244
  this.syncChromeLayoutVars(panelNav, bar, panelPosition, barPosition);
220
245
 
221
- if (!this.gradient) {
222
- this.clearGradientPaintVars(targets);
223
- return;
224
- }
225
-
226
246
  const image = this.gradientSrc.trim()
227
247
  ? `url(${this.gradientSrc.trim()})`
228
248
  : buildShellRadialGradient();
@@ -258,16 +278,18 @@ export class AppShell {
258
278
  }
259
279
 
260
280
  render() {
281
+ const chromeActive = this.chromeLayerActive();
261
282
  const shellCls: Record<string, boolean> = {
262
283
  'app-shell': true,
263
284
  'app-shell--gradient': this.gradient,
285
+ 'app-shell--grid': this.grid,
264
286
  [`app-shell--${this.navStyle}`]: true,
265
287
  };
266
288
 
267
289
  return (
268
290
  <Host class={shellCls}>
269
291
  <div class="app-shell__row">
270
- {this.gradient ? <div class="app-shell__chrome" aria-hidden="true" /> : null}
292
+ {chromeActive ? <div class="app-shell__chrome" aria-hidden="true" /> : null}
271
293
  <div class="app-shell__panel">
272
294
  <slot name="panel" />
273
295
  </div>
@@ -99,7 +99,7 @@ export namespace Components {
99
99
  }
100
100
  interface DsAppShell {
101
101
  /**
102
- * When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools.
102
+ * When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
103
103
  * @default false
104
104
  */
105
105
  "gradient": boolean;
@@ -108,6 +108,11 @@ export namespace Components {
108
108
  * @default ''
109
109
  */
110
110
  "gradientSrc": string;
111
+ /**
112
+ * When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
113
+ * @default false
114
+ */
115
+ "grid": boolean;
111
116
  /**
112
117
  * Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
113
118
  * @default 'dashboard'
@@ -1781,7 +1786,7 @@ declare namespace LocalJSX {
1781
1786
  }
1782
1787
  interface DsAppShell {
1783
1788
  /**
1784
- * When `true`, paints the shared chrome surface (bg + wash + grid) behind panel, bar, and tools.
1789
+ * When `true`, paints the radial wash behind panel, bar, and tools (synced to shell layout).
1785
1790
  * @default false
1786
1791
  */
1787
1792
  "gradient"?: boolean;
@@ -1790,6 +1795,11 @@ declare namespace LocalJSX {
1790
1795
  * @default ''
1791
1796
  */
1792
1797
  "gradientSrc"?: string;
1798
+ /**
1799
+ * When `true`, paints the diagonal grid overlay on the shared chrome layer. Independent of `gradient`.
1800
+ * @default false
1801
+ */
1802
+ "grid"?: boolean;
1793
1803
  /**
1794
1804
  * Chrome style propagated to slotted `ds-panel-nav` and `ds-bar-nav`.
1795
1805
  * @default 'dashboard'
@@ -2871,6 +2881,7 @@ declare namespace LocalJSX {
2871
2881
  interface DsAppShellAttributes {
2872
2882
  "navStyle": NavChromeStyle;
2873
2883
  "gradient": boolean;
2884
+ "grid": boolean;
2874
2885
  "gradientSrc": string;
2875
2886
  }
2876
2887
  interface DsBadgeAttributes {
@@ -33,6 +33,7 @@ export {
33
33
  shellGradientPositionPanel,
34
34
  shellGradientPositionBar,
35
35
  shellChromeSurfacePosition,
36
+ shellChromeLayerActive,
36
37
  } from './shell-gradient';
37
38
  export type { ShellGradientLayout } from './shell-gradient';
38
39
  export type { ChromeTransitionDetail, ChromeTransitionSource } from './chrome-transition';
@@ -10,6 +10,11 @@ export const SHELL_CHROME_SURFACE_POSITION_VAR = '--ds-shell-chrome-surface-posi
10
10
  /** Layer opacity for the nav gradient wash. */
11
11
  export const SHELL_GRADIENT_OPACITY = '0.1';
12
12
 
13
+ /** Whether the shared chrome layer (bg + optional wash/grid) should mount. */
14
+ export function shellChromeLayerActive(gradient: boolean, grid: boolean): boolean {
15
+ return gradient || grid;
16
+ }
17
+
13
18
  const GRADIENT_GEOMETRY = '100% 100% at 0% 0%';
14
19
 
15
20
  /**