@leef-l/macos-ui-kit 0.0.0-stage → 0.1.1

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.
Files changed (45) hide show
  1. package/README.md +215 -2
  2. package/dist/assets/Inter Font License.txt +93 -0
  3. package/dist/assets/Inter-Medium.ttf +0 -0
  4. package/dist/component.d.ts +47 -0
  5. package/dist/controls/badge.d.ts +27 -0
  6. package/dist/controls/button.d.ts +41 -0
  7. package/dist/controls/checkbox.d.ts +40 -0
  8. package/dist/controls/combobox.d.ts +105 -0
  9. package/dist/controls/datepicker.d.ts +42 -0
  10. package/dist/controls/disclosure.d.ts +40 -0
  11. package/dist/controls/numberfield.d.ts +60 -0
  12. package/dist/controls/progress.d.ts +33 -0
  13. package/dist/controls/segmented.d.ts +64 -0
  14. package/dist/controls/select.d.ts +42 -0
  15. package/dist/controls/slider.d.ts +49 -0
  16. package/dist/controls/spinner.d.ts +24 -0
  17. package/dist/controls/splitview.d.ts +76 -0
  18. package/dist/controls/switch.d.ts +47 -0
  19. package/dist/controls/table.d.ts +103 -0
  20. package/dist/controls/textarea.d.ts +49 -0
  21. package/dist/controls/textfield.d.ts +63 -0
  22. package/dist/dom.d.ts +35 -0
  23. package/dist/index.d.ts +85 -0
  24. package/dist/index.js +2053 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/layout/sidebar.d.ts +47 -0
  27. package/dist/layout/statusbar.d.ts +30 -0
  28. package/dist/layout/tabview.d.ts +74 -0
  29. package/dist/layout/titlebar.d.ts +49 -0
  30. package/dist/layout/toolbar.d.ts +54 -0
  31. package/dist/layout/traffic-lights.d.ts +37 -0
  32. package/dist/macos-ui-kit.css +2 -0
  33. package/dist/macos-ui-kit.iife.js +2 -0
  34. package/dist/macos-ui-kit.iife.js.map +1 -0
  35. package/dist/mobile/shell.d.ts +59 -0
  36. package/dist/overlay.d.ts +41 -0
  37. package/dist/overlays/dialog.d.ts +46 -0
  38. package/dist/overlays/menu.d.ts +58 -0
  39. package/dist/overlays/popover.d.ts +90 -0
  40. package/dist/overlays/sheet.d.ts +56 -0
  41. package/dist/overlays/toast.d.ts +37 -0
  42. package/dist/overlays/tooltip.d.ts +48 -0
  43. package/dist/style.css.d.ts +2 -0
  44. package/dist/theme.d.ts +100 -0
  45. package/package.json +55 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos-ui-kit.iife.js","names":["EXIT_MS","exports","Component","target","type","handler","options","cleanup","name","on","value","parent","error","el","children","node","tag","key","attrs","append","child","clear","svgIcon","ns","svg","viewBox","size","path","rafThrottle","queued","fn","idCounter","uid","prefix","OPPOSITE","positionOverlay","anchorRect","anchor","matchWidth","panelRect","panel","viewportWidth","viewportHeight","side","align","placement","resolvedSide","roomBelow","offset","roomAbove","roomRight","roomLeft","x","y","padding","trackOverlay","update","zIndexCounter","nextZIndex","FOCUSABLE","focusableWithin","container","trapFocus","previous","onKeyDown","event","focusable","first","last","active","BUILTIN_THEMES","DECLARED_THEMES","cssVar","ThemeManager","root","theme","listener","computed","tokens","bare","themeManager","setDensity","density","getDensity","Button","classes","variant","block","icon","iconWrap","label","accessibleName","ariaLabel","onClick","text","disabled","primary","TextField","onInput","onChange","inputId","labelNode","control","clearable","button","force","NumberField","min","max","step","stepper","next","raw","parsed","base","direction","off","atMin","atMax","Slider","showValue","span","ratio","percent","Progress","indeterminate","Badge","Switch","checked","Checkbox","box","tick","dash","SegmentedControl","option","selected","enabled","b","index","observer","match","notify","animate","inset","Select","fragment","fallback","Spinner","TextArea","maxLength","id","controlWrap","meta","length","CHEVRON","Disclosure","open","onToggle","chevron","title","content","defaultRowKey","row","alignClass","Table","scroll","table","head","live","rows","group","column","col","cell","check","tr","total","mixed","KEYBOARD_STEP","SplitView","horizontal","initial","delta","decrease","increase","available","rect","now","ComboBox","needle","custom","count","CALENDAR_PATH","DatePicker","TrafficLights","kind","className","p","d","a","zoomed","focused","TitleBar","leadingCluster","trafficLights","leading","trailingCluster","trailing","onDoubleClick","lastPress","lastX","lastY","lastFire","fire","near","Sidebar","groupNode","item","isMatch","collapsed","StatusBar","state","Toolbar","position","bordered","center","TabView","tab","tabId","panelId","entry","current","CHECK_PATH","Menu","selectable","currentPos","nextPos","i","showMenu","menu","Tooltip","trigger","bind","Dialog","onConfirm","onCancel","titleId","titleNode","bodyNode","body","actions","cancelButton","cancelLabel","confirmButton","confirmLabel","EXIT_MS$1","Popover","onClose","arrow","boxLeft","boxTop","boxWidth","boxHeight","half","centre","EXIT_MS","Sheet","\"class\"","stack","ensureStack","Toast","message","detail","duration","showToast","toast","MobileShell","tabs","onTabChange","navbar","action","slot","tabBar","looksLikeMobile","coarse","narrow"],"sources":["../src/component.ts","../src/dom.ts","../src/overlay.ts","../src/theme.ts","../src/controls/button.ts","../src/controls/textfield.ts","../src/controls/numberfield.ts","../src/controls/slider.ts","../src/controls/progress.ts","../src/controls/badge.ts","../src/controls/switch.ts","../src/controls/checkbox.ts","../src/controls/segmented.ts","../src/controls/select.ts","../src/controls/spinner.ts","../src/controls/textarea.ts","../src/controls/disclosure.ts","../src/controls/table.ts","../src/controls/splitview.ts","../src/controls/combobox.ts","../src/controls/datepicker.ts","../src/layout/traffic-lights.ts","../src/layout/titlebar.ts","../src/layout/sidebar.ts","../src/layout/statusbar.ts","../src/layout/toolbar.ts","../src/layout/tabview.ts","../src/overlays/menu.ts","../src/overlays/tooltip.ts","../src/overlays/dialog.ts","../src/overlays/popover.ts","../src/overlays/sheet.ts","../src/overlays/toast.ts","../src/mobile/shell.ts"],"sourcesContent":["/**\n * Base class for every macOS UI Kit component.\n *\n * It owns two things that raw DOM code gets wrong most often:\n *\n * 1. **Listener lifetime.** Vanilla UI leaks because listeners outlive the\n * elements they touch. `listen` records every binding so `destroy()` can\n * remove all of them in one pass.\n * 2. **A stable root element.** Subclasses build their markup in the\n * constructor; callers only ever see `element`, which keeps the internal DOM\n * structure free to change.\n *\n * Deliberately light DOM: shadow roots would retarget `event.target`, and\n * Wails' window-drag detection reads exactly that property\n * (`internal/runtime/.../drag.ts` uses `event.target`, never `composedPath`).\n */\nexport abstract class Component<T extends HTMLElement = HTMLElement> {\n /** The root element. Subclasses fill this in before the constructor returns. */\n protected abstract readonly root: T;\n\n private readonly cleanups: Array<() => void> = [];\n private destroyed = false;\n\n /** The component's root element, for mounting into the page. */\n get element(): T {\n return this.root;\n }\n\n /** True once `destroy()` has run. */\n get isDestroyed(): boolean {\n return this.destroyed;\n }\n\n /**\n * Add an event listener whose removal `destroy()` handles.\n *\n * Prefer this over `addEventListener` everywhere: a component that is torn\n * down mid-session (a closed dialog, a re-rendered list) must not leave\n * handlers pointing at detached nodes.\n */\n protected listen<K extends keyof HTMLElementEventMap>(\n target: T | HTMLElement | Document | Window,\n type: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n ): void;\n protected listen(\n target: T | HTMLElement | Document | Window,\n type: string,\n handler: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void;\n protected listen(\n target: T | HTMLElement | Document | Window,\n type: string,\n handler: EventListenerOrEventListenerObject,\n options?: AddEventListenerOptions,\n ): void {\n target.addEventListener(type, handler, options);\n this.cleanups.push(() => target.removeEventListener(type, handler, options));\n }\n\n /** Register an arbitrary teardown step (timers, observers, subscriptions). */\n protected onDestroy(cleanup: () => void): void {\n this.cleanups.push(cleanup);\n }\n\n /** Reflect a boolean state as a class on the root, skipping no-op writes. */\n protected toggleClass(name: string, on: boolean): void {\n if (this.root.classList.contains(name) === on) return;\n this.root.classList.toggle(name, on);\n }\n\n /** Set an attribute only when its value actually changes. */\n protected setAttr(name: string, value: string): void {\n if (this.root.getAttribute(name) === value) return;\n this.root.setAttribute(name, value);\n }\n\n /** Mount the component into a parent, replacing that parent's content. */\n mount(parent: Element = document.body): this {\n parent.replaceChildren(this.root);\n return this;\n }\n\n /** Remove the component from the document and release everything it holds. */\n remove(): void {\n this.root.remove();\n this.destroy();\n }\n\n /** Tear down listeners and timers. Safe to call more than once. */\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n for (const cleanup of this.cleanups.splice(0)) {\n try {\n cleanup();\n } catch (error) {\n console.error(\"mac ui: cleanup failed\", error);\n }\n }\n }\n}\n","/**\n * Small DOM helpers shared by every macOS UI Kit component.\n *\n * The kit builds elements imperatively instead of shipping a framework: a\n * desktop window creates its UI once and then updates small pieces of it, which\n * is exactly the workload where direct DOM work is fastest and a framework\n * runtime is pure overhead.\n */\n\n/** Anything that can be placed inside an element. */\nexport type Child = Node | string | number | null | undefined | false;\n\n/**\n * Create an element with attributes and children in one call.\n *\n * Attributes are assigned as properties when the name is a known property on\n * the element (so `value`, `checked`, `disabled` behave correctly) and as\n * attributes otherwise. `class`, `dataset`, `style`, `on*` handlers and\n * `textContent` get special treatment.\n */\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs: Attrs = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n\n for (const [key, value] of Object.entries(attrs)) {\n if (value === undefined || value === null || value === false) continue;\n\n if (key === \"class\" || key === \"className\") {\n node.className = String(value);\n } else if (key === \"dataset\") {\n Object.assign(node.dataset, value as Record<string, string>);\n } else if (key === \"style\") {\n Object.assign(node.style, value as Partial<CSSStyleDeclaration>);\n } else if (key === \"text\") {\n node.textContent = String(value);\n } else if (key === \"html\") {\n node.innerHTML = String(value);\n } else if (key.startsWith(\"on\") && typeof value === \"function\") {\n node.addEventListener(key.slice(2).toLowerCase(), value as EventListener);\n } else if (key in node) {\n (node as unknown as Record<string, unknown>)[key] = value;\n } else {\n node.setAttribute(key, String(value));\n }\n }\n\n append(node, children);\n return node;\n}\n\nexport type Attrs = Record<string, unknown>;\n\n/** Append children, skipping null/undefined/false and stringifying numbers. */\nexport function append(parent: Element, children: Child[]): void {\n for (const child of children) {\n if (child === null || child === undefined || child === false) continue;\n parent.appendChild(\n typeof child === \"string\" || typeof child === \"number\"\n ? document.createTextNode(String(child))\n : child,\n );\n }\n}\n\n/** Remove every child of a node. */\nexport function clear(node: Element): void {\n while (node.firstChild) node.removeChild(node.firstChild);\n}\n\n/** Create an SVG element from a path definition, sized for a control icon. */\nexport function svgIcon(path: string, size = 16, viewBox = \"0 0 24 24\"): SVGSVGElement {\n const ns = \"http://www.w3.org/2000/svg\";\n const svg = document.createElementNS(ns, \"svg\");\n svg.setAttribute(\"viewBox\", viewBox);\n svg.setAttribute(\"width\", String(size));\n svg.setAttribute(\"height\", String(size));\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"1.8\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n\n const el = document.createElementNS(ns, \"path\");\n el.setAttribute(\"d\", path);\n svg.appendChild(el);\n return svg;\n}\n\n/**\n * Run `fn` on the next animation frame, coalescing repeated calls.\n *\n * Scroll and resize handlers use this so a burst of events produces one layout\n * read instead of dozens.\n */\nexport function rafThrottle(fn: () => void): () => void {\n let queued = false;\n return () => {\n if (queued) return;\n queued = true;\n requestAnimationFrame(() => {\n queued = false;\n fn();\n });\n };\n}\n\nlet idCounter = 0;\n\n/** Generate a unique id, used to link labels to controls. */\nexport function uid(prefix = \"mac\"): string {\n idCounter += 1;\n return `${prefix}-${idCounter}`;\n}\n","/**\n * Overlay plumbing shared by Menu, Tooltip and Dialog.\n *\n * Positioning is the part every hand-written popover gets wrong: it clamps in\n * one direction, forgets the scroll offset, or escapes the viewport when the\n * anchor sits near an edge. This module resolves a placement against the\n * viewport, flips when there is no room, and re-resolves on scroll and resize\n * (via rAF so a burst of events costs one layout pass).\n */\n\nimport {rafThrottle} from \"./dom\";\n\nexport type Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"right\";\n\nexport interface PositionOptions {\n /** Element the overlay points at. */\n anchor: HTMLElement;\n /** Overlay to place. Must already be in the document. */\n panel: HTMLElement;\n /** Preferred side; flips automatically when there is no room. */\n placement?: Placement;\n /** Gap between the anchor and the panel, in px. */\n offset?: number;\n /** Keep this much space from the viewport edges. */\n padding?: number;\n /** Align the panel to the anchor's width (menus attached to fields). */\n matchWidth?: boolean;\n}\n\nconst OPPOSITE: Record<string, Placement> = {\n top: \"bottom\",\n bottom: \"top\",\n left: \"right\",\n right: \"left\",\n};\n\n/** Resolve coordinates for a placement and write them to the panel. */\nexport function positionOverlay(options: PositionOptions): void {\n const {\n anchor,\n panel,\n placement = \"bottom-start\",\n offset = 6,\n padding = 8,\n matchWidth = false,\n } = options;\n\n const anchorRect = anchor.getBoundingClientRect();\n\n // The panel must be measurable: make it visible and laid out before\n // reading its size, or a `display:none` panel measures as 0x0.\n panel.style.position = \"fixed\";\n panel.style.visibility = \"hidden\";\n panel.style.display = \"block\";\n\n if (matchWidth) {\n panel.style.minWidth = `${Math.round(anchorRect.width)}px`;\n }\n\n const panelRect = panel.getBoundingClientRect();\n const viewportWidth = document.documentElement.clientWidth;\n const viewportHeight = document.documentElement.clientHeight;\n\n const [side, align] = placement.split(\"-\") as [string, string?];\n\n // Flip to the opposite side when the preferred one does not fit.\n let resolvedSide = side;\n const roomBelow = viewportHeight - anchorRect.bottom - offset;\n const roomAbove = anchorRect.top - offset;\n const roomRight = viewportWidth - anchorRect.right - offset;\n const roomLeft = anchorRect.left - offset;\n\n if (side === \"bottom\" && panelRect.height > roomBelow && roomAbove > roomBelow) {\n resolvedSide = \"top\";\n } else if (side === \"top\" && panelRect.height > roomAbove && roomBelow > roomAbove) {\n resolvedSide = \"bottom\";\n } else if (side === \"right\" && panelRect.width > roomRight && roomLeft > roomRight) {\n resolvedSide = \"left\";\n } else if (side === \"left\" && panelRect.width > roomLeft && roomRight > roomLeft) {\n resolvedSide = \"right\";\n } else if (side in OPPOSITE && panelRect.height > roomBelow && side === \"bottom\") {\n // Kept explicit for readability: bottom with no room picks top.\n resolvedSide = \"top\";\n }\n\n let x: number;\n let y: number;\n\n if (resolvedSide === \"top\" || resolvedSide === \"bottom\") {\n y =\n resolvedSide === \"bottom\"\n ? anchorRect.bottom + offset\n : anchorRect.top - panelRect.height - offset;\n\n if (align === \"start\") {\n x = anchorRect.left;\n } else if (align === \"end\") {\n x = anchorRect.right - panelRect.width;\n } else {\n x = anchorRect.left + (anchorRect.width - panelRect.width) / 2;\n }\n } else {\n x =\n resolvedSide === \"right\"\n ? anchorRect.right + offset\n : anchorRect.left - panelRect.width - offset;\n\n if (align === \"start\") {\n y = anchorRect.top;\n } else if (align === \"end\") {\n y = anchorRect.bottom - panelRect.height;\n } else {\n y = anchorRect.top + (anchorRect.height - panelRect.height) / 2;\n }\n }\n\n // Clamp into the viewport so a panel near an edge stays fully visible.\n x = Math.max(padding, Math.min(x, viewportWidth - panelRect.width - padding));\n y = Math.max(padding, Math.min(y, viewportHeight - panelRect.height - padding));\n\n panel.style.left = `${Math.round(x)}px`;\n panel.style.top = `${Math.round(y)}px`;\n panel.style.visibility = \"\";\n panel.dataset.side = resolvedSide;\n}\n\n/**\n * Keep an overlay placed while the page changes.\n *\n * Returns a disposer. Callers must invoke it when the overlay closes;\n * otherwise the scroll listener outlives the panel.\n */\nexport function trackOverlay(options: PositionOptions): () => void {\n const update = rafThrottle(() => positionOverlay(options));\n update();\n\n window.addEventListener(\"scroll\", update, true);\n window.addEventListener(\"resize\", update);\n\n return () => {\n window.removeEventListener(\"scroll\", update, true);\n window.removeEventListener(\"resize\", update);\n };\n}\n\n/**\n * Stacking order for open overlays.\n *\n * Nested popovers (a menu opened from a dialog) must sit above their parent, so\n * every open overlay takes the next z-index instead of all sharing one.\n */\nlet zIndexCounter = 1000;\n\nexport function nextZIndex(): number {\n zIndexCounter += 1;\n return zIndexCounter;\n}\n\n/** Elements that can receive keyboard focus. */\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), ' +\n 'textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** All focusable descendants of a container, in DOM order. */\nexport function focusableWithin(container: HTMLElement): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (node) => node.offsetParent !== null || node === document.activeElement,\n );\n}\n\n/**\n * Trap Tab focus inside a container (dialogs).\n * Returns a disposer that also restores the previously focused element.\n */\nexport function trapFocus(container: HTMLElement): () => void {\n const previous = document.activeElement as HTMLElement | null;\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") return;\n const focusable = focusableWithin(container);\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && (active === first || !container.contains(active))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n };\n\n container.addEventListener(\"keydown\", onKeyDown);\n\n // Move focus in on the next frame so the element is already laid out.\n requestAnimationFrame(() => {\n const focusable = focusableWithin(container);\n (focusable[0] ?? container).focus();\n });\n\n return () => {\n container.removeEventListener(\"keydown\", onKeyDown);\n previous?.focus?.();\n };\n}\n","/**\n * Runtime theme system.\n *\n * Two-layer tokens (see tokens.css) split the problem in half:\n *\n * - The built-in `macos-dark` / `macos-light` appearances are **pure CSS**. They\n * apply through `[data-appearance]` with no JavaScript at all, so the first\n * paint is already correct — a theme applied by script would flash the wrong\n * colours for one frame.\n *\n * - Everything else is a *custom* theme: an appearance plus a handful of\n * semantic-token overrides. Overrides land as inline custom properties on\n * <html>, which beat the stylesheet, so a brand theme ships 3 values instead\n * of 60.\n *\n * The rule that keeps this honest: custom themes may only override **semantic**\n * tokens (`accent`, `bg-window`, …), never base ones (`base-gray-800`). Base\n * tokens are shared by both appearances; overriding one would silently damage\n * the other.\n */\n\nexport type Appearance = \"light\" | \"dark\";\n\nexport interface ThemeDefinition {\n /** Unique name, written to `data-theme`. */\n name: string;\n /** Which built-in appearance the theme starts from. */\n appearance: Appearance;\n /**\n * Semantic-token overrides. Keys are written **without** the `--mac-`\n * prefix for brevity, e.g. `{accent: \"#ff6b35\"}` not\n * `{\"--mac-accent\": \"#ff6b35\"}`.\n */\n tokens?: Record<string, string>;\n}\n\n/** The built-in appearances. No tokens: their values live in tokens.css. */\nexport const BUILTIN_THEMES: ThemeDefinition[] = [\n {name: \"macos-dark\", appearance: \"dark\"},\n {name: \"macos-light\", appearance: \"light\"},\n {\n name: \"hc-dark\",\n appearance: \"dark\",\n // High contrast is defined declaratively in tokens.css as well; the\n // entry here only makes it discoverable and switchable by name.\n },\n {\n name: \"hc-light\",\n appearance: \"light\",\n },\n];\n\n/** Tokens written to `data-theme` in CSS, kept in step with tokens.css. */\nconst DECLARED_THEMES = new Set([\"hc-dark\", \"hc-light\"]);\n\n/** Prefix a bare token name with the custom-property namespace. */\nfunction cssVar(name: string): string {\n return name.startsWith(\"--\") ? name : `--mac-${name}`;\n}\n\n/**\n * Owns the appearance on <html> and any custom-token overrides.\n *\n * One instance per document; `themeManager` below is the shared one.\n */\nexport class ThemeManager {\n private readonly root: HTMLElement;\n private readonly themes = new Map<string, ThemeDefinition>();\n /** Custom properties this manager wrote, so they can be removed cleanly. */\n private applied: string[] = [];\n private current: ThemeDefinition;\n private mediaQuery: MediaQueryList | null = null;\n private systemListener: (() => void) | null = null;\n private listeners = new Set<(theme: ThemeDefinition) => void>();\n\n constructor(root: HTMLElement = document.documentElement) {\n this.root = root;\n\n for (const theme of BUILTIN_THEMES) {\n this.themes.set(theme.name, theme);\n }\n\n this.current = this.themes.get(\"macos-dark\")!;\n\n // Follow the OS until the host applies an explicit theme.\n this.followSystem();\n }\n\n /** Register a custom theme. Throws on an empty or duplicate name. */\n register(theme: ThemeDefinition): void {\n if (!theme.name) throw new Error(\"mac ui: theme needs a name\");\n if (theme.appearance !== \"light\" && theme.appearance !== \"dark\") {\n throw new Error(`mac ui: theme \"${theme.name}\" has an invalid appearance`);\n }\n this.themes.set(theme.name, theme);\n }\n\n /** Remove a custom theme. Built-in appearances cannot be removed. */\n unregister(name: string): void {\n if (BUILTIN_THEMES.some((theme) => theme.name === name)) {\n throw new Error(`mac ui: cannot remove the built-in theme \"${name}\"`);\n }\n this.themes.delete(name);\n if (this.current.name === name) this.apply(\"macos-dark\");\n }\n\n /** Names of every registered theme. */\n list(): string[] {\n return [...this.themes.keys()];\n }\n\n /** The theme currently applied. */\n get active(): ThemeDefinition {\n return this.current;\n }\n\n /**\n * Apply a theme by name.\n *\n * Applies the appearance first (pure CSS takes over), then the token\n * overrides. Re-applying the same theme is a no-op when nothing changed.\n */\n apply(name: string): void {\n const theme = this.themes.get(name);\n if (!theme) {\n console.error(`mac ui: unknown theme \"${name}\"`);\n return;\n }\n\n this.stopFollowingSystem();\n this.applyDefinition(theme);\n }\n\n /**\n * Follow the OS appearance: dark when the system is dark, light otherwise.\n * Re-applies automatically when the user flips the system setting.\n */\n followSystem(): void {\n this.stopFollowingSystem();\n\n this.mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const update = () => {\n const name = this.mediaQuery?.matches ? \"macos-dark\" : \"macos-light\";\n const theme = this.themes.get(name);\n if (theme) this.applyDefinition(theme);\n };\n\n this.systemListener = update;\n this.mediaQuery.addEventListener(\"change\", update);\n update();\n }\n\n /** True while the OS appearance drives the theme. */\n get isFollowingSystem(): boolean {\n return this.systemListener !== null;\n }\n\n /** Subscribe to theme changes. Returns an unsubscribe function. */\n onChange(listener: (theme: ThemeDefinition) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Export the active theme as a definition.\n *\n * Reads the computed custom properties, so it captures values that came from\n * the stylesheet as well as inline overrides — useful for \"save this look\".\n */\n exportActive(): ThemeDefinition {\n const computed = getComputedStyle(this.root);\n const tokens: Record<string, string> = {};\n for (const name of this.applied) {\n const bare = name.replace(/^--mac-/, \"\");\n const value = computed.getPropertyValue(name).trim();\n if (value) tokens[bare] = value;\n }\n return {...this.current, tokens: {...this.current.tokens, ...tokens}};\n }\n\n /** Drop listeners. Call when tearing the app down. */\n destroy(): void {\n this.stopFollowingSystem();\n this.clearOverrides();\n this.listeners.clear();\n }\n\n // --- internals ---------------------------------------------------------\n\n private applyDefinition(theme: ThemeDefinition): void {\n this.clearOverrides();\n\n this.root.setAttribute(\"data-appearance\", theme.appearance);\n this.root.setAttribute(\"data-theme\", theme.name);\n\n if (DECLARED_THEMES.has(theme.name)) {\n // These themes are fully expressed in CSS; nothing to inject.\n } else if (theme.tokens) {\n for (const [key, value] of Object.entries(theme.tokens)) {\n const name = cssVar(key);\n this.root.style.setProperty(name, value);\n this.applied.push(name);\n }\n }\n\n this.current = theme;\n for (const listener of this.listeners) {\n try {\n listener(theme);\n } catch (error) {\n console.error(\"mac ui: theme listener failed\", error);\n }\n }\n }\n\n /** Remove every inline custom property this manager wrote. */\n private clearOverrides(): void {\n for (const name of this.applied.splice(0)) {\n this.root.style.removeProperty(name);\n }\n }\n\n private stopFollowingSystem(): void {\n if (this.mediaQuery && this.systemListener) {\n this.mediaQuery.removeEventListener(\"change\", this.systemListener);\n }\n this.mediaQuery = null;\n this.systemListener = null;\n }\n}\n\n/** The application-wide theme manager. */\nexport const themeManager = new ThemeManager();\n\n/**\n * Set the density preset (\"compact\" | \"comfortable\" | \"touch\").\n *\n * Density is pure CSS: it only moves `--mac-scale`, which every scalable token\n * multiplies. Removing the attribute hands control back to the media query that\n * enlarges controls on touch devices.\n */\nexport function setDensity(density: \"compact\" | \"comfortable\" | \"touch\" | null): void {\n if (density === null) {\n document.documentElement.removeAttribute(\"data-density\");\n return;\n }\n document.documentElement.setAttribute(\"data-density\", density);\n}\n\n/** The density currently in effect, including the automatic touch default. */\nexport function getDensity(): string {\n const explicit = document.documentElement.getAttribute(\"data-density\");\n if (explicit) return explicit;\n return window.matchMedia(\"(pointer: coarse)\").matches ? \"touch\" : \"comfortable\";\n}\n","import {Component} from \"../component\";\nimport {append, el, svgIcon, type Child} from \"../dom\";\nimport \"./button.css\";\n\nexport type ButtonVariant = \"default\" | \"primary\" | \"destructive\" | \"plain\";\nexport type ButtonSize = \"small\" | \"default\" | \"large\";\n\nexport interface ButtonOptions {\n /** Visible label. Omit for an icon-only button (then set `ariaLabel`). */\n label?: Child;\n /** Leading SVG path data, drawn at the control's icon size. */\n icon?: string;\n variant?: ButtonVariant;\n size?: ButtonSize;\n /** Accessible name; required when there is no visible label. */\n ariaLabel?: string;\n /** Stretch to the container's width. */\n block?: boolean;\n disabled?: boolean;\n /** Native `type`, so a button inside a form does not submit by accident. */\n type?: \"button\" | \"submit\" | \"reset\";\n /** Fires on activation (click, Enter, Space — the browser handles the rest). */\n onClick?: (event: MouseEvent) => void;\n}\n\n/**\n * A native `<button>` with macOS styling.\n *\n * Built on the real element rather than a `div[role=button]` so keyboard\n * activation, disabled handling and focus order come from the browser.\n */\nexport class Button extends Component<HTMLButtonElement> {\n protected readonly root: HTMLButtonElement;\n\n private readonly labelNode: HTMLSpanElement | null = null;\n\n constructor(options: ButtonOptions = {}) {\n super();\n\n const {\n label,\n icon,\n variant = \"default\",\n size = \"default\",\n ariaLabel,\n block = false,\n disabled = false,\n type = \"button\",\n onClick,\n } = options;\n\n const classes = [\"mac-button\"];\n if (variant !== \"default\") classes.push(`mac-button--${variant}`);\n if (size !== \"default\") classes.push(`mac-button--${size}`);\n if (block) classes.push(\"mac-button--block\");\n\n this.root = el(\"button\", {\n class: classes.join(\" \"),\n type,\n disabled,\n });\n\n if (icon) {\n const iconWrap = el(\"span\", {class: \"mac-button__icon\"});\n iconWrap.appendChild(svgIcon(icon, 14));\n this.root.appendChild(iconWrap);\n }\n\n if (label !== undefined) {\n this.labelNode = el(\"span\", {class: \"mac-button__label\"});\n append(this.labelNode, [label]);\n this.root.appendChild(this.labelNode);\n }\n\n // Accessibility: a button without a visible label still needs a name.\n const accessibleName =\n ariaLabel ?? (typeof label === \"string\" ? label : undefined);\n if (accessibleName) {\n this.root.setAttribute(\"aria-label\", accessibleName);\n }\n\n if (onClick) {\n this.listen(this.root, \"click\", onClick);\n }\n }\n\n /** Replace the visible label text. */\n setLabel(text: string): void {\n if (this.labelNode) {\n this.labelNode.textContent = text;\n }\n }\n\n /** Enable or disable the button. */\n setDisabled(disabled: boolean): void {\n if (this.root.disabled === disabled) return;\n this.root.disabled = disabled;\n }\n\n /** Toggle the primary (blue) treatment at runtime. */\n setPrimary(primary: boolean): void {\n this.root.classList.toggle(\"mac-button--primary\", primary);\n }\n\n /** Give the caller a way to move focus programmatically. */\n focus(): void {\n this.root.focus();\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, svgIcon, uid, type Child} from \"../dom\";\nimport \"./textfield.css\";\n\nexport interface TextFieldOptions {\n /** Label rendered above the field; it is wired to the input through `for`/`id`. */\n label?: Child;\n /** Placeholder shown while the field is empty. */\n placeholder?: string;\n /** Leading SVG path data, drawn at the control's icon size. */\n icon?: string;\n /** Show a clear button at the trailing edge once the field holds text. */\n clearable?: boolean;\n disabled?: boolean;\n readOnly?: boolean;\n /** Initial value. */\n value?: string;\n /** Fires on every keystroke with the current value. */\n onInput?: (value: string) => void;\n /**\n * Fires when the value is committed: blur, Enter, or the clear button.\n * Typing alone only raises `onInput`.\n */\n onChange?: (value: string) => void;\n}\n\n/**\n * A single-line text field on a native `<input type=\"text\">`.\n *\n * The input is the real element rather than a styled `div`, so the caret,\n * selection, IME composition and `readOnly` come from the browser. The label\n * gets a generated id and is linked with `for`/`id`, so clicking it focuses the\n * field without a line of JS.\n *\n * `onInput` reports every keystroke; `onChange` reports committed values only,\n * which is what the DOM's own `change` event means.\n */\nexport class TextField extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly input: HTMLInputElement;\n private readonly clearButton: HTMLButtonElement | null = null;\n private readonly onInput?: (value: string) => void;\n private readonly onChange?: (value: string) => void;\n /** True while the value has changed since the last commit. */\n private dirty = false;\n\n constructor(options: TextFieldOptions = {}) {\n super();\n\n const {\n label,\n placeholder = \"\",\n icon,\n clearable = false,\n disabled = false,\n readOnly = false,\n value = \"\",\n onInput,\n onChange,\n } = options;\n\n this.onInput = onInput;\n this.onChange = onChange;\n\n this.root = el(\"div\", {class: \"mac-textfield\"});\n\n const inputId = uid(\"mac-textfield\");\n\n if (label !== undefined) {\n const labelNode = el(\"label\", {\n class: \"mac-textfield__label\",\n for: inputId,\n });\n append(labelNode, [label]);\n this.root.appendChild(labelNode);\n }\n\n const control = el(\"div\", {class: \"mac-textfield__control\"});\n\n if (icon) {\n const iconWrap = el(\"span\", {class: \"mac-textfield__icon\"});\n iconWrap.appendChild(svgIcon(icon, 14));\n control.appendChild(iconWrap);\n }\n\n this.input = el(\"input\", {\n class: \"mac-textfield__input\",\n type: \"text\",\n id: inputId,\n placeholder,\n value,\n disabled,\n readOnly,\n autocomplete: \"off\",\n });\n\n // `el` skips falsy values, so the spellchecker is switched off here.\n this.input.spellcheck = false;\n\n control.appendChild(this.input);\n\n if (clearable) {\n this.clearButton = el(\"button\", {\n class: \"mac-textfield__clear\",\n type: \"button\",\n tabindex: -1,\n \"aria-label\": \"清除\",\n title: \"清除\",\n });\n this.clearButton.appendChild(svgIcon(\"M6 6 L12 12 M12 6 L6 12\", 10));\n control.appendChild(this.clearButton);\n }\n\n this.root.appendChild(control);\n\n this.listen(this.input, \"input\", () => {\n this.dirty = true;\n this.syncClearState();\n this.onInput?.(this.input.value);\n });\n\n this.listen(this.input, \"change\", () => {\n this.commit();\n });\n\n // Enter commits, the way submitting a form would.\n this.listen(this.input, \"keydown\", (event) => {\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n this.commit();\n });\n\n if (this.clearButton) {\n const button = this.clearButton;\n this.listen(button, \"click\", () => {\n this.input.value = \"\";\n this.syncClearState();\n this.onInput?.(\"\");\n this.commit(true);\n this.input.focus();\n });\n }\n\n this.syncClearState();\n }\n\n /** The current value. */\n get value(): string {\n return this.input.value;\n }\n\n /** Replace the value; does not raise the callbacks, as a DOM write would not. */\n setValue(value: string): void {\n if (this.input.value === value) return;\n this.input.value = value;\n this.dirty = false;\n this.syncClearState();\n }\n\n /** Enable or disable the input, using the native attribute. */\n setDisabled(disabled: boolean): void {\n if (this.input.disabled === disabled) return;\n this.input.disabled = disabled;\n this.toggleClass(\"is-disabled\", disabled);\n }\n\n /** Move keyboard focus into the input. */\n focus(): void {\n this.input.focus();\n }\n\n /**\n * Report the value once per edit.\n *\n * Enter can be followed by a native `change` on blur, so a commit only fires\n * while the field is dirty; `force` is for the clear button, which must raise\n * `onChange` even when nothing was typed.\n */\n private commit(force = false): void {\n if (!force && !this.dirty) return;\n this.dirty = false;\n this.onChange?.(this.input.value);\n }\n\n /** The clear button only appears when there is something to clear. */\n private syncClearState(): void {\n if (!this.clearButton) return;\n this.clearButton.hidden = this.input.value === \"\";\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, svgIcon, uid, type Child} from \"../dom\";\nimport \"./numberfield.css\";\n\nexport interface NumberFieldOptions {\n /** Label rendered above the field; it is wired to the input through `for`/`id`. */\n label?: Child;\n /** Smallest accepted value; also bounds the native spinner. */\n min?: number;\n /** Largest accepted value; also bounds the native spinner. */\n max?: number;\n /** Increment used by the stepper buttons and the arrow keys. Default 1. */\n step?: number;\n /** Initial value; defaults to `min`, then 0. */\n value?: number;\n /**\n * Fires only for a legal value — a finite number inside `min`/`max`.\n * A blank or out-of-range entry stays in the field but is not committed.\n */\n onChange?: (value: number) => void;\n}\n\n/**\n * A numeric field on a native `<input type=\"number\">`.\n *\n * Validation runs on every `input` event: a blank, non-numeric or out-of-range\n * entry is left exactly as typed but never committed, because handing the\n * caller a number the field itself rejects would put its state at odds with\n * what is on screen. `getValue()` therefore reports the last committed value,\n * never a half-typed one.\n *\n * The native spinner cannot be themed, so it is hidden and replaced by two\n * chevron buttons that step by `step` and disable themselves at the bounds.\n */\nexport class NumberField extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly input: HTMLInputElement;\n private readonly stepUp: HTMLButtonElement;\n private readonly stepDown: HTMLButtonElement;\n private readonly min?: number;\n private readonly max?: number;\n private readonly step?: number;\n private readonly onChange?: (value: number) => void;\n\n private committed: number;\n\n constructor(options: NumberFieldOptions = {}) {\n super();\n\n const {label, min, max, step, value, onChange} = options;\n\n this.min = min;\n this.max = max;\n this.step = step;\n this.onChange = onChange;\n this.committed = this.clamp(value ?? min ?? 0);\n\n this.root = el(\"div\", {class: \"mac-numberfield\"});\n\n const inputId = uid(\"mac-numberfield\");\n\n if (label !== undefined) {\n const labelNode = el(\"label\", {\n class: \"mac-numberfield__label\",\n for: inputId,\n });\n append(labelNode, [label]);\n this.root.appendChild(labelNode);\n }\n\n const control = el(\"div\", {class: \"mac-numberfield__control\"});\n\n this.input = el(\"input\", {\n class: \"mac-numberfield__input\",\n type: \"number\",\n id: inputId,\n min,\n max,\n step,\n autocomplete: \"off\",\n inputmode: \"decimal\",\n });\n this.input.value = String(this.committed);\n\n const stepper = el(\"div\", {class: \"mac-numberfield__stepper\"});\n\n this.stepUp = el(\"button\", {\n class: \"mac-numberfield__step mac-numberfield__step--up\",\n type: \"button\",\n tabindex: -1,\n \"aria-label\": \"增加\",\n title: \"增加\",\n });\n this.stepUp.appendChild(svgIcon(\"M6 15 L12 8 L18 15\", 10));\n\n this.stepDown = el(\"button\", {\n class: \"mac-numberfield__step mac-numberfield__step--down\",\n type: \"button\",\n tabindex: -1,\n \"aria-label\": \"减少\",\n title: \"减少\",\n });\n this.stepDown.appendChild(svgIcon(\"M6 9 L12 16 L18 9\", 10));\n\n stepper.append(this.stepUp, this.stepDown);\n control.append(this.input, stepper);\n this.root.appendChild(control);\n\n this.listen(this.input, \"input\", () => this.handleInput());\n\n this.listen(this.stepUp, \"click\", () => this.nudge(1));\n this.listen(this.stepDown, \"click\", () => this.nudge(-1));\n\n // Arrow keys step the field, as a macOS stepper does.\n this.listen(this.input, \"keydown\", (event) => {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") return;\n event.preventDefault();\n this.nudge(event.key === \"ArrowUp\" ? 1 : -1);\n });\n\n this.syncStepperState();\n }\n\n /** The last committed value. A blank or illegal entry is not reported. */\n get value(): number {\n return this.committed;\n }\n\n /** Replace the value; out-of-range input is clamped rather than committed as-is. */\n setValue(value: number): void {\n if (!Number.isFinite(value)) return;\n const next = this.clamp(value);\n if (next === this.committed && this.input.value === String(next)) return;\n this.committed = next;\n this.input.value = String(next);\n this.syncStepperState();\n }\n\n /** Enable or disable the field and its stepper buttons. */\n setDisabled(disabled: boolean): void {\n if (this.input.disabled === disabled) return;\n this.input.disabled = disabled;\n this.syncStepperState();\n this.toggleClass(\"is-disabled\", disabled);\n }\n\n /** Validate on every keystroke; only legal values are committed. */\n private handleInput(): void {\n const raw = this.input.value.trim();\n if (raw === \"\") return;\n\n const parsed = Number(raw);\n if (!Number.isFinite(parsed) || !this.isInRange(parsed)) return;\n\n this.committed = parsed;\n this.syncStepperState();\n this.onChange?.(parsed);\n }\n\n /** Step from the committed value and commit the result. */\n private nudge(direction: 1 | -1): void {\n const base = this.committed;\n this.setValue(base + (this.step ?? 1) * direction);\n this.onChange?.(this.committed);\n }\n\n /** True when a value sits inside the field's bounds. */\n private isInRange(value: number): boolean {\n if (this.min !== undefined && value < this.min) return false;\n if (this.max !== undefined && value > this.max) return false;\n return true;\n }\n\n /** Fold a value back into the bounds. */\n private clamp(value: number): number {\n let next = value;\n if (this.min !== undefined && next < this.min) next = this.min;\n if (this.max !== undefined && next > this.max) next = this.max;\n return next;\n }\n\n /** Disable a stepper button at the edge of the range, or with the field. */\n private syncStepperState(): void {\n const off = this.input.disabled;\n const atMin = this.min !== undefined && this.committed <= this.min;\n const atMax = this.max !== undefined && this.committed >= this.max;\n this.stepDown.disabled = off || atMin;\n this.stepUp.disabled = off || atMax;\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, uid, type Child} from \"../dom\";\nimport \"./slider.css\";\n\nexport interface SliderOptions {\n /** Label rendered above the track; it is wired to the input through `for`/`id`. */\n label?: Child;\n /** Smallest value. Default 0. */\n min?: number;\n /** Largest value. Default 100. */\n max?: number;\n /** Increment between stops. Default 1. */\n step?: number;\n /** Initial value; defaults to `min`. */\n value?: number;\n /** Show the current value at the trailing edge. */\n showValue?: boolean;\n /** Fires on every move with the new number. */\n onChange?: (value: number) => void;\n}\n\n/**\n * A horizontal slider on a native `<input type=\"range\">`.\n *\n * The range input already handles pointer capture, dragging, Home/End and the\n * arrow keys, so the component only adds the macOS look and an optional\n * read-out. The filled part of the track is a gradient stop driven by\n * `--mac-slider-value`, so a move costs one custom-property write instead of a\n * second element to resize.\n */\nexport class Slider extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly input: HTMLInputElement;\n private readonly valueNode: HTMLSpanElement | null = null;\n private readonly onChange?: (value: number) => void;\n private readonly min: number;\n private readonly max: number;\n\n constructor(options: SliderOptions = {}) {\n super();\n\n const {\n label,\n min = 0,\n max = 100,\n step = 1,\n value,\n showValue = false,\n onChange,\n } = options;\n\n this.min = min;\n this.max = max;\n this.onChange = onChange;\n\n this.root = el(\"div\", {class: \"mac-slider\"});\n\n const inputId = uid(\"mac-slider\");\n\n if (label !== undefined) {\n const labelNode = el(\"label\", {\n class: \"mac-slider__label\",\n for: inputId,\n });\n append(labelNode, [label]);\n this.root.appendChild(labelNode);\n }\n\n const control = el(\"div\", {class: \"mac-slider__control\"});\n\n this.input = el(\"input\", {\n class: \"mac-slider__input\",\n type: \"range\",\n id: inputId,\n min,\n max,\n step,\n });\n // A range input defaults to its midpoint, so the start value is explicit.\n this.input.value = String(this.clamp(value ?? min));\n\n control.appendChild(this.input);\n\n if (showValue) {\n this.valueNode = el(\"span\", {class: \"mac-slider__value\"});\n this.valueNode.textContent = this.input.value;\n control.appendChild(this.valueNode);\n }\n\n this.root.appendChild(control);\n\n this.listen(this.input, \"input\", () => {\n const next = Number(this.input.value);\n this.syncFill();\n this.renderValue(next);\n this.onChange?.(next);\n });\n\n this.syncFill();\n }\n\n /** The current value, clamped to the configured range. */\n get value(): number {\n return Number(this.input.value);\n }\n\n /** Replace the value; out-of-range input is clamped rather than committed as-is. */\n setValue(value: number): void {\n if (!Number.isFinite(value)) return;\n const next = this.clamp(value);\n if (this.input.value === String(next)) return;\n this.input.value = String(next);\n this.syncFill();\n this.renderValue(next);\n }\n\n /** Enable or disable the slider, using the native attribute. */\n setDisabled(disabled: boolean): void {\n if (this.input.disabled === disabled) return;\n this.input.disabled = disabled;\n this.toggleClass(\"is-disabled\", disabled);\n }\n\n /** Paint the filled part of the track up to the current value. */\n private syncFill(): void {\n const value = Number(this.input.value);\n const span = this.max - this.min;\n // A zero-width range would divide by zero; treat it as fully filled.\n const ratio = span === 0 ? 1 : (value - this.min) / span;\n const percent = Math.min(1, Math.max(0, ratio)) * 100;\n this.root.style.setProperty(\"--mac-slider-value\", `${percent}%`);\n }\n\n /** Keep the read-out in step with the value. */\n private renderValue(value: number): void {\n if (!this.valueNode) return;\n const text = String(value);\n if (this.valueNode.textContent === text) return;\n this.valueNode.textContent = text;\n }\n\n /** Fold a value back into the bounds. */\n private clamp(value: number): number {\n if (value < this.min) return this.min;\n if (value > this.max) return this.max;\n return value;\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./progress.css\";\n\nexport interface ProgressOptions {\n /** Progress from 0 to 100. Ignored while `indeterminate` is set. */\n value?: number;\n /** Show the barber-pole used when the duration is unknown. */\n indeterminate?: boolean;\n}\n\n/**\n * A progress bar with a determinate and an indeterminate face.\n *\n * Both states are the same element; only the `is-` class and the ARIA surface\n * change. Indeterminate removes `aria-valuenow` outright — a stale value would\n * tell a screen reader a percentage that does not exist — while\n * `aria-valuemin`/`aria-valuemax` stay, so the widget still describes its scale.\n */\nexport class Progress extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly fill: HTMLDivElement;\n private indeterminate: boolean;\n private current: number;\n\n constructor(options: ProgressOptions = {}) {\n super();\n\n const {value = 0, indeterminate = false} = options;\n\n this.indeterminate = indeterminate;\n this.current = this.constrain(value);\n\n this.root = el(\"div\", {\n class: \"mac-progress\",\n role: \"progressbar\",\n \"aria-valuemin\": \"0\",\n \"aria-valuemax\": \"100\",\n });\n\n this.fill = el(\"div\", {class: \"mac-progress__fill\"});\n this.fill.style.setProperty(\"--mac-progress-scale\", String(this.current / 100));\n\n this.root.appendChild(this.fill);\n this.syncState();\n }\n\n /** The percentage currently shown, 0–100. */\n get value(): number {\n return this.current;\n }\n\n /** Set the percentage; values outside 0–100 are folded into the range. */\n setValue(value: number): void {\n const next = this.constrain(value);\n if (next === this.current) return;\n\n this.current = next;\n // The fill is scaled, not resized: motion stays on the compositor and\n // the bar never triggers layout. See progress.css.\n this.fill.style.setProperty(\"--mac-progress-scale\", String(next / 100));\n\n // While indeterminate there is deliberately no aria-valuenow to update;\n // the pending value is applied when the bar becomes determinate again.\n if (!this.indeterminate) {\n this.root.setAttribute(\"aria-valuenow\", String(next));\n }\n }\n\n /** Switch between the determinate bar and the barber-pole. */\n setIndeterminate(indeterminate: boolean): void {\n if (this.indeterminate === indeterminate) return;\n this.indeterminate = indeterminate;\n this.syncState();\n }\n\n /** Apply the ARIA surface and the class that match the current state. */\n private syncState(): void {\n this.root.classList.toggle(\"is-indeterminate\", this.indeterminate);\n if (this.indeterminate) {\n this.root.removeAttribute(\"aria-valuenow\");\n } else {\n this.root.setAttribute(\"aria-valuenow\", String(this.current));\n }\n }\n\n /** Fold a value into 0–100. */\n private constrain(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.min(100, Math.max(0, value));\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./badge.css\";\n\nexport type BadgeVariant = \"default\" | \"accent\" | \"success\" | \"warning\" | \"danger\";\n\nexport interface BadgeOptions {\n /** Badge text. */\n text: string;\n /** Colour treatment. Default `default` (neutral grey). */\n variant?: BadgeVariant;\n}\n\n/**\n * A small pill used to tag rows, counts and states.\n *\n * Renders inline, so a badge dropped inside a sidebar row or a paragraph sits\n * on the text baseline rather than breaking that row's layout. The text is\n * written with `textContent` only — a badge is the component most likely to\n * receive a server-supplied string, so it never goes anywhere near HTML.\n */\nexport class Badge extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private text: string;\n private variant: BadgeVariant;\n\n constructor(options: BadgeOptions) {\n super();\n\n const {variant = \"default\"} = options;\n\n this.text = options.text;\n this.variant = variant;\n\n const classes = [\"mac-badge\"];\n if (variant !== \"default\") classes.push(`mac-badge--${variant}`);\n\n this.root = el(\"span\", {class: classes.join(\" \"), text: this.text});\n }\n\n /** Replace the badge text. */\n setText(text: string): void {\n if (this.text === text) return;\n this.text = text;\n this.root.textContent = text;\n }\n\n /** Swap the colour treatment. */\n setVariant(variant: BadgeVariant): void {\n if (this.variant === variant) return;\n this.root.classList.remove(`mac-badge--${this.variant}`);\n this.variant = variant;\n if (variant !== \"default\") {\n this.root.classList.add(`mac-badge--${variant}`);\n }\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./switch.css\";\n\nexport interface SwitchOptions {\n /** Initial on/off state. Default off. */\n checked?: boolean;\n disabled?: boolean;\n /**\n * Accessible name, exposed as `aria-label`. A switch is drawn as a bare\n * track, so the label cannot sit inside the control itself.\n */\n label?: string;\n /** Fires with the new state after a user toggle; setters stay silent. */\n onChange?: (checked: boolean) => void;\n}\n\n/**\n * A macOS switch on a native `<button role=\"switch\">`.\n *\n * The track and the knob are two elements, and the knob travels with\n * `transform` only: animating `left` or `width` would lay the control out on\n * every frame (design rule 7). Pointer activation arrives as a `click`, while\n * Space and Enter are handled on `keydown` so the state flips once per press\n * and Space never scrolls the pane behind the switch.\n */\nexport class Switch extends Component<HTMLElement> {\n protected readonly root: HTMLButtonElement;\n\n private readonly onChange?: (checked: boolean) => void;\n private isOn: boolean;\n\n constructor(options: SwitchOptions = {}) {\n super();\n\n const {checked = false, disabled = false, label, onChange} = options;\n\n this.isOn = checked;\n this.onChange = onChange;\n\n this.root = el(\"button\", {\n class: \"mac-switch\",\n type: \"button\",\n role: \"switch\",\n \"aria-checked\": String(checked),\n \"aria-label\": label,\n disabled,\n });\n\n this.root.appendChild(el(\"span\", {class: \"mac-switch__knob\"}));\n\n this.listen(this.root, \"click\", () => this.toggle());\n\n // A native button synthesises its own click for these two keys. Flipping\n // here and cancelling the default keeps one press to one toggle, and\n // stops Space from scrolling the pane behind the switch.\n this.listen(this.root, \"keydown\", (event) => {\n if (event.key !== \" \" && event.key !== \"Enter\") return;\n event.preventDefault();\n this.toggle();\n });\n }\n\n /** The current state. */\n get checked(): boolean {\n return this.isOn;\n }\n\n /** Set the state; a write that changes nothing is skipped, and `onChange` stays silent. */\n setChecked(checked: boolean): void {\n if (this.isOn === checked) return;\n this.isOn = checked;\n this.syncState();\n }\n\n /** Enable or disable the switch, using the native attribute. */\n setDisabled(disabled: boolean): void {\n if (this.root.disabled === disabled) return;\n this.root.disabled = disabled;\n }\n\n /** Move keyboard focus onto the switch. */\n focus(): void {\n this.root.focus();\n }\n\n /** Flip the state from a user gesture and report it. */\n private toggle(): void {\n this.setChecked(!this.isOn);\n this.onChange?.(this.isOn);\n }\n\n /**\n * Write the state to `aria-checked`.\n *\n * That attribute is what the stylesheet slides the knob on and what a\n * screen reader announces, so one write keeps the picture and the\n * semantics in step.\n */\n private syncState(): void {\n this.setAttr(\"aria-checked\", String(this.isOn));\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, svgIcon, type Child} from \"../dom\";\nimport \"./checkbox.css\";\n\nexport interface CheckboxOptions {\n /** Initial ticked state. Default false. */\n checked?: boolean;\n /** Show the dash instead of the tick; it takes precedence over `checked`. */\n indeterminate?: boolean;\n disabled?: boolean;\n /** Text rendered next to the box; it also names the control for assistive tech. */\n label?: Child;\n /** Fires with the new state after a user toggle; setters stay silent. */\n onChange?: (checked: boolean) => void;\n}\n\n/**\n * A styled checkbox built around a real `<input type=\"checkbox\">`.\n *\n * The input stays in the document — clipped to a pixel, not `display: none` —\n * so it keeps its place in the tab order, its focus ring and its click target.\n * The drawn box is pure decoration: `:checked` and `:indeterminate` in CSS\n * follow the input, and one native `change` listener mirrors the result to the\n * caller. Space, screen readers and form submission therefore stay native;\n * nothing in this component toggles state behind the browser's back.\n */\nexport class Checkbox extends Component<HTMLElement> {\n protected readonly root: HTMLLabelElement;\n\n private readonly input: HTMLInputElement;\n private readonly onChange?: (checked: boolean) => void;\n\n constructor(options: CheckboxOptions = {}) {\n super();\n\n const {\n checked = false,\n indeterminate = false,\n disabled = false,\n label,\n onChange,\n } = options;\n\n this.onChange = onChange;\n\n this.root = el(\"label\", {class: \"mac-checkbox\"});\n\n this.input = el(\"input\", {\n class: \"mac-checkbox__input\",\n type: \"checkbox\",\n checked,\n disabled,\n });\n\n // `indeterminate` exists only as a property, with no attribute form, so\n // the initial state cannot ride along in the `el` call above.\n this.input.indeterminate = indeterminate;\n\n const box = el(\"span\", {class: \"mac-checkbox__box\"});\n\n // The tick is an SVG path rather than a glyph from the font: it keeps\n // its shape at any box size and on any DPI. Both marks share one grid\n // cell and swap through `opacity` (design rule 7).\n const tick = svgIcon(\"M5 12.5 L9.5 17 L19 7\", 12);\n tick.setAttribute(\"class\", \"mac-checkbox__tick\");\n\n const dash = svgIcon(\"M7 12 H17\", 12);\n dash.setAttribute(\"class\", \"mac-checkbox__dash\");\n\n box.append(tick, dash);\n this.root.append(this.input, box);\n\n if (label !== undefined) {\n const labelNode = el(\"span\", {class: \"mac-checkbox__label\"});\n append(labelNode, [label]);\n this.root.appendChild(labelNode);\n }\n\n this.toggleClass(\"is-disabled\", disabled);\n\n // Every way of toggling — Space, a click on the label, a screen reader\n // activating the input — converges on this one native event.\n this.listen(this.input, \"change\", () => {\n this.onChange?.(this.input.checked);\n });\n }\n\n /** The current ticked state. */\n get checked(): boolean {\n return this.input.checked;\n }\n\n /** Set the ticked state; a write that changes nothing is skipped, and `onChange` stays silent. */\n setChecked(checked: boolean): void {\n if (this.input.checked === checked) return;\n this.input.checked = checked;\n }\n\n /** Show the mixed state (a dash instead of the tick). */\n setIndeterminate(indeterminate: boolean): void {\n if (this.input.indeterminate === indeterminate) return;\n this.input.indeterminate = indeterminate;\n }\n\n /** Enable or disable the checkbox, using the native attribute. */\n setDisabled(disabled: boolean): void {\n if (this.input.disabled === disabled) return;\n this.input.disabled = disabled;\n this.toggleClass(\"is-disabled\", disabled);\n }\n\n /** Move keyboard focus onto the native input. */\n focus(): void {\n this.input.focus();\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon} from \"../dom\";\nimport \"./segmented.css\";\n\nexport interface SegmentedOption {\n label: string;\n value: string;\n /** Optional leading SVG path data. */\n icon?: string;\n disabled?: boolean;\n}\n\nexport interface SegmentedControlOptions {\n options: SegmentedOption[];\n /** Initially selected value. Defaults to the first enabled option. */\n value?: string;\n /** Stretch to the container's width. */\n block?: boolean;\n onChange?: (value: string) => void;\n}\n\n/**\n * A macOS segmented control.\n *\n * Semantics follow the native control: it is a radio group, so it exposes\n * `role=\"radiogroup\"` with one `role=\"radio\"` per segment, arrow keys move the\n * selection, and only the selected segment is in the tab order (roving\n * tabindex). That is what a screen-reader user expects from this widget.\n *\n * The selection pill is a single element moved with `transform`, so switching\n * segments never re-lays-out the buttons themselves.\n */\nexport class SegmentedControl extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly thumb: HTMLDivElement;\n private readonly buttons: HTMLButtonElement[] = [];\n private readonly options: SegmentedOption[];\n private readonly onChange?: (value: string) => void;\n\n private current: string;\n\n constructor(options: SegmentedControlOptions) {\n super();\n\n this.options = options.options;\n this.onChange = options.onChange;\n\n // Fall back to the first enabled option so the control always has a\n // selection, like AppKit's NSSegmentedControl.\n this.current =\n options.value ??\n this.options.find((option) => !option.disabled)?.value ??\n \"\";\n\n const classes = [\"mac-segmented\"];\n if (options.block) classes.push(\"mac-segmented--block\");\n\n this.root = el(\"div\", {class: classes.join(\" \"), role: \"radiogroup\"});\n this.thumb = el(\"div\", {class: \"mac-segmented__thumb\", \"aria-hidden\": \"true\"});\n this.root.appendChild(this.thumb);\n\n for (const option of this.options) {\n const selected = option.value === this.current;\n const button = el(\"button\", {\n class: `mac-segmented__item${selected ? \" is-selected\" : \"\"}`,\n type: \"button\",\n role: \"radio\",\n \"aria-checked\": String(selected),\n tabindex: selected ? 0 : -1,\n disabled: option.disabled ?? false,\n dataset: {value: option.value},\n });\n\n if (option.icon) {\n button.appendChild(svgIcon(option.icon, 13));\n }\n button.appendChild(el(\"span\", {text: option.label}));\n\n this.buttons.push(button);\n this.root.appendChild(button);\n }\n\n // Delegated click: one listener for every segment.\n this.listen(this.root, \"click\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-segmented__item\",\n );\n if (!button || button.disabled) return;\n const value = button.dataset.value;\n if (value) this.applySelection(value, true);\n });\n\n // Arrow keys move the selection, matching the native control.\n this.listen(this.root, \"keydown\", (event) => {\n const enabled = this.buttons.filter((b) => !b.disabled);\n if (enabled.length === 0) return;\n\n const index = enabled.findIndex((b) => b.dataset.value === this.current);\n let next = index;\n\n if (event.key === \"ArrowRight\" || event.key === \"ArrowDown\") {\n next = (index + 1) % enabled.length;\n } else if (event.key === \"ArrowLeft\" || event.key === \"ArrowUp\") {\n next = (index - 1 + enabled.length) % enabled.length;\n } else if (event.key === \"Home\") {\n next = 0;\n } else if (event.key === \"End\") {\n next = enabled.length - 1;\n } else {\n return;\n }\n\n event.preventDefault();\n const target = enabled[next];\n const value = target.dataset.value;\n if (!value) return;\n this.applySelection(value, true);\n target.focus();\n });\n\n // Place the pill once the element is measurable, and keep it correct\n // whenever the box changes: a window resize, a font swap, or the control\n // becoming visible after being built inside a hidden panel all move the\n // segments under the pill.\n requestAnimationFrame(() => this.moveThumb(false));\n this.listen(window, \"resize\", () => this.moveThumb(false));\n\n // ResizeObserver is the reliable signal for \"the control just got a\n // size\": it covers the hidden-panel case, which a resize listener cannot\n // see. Disconnecting is handled through onDestroy.\n if (typeof ResizeObserver !== \"undefined\") {\n const observer = new ResizeObserver(() => this.moveThumb(false));\n observer.observe(this.root);\n this.onDestroy(() => observer.disconnect());\n }\n }\n\n /**\n * Apply a selection.\n *\n * `notify` is true only for a user-initiated change (click or arrow key), so\n * the programmatic `setValue` below stays silent — the same contract every\n * other setter in the kit has.\n */\n private applySelection(value: string, notify: boolean): void {\n if (this.current === value) return;\n this.current = value;\n\n for (const button of this.buttons) {\n const match = button.dataset.value === value;\n button.classList.toggle(\"is-selected\", match);\n button.setAttribute(\"aria-checked\", String(match));\n // Roving tabindex: only the selected segment is tabbable.\n button.tabIndex = match ? 0 : -1;\n }\n\n this.moveThumb(true);\n\n if (notify) this.onChange?.(value);\n }\n\n /**\n * Position the selection pill.\n *\n * Uses `offsetLeft`/`offsetWidth` (layout reads) once per change, then\n * writes only transform and a width variable, so the animation stays on the\n * compositor.\n *\n * The measurement is skipped when the control has no layout box: a segmented\n * control built inside a hidden tab panel reports 0 for every offset, which\n * would collapse the pill to zero width and leave it at the far left. A\n * ResizeObserver re-measures when the control finally gets a size.\n */\n private moveThumb(animate: boolean): void {\n const button = this.buttons.find((b) => b.dataset.value === this.current);\n if (!button) return;\n\n // No layout box yet (hidden ancestor): keep the previous geometry and\n // wait for the observer to fire. Writing zeros here is what produced a\n // pill stuck at the left edge.\n if (this.root.offsetWidth === 0) return;\n\n if (!animate) {\n this.thumb.style.transition = \"none\";\n }\n\n // offsetLeft is relative to the padded border box; the thumb is inset by\n // the container's padding, so subtract it rather than a literal 2px.\n const inset = parseFloat(getComputedStyle(this.root).paddingLeft) || 0;\n\n this.thumb.style.setProperty(\"--mac-segmented-thumb-x\", `${button.offsetLeft - inset}px`);\n this.thumb.style.setProperty(\"--mac-segmented-thumb-width\", `${button.offsetWidth}px`);\n\n if (!animate) {\n // Force the style to settle, then restore the transition.\n void this.thumb.offsetWidth;\n this.thumb.style.transition = \"\";\n }\n }\n\n /** The selected value. */\n get value(): string {\n return this.current;\n }\n\n /** Change the selection programmatically. */\n setValue(value: string): void {\n this.applySelection(value, false);\n }\n\n /** Disable every segment. */\n setDisabled(disabled: boolean): void {\n for (const button of this.buttons) {\n button.disabled = disabled;\n }\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./select.css\";\n\nexport interface SelectOption {\n label: string;\n value: string;\n disabled?: boolean;\n}\n\nexport interface SelectOptions {\n options: SelectOption[];\n /** Initially selected value. Defaults to the first enabled option. */\n value?: string;\n disabled?: boolean;\n /** Stretch to the container's width. */\n block?: boolean;\n /** Accessible name, since the control has no visible label. */\n ariaLabel?: string;\n onChange?: (value: string) => void;\n}\n\n/**\n * A macOS pop-up button.\n *\n * Built on a real `<select>`. Replacing it with a custom listbox would mean\n * reimplementing type-ahead, mobile wheel pickers, screen-reader announcement\n * and the OS popup — a lot of behaviour to lose for a styling win. Only the\n * appearance is replaced (`appearance: none` plus our own chevron).\n */\nexport class Select extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly control: HTMLSelectElement;\n private readonly onChange?: (value: string) => void;\n\n constructor(options: SelectOptions) {\n super();\n\n this.onChange = options.onChange;\n\n const classes = [\"mac-select\"];\n if (options.block) classes.push(\"mac-select--block\");\n\n this.root = el(\"div\", {class: classes.join(\" \")});\n\n this.control = el(\"select\", {\n class: \"mac-select__control\",\n disabled: options.disabled ?? false,\n });\n\n if (options.ariaLabel) {\n this.control.setAttribute(\"aria-label\", options.ariaLabel);\n }\n\n this.setOptions(options.options, options.value);\n this.root.appendChild(this.control);\n this.root.appendChild(this.makeChevron());\n\n this.listen(this.control, \"change\", () => {\n this.onChange?.(this.control.value);\n });\n }\n\n /** The small dropdown chevron drawn over the select. */\n private makeChevron(): SVGElement {\n const ns = \"http://www.w3.org/2000/svg\";\n const svg = document.createElementNS(ns, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 12 12\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.classList.add(\"mac-select__arrow\");\n const path = document.createElementNS(ns, \"path\");\n path.setAttribute(\"d\", \"M2.5 4.5 L6 8 L9.5 4.5\");\n path.setAttribute(\"fill\", \"none\");\n path.setAttribute(\"stroke\", \"currentColor\");\n path.setAttribute(\"stroke-width\", \"1.6\");\n path.setAttribute(\"stroke-linecap\", \"round\");\n path.setAttribute(\"stroke-linejoin\", \"round\");\n svg.appendChild(path);\n return svg;\n }\n\n /** Replace the option list. */\n setOptions(options: SelectOption[], value?: string): void {\n const fragment = document.createDocumentFragment();\n\n for (const option of options) {\n const node = el(\"option\", {\n value: option.value,\n text: option.label,\n disabled: option.disabled ?? false,\n });\n fragment.appendChild(node);\n }\n\n this.control.replaceChildren(fragment);\n\n const fallback =\n value ?? options.find((option) => !option.disabled)?.value ?? \"\";\n if (fallback) {\n this.control.value = fallback;\n }\n }\n\n /** The selected value. */\n get value(): string {\n return this.control.value;\n }\n\n /** Change the selection programmatically. */\n setValue(value: string): void {\n if (this.control.value === value) return;\n this.control.value = value;\n }\n\n /** Enable or disable the control. */\n setDisabled(disabled: boolean): void {\n if (this.control.disabled === disabled) return;\n this.control.disabled = disabled;\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./spinner.css\";\n\nexport type SpinnerSize = \"small\" | \"default\" | \"large\";\n\nexport interface SpinnerOptions {\n /** Optional caption beside the ring. */\n label?: string;\n size?: SpinnerSize;\n /** Accessible name. Defaults to the label, then to a generic busy message. */\n ariaLabel?: string;\n}\n\n/**\n * An indeterminate activity indicator.\n *\n * Semantics matter here: the spinner is announced through `role=\"progressbar\"`\n * with no `aria-valuenow`, which is the standard way to say \"busy, duration\n * unknown\". Setting a value would tell a screen reader the work is measurable.\n */\nexport class Spinner extends Component<HTMLSpanElement> {\n protected readonly root: HTMLSpanElement;\n\n private readonly labelNode: HTMLSpanElement | null = null;\n\n constructor(options: SpinnerOptions = {}) {\n super();\n\n const {label, size = \"default\", ariaLabel} = options;\n\n const classes = [\"mac-spinner\"];\n if (size !== \"default\") classes.push(`mac-spinner--${size}`);\n\n this.root = el(\"span\", {\n class: classes.join(\" \"),\n role: \"progressbar\",\n \"aria-busy\": \"true\",\n \"aria-label\": ariaLabel ?? label ?? \"正在加载\",\n });\n\n this.root.appendChild(el(\"span\", {class: \"mac-spinner__ring\"}));\n\n if (label) {\n this.labelNode = el(\"span\", {class: \"mac-spinner__label\", text: label});\n this.root.appendChild(this.labelNode);\n }\n }\n\n /** Replace the visible caption. */\n setLabel(label: string): void {\n if (!this.labelNode) return;\n if (this.labelNode.textContent === label) return;\n this.labelNode.textContent = label;\n }\n}\n","import {Component} from \"../component\";\nimport {el, uid, type Child} from \"../dom\";\nimport \"./textarea.css\";\n\nexport interface TextAreaOptions {\n /** Label rendered above the field. */\n label?: Child;\n placeholder?: string;\n /** Initial text. */\n value?: string;\n /** Visible rows; drives the initial height. */\n rows?: number;\n /** Hard limit; shows a counter when set. */\n maxLength?: number;\n disabled?: boolean;\n readOnly?: boolean;\n onInput?: (value: string) => void;\n onChange?: (value: string) => void;\n}\n\n/**\n * A multi-line text field.\n *\n * Built on a real `<textarea>` for the same reason TextField uses a real input:\n * caret navigation, IME composition, undo and selection are the browser's, and a\n * `div[contenteditable]` would mean reimplementing them badly.\n *\n * Like TextField, `onInput` fires per keystroke and `onChange` only on commit\n * (blur), which is what the DOM's own event means.\n */\nexport class TextArea extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly control: HTMLTextAreaElement;\n private readonly counter: HTMLSpanElement | null = null;\n private readonly maxLength?: number;\n private readonly onChange?: (value: string) => void;\n\n constructor(options: TextAreaOptions = {}) {\n super();\n\n const {\n label,\n placeholder = \"\",\n value = \"\",\n rows = 4,\n maxLength,\n disabled = false,\n readOnly = false,\n onInput,\n onChange,\n } = options;\n\n this.maxLength = maxLength;\n this.onChange = onChange;\n\n this.root = el(\"div\", {class: \"mac-textarea\"});\n\n const id = uid(\"mac-textarea\");\n\n if (label !== undefined) {\n const labelNode = el(\"label\", {class: \"mac-textarea__label\", htmlFor: id});\n if (typeof label === \"string\" || typeof label === \"number\") {\n labelNode.textContent = String(label);\n } else if (label instanceof Node) {\n labelNode.appendChild(label);\n }\n this.root.appendChild(labelNode);\n }\n\n const controlWrap = el(\"div\", {class: \"mac-textarea__control\"});\n this.control = el(\"textarea\", {\n class: \"mac-textarea__input\",\n id,\n rows,\n placeholder,\n disabled,\n readOnly,\n });\n if (maxLength !== undefined) {\n this.control.maxLength = maxLength;\n }\n this.control.value = value;\n controlWrap.appendChild(this.control);\n this.root.appendChild(controlWrap);\n\n if (maxLength !== undefined) {\n const meta = el(\"div\", {class: \"mac-textarea__meta\"});\n this.counter = el(\"span\", {text: `${value.length}/${maxLength}`});\n meta.appendChild(this.counter);\n this.root.appendChild(meta);\n this.updateCounter();\n }\n\n if (onInput) {\n this.listen(this.control, \"input\", () => {\n onInput(this.control.value);\n this.updateCounter();\n });\n } else {\n this.listen(this.control, \"input\", () => this.updateCounter());\n }\n\n // `change` on a textarea fires when the value is committed (blur).\n this.listen(this.control, \"change\", () => this.onChange?.(this.control.value));\n\n if (disabled) {\n this.root.classList.add(\"is-disabled\");\n }\n }\n\n /** The current text. */\n get value(): string {\n return this.control.value;\n }\n\n /** Replace the text. */\n setValue(value: string): void {\n if (this.control.value === value) return;\n this.control.value = value;\n this.updateCounter();\n }\n\n /** Enable or disable the editor. */\n setDisabled(disabled: boolean): void {\n if (this.control.disabled === disabled) return;\n this.control.disabled = disabled;\n this.root.classList.toggle(\"is-disabled\", disabled);\n }\n\n /** Move keyboard focus into the editor. */\n focus(): void {\n this.control.focus();\n }\n\n /**\n * Keep the character counter in step, and flag the field when it is at or\n * over the limit.\n */\n private updateCounter(): void {\n if (!this.counter || this.maxLength === undefined) return;\n const length = this.control.value.length;\n this.counter.textContent = `${length}/${this.maxLength}`;\n this.counter.parentElement?.classList.toggle(\"is-over\", length >= this.maxLength);\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon, type Child} from \"../dom\";\nimport \"./disclosure.css\";\n\nconst CHEVRON = \"M9 6 L15 12 L9 18\";\n\nexport interface DisclosureOptions {\n /** Section title shown in the header. */\n title: string;\n /** Collapsible content. */\n content?: Child;\n /** Start expanded. Defaults to false, the macOS convention. */\n open?: boolean;\n /** Optional trailing summary, visible whether open or closed. */\n meta?: string;\n disabled?: boolean;\n onToggle?: (open: boolean) => void;\n}\n\n/**\n * A collapsible section.\n *\n * Uses `aria-expanded` on the header button and `hidden` on the region, which is\n * the pattern screen readers understand for a disclosure widget: the button\n * announces whether the section is open, and the content is genuinely removed\n * from the accessibility tree when closed rather than merely scrolled away.\n */\nexport class Disclosure extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly header: HTMLButtonElement;\n private readonly content: HTMLDivElement;\n private readonly metaNode: HTMLSpanElement | null;\n private readonly onToggle?: (open: boolean) => void;\n\n private open: boolean;\n\n constructor(options: DisclosureOptions) {\n super();\n\n const {title, content, open = false, meta, disabled = false, onToggle} = options;\n\n this.open = open;\n this.onToggle = onToggle;\n\n this.root = el(\"div\", {\n class: `mac-disclosure${open ? \" is-open\" : \"\"}`,\n });\n\n this.header = el(\"button\", {\n class: \"mac-disclosure__header\",\n type: \"button\",\n \"aria-expanded\": String(open),\n disabled,\n });\n\n const chevron = svgIcon(CHEVRON, 14);\n chevron.classList.add(\"mac-disclosure__chevron\");\n this.header.appendChild(chevron);\n\n this.header.appendChild(el(\"span\", {class: \"mac-disclosure__title\", text: title}));\n\n if (meta !== undefined) {\n this.metaNode = el(\"span\", {class: \"mac-disclosure__meta\", text: meta});\n this.header.appendChild(this.metaNode);\n } else {\n this.metaNode = null;\n }\n\n this.content = el(\"div\", {class: \"mac-disclosure__content\"});\n if (typeof content === \"string\" || typeof content === \"number\") {\n this.content.textContent = String(content);\n } else if (content instanceof Node) {\n this.content.appendChild(content);\n }\n this.content.hidden = !open;\n\n this.root.append(this.header, this.content);\n\n this.listen(this.header, \"click\", () => {\n if (this.header.disabled) return;\n this.setOpen(!this.open, true);\n });\n }\n\n /** Expand or collapse the section. */\n setOpen(open: boolean, notify = false): void {\n if (this.open === open) return;\n this.open = open;\n\n this.root.classList.toggle(\"is-open\", open);\n this.header.setAttribute(\"aria-expanded\", String(open));\n this.content.hidden = !open;\n\n if (notify) this.onToggle?.(open);\n }\n\n /** Whether the section is currently expanded. */\n get isOpen(): boolean {\n return this.open;\n }\n\n /** Update the trailing summary text. */\n setMeta(meta: string): void {\n if (!this.metaNode) return;\n if (this.metaNode.textContent === meta) return;\n this.metaNode.textContent = meta;\n }\n\n /** The region content is appended to after construction. */\n get contentElement(): HTMLDivElement {\n return this.content;\n }\n}\n","import {Component} from \"../component\";\nimport {append, el} from \"../dom\";\nimport \"./table.css\";\n\n/** Horizontal alignment of a column's header and cells. */\nexport type TableAlign = \"left\" | \"center\" | \"right\";\n\n/** One column definition. */\nexport interface TableColumn {\n /** Property name read from each row object. */\n key: string;\n /** Header label. */\n title: string;\n /** Fixed width in CSS pixels; columns without one share the rest. */\n width?: number;\n /** Alignment for the header and every cell in the column. Default `left`. */\n align?: TableAlign;\n /**\n * Custom cell renderer — the main extension point.\n *\n * Called once per cell during a render with the value at `column.key`, the\n * whole row and the row index. Returning a `Node` lets the cell hold live\n * UI (a badge, a button, a switch); returning a string renders as text.\n * Omit it to get the default `String(value)` treatment.\n */\n render?: (value: any, row: any, index: number) => Node | string;\n}\n\nexport interface TableOptions {\n columns: TableColumn[];\n rows: any[];\n /**\n * Stable identity per row, used by selection and by the `is-selected`\n * class. Defaults to a primitive `row.id` when present, otherwise the row\n * index — so pass one if rows are reordered or paginated.\n */\n rowKey?: (row: any, index: number) => string;\n /** Render a leading checkbox column with select-all in the header. */\n selectable?: boolean;\n /** Keys selected on first paint; only meaningful with `selectable`. */\n selectedKeys?: string[];\n /** Fires after a user-driven selection change, with the new key list. */\n onSelectionChange?: (keys: string[]) => void;\n /** Text shown in place of rows when `rows` is empty. */\n emptyText?: string;\n}\n\n/** Fallback identity: an `id` field when the data has one, else the index. */\nfunction defaultRowKey(row: any, index: number): string {\n const id = row?.id;\n return typeof id === \"string\" || typeof id === \"number\" ? String(id) : String(index);\n}\n\n/** BEM modifier for a column's alignment, empty for the default left. */\nfunction alignClass(align: TableAlign | undefined): string {\n if (align === \"center\") return \" mac-table__align--center\";\n if (align === \"right\") return \" mac-table__align--right\";\n return \"\";\n}\n\n/**\n * A macOS data table.\n *\n * Built on a real `<table>` (`th scope=\"col\"`, a body of `<tr>`s) so screen\n * readers, header associations and text selection behave; the styling and the\n * independent horizontal scroller are what this component adds.\n *\n * Rendering strategy: every row is built into a `DocumentFragment` and the\n * body is swapped in one `replaceChildren()` call. Appending row by row would\n * re-run layout once per row, which on a few hundred rows is the difference\n * between one frame and a visible stall.\n *\n * Events are delegated from the (stable) body element, so re-rendering rows\n * never leaks listeners.\n */\nexport class Table extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly body: HTMLTableSectionElement;\n\n private readonly columns: TableColumn[];\n private readonly rowKey: (row: any, index: number) => string;\n private readonly selectable: boolean;\n private readonly emptyText: string;\n private readonly onSelectionChange?: (keys: string[]) => void;\n\n /** Selected row keys, in selection order. */\n private readonly selected = new Set<string>();\n\n /** Select-all box in the header; null when the table is not selectable. */\n private headerCheck: HTMLInputElement | null = null;\n /** Row checkboxes, index-aligned with `rowKeys`. */\n private rowChecks: HTMLInputElement[] = [];\n /** Keys of the currently rendered rows, in row order. */\n private rowKeys: string[] = [];\n\n private rows: any[] = [];\n\n constructor(options: TableOptions) {\n super();\n\n this.columns = options.columns;\n this.rows = options.rows ?? [];\n this.selectable = options.selectable ?? false;\n this.emptyText = options.emptyText ?? \"暂无数据\";\n this.rowKey = options.rowKey ?? defaultRowKey;\n this.onSelectionChange = options.onSelectionChange;\n\n for (const key of options.selectedKeys ?? []) {\n this.selected.add(key);\n }\n\n const classes = [\"mac-table\"];\n if (this.selectable) classes.push(\"mac-table--selectable\");\n\n this.root = el(\"div\", {class: classes.join(\" \")});\n\n const scroll = el(\"div\", {class: \"mac-table__scroll\"});\n const table = el(\"table\", {class: \"mac-table__table\"});\n const head = el(\"thead\", {class: \"mac-table__head\"});\n\n this.body = el(\"tbody\", {class: \"mac-table__body\"});\n\n table.append(this.makeColGroup(), head, this.body);\n scroll.appendChild(table);\n this.root.appendChild(scroll);\n\n head.appendChild(this.makeHeaderRow());\n\n // One listener for every row checkbox, on an element that outlives the\n // rows themselves.\n this.listen(this.body, \"change\", (event) => this.onBodyChange(event));\n\n this.renderRows();\n }\n\n /**\n * Replace the row data.\n *\n * Existing selection is kept for keys that still exist and dropped for\n * keys that do not; like every setter in the kit, this does not raise\n * `onSelectionChange`.\n */\n setRows(rows: any[]): void {\n const live = new Set<string>();\n rows.forEach((row, index) => live.add(this.rowKey(row, index)));\n\n for (const key of Array.from(this.selected)) {\n if (!live.has(key)) this.selected.delete(key);\n }\n\n this.rows = rows;\n this.renderRows();\n }\n\n /** Keys of the selected rows, in the order they were selected. */\n get selectedKeys(): string[] {\n return Array.from(this.selected);\n }\n\n /** Drop the selection without raising `onSelectionChange`. */\n clearSelection(): void {\n if (this.selected.size === 0) return;\n this.selected.clear();\n this.syncRowChecks();\n this.syncHeaderCheck();\n }\n\n // --- construction --------------------------------------------------------\n\n /** `<col>` per column, so `width` is only ever a hint on one element. */\n private makeColGroup(): HTMLTableColElement {\n const group = el(\"colgroup\", {class: \"mac-table__cols\"});\n\n if (this.selectable) {\n group.appendChild(el(\"col\", {class: \"mac-table__col mac-table__col--select\"}));\n }\n\n for (const column of this.columns) {\n const col = el(\"col\", {class: \"mac-table__col\"});\n if (column.width !== undefined) {\n // Widths come from the caller's data model (column sizing is\n // content, like `render`), not from the design tokens.\n col.style.width = `${column.width}px`;\n }\n group.appendChild(col);\n }\n\n return group;\n }\n\n private makeHeaderRow(): HTMLTableRowElement {\n const row = el(\"tr\", {class: \"mac-table__row\"});\n\n if (this.selectable) {\n const cell = el(\"th\", {class: \"mac-table__th mac-table__th--select\", scope: \"col\"});\n const check = el(\"input\", {\n class: \"mac-table__checkbox\",\n type: \"checkbox\",\n \"aria-label\": \"全选\",\n });\n\n // `indeterminate` only exists as a property, so it cannot ride\n // along in the `el` call above.\n this.headerCheck = check;\n this.listen(check, \"change\", () => this.toggleAll(check.checked));\n\n cell.appendChild(check);\n row.appendChild(cell);\n }\n\n for (const column of this.columns) {\n const cell = el(\"th\", {\n class: `mac-table__th${alignClass(column.align)}`,\n scope: \"col\",\n });\n cell.textContent = column.title;\n row.appendChild(cell);\n }\n\n return row;\n }\n\n // --- rendering -----------------------------------------------------------\n\n /** Rebuild the whole body in one insertion. */\n private renderRows(): void {\n const fragment = document.createDocumentFragment();\n\n this.rowChecks = [];\n this.rowKeys = [];\n\n if (this.rows.length === 0) {\n fragment.appendChild(this.makeEmptyRow());\n } else {\n this.rows.forEach((row, index) => {\n fragment.appendChild(this.makeRow(row, index));\n });\n }\n\n this.body.replaceChildren(fragment);\n this.syncHeaderCheck();\n }\n\n private makeRow(row: any, index: number): HTMLTableRowElement {\n const key = this.rowKey(row, index);\n this.rowKeys.push(key);\n\n const tr = el(\"tr\", {\n class: `mac-table__row${this.selected.has(key) ? \" is-selected\" : \"\"}`,\n dataset: {key},\n });\n\n if (this.selectable) {\n const cell = el(\"td\", {class: \"mac-table__cell mac-table__cell--select\"});\n const check = el(\"input\", {\n class: \"mac-table__checkbox\",\n type: \"checkbox\",\n \"aria-label\": \"选择此行\",\n checked: this.selected.has(key),\n });\n\n this.rowChecks.push(check);\n cell.appendChild(check);\n tr.appendChild(cell);\n }\n\n for (const column of this.columns) {\n tr.appendChild(this.makeCell(column, row, index));\n }\n\n return tr;\n }\n\n private makeCell(\n column: TableColumn,\n row: any,\n index: number,\n ): HTMLTableCellElement {\n const cell = el(\"td\", {class: `mac-table__cell${alignClass(column.align)}`});\n const value = row?.[column.key];\n\n if (column.render) {\n // `append` takes a Node or a string, so a renderer can return\n // either rich UI or plain text without the table knowing which.\n append(cell, [column.render(value, row, index)]);\n return cell;\n }\n\n if (value !== null && value !== undefined) {\n cell.textContent = String(value);\n }\n\n return cell;\n }\n\n private makeEmptyRow(): HTMLTableRowElement {\n const row = el(\"tr\", {class: \"mac-table__row mac-table__row--empty\"});\n const span = this.columns.length + (this.selectable ? 1 : 0);\n const cell = el(\"td\", {\n class: \"mac-table__cell mac-table__empty\",\n colSpan: Math.max(1, span),\n });\n\n cell.textContent = this.emptyText;\n row.appendChild(cell);\n return row;\n }\n\n // --- selection -----------------------------------------------------------\n\n private onBodyChange(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement)) return;\n if (!target.classList.contains(\"mac-table__checkbox\")) return;\n\n const row = target.closest<HTMLTableRowElement>(\".mac-table__row\");\n const key = row?.dataset.key;\n if (key === undefined) return;\n\n if (target.checked) this.selected.add(key);\n else this.selected.delete(key);\n\n row?.classList.toggle(\"is-selected\", target.checked);\n this.syncHeaderCheck();\n this.emitSelection();\n }\n\n /** Header box: check selects everything, uncheck clears everything. */\n private toggleAll(checked: boolean): void {\n this.selected.clear();\n if (checked) {\n for (const key of this.rowKeys) this.selected.add(key);\n }\n\n this.syncRowChecks();\n this.syncHeaderCheck();\n this.emitSelection();\n }\n\n private syncRowChecks(): void {\n this.rowChecks.forEach((check, index) => {\n const selected = this.selected.has(this.rowKeys[index]);\n if (check.checked !== selected) check.checked = selected;\n const row = check.closest<HTMLTableRowElement>(\".mac-table__row\");\n row?.classList.toggle(\"is-selected\", selected);\n });\n }\n\n /** Header box state: unchecked, checked, or the mixed dash. */\n private syncHeaderCheck(): void {\n const check = this.headerCheck;\n if (!check) return;\n\n const total = this.rowKeys.length;\n let selected = 0;\n for (const key of this.rowKeys) {\n if (this.selected.has(key)) selected += 1;\n }\n\n check.disabled = total === 0;\n check.checked = total > 0 && selected === total;\n // `indeterminate` has no attribute form; it must be written as a\n // property, and only when it actually changes (a no-op write would\n // still reset the browser's paint state on some engines).\n const mixed = selected > 0 && selected < total;\n if (check.indeterminate !== mixed) check.indeterminate = mixed;\n }\n\n private emitSelection(): void {\n this.onSelectionChange?.(Array.from(this.selected));\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, uid} from \"../dom\";\nimport \"./splitview.css\";\n\n/** Split direction: `horizontal` places the panes side by side. */\nexport type SplitViewOrientation = \"horizontal\" | \"vertical\";\n\nexport interface SplitViewOptions {\n /** `horizontal` puts the panes left/right, `vertical` stacks them. */\n orientation: SplitViewOrientation;\n /** Content of the leading pane (left or top). */\n primary?: Node;\n /** Content of the trailing pane (right or bottom); it takes the remainder. */\n secondary?: Node;\n /** Initial primary size: pixels as a number, or any CSS length e.g. \"40%\". */\n primarySize?: number | string;\n /** Smallest allowed primary size in pixels. Default 0. */\n minPrimary?: number;\n /** Smallest allowed secondary size in pixels. Default 0. */\n minSecondary?: number;\n /** Fires while dragging and on keyboard resize, with the new size in pixels. */\n onResize?: (size: number) => void;\n}\n\n/**\n * Arrow-key step, in CSS pixels.\n *\n * Ten is the increment AppKit's split view uses for a keyboard nudge: small\n * enough to fine-tune, large enough that a reasonable range is a few presses.\n * It is a behaviour constant, not a visual one, so it is not tokenised.\n */\nconst KEYBOARD_STEP = 10;\n\n/**\n * A draggable two-pane split view.\n *\n * The primary pane carries an explicit `flex-basis` driven by a custom property\n * (`--mac-splitview-primary`), so a drag writes one number instead of touching\n * both panes' styles; the secondary pane simply takes the remainder. That also\n * means a resize never re-enters layout twice per pointer event.\n *\n * The divider is a real focus stop: `role=\"separator\"` with\n * `aria-orientation`, `aria-valuenow/min/max` and arrow-key resizing, which is\n * what assistive tech needs to expose an adjustable split.\n *\n * Dragging uses pointer capture, so a fast drag that outruns the one-pixel\n * divider keeps delivering moves to it instead of losing the gesture.\n */\nexport class SplitView extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly orientation: SplitViewOrientation;\n private readonly primaryPane: HTMLDivElement;\n private readonly secondaryPane: HTMLDivElement;\n private readonly divider: HTMLDivElement;\n private readonly minPrimary: number;\n private readonly minSecondary: number;\n private readonly onResize?: (size: number) => void;\n\n private dragging = false;\n private dragPointer = -1;\n /** Pointer coordinate when the drag started. */\n private dragStart = 0;\n /** Primary pane extent when the drag started. */\n private dragOrigin = 0;\n\n constructor(options: SplitViewOptions) {\n super();\n\n this.orientation = options.orientation;\n this.minPrimary = options.minPrimary ?? 0;\n this.minSecondary = options.minSecondary ?? 0;\n this.onResize = options.onResize;\n\n const horizontal = this.orientation === \"horizontal\";\n\n this.root = el(\"div\", {\n class: `mac-splitview mac-splitview--${this.orientation}`,\n });\n\n this.primaryPane = el(\"div\", {\n class: \"mac-splitview__pane mac-splitview__pane--primary\",\n });\n this.secondaryPane = el(\"div\", {\n class: \"mac-splitview__pane mac-splitview__pane--secondary\",\n });\n\n append(this.primaryPane, [options.primary]);\n append(this.secondaryPane, [options.secondary]);\n\n this.primaryPane.id = uid(\"mac-splitview-pane\");\n this.secondaryPane.id = uid(\"mac-splitview-pane\");\n\n this.divider = el(\"div\", {\n class: \"mac-splitview__divider\",\n role: \"separator\",\n tabindex: 0,\n // A side-by-side split is separated by a vertical line: the ARIA\n // orientation follows the divider, not the layout.\n \"aria-orientation\": horizontal ? \"vertical\" : \"horizontal\",\n \"aria-label\": \"调整面板大小\",\n });\n\n // `aria-controls` names both panes: a separator between two regions\n // adjusts the boundary, so pointing at only one would misdescribe it.\n this.divider.setAttribute(\n \"aria-controls\",\n `${this.primaryPane.id} ${this.secondaryPane.id}`,\n );\n\n this.root.append(this.primaryPane, this.divider, this.secondaryPane);\n\n const initial = options.primarySize ?? \"50%\";\n this.writeSize(typeof initial === \"number\" ? `${initial}px` : initial);\n\n this.listen(this.divider, \"pointerdown\", (event) => this.onPointerDown(event));\n this.listen(this.divider, \"pointermove\", (event) => this.onPointerMove(event));\n this.listen(this.divider, \"pointerup\", (event) => this.onPointerUp(event));\n this.listen(this.divider, \"pointercancel\", (event) => this.onPointerUp(event));\n // Capture can be pulled away by the browser (a modal, a context menu);\n // without this the next move would keep resizing a stale gesture.\n this.listen(this.divider, \"lostpointercapture\", () => this.endDrag());\n this.listen(this.divider, \"keydown\", (event) => this.onKeyDown(event));\n\n // Keep the ARIA values truthful when the container changes size, e.g. a\n // window resize that moves the available extent under the panes.\n if (typeof ResizeObserver !== \"undefined\") {\n const observer = new ResizeObserver(() => this.syncAria());\n observer.observe(this.root);\n this.onDestroy(() => observer.disconnect());\n }\n\n this.syncAria();\n }\n\n /**\n * Set the primary pane's size: a pixel number or any CSS length (\"40%\").\n *\n * A number is clamped like a drag would be, so the panes never violate the\n * declared minimums. Silent — the caller already knows, and a programmatic\n * resize is not a user gesture.\n */\n setPrimarySize(size: number | string): void {\n const next =\n typeof size === \"number\" ? `${this.clampSize(size)}px` : size;\n this.writeSize(next);\n this.syncAria();\n }\n\n // --- drag ----------------------------------------------------------------\n\n private onPointerDown(event: PointerEvent): void {\n // Left button (or a touch/pen contact, which reports button 0).\n if (event.button !== 0) return;\n event.preventDefault();\n\n this.dragging = true;\n this.dragPointer = event.pointerId;\n this.dragStart = this.pointerAxis(event);\n this.dragOrigin = this.paneExtent(this.primaryPane);\n\n this.root.classList.add(\"is-dragging\");\n this.divider.setPointerCapture(event.pointerId);\n // Focus follows the grab, so the arrow keys work straight after a drag\n // without a second click.\n this.divider.focus();\n }\n\n private onPointerMove(event: PointerEvent): void {\n if (!this.dragging || event.pointerId !== this.dragPointer) return;\n const delta = this.pointerAxis(event) - this.dragStart;\n this.applySize(this.dragOrigin + delta, true);\n }\n\n private onPointerUp(event: PointerEvent): void {\n if (event.pointerId !== this.dragPointer) return;\n this.endDrag();\n if (this.divider.hasPointerCapture(event.pointerId)) {\n this.divider.releasePointerCapture(event.pointerId);\n }\n }\n\n private endDrag(): void {\n if (!this.dragging) return;\n this.dragging = false;\n this.dragPointer = -1;\n this.root.classList.remove(\"is-dragging\");\n }\n\n // --- keyboard ------------------------------------------------------------\n\n private onKeyDown(event: KeyboardEvent): void {\n const horizontal = this.orientation === \"horizontal\";\n // The arrow keys that move the divider along its own axis.\n const decrease = horizontal ? \"ArrowLeft\" : \"ArrowUp\";\n const increase = horizontal ? \"ArrowRight\" : \"ArrowDown\";\n\n let target: number;\n\n if (event.key === decrease) {\n target = this.paneExtent(this.primaryPane) - KEYBOARD_STEP;\n } else if (event.key === increase) {\n target = this.paneExtent(this.primaryPane) + KEYBOARD_STEP;\n } else if (event.key === \"Home\") {\n target = this.minPrimary;\n } else if (event.key === \"End\") {\n target = Number.MAX_SAFE_INTEGER;\n } else {\n return;\n }\n\n event.preventDefault();\n this.applySize(target, true);\n }\n\n // --- sizing --------------------------------------------------------------\n\n /** Clamp, write and (optionally) report a new primary size in pixels. */\n private applySize(size: number, notify: boolean): void {\n const next = this.clampSize(size);\n this.writeSize(`${next}px`);\n this.syncAria();\n if (notify) this.onResize?.(next);\n }\n\n /** One write to the custom property; the flex basis picks it up. */\n private writeSize(value: string): void {\n this.root.style.setProperty(\"--mac-splitview-primary\", value);\n }\n\n /** Fold a pixel size into the min/max the options declare. */\n private clampSize(size: number): number {\n const available = this.availableExtent();\n // Before the first layout there is nothing to clamp against; keep the\n // requested value so it is already right on the first paint.\n if (available <= 0) return Math.max(0, size);\n\n const max = Math.max(0, available - this.minSecondary);\n const min = Math.min(this.minPrimary, max);\n return Math.min(max, Math.max(min, size));\n }\n\n private availableExtent(): number {\n return (\n this.paneExtent(this.primaryPane) + this.paneExtent(this.secondaryPane)\n );\n }\n\n private paneExtent(node: HTMLElement): number {\n const rect = node.getBoundingClientRect();\n return this.orientation === \"horizontal\" ? rect.width : rect.height;\n }\n\n private pointerAxis(event: PointerEvent): number {\n return this.orientation === \"horizontal\" ? event.clientX : event.clientY;\n }\n\n /** Keep `aria-valuenow/min/max` in step with the pixels actually on screen. */\n private syncAria(): void {\n const now = Math.round(this.paneExtent(this.primaryPane));\n const available = this.availableExtent();\n const max =\n available > 0\n ? Math.max(0, Math.round(available - this.minSecondary))\n : Math.max(0, now);\n const min = Math.min(this.minPrimary, max);\n\n this.divider.setAttribute(\"aria-valuemin\", String(min));\n this.divider.setAttribute(\"aria-valuemax\", String(max));\n this.divider.setAttribute(\n \"aria-valuenow\",\n String(Math.min(max, Math.max(min, now))),\n );\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon, uid} from \"../dom\";\nimport \"./combobox.css\";\n\n/** One suggestion in the list. */\nexport interface ComboBoxOption {\n label: string;\n value: string;\n}\n\nexport interface ComboBoxOptions {\n /** Full suggestion list; typing filters it. */\n options: ComboBoxOption[];\n /** Initially selected value; its label fills the field. */\n value?: string;\n placeholder?: string;\n /**\n * Custom rendering for a suggestion row. Returning a `Node` lets the list\n * hold live UI; returning a string renders as text. The list keeps its own\n * ARIA wiring around whatever is returned.\n */\n renderOption?: (option: ComboBoxOption) => Node | string;\n /** Fires when a suggestion is committed (click, Enter or Tab). */\n onChange?: (value: string) => void;\n /** Fires on every keystroke with the raw text. */\n onInput?: (text: string) => void;\n}\n\n/**\n * A macOS combo box: a text field with an editable suggestion list.\n *\n * The list is absolutely positioned inside the component's own box rather than\n * appended to `document.body` like the overlay components. A combo box's\n * suggestions are conceptually part of the field, so staying in the container\n * avoids portal teardown, scroll re-positioning and a `z-index` war with the\n * dialogs above it. The trade-off is that an ancestor with `overflow: hidden`\n * clips the list — acceptable for a control that rarely sits at the edge of a\n * clipped card.\n *\n * Implements the ARIA combobox pattern: the input owns `role=\"combobox\"`,\n * `aria-expanded` and `aria-activedescendant`; the list is a `role=\"listbox\"`\n * of `role=\"option\"` rows. Focus never leaves the input, so typing continues\n * while the arrow keys move the highlight.\n */\nexport class ComboBox extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly input: HTMLInputElement;\n private readonly list: HTMLDivElement;\n private readonly onChange?: (value: string) => void;\n private readonly onInput?: (text: string) => void;\n private readonly renderOption?: (option: ComboBoxOption) => Node | string;\n private readonly listId: string;\n\n private options: ComboBoxOption[];\n /** Options currently drawn, i.e. after filtering. */\n private filtered: ComboBoxOption[] = [];\n /** Option rows, index-aligned with `filtered`. */\n private rows: HTMLDivElement[] = [];\n /** Highlighted row index; -1 means nothing is highlighted. */\n private active = -1;\n /**\n * Value of the committed option, or null once the text is free-form.\n * Kept separately from `input.value`, because the field shows a label while\n * the caller may have handed us a machine value (an id, a slug).\n */\n private selectedValue: string | null;\n\n constructor(options: ComboBoxOptions) {\n super();\n\n this.options = options.options;\n this.renderOption = options.renderOption;\n this.onChange = options.onChange;\n this.onInput = options.onInput;\n this.selectedValue = options.value ?? null;\n this.listId = uid(\"mac-combobox-list\");\n\n this.root = el(\"div\", {class: \"mac-combobox\"});\n\n this.input = el(\"input\", {\n class: \"mac-combobox__input\",\n type: \"text\",\n role: \"combobox\",\n autocomplete: \"off\",\n placeholder: options.placeholder ?? \"\",\n \"aria-expanded\": \"false\",\n \"aria-autocomplete\": \"list\",\n \"aria-controls\": this.listId,\n });\n\n // `el` skips falsy values, so `spellcheck` is set as a property here.\n this.input.spellcheck = false;\n\n const initial = this.optionFor(this.selectedValue ?? \"\");\n this.input.value = initial ? initial.label : (this.selectedValue ?? \"\");\n\n this.list = el(\"div\", {\n class: \"mac-combobox__list\",\n id: this.listId,\n role: \"listbox\",\n hidden: true,\n });\n\n // The chevron is decoration: it must not swallow clicks on the field.\n const chevron = el(\"span\", {class: \"mac-combobox__chevron\", \"aria-hidden\": \"true\"});\n chevron.appendChild(svgIcon(\"M2.5 4.5 L6 8 L9.5 4.5\", 12, \"0 0 12 12\"));\n\n this.root.append(this.input, chevron, this.list);\n\n this.listen(this.input, \"input\", () => {\n // Typing invalidates the committed value: the text is now the truth.\n this.selectedValue = null;\n // Filter before opening: `open` decides visibility from the *new*\n // result set, so typing a character that finally matches a\n // previously empty filter reopens the list.\n this.filter(this.input.value);\n this.open();\n this.onInput?.(this.input.value);\n });\n\n this.listen(this.input, \"keydown\", (event) => this.onKeyDown(event));\n\n // Clicking the field opens the list. Without this the only way in was to\n // type, which meant a user who wanted to *pick* from a pre-filled list\n // first had to delete the text to make the panel appear.\n //\n // Opened on pointerdown rather than click so the panel is already up by\n // the time the click resolves; the input's focus handler below then sees\n // an open list and leaves it alone.\n this.listen(this.input, \"pointerdown\", () => this.openWithFullList());\n\n // Keyboard focus must also reveal the suggestions: tabbing into the field\n // is the same intent as clicking it.\n //\n // `focusin` rather than `focus`: focusin bubbles and, unlike focus, is\n // dispatched even when the document does not hold OS focus — which is the\n // state a window is in while another app is in front.\n this.listen(this.input, \"focusin\", () => this.openWithFullList());\n\n // Keeping focus on the input for the whole click is what stops the\n // input's blur from tearing the list down before the click lands.\n this.listen(this.list, \"pointerdown\", (event) => event.preventDefault());\n this.listen(this.list, \"click\", (event) => this.onListClick(event));\n\n this.listen(this.input, \"blur\", () => this.close());\n\n this.filter(\"\");\n }\n\n /**\n * Show every option.\n *\n * Opening from a click or a focus must not filter by whatever text is\n * already in the field: a field showing \"张三\" would otherwise open to a\n * single row, which reads as a broken control. Typing is the only thing that\n * narrows the list, and that path filters explicitly.\n */\n private openWithFullList(): void {\n this.filter(\"\");\n this.open();\n }\n\n /**\n * The committed value.\n *\n * The highlight wins (an arrowed-onto row is what the user means), then the\n * value behind the current label, and only then the raw text. Going through\n * `selectedValue` is what keeps a field showing \"Apricot\" answering with its\n * machine value \"b\".\n */\n get value(): string {\n const option = this.filtered[this.active];\n if (option) return option.value;\n return this.selectedValue ?? this.input.value;\n }\n\n /**\n * Set the text and value programmatically.\n *\n * A value that matches an option shows that option's label, so the field\n * never displays a bare id where a name belongs. Silent, like every setter.\n */\n setValue(value: string): void {\n this.selectedValue = value;\n const match = this.optionFor(value);\n this.input.value = match ? match.label : value;\n this.active = -1;\n this.close();\n this.filter(this.input.value);\n }\n\n /** Replace the suggestion list; the current text is kept and re-filtered. */\n setOptions(options: ComboBoxOption[]): void {\n this.options = options;\n this.filter(this.input.value);\n }\n\n /** Move keyboard focus into the field and show the full list. */\n focus(): void {\n // The `focus` listener opens with the full list; doing it here as well\n // would filter twice on every call.\n this.input.focus();\n }\n\n // --- list ----------------------------------------------------------------\n\n private optionFor(value: string): ComboBoxOption | undefined {\n return this.options.find((option) => option.value === value);\n }\n\n /** Rebuild the visible rows for the given text. */\n private filter(text: string): void {\n const needle = text.trim().toLowerCase();\n this.filtered = needle\n ? this.options.filter((option) =>\n option.label.toLowerCase().includes(needle),\n )\n : this.options.slice();\n\n // A filter invalidates the highlight; it is re-established on the next\n // arrow key so a committed value is not silently replaced by row 0.\n this.active = -1;\n\n const fragment = document.createDocumentFragment();\n this.rows = [];\n\n this.filtered.forEach((option, index) => {\n const row = el(\"div\", {\n class: \"mac-combobox__option\",\n id: `${this.listId}-${index}`,\n role: \"option\",\n // `aria-selected` marks the *committed* choice; the transient\n // keyboard highlight rides on aria-activedescendant instead.\n \"aria-selected\": String(option.value === this.selectedValue),\n dataset: {index: String(index)},\n });\n\n if (this.renderOption) {\n const custom = this.renderOption(option);\n if (typeof custom === \"string\") row.textContent = custom;\n else row.appendChild(custom);\n } else {\n row.textContent = option.label;\n }\n\n this.rows.push(row);\n fragment.appendChild(row);\n });\n\n // One insertion for the whole list, like the table body.\n this.list.replaceChildren(fragment);\n\n // Filtering never *opens* the list — only `open()` does. It can close\n // it, though: an empty result set has nothing to show, and leaving the\n // panel up with no rows would look like a rendering bug.\n if (this.filtered.length === 0) {\n this.close();\n } else if (this.input.getAttribute(\"aria-expanded\") === \"true\") {\n this.syncActive();\n }\n }\n\n private open(): void {\n if (this.filtered.length === 0) {\n this.close();\n return;\n }\n this.list.hidden = false;\n this.input.setAttribute(\"aria-expanded\", \"true\");\n }\n\n private close(): void {\n this.active = -1;\n this.input.setAttribute(\"aria-expanded\", \"false\");\n this.input.removeAttribute(\"aria-activedescendant\");\n if (this.list.hidden) return;\n this.list.hidden = true;\n for (const row of this.rows) row.classList.remove(\"is-active\");\n }\n\n /** Move the highlight by `step` rows, wrapping at both ends. */\n private move(step: number): void {\n if (this.filtered.length === 0) return;\n\n const count = this.filtered.length;\n const from = this.active < 0 ? (step > 0 ? -1 : 0) : this.active;\n let next = from + step;\n\n if (next < 0) next = count - 1;\n if (next >= count) next = 0;\n\n this.active = next;\n this.open();\n this.syncActive();\n // `block: nearest` scrolls the list only when the row is out of view.\n this.rows[next]?.scrollIntoView({block: \"nearest\"});\n }\n\n /** Mirror `active` into the DOM and the input's ARIA wiring. */\n private syncActive(): void {\n this.rows.forEach((row, index) => {\n row.classList.toggle(\"is-active\", index === this.active);\n });\n\n const row = this.rows[this.active];\n if (row) {\n this.input.setAttribute(\"aria-activedescendant\", row.id);\n } else {\n this.input.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n private onKeyDown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\":\n case \"ArrowUp\": {\n event.preventDefault();\n this.move(event.key === \"ArrowDown\" ? 1 : -1);\n return;\n }\n case \"Enter\": {\n // Enter accepts the highlight; with no highlight it commits\n // whatever has been typed, the editable-combobox rule.\n if (this.active >= 0) {\n event.preventDefault();\n this.commit(this.filtered[this.active]);\n }\n return;\n }\n case \"Escape\": {\n if (this.list.hidden) return;\n event.preventDefault();\n this.close();\n return;\n }\n case \"Tab\": {\n // Leaving the field commits the highlight, so a keyboard user\n // who arrowed onto an option does not lose it.\n if (this.active >= 0) this.commit(this.filtered[this.active]);\n return;\n }\n default:\n return;\n }\n }\n\n private onListClick(event: MouseEvent): void {\n const row = (event.target as HTMLElement).closest<HTMLElement>(\n \".mac-combobox__option\",\n );\n if (!row) return;\n\n const option = this.filtered[Number(row.dataset.index)];\n if (option) this.commit(option);\n }\n\n /** Accept an option: fill the field, close the list, report the value. */\n private commit(option: ComboBoxOption): void {\n this.selectedValue = option.value;\n this.input.value = option.label;\n this.close();\n\n for (const [index, row] of this.rows.entries()) {\n row.setAttribute(\n \"aria-selected\",\n String(this.filtered[index]?.value === option.value),\n );\n }\n\n this.onChange?.(option.value);\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, svgIcon, uid} from \"../dom\";\nimport \"./datepicker.css\";\n\nexport interface DatePickerOptions {\n /** Initial date as `YYYY-MM-DD`. */\n value?: string;\n /** Earliest selectable date, inclusive, as `YYYY-MM-DD`. */\n min?: string;\n /** Latest selectable date, inclusive, as `YYYY-MM-DD`. */\n max?: string;\n disabled?: boolean;\n /** Optional label rendered above the control and linked through `for`/`id`. */\n label?: string;\n /** Fires with `YYYY-MM-DD` (or \"\" when cleared) once the value is committed. */\n onChange?: (value: string) => void;\n}\n\n/** Calendar glyph for the trailing affordance. */\nconst CALENDAR_PATH =\n \"M7 3 V6 M17 3 V6 M4 9 H20 M5 5 H19 A1 1 0 0 1 20 6 V19 A1 1 0 0 1 19 20 H5 A1 1 0 0 1 4 19 V6 A1 1 0 0 1 5 5 Z\";\n\n/**\n * A macOS date picker wrapping a native `<input type=\"date\">`.\n *\n * The native control is kept, not reimplemented: it brings the platform\n * calendar popup, keyboard editing of each date segment, locale-correct\n * formatting, `min`/`max` enforcement and the iOS wheel picker. A hand-built\n * calendar would have to re-earn all of that and would still not match the OS.\n *\n * What this component adds is purely visual — the macOS field chrome and a\n * trailing calendar icon that is `pointer-events: none`, so the click falls\n * through to the native picker indicator underneath.\n */\nexport class DatePicker extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly input: HTMLInputElement;\n private readonly labelNode: HTMLLabelElement | null = null;\n private readonly onChange?: (value: string) => void;\n\n constructor(options: DatePickerOptions = {}) {\n super();\n\n const {value, min, max, disabled = false, label, onChange} = options;\n\n this.onChange = onChange;\n\n this.root = el(\"div\", {class: \"mac-datepicker\"});\n\n const inputId = uid(\"mac-datepicker\");\n\n if (label !== undefined) {\n this.labelNode = el(\"label\", {\n class: \"mac-datepicker__label\",\n for: inputId,\n });\n append(this.labelNode, [label]);\n this.root.appendChild(this.labelNode);\n }\n\n const control = el(\"div\", {class: \"mac-datepicker__control\"});\n\n this.input = el(\"input\", {\n class: \"mac-datepicker__input\",\n type: \"date\",\n id: inputId,\n disabled,\n });\n\n // The date inputs take their bounds as attributes; `el` would assign\n // them as properties, which the date element does not reflect.\n if (value) this.input.value = value;\n if (min) this.input.min = min;\n if (max) this.input.max = max;\n\n const icon = el(\"span\", {class: \"mac-datepicker__icon\", \"aria-hidden\": \"true\"});\n icon.appendChild(svgIcon(CALENDAR_PATH, 16, \"0 0 24 24\"));\n\n control.append(this.input, icon);\n this.root.appendChild(control);\n\n this.toggleClass(\"is-disabled\", disabled);\n\n // `change` is the commit point for a date input: it fires when a day is\n // picked from the popup or when a segment edit completes, not on every\n // partial keystroke (`input` would fire mid-edit with no valid date).\n this.listen(this.input, \"change\", () => {\n this.onChange?.(this.input.value);\n });\n }\n\n /** The current date as `YYYY-MM-DD`, or \"\" when empty. */\n get value(): string {\n return this.input.value;\n }\n\n /** Set the date; an empty string clears it. Silent, like every setter. */\n setValue(value: string): void {\n if (this.input.value === value) return;\n this.input.value = value;\n }\n\n /** Enable or disable the field, using the native attribute. */\n setDisabled(disabled: boolean): void {\n if (this.input.disabled === disabled) return;\n this.input.disabled = disabled;\n this.toggleClass(\"is-disabled\", disabled);\n }\n\n /** Move keyboard focus into the field. */\n focus(): void {\n this.input.focus();\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./traffic-lights.css\";\n\nexport interface TrafficLightsOptions {\n /** Red light: close the window. */\n onClose?: () => void;\n /** Yellow light: minimise the window. */\n onMinimise?: () => void;\n /** Green light: zoom (or fullscreen while Alt is held). */\n onZoom?: (event: MouseEvent) => void;\n}\n\n/**\n * The three macOS window buttons.\n *\n * The component owns presentation only: what \"close\" means is the host app's\n * business, so behaviour arrives as callbacks. That keeps this file free of any\n * Wails dependency and testable in a plain browser.\n *\n * Glyph paths are drawn inline rather than imported so the exact macOS shapes\n * (the ×, the −, and the two opposing zoom triangles) stay under our control.\n */\nexport class TrafficLights extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly closeButton: HTMLButtonElement;\n private readonly minimiseButton: HTMLButtonElement;\n private readonly zoomButton: HTMLButtonElement;\n\n constructor(options: TrafficLightsOptions = {}) {\n super();\n\n this.root = el(\"div\", {\n class: \"mac-traffic-lights\",\n role: \"group\",\n \"aria-label\": \"窗口控制\",\n });\n\n this.closeButton = this.makeLight(\"close\", \"关闭\");\n this.minimiseButton = this.makeLight(\"minimise\", \"最小化\");\n this.zoomButton = this.makeLight(\"zoom\", \"缩放\");\n\n // Close glyph: a thin cross.\n this.addGlyph(this.closeButton, \"mac-traffic-light__glyph--close\",\n \"M3.5 3.5 L8.5 8.5 M8.5 3.5 L3.5 8.5\");\n\n // Minimise glyph: a single dash.\n this.addGlyph(this.minimiseButton, \"mac-traffic-light__glyph--minimise\",\n \"M2.6 6 L9.4 6\");\n\n // Zoom glyphs: two opposing triangles, swapped when zoomed.\n this.addTriangle(this.zoomButton, \"mac-traffic-light__glyph--enter\",\n \"M3.1 3.1 H5.1 L3.1 5.1 Z\", \"M8.9 8.9 H6.9 L8.9 6.9 Z\");\n this.addTriangle(this.zoomButton, \"mac-traffic-light__glyph--exit\",\n \"M5.1 3.1 H3.1 L5.1 5.1 Z\", \"M6.9 8.9 H8.9 L6.9 6.9 Z\");\n\n this.root.append(this.closeButton, this.minimiseButton, this.zoomButton);\n\n if (options.onClose) {\n this.listen(this.closeButton, \"click\", options.onClose);\n }\n if (options.onMinimise) {\n this.listen(this.minimiseButton, \"click\", options.onMinimise);\n }\n if (options.onZoom) {\n this.listen(this.zoomButton, \"click\", options.onZoom);\n }\n }\n\n /** Create one coloured button. */\n private makeLight(kind: \"close\" | \"minimise\" | \"zoom\", label: string): HTMLButtonElement {\n const button = el(\"button\", {\n class: `mac-traffic-light mac-traffic-light--${kind}`,\n type: \"button\",\n \"aria-label\": label,\n title: label,\n });\n return button;\n }\n\n /** Append a stroked path glyph to a light. */\n private addGlyph(button: HTMLButtonElement, className: string, path: string): void {\n const ns = \"http://www.w3.org/2000/svg\";\n const svg = document.createElementNS(ns, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 12 12\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.classList.add(className);\n const p = document.createElementNS(ns, \"path\");\n p.setAttribute(\"d\", path);\n svg.appendChild(p);\n button.appendChild(svg);\n }\n\n /** Append a zoom glyph made of two filled triangles. */\n private addTriangle(button: HTMLButtonElement, className: string, a: string, b: string): void {\n const ns = \"http://www.w3.org/2000/svg\";\n const svg = document.createElementNS(ns, \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 12 12\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n svg.classList.add(className);\n for (const d of [a, b]) {\n const p = document.createElementNS(ns, \"path\");\n p.setAttribute(\"d\", d);\n svg.appendChild(p);\n }\n button.appendChild(svg);\n }\n\n /** Reflect whether the window is zoomed (swaps the green glyph and title). */\n setZoomed(zoomed: boolean): void {\n if (this.root.classList.contains(\"is-zoomed\") === zoomed) return;\n this.root.classList.toggle(\"is-zoomed\", zoomed);\n\n const label = zoomed ? \"还原\" : \"缩放\";\n this.zoomButton.title = label;\n this.zoomButton.setAttribute(\"aria-label\", label);\n }\n\n /** Dim the lights when the window is not the active window. */\n setFocused(focused: boolean): void {\n if (this.root.classList.contains(\"is-unfocused\") === !focused) return;\n this.root.classList.toggle(\"is-unfocused\", !focused);\n }\n}\n","import {Component} from \"../component\";\nimport {el, type Child} from \"../dom\";\nimport {TrafficLights, type TrafficLightsOptions} from \"./traffic-lights\";\nimport \"./titlebar.css\";\n\nexport interface TitleBarOptions {\n /** Centred window title. */\n title?: string;\n /** Show the macOS traffic lights at the leading edge. Default true. */\n trafficLights?: boolean;\n /** Callbacks forwarded to the traffic lights. */\n onClose?: TrafficLightsOptions[\"onClose\"];\n onMinimise?: TrafficLightsOptions[\"onMinimise\"];\n onZoom?: TrafficLightsOptions[\"onZoom\"];\n /** Nodes placed at the trailing edge (search field, toolbar buttons). */\n trailing?: Child[];\n /** Nodes placed between the lights and the title (rarely needed). */\n leading?: Child[];\n /**\n * Double-clicking the empty part of the title bar zooms the window.\n *\n * Wails only wires this up on macOS: its runtime checks `IsMac()` before\n * sending `wails:drag:doubleclick` (see internal/runtime/.../drag.ts), so on\n * Windows and Linux the gesture reaches the page as an ordinary dblclick and\n * does nothing. The kit handles it itself so the behaviour is the same\n * everywhere.\n */\n onDoubleClick?: () => void;\n}\n\n/**\n * The window's title bar.\n *\n * It carries the `--wails-draggable: drag` custom property, which is what lets\n * the user move a frameless window: Wails' runtime reads that property off the\n * event target and starts a native drag. Interactive children declare\n * `--wails-draggable: no-drag` in their own stylesheets so they stay clickable.\n */\nexport class TitleBar extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly titleNode: HTMLDivElement;\n private readonly lights: TrafficLights | null;\n\n constructor(options: TitleBarOptions = {}) {\n super();\n\n const {title = \"\", trafficLights = true, leading = [], trailing = []} = options;\n\n this.root = el(\"header\", {class: \"mac-titlebar\"});\n\n const leadingCluster = el(\"div\", {class: \"mac-titlebar__leading\"});\n\n if (trafficLights) {\n this.lights = new TrafficLights({\n onClose: options.onClose,\n onMinimise: options.onMinimise,\n onZoom: options.onZoom,\n });\n leadingCluster.appendChild(this.lights.element);\n } else {\n this.lights = null;\n }\n\n for (const node of leading) {\n if (node === null || node === undefined || node === false) continue;\n leadingCluster.appendChild(\n typeof node === \"string\" || typeof node === \"number\"\n ? document.createTextNode(String(node))\n : node,\n );\n }\n\n this.titleNode = el(\"div\", {class: \"mac-titlebar__title\", text: title});\n\n const trailingCluster = el(\"div\", {class: \"mac-titlebar__trailing\"});\n for (const node of trailing) {\n if (node === null || node === undefined || node === false) continue;\n trailingCluster.appendChild(\n typeof node === \"string\" || typeof node === \"number\"\n ? document.createTextNode(String(node))\n : node,\n );\n }\n\n this.root.append(leadingCluster, this.titleNode, trailingCluster);\n\n // Double-click on the empty title-bar area zooms, matching every desktop\n // environment.\n //\n // Two detection paths, because the drag surface makes a single one\n // unreliable:\n // - `dblclick` is what a normal page gets; on macOS Wails also consumes\n // it to run its own zoom, so this covers Windows/Linux.\n // - a press-pair counter on `mousedown` catches the case where Wails\n // started a native window-move on the first press and the browser\n // never saw a complete click sequence.\n // Whichever fires first wins; the shared guard stops a double zoom.\n if (options.onDoubleClick) {\n const onDoubleClick = options.onDoubleClick;\n let lastPress = 0;\n let lastX = 0;\n let lastY = 0;\n let lastFire = 0;\n\n const fire = () => {\n const now = performance.now();\n // Collapse the dblclick and the press-pair into one action.\n if (now - lastFire < 350) return;\n lastFire = now;\n onDoubleClick();\n };\n\n // Path 1: the browser's own double-click.\n this.listen(this.root, \"dblclick\", (event) => {\n const target = event.target as HTMLElement;\n if (target.closest(\"button, input, a, [role='button'], [role='switch']\")) {\n return;\n }\n event.preventDefault();\n fire();\n });\n\n // Path 2: two presses in the same spot, in time.\n this.listen(\n this.root,\n \"mousedown\",\n (event) => {\n if (event.button !== 0) return;\n\n const target = event.target as HTMLElement;\n if (target.closest(\"button, input, a, [role='button'], [role='switch']\")) {\n return;\n }\n\n const now = event.timeStamp;\n const near =\n Math.abs(event.clientX - lastX) < 8 &&\n Math.abs(event.clientY - lastY) < 8;\n const isDouble = now - lastPress < 500 && near;\n\n if (isDouble) {\n lastPress = 0;\n fire();\n return;\n }\n\n lastPress = now;\n lastX = event.clientX;\n lastY = event.clientY;\n },\n // Capture, so a child that stops propagation cannot hide the press.\n {capture: true},\n );\n }\n }\n\n /** The traffic lights, or null when they were disabled. */\n get trafficLights(): TrafficLights | null {\n return this.lights;\n }\n\n /** Update the centred title. */\n setTitle(title: string): void {\n if (this.titleNode.textContent === title) return;\n this.titleNode.textContent = title;\n }\n\n /** Add a node to the trailing toolbar cluster. */\n addTrailing(node: HTMLElement): void {\n const cluster = this.root.querySelector(\".mac-titlebar__trailing\");\n cluster?.appendChild(node);\n }\n\n override destroy(): void {\n this.lights?.destroy();\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon} from \"../dom\";\nimport \"./sidebar.css\";\n\nexport interface SidebarItem {\n /** Stable key used to identify the item. */\n id: string;\n /** Visible text. */\n label: string;\n /** Optional leading SVG path data. */\n icon?: string;\n /** Optional trailing count badge. */\n count?: number;\n disabled?: boolean;\n}\n\nexport interface SidebarOptions {\n /** Grouped items; each group renders as a block with optional heading. */\n groups: Array<{heading?: string; items: SidebarItem[]}>;\n /** Initially selected item id. */\n selected?: string;\n /** Fires when the selection changes. */\n onSelect?: (id: string) => void;\n}\n\n/**\n * A macOS source list.\n *\n * Rows are `<button>` elements so each one is natively focusable and announces\n * its state; a single delegated listener on the container handles activation,\n * which keeps the listener count constant no matter how many rows exist.\n */\nexport class Sidebar extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private selectedId: string | null;\n private readonly onSelect?: (id: string) => void;\n\n constructor(options: SidebarOptions) {\n super();\n\n this.selectedId = options.selected ?? null;\n this.onSelect = options.onSelect;\n\n this.root = el(\"aside\", {class: \"mac-sidebar\"});\n\n for (const group of options.groups) {\n const groupNode = el(\"nav\", {\n class: \"mac-sidebar__group\",\n \"aria-label\": group.heading ?? \"导航\",\n });\n\n if (group.heading) {\n groupNode.appendChild(\n el(\"div\", {class: \"mac-sidebar__heading\", text: group.heading}),\n );\n }\n\n for (const item of group.items) {\n groupNode.appendChild(this.makeItem(item));\n }\n\n this.root.appendChild(groupNode);\n }\n\n // One delegated listener for every row.\n this.listen(this.root, \"click\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-sidebar__item\",\n );\n if (!button || button.disabled) return;\n const id = button.dataset.id;\n if (id) this.setSelected(id, true);\n });\n }\n\n /** Build one row. */\n private makeItem(item: SidebarItem): HTMLButtonElement {\n const selected = item.id === this.selectedId;\n\n const button = el(\"button\", {\n class: `mac-sidebar__item${selected ? \" is-selected\" : \"\"}`,\n type: \"button\",\n disabled: item.disabled ?? false,\n dataset: {id: item.id, section: item.id},\n \"aria-current\": selected ? \"page\" : \"false\",\n });\n\n if (item.icon) {\n button.appendChild(svgIcon(item.icon, 16));\n }\n\n const label = el(\"span\", {text: item.label});\n button.appendChild(label);\n\n if (item.count !== undefined) {\n button.appendChild(\n el(\"span\", {class: \"mac-sidebar__count\", text: String(item.count)}),\n );\n }\n\n return button;\n }\n\n /** Select an item by id and optionally notify the caller. */\n setSelected(id: string, notify = false): void {\n if (this.selectedId === id && !notify) return;\n if (this.selectedId === id) return;\n\n this.selectedId = id;\n\n for (const node of this.root.querySelectorAll<HTMLButtonElement>(\".mac-sidebar__item\")) {\n const isMatch = node.dataset.id === id;\n node.classList.toggle(\"is-selected\", isMatch);\n node.setAttribute(\"aria-current\", isMatch ? \"page\" : \"false\");\n }\n\n if (notify) this.onSelect?.(id);\n }\n\n /** The currently selected item id. */\n get selected(): string | null {\n return this.selectedId;\n }\n\n /** Collapse or expand the sidebar. */\n setCollapsed(collapsed: boolean): void {\n if (this.root.classList.contains(\"is-collapsed\") === collapsed) return;\n this.root.classList.toggle(\"is-collapsed\", collapsed);\n this.root.setAttribute(\"aria-hidden\", String(collapsed));\n }\n\n /** True when the sidebar is collapsed. */\n get collapsed(): boolean {\n return this.root.classList.contains(\"is-collapsed\");\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./statusbar.css\";\n\nexport type StatusDotState = \"ok\" | \"warning\" | \"error\" | \"idle\";\n\nexport interface StatusBarOptions {\n /** Leading status text. */\n text?: string;\n /** Colour of the leading dot. */\n state?: StatusDotState;\n /** Trailing text, typically a clock. */\n trailing?: string;\n}\n\n/**\n * The thin bar along the bottom of the window.\n *\n * Both texts update independently, so a ticking clock never forces a layout of\n * the leading status and vice versa.\n */\nexport class StatusBar extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly dot: HTMLSpanElement;\n private readonly textNode: HTMLSpanElement;\n private readonly trailingNode: HTMLSpanElement;\n\n constructor(options: StatusBarOptions = {}) {\n super();\n\n const {text = \"\", state = \"ok\", trailing = \"\"} = options;\n\n this.root = el(\"footer\", {class: \"mac-statusbar\"});\n\n const leading = el(\"span\", {class: \"mac-statusbar__leading\"});\n this.dot = el(\"span\", {class: `mac-statusbar__dot mac-statusbar__dot--${state}`});\n this.textNode = el(\"span\", {class: \"mac-statusbar__text\", text});\n leading.append(this.dot, this.textNode);\n\n this.trailingNode = el(\"span\", {class: \"mac-statusbar__time\", text: trailing});\n\n this.root.append(leading, this.trailingNode);\n }\n\n /** Update the leading text. */\n setText(text: string): void {\n if (this.textNode.textContent === text) return;\n this.textNode.textContent = text;\n }\n\n /** Update the dot colour. */\n setState(state: StatusDotState): void {\n const next = `mac-statusbar__dot mac-statusbar__dot--${state}`;\n if (this.dot.className === next) return;\n this.dot.className = next;\n }\n\n /** Update the trailing text. */\n setTrailing(text: string): void {\n if (this.trailingNode.textContent === text) return;\n this.trailingNode.textContent = text;\n }\n}\n","import {Component} from \"../component\";\nimport {append, el, type Child} from \"../dom\";\nimport \"./toolbar.css\";\n\nexport interface ToolbarOptions {\n /** Cluster pinned to the left edge. */\n leading?: Child[];\n /** Cluster centred in the bar. */\n center?: Child[];\n /** Cluster pinned to the right edge. */\n trailing?: Child[];\n /** Which window edge the bar sits against; drives the separator side. */\n position?: \"top\" | \"bottom\";\n /** Draw the hairline separator. Defaults to true. */\n bordered?: boolean;\n /** Accessible name for the bar, e.g. \"编辑器工具栏\". */\n ariaLabel?: string;\n}\n\n/**\n * A macOS window toolbar.\n *\n * Three slots, not a fixed layout: leading, centre and trailing. The centre slot\n * is positioned against the bar's own midpoint, so a wide leading cluster cannot\n * push it off centre — with `margin: auto` alone that is exactly what happens,\n * and it is the usual failure of a flexbox toolbar.\n *\n * The bar handles no keys itself. Everything placed in it is a real control with\n * its own keyboard behaviour, and a container that swallowed arrow keys would\n * break the text fields that toolbars normally carry. `role=\"toolbar\"` is kept\n * because it names the grouping correctly; the rotation keys the ARIA pattern\n * mentions remain the children's own responsibility by design.\n */\nexport class Toolbar extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly leadingSlot: HTMLDivElement;\n private readonly centerSlot: HTMLDivElement;\n private readonly trailingSlot: HTMLDivElement;\n\n constructor(options: ToolbarOptions = {}) {\n super();\n\n const {\n leading = [],\n center = [],\n trailing = [],\n position = \"top\",\n bordered = true,\n ariaLabel,\n } = options;\n\n const classes = [\"mac-toolbar\", `mac-toolbar--${position}`];\n if (!bordered) classes.push(\"mac-toolbar--borderless\");\n\n this.root = el(\"div\", {\n class: classes.join(\" \"),\n role: \"toolbar\",\n \"aria-orientation\": \"horizontal\",\n });\n this.root.dataset.position = position;\n if (ariaLabel) this.root.setAttribute(\"aria-label\", ariaLabel);\n\n this.leadingSlot = el(\"div\", {class: \"mac-toolbar__leading\"});\n this.centerSlot = el(\"div\", {class: \"mac-toolbar__center\"});\n this.trailingSlot = el(\"div\", {class: \"mac-toolbar__trailing\"});\n\n append(this.leadingSlot, leading);\n append(this.centerSlot, center);\n append(this.trailingSlot, trailing);\n\n this.root.append(this.leadingSlot, this.centerSlot, this.trailingSlot);\n }\n\n /** Append a control to the right-hand cluster. */\n addTrailing(node: Child): void {\n append(this.trailingSlot, [node]);\n }\n\n /** Append a control to the left-hand cluster. */\n addLeading(node: Child): void {\n append(this.leadingSlot, [node]);\n }\n\n /** Append a control to the centred cluster. */\n addCenter(node: Child): void {\n append(this.centerSlot, [node]);\n }\n\n /** Show or hide the separator without rebuilding the bar. */\n setBordered(bordered: boolean): void {\n this.root.classList.toggle(\"mac-toolbar--borderless\", !bordered);\n this.root.dataset.bordered = String(bordered);\n }\n\n /** True when the separator is drawn. */\n get bordered(): boolean {\n return !this.root.classList.contains(\"mac-toolbar--borderless\");\n }\n\n /** The three slot elements, for callers that mount their own children. */\n get slots(): {leading: HTMLElement; center: HTMLElement; trailing: HTMLElement} {\n return {\n leading: this.leadingSlot,\n center: this.centerSlot,\n trailing: this.trailingSlot,\n };\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon, uid} from \"../dom\";\nimport \"./tabview.css\";\n\nexport interface TabItem {\n /** Stable key; also the value passed to `onSelect`, `select` and `addPanel`. */\n id: string;\n /** Visible text on the tab. */\n label: string;\n /** Leading SVG path data. */\n icon?: string;\n /** Disabled tabs stay visible but are skipped by the keyboard. */\n disabled?: boolean;\n}\n\nexport interface TabViewOptions {\n tabs: TabItem[];\n /** Initially selected tab. Defaults to the first enabled tab. */\n selected?: string;\n /** Fires when the user changes the tab (not on programmatic `select`). */\n onSelect?: (id: string) => void;\n /** Accessible name for the tab list. */\n ariaLabel?: string;\n}\n\n/**\n * A macOS tab view.\n *\n * The ARIA tabs pattern has three parts and all three are wired here, because a\n * tab strip that only *looks* right is unusable with a screen reader:\n *\n * 1. `role=\"tablist\"` on the strip; `role=\"tab\"` + `aria-selected` +\n * `aria-controls` on each tab; `role=\"tabpanel\"` + `aria-labelledby` on each\n * panel. The panel is created with its tab, so `aria-controls` always\n * resolves to a real element even before the host injects content.\n * 2. **Roving tabindex**: exactly one tab is tabbable — the selected one. Tab\n * then leaves the strip instead of walking through every tab, which is what\n * a native NSTabView does.\n * 3. Arrow keys move the selection *and* focus together (automatic activation),\n * Home/End jump to the ends, and disabled tabs are stepped over rather than\n * focused-and-swallowed.\n *\n * Panels are injected as nodes, so the view owns no content and a panel can be\n * swapped without touching the strip. Panels are never detached: inactive ones\n * are `hidden`, which preserves scroll position and any focus inside them —\n * detaching would silently reset each pane on every switch.\n */\nexport class TabView extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly tabs: TabItem[];\n private readonly onSelect?: (id: string) => void;\n private readonly listNode: HTMLDivElement;\n private readonly panelHost: HTMLDivElement;\n\n /** Tab id → its button in the strip. */\n private readonly tabButtons = new Map<string, HTMLButtonElement>();\n /** Tab id → its panel element. Both are created together in the constructor. */\n private readonly panels = new Map<string, HTMLDivElement>();\n\n private selectedId: string;\n\n constructor(options: TabViewOptions) {\n super();\n\n this.tabs = options.tabs;\n this.onSelect = options.onSelect;\n\n // Fall back to the first enabled tab so the view always has a selection,\n // like NSTabView. An all-disabled strip simply starts with no selection.\n this.selectedId =\n options.selected ?? this.tabs.find((tab) => !tab.disabled)?.id ?? \"\";\n\n this.root = el(\"div\", {class: \"mac-tabview\"});\n\n this.listNode = el(\"div\", {\n class: \"mac-tabview__list\",\n role: \"tablist\",\n \"aria-orientation\": \"horizontal\",\n });\n if (options.ariaLabel) this.listNode.setAttribute(\"aria-label\", options.ariaLabel);\n\n this.panelHost = el(\"div\", {class: \"mac-tabview__panels\"});\n\n for (const tab of this.tabs) {\n const selected = tab.id === this.selectedId;\n const tabId = uid(\"mac-tabview-tab\");\n const panelId = uid(\"mac-tabview-panel\");\n\n const button = el(\"button\", {\n class: `mac-tabview__tab${selected ? \" is-selected\" : \"\"}`,\n type: \"button\",\n role: \"tab\",\n id: tabId,\n \"aria-selected\": String(selected),\n \"aria-controls\": panelId,\n // Roving tabindex: only the selected tab is in the tab order.\n tabindex: selected ? 0 : -1,\n disabled: tab.disabled ?? false,\n dataset: {id: tab.id},\n });\n\n if (tab.icon) button.appendChild(svgIcon(tab.icon, 14));\n button.appendChild(el(\"span\", {class: \"mac-tabview__label\", text: tab.label}));\n\n const panel = el(\"div\", {\n class: \"mac-tabview__panel\",\n role: \"tabpanel\",\n id: panelId,\n \"aria-labelledby\": tabId,\n // Focusable as a fallback: a panel with no focusable content of\n // its own must still be reachable with Tab.\n tabindex: 0,\n hidden: !selected,\n });\n\n this.tabButtons.set(tab.id, button);\n this.panels.set(tab.id, panel);\n\n this.listNode.appendChild(button);\n this.panelHost.appendChild(panel);\n }\n\n // Delegated activation: constant listener count however many tabs exist.\n this.listen(this.listNode, \"click\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-tabview__tab\",\n );\n if (!button || button.disabled) return;\n const id = button.dataset.id;\n if (!id || id === this.selectedId) return;\n this.apply(id);\n this.onSelect?.(id);\n });\n\n this.listen(this.listNode, \"keydown\", (event) => this.onKeyDown(event));\n\n this.root.append(this.listNode, this.panelHost);\n }\n\n /**\n * Attach content to a tab.\n *\n * Replacing the panel's children keeps repeated calls idempotent instead of\n * stacking content. An unknown id is ignored: the panel set is fixed when\n * the view is constructed, so a typo must not create an orphan panel that\n * no tab can ever reveal.\n */\n addPanel(id: string, node: Node): void {\n const panel = this.panels.get(id);\n if (!panel) return;\n panel.replaceChildren(node);\n }\n\n /** Select a tab programmatically. Does not fire `onSelect`. */\n setSelected(id: string): void {\n if (id === this.selectedId) return;\n const tab = this.tabs.find((entry) => entry.id === id);\n if (!tab || tab.disabled) return;\n this.apply(id);\n }\n\n /** The selected tab id, or an empty string when no tab is selectable. */\n get selected(): string {\n return this.selectedId;\n }\n\n /** Write the selection: state, ARIA, roving tabindex and the visible panel. */\n private apply(id: string): void {\n this.selectedId = id;\n\n for (const [tabId, button] of this.tabButtons) {\n const match = tabId === id;\n button.classList.toggle(\"is-selected\", match);\n button.setAttribute(\"aria-selected\", String(match));\n button.tabIndex = match ? 0 : -1;\n }\n\n for (const [panelId, panel] of this.panels) {\n panel.hidden = panelId !== id;\n }\n }\n\n /** Arrow keys, Home and End: move selection and focus as one gesture. */\n private onKeyDown(event: KeyboardEvent): void {\n const enabled = this.tabs.filter(\n (tab) => !tab.disabled && this.tabButtons.has(tab.id),\n );\n if (enabled.length === 0) return;\n\n const current = enabled.findIndex((tab) => tab.id === this.selectedId);\n let next = current;\n\n switch (event.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n next = current === -1 ? 0 : (current + 1) % enabled.length;\n break;\n case \"ArrowLeft\":\n case \"ArrowUp\":\n next =\n current === -1\n ? enabled.length - 1\n : (current - 1 + enabled.length) % enabled.length;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = enabled.length - 1;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n const target = enabled[next];\n this.apply(target.id);\n // Focus follows the selection (automatic activation), so the roving\n // tabindex and the focus ring never point at different tabs.\n this.tabButtons.get(target.id)?.focus();\n this.onSelect?.(target.id);\n }\n\n override destroy(): void {\n this.panels.clear();\n this.tabButtons.clear();\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon} from \"../dom\";\nimport {\n nextZIndex,\n trackOverlay,\n type Placement,\n} from \"../overlay\";\nimport \"./menu.css\";\n\nexport interface MenuItem {\n label: string;\n value: string;\n /** Leading SVG path data. */\n icon?: string;\n disabled?: boolean;\n /** Show a checkmark (set `role=menuitemcheckbox` semantics). */\n checked?: boolean;\n /** Draw a separator instead of an item. */\n separator?: boolean;\n /** Grey trailing text, e.g. \"⌘S\". */\n shortcut?: string;\n}\n\nexport interface MenuOptions {\n items: MenuItem[];\n placement?: Placement;\n /** Fires with the chosen value. */\n onSelect?: (value: string) => void;\n}\n\nconst CHECK_PATH = \"M3 7 L5.5 9.5 L11 4\";\n\n/**\n * A macOS pop-up menu.\n *\n * Appended to `document.body` rather than the trigger's parent: a menu inside a\n * scrolling pane or an `overflow: hidden` card would otherwise be clipped.\n *\n * Keyboard support follows the native menu: arrows move the highlight, Home/End\n * jump, Enter selects, Escape closes and returns focus to the trigger.\n */\nexport class Menu extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly items: MenuItem[];\n private readonly onSelect?: (value: string) => void;\n private readonly menuItems: HTMLButtonElement[] = [];\n\n private opened = false;\n private disposeTracking: (() => void) | null = null;\n private restoreFocusTo: HTMLElement | null = null;\n private highlighted = -1;\n\n constructor(options: MenuOptions) {\n super();\n\n this.items = options.items;\n this.onSelect = options.onSelect;\n\n this.root = el(\"div\", {\n class: \"mac-menu\",\n role: \"menu\",\n tabindex: -1,\n });\n this.root.style.display = \"none\";\n this.root.style.visibility = \"hidden\";\n this.root.dataset.placement = options.placement ?? \"bottom-start\";\n\n for (const item of this.items) {\n if (item.separator) {\n this.root.appendChild(\n el(\"div\", {class: \"mac-menu__separator\", role: \"separator\"}),\n );\n continue;\n }\n\n const button = el(\"button\", {\n class: \"mac-menu__item\",\n type: \"button\",\n role: item.checked === undefined ? \"menuitem\" : \"menuitemcheckbox\",\n disabled: item.disabled ?? false,\n dataset: {value: item.value},\n });\n if (item.checked !== undefined) {\n button.setAttribute(\"aria-checked\", String(item.checked));\n }\n\n // Leading slot: a checkmark for toggleable items, otherwise an icon.\n if (item.checked !== undefined) {\n const check = el(\"span\", {class: \"mac-menu__check\"});\n if (item.checked) check.appendChild(svgIcon(CHECK_PATH, 14, \"0 0 14 14\"));\n button.appendChild(check);\n } else if (item.icon) {\n button.appendChild(svgIcon(item.icon, 14));\n }\n\n button.appendChild(el(\"span\", {class: \"mac-menu__label\", text: item.label}));\n\n if (item.shortcut) {\n button.appendChild(\n el(\"span\", {class: \"mac-menu__shortcut\", text: item.shortcut}),\n );\n }\n\n this.menuItems.push(button);\n this.root.appendChild(button);\n }\n\n // Delegated activation.\n this.listen(this.root, \"click\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-menu__item\",\n );\n if (!button || button.disabled) return;\n this.choose(button.dataset.value ?? \"\");\n });\n\n this.listen(this.root, \"keydown\", (event) => this.onKeyDown(event));\n\n // Pointer hover moves the highlight so click and keyboard agree.\n this.listen(this.root, \"mousemove\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-menu__item\",\n );\n if (!button || button.disabled) return;\n const index = this.menuItems.indexOf(button);\n if (index >= 0) this.highlight(index);\n });\n\n // Close on any click that lands outside the menu.\n this.listen(document, \"mousedown\", (event) => {\n if (!this.opened) return;\n const target = event.target as Node;\n if (!this.root.contains(target)) this.close();\n });\n\n // Escape closes no matter which element holds focus.\n this.listen(document, \"keydown\", (event) => {\n if (this.opened && event.key === \"Escape\") {\n event.preventDefault();\n this.close();\n }\n });\n }\n\n /** Open the menu anchored to an element. */\n open(anchor: HTMLElement): void {\n if (this.opened) return;\n\n this.restoreFocusTo = document.activeElement as HTMLElement | null;\n this.root.style.display = \"block\";\n this.root.style.visibility = \"hidden\";\n this.root.style.zIndex = String(nextZIndex());\n document.body.appendChild(this.root);\n\n this.disposeTracking = trackOverlay({\n anchor,\n panel: this.root,\n placement: (this.root.dataset.placement as Placement) ?? \"bottom-start\",\n });\n\n this.opened = true;\n // Force a frame so the scale/opacity transition runs from its start.\n requestAnimationFrame(() => {\n this.root.classList.add(\"is-open\");\n this.highlight(0);\n this.root.focus();\n });\n }\n\n /** Close the menu and restore focus to whatever opened it. */\n close(): void {\n if (!this.opened) return;\n this.opened = false;\n this.root.classList.remove(\"is-open\");\n\n this.disposeTracking?.();\n this.disposeTracking = null;\n\n const cleanup = () => {\n // A nested menu may have been opened from inside the transition.\n if (!this.opened) this.root.remove();\n };\n // Wait for the fade-out, but never rely on it firing.\n window.setTimeout(cleanup, 140);\n this.root.remove();\n this.highlight(-1);\n\n this.restoreFocusTo?.focus?.();\n this.restoreFocusTo = null;\n }\n\n /** Whether the menu is currently open. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n private choose(value: string): void {\n this.onSelect?.(value);\n this.close();\n }\n\n /** Move the keyboard highlight to an index, skipping disabled and separator\n rows. */\n private highlight(index: number): void {\n if (index === this.highlighted) return;\n\n this.menuItems[this.highlighted]?.classList.remove(\"is-highlighted\");\n this.highlighted = index;\n this.menuItems[index]?.classList.add(\"is-highlighted\");\n }\n\n /** Step the highlight by ±1, wrapping and skipping disabled entries. */\n private step(direction: 1 | -1): void {\n const selectable = this.menuItems\n .map((button, index) => ({button, index}))\n .filter((entry) => !entry.button.disabled);\n if (selectable.length === 0) return;\n\n const currentPos = selectable.findIndex((entry) => entry.index === this.highlighted);\n const nextPos =\n currentPos === -1\n ? direction === 1\n ? 0\n : selectable.length - 1\n : (currentPos + direction + selectable.length) % selectable.length;\n\n this.highlight(selectable[nextPos].index);\n }\n\n private onKeyDown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.step(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.step(-1);\n break;\n case \"Home\": {\n event.preventDefault();\n const first = this.menuItems.find((b) => !b.disabled);\n const index = first ? this.menuItems.indexOf(first) : -1;\n if (index >= 0) this.highlight(index);\n break;\n }\n case \"End\": {\n event.preventDefault();\n for (let i = this.menuItems.length - 1; i >= 0; i -= 1) {\n if (!this.menuItems[i].disabled) {\n this.highlight(i);\n break;\n }\n }\n break;\n }\n case \"Enter\":\n case \" \": {\n event.preventDefault();\n const active = this.menuItems[this.highlighted];\n if (active && !active.disabled) {\n this.choose(active.dataset.value ?? \"\");\n }\n break;\n }\n default:\n break;\n }\n }\n\n override destroy(): void {\n this.disposeTracking?.();\n this.disposeTracking = null;\n this.root.remove();\n super.destroy();\n }\n}\n\n/** Convenience: build a menu, open it at an anchor, and release it on close. */\nexport function showMenu(anchor: HTMLElement, options: MenuOptions): Menu {\n const menu = new Menu(options);\n menu.open(anchor);\n return menu;\n}\n","import {Component} from \"../component\";\nimport {el, uid} from \"../dom\";\nimport {nextZIndex, trackOverlay, type Placement} from \"../overlay\";\nimport \"./tooltip.css\";\n\nexport interface TooltipOptions {\n content: string;\n placement?: Placement;\n /** Delay before showing, in ms. macOS uses a noticeable pause. */\n delay?: number;\n}\n\n/**\n * A macOS tooltip.\n *\n * Two things make tooltips usable that hand-written ones usually miss:\n *\n * 1. **Focus triggers it too.** Keyboard and screen-reader users never hover,\n * so the tooltip opens on focus as well and the trigger is linked with\n * `aria-describedby`.\n * 2. **The delay cancels.** A quick pointer sweep across a toolbar must not\n * flash a tooltip on every button, so leaving clears the pending timer.\n */\nexport class Tooltip extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly content: string;\n private readonly placement: Placement;\n private readonly delay: number;\n\n private trigger: HTMLElement | null = null;\n private disposeTracking: (() => void) | null = null;\n private showTimer: ReturnType<typeof setTimeout> | null = null;\n private visible = false;\n\n /**\n * Listeners bound to the current trigger. Tracked separately from the\n * component's own list because `attachTo` can rebind many times over one\n * component's life; `listen()` alone would accumulate every binding.\n */\n private triggerListeners: Array<() => void> = [];\n\n constructor(options: TooltipOptions) {\n super();\n\n this.content = options.content;\n this.placement = options.placement ?? \"top\";\n this.delay = options.delay ?? 400;\n\n this.root = el(\"div\", {\n class: \"mac-tooltip\",\n role: \"tooltip\",\n id: uid(\"mac-tooltip\"),\n });\n this.root.textContent = this.content;\n this.root.style.display = \"none\";\n }\n\n /** Bind the tooltip to an element. A tooltip serves one trigger at a time. */\n attachTo(trigger: HTMLElement): void {\n this.detach();\n this.trigger = trigger;\n trigger.setAttribute(\"aria-describedby\", this.root.id);\n\n const bind = <K extends keyof HTMLElementEventMap>(\n type: K,\n handler: (event: HTMLElementEventMap[K]) => void,\n ) => {\n trigger.addEventListener(type, handler);\n this.triggerListeners.push(() => trigger.removeEventListener(type, handler));\n };\n\n bind(\"mouseenter\", () => this.schedule());\n bind(\"mouseleave\", () => this.close());\n bind(\"focus\", () => this.schedule());\n bind(\"blur\", () => this.close());\n // A click means the user committed; the tooltip is in the way.\n bind(\"mousedown\", () => this.close());\n }\n\n /** Unbind from the current trigger and remove the tooltip. */\n detach(): void {\n if (this.trigger) {\n this.trigger.removeAttribute(\"aria-describedby\");\n this.trigger = null;\n }\n this.cancelTimer();\n this.close();\n for (const cleanup of this.triggerListeners.splice(0)) cleanup();\n }\n\n private schedule(): void {\n this.cancelTimer();\n this.showTimer = setTimeout(() => this.open(), this.delay);\n }\n\n private cancelTimer(): void {\n if (this.showTimer !== null) {\n clearTimeout(this.showTimer);\n this.showTimer = null;\n }\n }\n\n /** Show the bubble next to the trigger. */\n open(): void {\n if (this.visible || !this.trigger) return;\n\n this.root.style.display = \"block\";\n this.root.style.visibility = \"hidden\";\n this.root.style.zIndex = String(nextZIndex());\n document.body.appendChild(this.root);\n\n this.disposeTracking = trackOverlay({\n anchor: this.trigger,\n panel: this.root,\n placement: this.placement,\n offset: 8,\n });\n\n this.visible = true;\n requestAnimationFrame(() => this.root.classList.add(\"is-open\"));\n }\n\n /** Hide the bubble. */\n close(): void {\n this.cancelTimer();\n if (!this.visible) return;\n this.visible = false;\n\n this.root.classList.remove(\"is-open\");\n this.disposeTracking?.();\n this.disposeTracking = null;\n this.root.remove();\n }\n\n override destroy(): void {\n this.triggerListeners.splice(0).forEach((cleanup) => cleanup());\n this.cancelTimer();\n this.disposeTracking?.();\n this.disposeTracking = null;\n this.root.remove();\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el, uid, type Child} from \"../dom\";\nimport {trapFocus} from \"../overlay\";\nimport {Button} from \"../controls/button\";\nimport \"./dialog.css\";\n\nexport interface DialogOptions {\n title: string;\n /** Body content: a string, or any node for rich content. */\n body?: Child;\n confirmLabel?: string;\n cancelLabel?: string;\n /** Fires when the user confirms. */\n onConfirm?: () => void;\n /** Fires when the user cancels, presses Escape or clicks the scrim. */\n onCancel?: () => void;\n}\n\n/**\n * A modal macOS alert.\n *\n * Three things a dialog must get right, and this one does because it delegates\n * to `trapFocus` from overlay.ts:\n *\n * - Tab cycles inside the dialog instead of escaping to the page behind it.\n * - Escape closes it, which is what a mac user reaches for.\n * - Focus returns to whatever had it before the dialog opened.\n *\n * The scrim absorbs clicks on the \"outside\", treating them as a cancel.\n */\nexport class Dialog extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly scrim: HTMLDivElement;\n private readonly onCancel?: () => void;\n private readonly onConfirm?: () => void;\n\n private releaseFocus: (() => void) | null = null;\n private opened = false;\n\n constructor(options: DialogOptions) {\n super();\n\n const {\n title,\n body,\n confirmLabel = \"确定\",\n cancelLabel = \"取消\",\n onConfirm,\n onCancel,\n } = options;\n\n this.onConfirm = onConfirm;\n this.onCancel = onCancel;\n\n const titleId = uid(\"mac-dialog-title\");\n\n this.scrim = el(\"div\", {class: \"mac-dialog__scrim\"});\n\n this.root = el(\"div\", {\n class: \"mac-dialog\",\n role: \"dialog\",\n \"aria-modal\": \"true\",\n \"aria-labelledby\": titleId,\n tabindex: -1,\n });\n\n const titleNode = el(\"h2\", {class: \"mac-dialog__title\", text: title, id: titleId});\n const bodyNode = el(\"div\", {class: \"mac-dialog__body\"});\n\n if (typeof body === \"string\" || typeof body === \"number\") {\n bodyNode.textContent = String(body);\n } else if (body) {\n bodyNode.appendChild(body);\n }\n\n const actions = el(\"div\", {class: \"mac-dialog__actions\"});\n const cancelButton = new Button({label: cancelLabel, onClick: () => this.cancel()});\n const confirmButton = new Button({\n label: confirmLabel,\n variant: \"primary\",\n onClick: () => this.confirm(),\n });\n actions.append(cancelButton.element, confirmButton.element);\n\n this.root.append(titleNode, bodyNode, actions);\n this.scrim.appendChild(this.root);\n\n // A click on the scrim (but not inside the dialog) cancels.\n this.listen(this.scrim, \"mousedown\", (event) => {\n if (event.target === this.scrim) this.cancel();\n });\n\n // Escape is captured on the document so it works regardless of focus.\n this.listen(document, \"keydown\", (event) => {\n if (this.opened && event.key === \"Escape\") {\n event.preventDefault();\n this.cancel();\n }\n });\n }\n\n /** Show the dialog modally. */\n open(): void {\n if (this.opened) return;\n this.opened = true;\n\n document.body.appendChild(this.scrim);\n this.releaseFocus = trapFocus(this.root);\n\n requestAnimationFrame(() => this.scrim.classList.add(\"is-open\"));\n }\n\n /** Hide the dialog and restore focus. */\n close(): void {\n if (!this.opened) return;\n this.opened = false;\n\n this.scrim.classList.remove(\"is-open\");\n this.releaseFocus?.();\n this.releaseFocus = null;\n\n // Remove after the fade; also handle a rapid re-open.\n setTimeout(() => {\n if (!this.opened) this.scrim.remove();\n }, 200);\n }\n\n /** Confirm and close. */\n confirm(): void {\n this.onConfirm?.();\n this.close();\n }\n\n /** Cancel and close. */\n cancel(): void {\n this.onCancel?.();\n this.close();\n }\n\n /** Whether the dialog is on screen. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n override destroy(): void {\n this.releaseFocus?.();\n this.releaseFocus = null;\n this.scrim.remove();\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport {\n focusableWithin,\n nextZIndex,\n positionOverlay,\n trackOverlay,\n type Placement,\n type PositionOptions,\n} from \"../overlay\";\nimport \"./popover.css\";\n\nexport interface PopoverOptions {\n /** Arbitrary content: a string becomes text, a node is adopted as-is. */\n content: Node | string;\n /** Preferred side; flips automatically when there is no room. */\n placement?: Placement;\n /** Gap between the anchor and the panel, in px. */\n offset?: number;\n /** Draw a pointer aimed at the anchor. */\n arrow?: boolean;\n /** Accessible name for the dialog; a popover has no title of its own. */\n ariaLabel?: string;\n /** Fires whenever the popover closes, whatever closed it. */\n onClose?: () => void;\n}\n\n/**\n * How long the fade-out runs before the panel is detached. Slightly longer than\n * `--mac-duration-fast` (120ms) so the transition finishes first; under\n * `prefers-reduced-motion` the durations are 0ms and this is just a short delay.\n */\nconst EXIT_MS = 160;\n\n/**\n * A macOS popover: arbitrary content attached to an anchor element.\n *\n * This is the generalisation of `Menu` — the same anchoring plumbing, but the\n * content is the caller's and the panel is non-modal, so the page behind stays\n * interactive. Three consequences shape the implementation:\n *\n * - The panel is appended to `document.body`, never inside the trigger: a\n * popover opened from a scrolling pane or an `overflow: hidden` card would\n * otherwise be clipped.\n * - It carries `role=\"dialog\"` with `aria-modal=\"false\"`, so assistive tech\n * announces a dialog without promising that the rest of the page is inert.\n * - Focus returns to the anchor on close, but only while the popover still owns\n * it — clicking another field must not yank focus back.\n *\n * The markup is root → surface → body. The split exists because\n * `positionOverlay` measures with `getBoundingClientRect`, which *includes*\n * transforms: any entrance transform on the element being positioned would make\n * the overlay place itself against a shrunken box. The root therefore stays\n * untransformed and is what overlay.ts positions; only `__surface` animates.\n */\nexport class Popover extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly placement: Placement;\n private readonly offset: number;\n private readonly onClose?: () => void;\n private readonly surface: HTMLDivElement;\n private readonly arrowNode: HTMLSpanElement | null = null;\n\n private anchor: HTMLElement | null = null;\n private opened = false;\n private disposeTracking: (() => void) | null = null;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n private arrowFrame = 0;\n\n constructor(options: PopoverOptions) {\n super();\n\n const {\n content,\n placement = \"bottom\",\n offset = 8,\n arrow = false,\n ariaLabel,\n onClose,\n } = options;\n\n this.placement = placement;\n this.offset = offset;\n this.onClose = onClose;\n\n this.root = el(\"div\", {\n class: \"mac-popover\",\n role: \"dialog\",\n \"aria-modal\": \"false\",\n tabindex: -1,\n });\n // Hidden until `open` places it; positionOverlay then makes it a block.\n this.root.style.display = \"none\";\n if (ariaLabel) this.root.setAttribute(\"aria-label\", ariaLabel);\n\n this.surface = el(\"div\", {class: \"mac-popover__surface\"});\n\n const body = el(\"div\", {class: \"mac-popover__body\"});\n if (typeof content === \"string\") {\n body.textContent = content;\n } else {\n body.appendChild(content);\n }\n this.surface.appendChild(body);\n\n if (arrow) {\n // Inside the surface so the pointer animates with the panel it\n // belongs to, instead of hanging in place while the panel slides.\n this.arrowNode = el(\"span\", {\n class: \"mac-popover__arrow\",\n \"aria-hidden\": \"true\",\n });\n this.surface.appendChild(this.arrowNode);\n }\n\n this.root.appendChild(this.surface);\n\n // A press outside dismisses. The anchor is excluded: its own handler\n // usually toggles, and closing here would race it into a double toggle.\n this.listen(document, \"mousedown\", (event) => {\n if (!this.opened) return;\n const target = event.target as Node | null;\n if (!target || this.root.contains(target) || this.anchor?.contains(target)) return;\n this.close();\n });\n\n // Escape works no matter which element inside currently has focus.\n this.listen(document, \"keydown\", (event) => {\n if (!this.opened || event.key !== \"Escape\") return;\n event.preventDefault();\n this.close();\n });\n\n // The panel follows the anchor while the page scrolls or the window\n // resizes; the pointer has to be re-aimed after that re-placement.\n this.listen(window, \"scroll\", () => this.scheduleArrowUpdate(), {capture: true});\n this.listen(window, \"resize\", () => this.scheduleArrowUpdate());\n\n // The tracker disposer from overlay.ts is owned here rather than in\n // open/close alone: this single entry always releases the current\n // tracker, so open/close cycles cannot pile up cleanup closures.\n this.onDestroy(() => {\n this.disposeTracking?.();\n this.disposeTracking = null;\n });\n }\n\n /** Show the popover next to an anchor. Re-opening with a new anchor moves it. */\n open(anchor: HTMLElement): void {\n if (this.closeTimer !== null) {\n window.clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n\n if (this.opened) {\n if (this.anchor !== anchor) {\n // The tracker closed over the previous anchor, so it has to be\n // replaced; reusing it would keep placing against the old one.\n this.anchor = anchor;\n this.place();\n this.positionArrow();\n this.disposeTracking?.();\n this.disposeTracking = trackOverlay(this.positionOptions(anchor));\n }\n return;\n }\n\n this.opened = true;\n this.anchor = anchor;\n\n this.root.style.display = \"block\";\n this.root.style.zIndex = String(nextZIndex());\n document.body.appendChild(this.root);\n\n // Place synchronously so the first painted frame is already correct and\n // the entrance only fades and slides in place.\n this.place();\n this.positionArrow();\n this.disposeTracking = trackOverlay(this.positionOptions(anchor));\n\n requestAnimationFrame(() => {\n if (!this.opened) return;\n this.root.classList.add(\"is-open\");\n this.focusInto();\n });\n }\n\n /** Hide the popover and hand focus back to the anchor. */\n close(): void {\n if (!this.opened) return;\n this.opened = false;\n\n this.root.classList.remove(\"is-open\");\n this.disposeTracking?.();\n this.disposeTracking = null;\n this.cancelArrowUpdate();\n\n const anchor = this.anchor;\n this.anchor = null;\n\n // Restore the trigger only if the popover still owns focus. If the user\n // clicked another control, that control keeps it.\n const active = document.activeElement as HTMLElement | null;\n if (!active || active === document.body || this.root.contains(active)) {\n anchor?.focus?.();\n }\n\n // Stay in the DOM for the fade-out, then drop out of the body entirely.\n this.closeTimer = window.setTimeout(() => {\n this.closeTimer = null;\n if (!this.opened) this.root.remove();\n }, EXIT_MS);\n\n this.onClose?.();\n }\n\n /** Whether the popover is currently on screen. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n /** Position options shared by the one-shot placement and the tracker. */\n private positionOptions(anchor: HTMLElement): PositionOptions {\n return {anchor, panel: this.root, placement: this.placement, offset: this.offset};\n }\n\n /** Resolve the panel against the current anchor. */\n private place(): void {\n if (!this.anchor) return;\n positionOverlay(this.positionOptions(this.anchor));\n }\n\n /**\n * Re-aim the pointer one frame after a scroll or resize.\n *\n * Placement is itself rAF-throttled inside `trackOverlay`, and this callback\n * may run before that pass, so it re-places first: measuring the panel where\n * it is about to be drawn is the only reading that matters.\n */\n private scheduleArrowUpdate(): void {\n if (!this.opened || !this.arrowNode || this.arrowFrame !== 0) return;\n this.arrowFrame = requestAnimationFrame(() => {\n this.arrowFrame = 0;\n if (!this.opened || !this.arrowNode) return;\n this.place();\n this.positionArrow();\n });\n }\n\n private cancelArrowUpdate(): void {\n if (this.arrowFrame === 0) return;\n cancelAnimationFrame(this.arrowFrame);\n this.arrowFrame = 0;\n }\n\n /**\n * Aim the pointer at the anchor's centre.\n *\n * Only the position *along* the panel's edge is measured here; CSS owns the\n * side, taken from `data-side`, which positionOverlay sets. Both ends are\n * clamped by half the arrow's own width, so a rotated square never pokes\n * past a rounded corner — and no pixel constant has to be invented for it.\n *\n * `offsetLeft`/`offsetTop` are used instead of a rect because they report\n * layout position and ignore transforms: the surface is mid-transition when\n * this first runs, and a rect would describe the sliding box rather than the\n * box the pointer is attached to. `clientLeft`/`clientWidth` then move the\n * origin to the padding edge, which is what an absolutely positioned child\n * is offset from — so the surface's own hairline border cannot skew it.\n */\n private positionArrow(): void {\n const arrow = this.arrowNode;\n if (!arrow || !this.anchor) return;\n\n const boxLeft = this.surface.offsetLeft + this.surface.clientLeft;\n const boxTop = this.surface.offsetTop + this.surface.clientTop;\n const boxWidth = this.surface.clientWidth;\n const boxHeight = this.surface.clientHeight;\n if (boxWidth === 0 || boxHeight === 0) return;\n\n const anchorRect = this.anchor.getBoundingClientRect();\n const half = arrow.offsetWidth / 2;\n const side = this.root.dataset.side ?? \"bottom\";\n\n if (side === \"top\" || side === \"bottom\") {\n const centre = anchorRect.left + anchorRect.width / 2 - boxLeft;\n const x = Math.max(half, Math.min(centre, boxWidth - half));\n this.surface.style.setProperty(\"--mac-popover-arrow-x\", `${Math.round(x)}px`);\n } else {\n const centre = anchorRect.top + anchorRect.height / 2 - boxTop;\n const y = Math.max(half, Math.min(centre, boxHeight - half));\n this.surface.style.setProperty(\"--mac-popover-arrow-y\", `${Math.round(y)}px`);\n }\n }\n\n /** Put focus on the first control inside, or on the panel itself. */\n private focusInto(): void {\n const focusable = focusableWithin(this.root);\n (focusable[0] ?? this.root).focus();\n }\n\n override destroy(): void {\n if (this.closeTimer !== null) {\n window.clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n this.cancelArrowUpdate();\n this.root.remove();\n // Releases the listeners and the tracking disposer registered above.\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el, uid, type Child} from \"../dom\";\nimport {trapFocus} from \"../overlay\";\nimport {Button} from \"../controls/button\";\nimport \"./sheet.css\";\n\nexport interface SheetOptions {\n /** Sheet heading, also the accessible name of the dialog. */\n title: string;\n /** Body content: a string, or any node for rich content. */\n body?: Child;\n confirmLabel?: string;\n cancelLabel?: string;\n /** Fires when the user confirms. */\n onConfirm?: () => void;\n /** Fires when the user cancels, presses Escape or clicks the scrim. */\n onCancel?: () => void;\n}\n\n/**\n * How long the slide-out runs before the sheet leaves the document. Slightly\n * longer than `--mac-duration-slow` (260ms) so the transition always finishes\n * first; under `prefers-reduced-motion` that token is 0ms and only this short\n * delay remains.\n */\nconst EXIT_MS = 300;\n\n/**\n * A macOS sheet: a modal attached to the window's top edge that slides down.\n *\n * Sheets differ from `Dialog` in two ways that shape the implementation:\n *\n * - **Geometry.** A dialog is centred in the viewport; a sheet is pinned to the\n * top and grows downward, so the window's title bar stays visible behind it.\n * That is the point of the pattern: it reads as belonging to *this* window\n * rather than to the application.\n * - **Motion.** The entrance is `transform: translateY()` on the panel, never\n * `top` or `height`. A compositor-animated transform leaves the sheet's own\n * layout untouched, so a form inside it does not reflow while it slides in.\n *\n * Modal behaviour is delegated to `trapFocus` from overlay.ts: Tab cycles inside\n * the sheet, and the disposer restores focus to whatever had it before.\n */\nexport class Sheet extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly scrim: HTMLDivElement;\n private readonly panel: HTMLDivElement;\n private readonly bodyNode: HTMLDivElement;\n private readonly onConfirm?: () => void;\n private readonly onCancel?: () => void;\n\n private releaseFocus: (() => void) | null = null;\n private opened = false;\n private closeTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(options: SheetOptions) {\n super();\n\n const {\n title,\n body,\n confirmLabel = \"确定\",\n cancelLabel = \"取消\",\n onConfirm,\n onCancel,\n } = options;\n\n this.onConfirm = onConfirm;\n this.onCancel = onCancel;\n\n const titleId = uid(\"mac-sheet-title\");\n\n // The root *is* the dialog: it carries the role, the modality and the\n // accessible name. The scrim inside it is decorative and hidden from\n // assistive tech, so it cannot be mistaken for dialog content.\n this.root = el(\"div\", {\n class: \"mac-sheet\",\n role: \"dialog\",\n \"aria-modal\": \"true\",\n \"aria-labelledby\": titleId,\n tabindex: -1,\n });\n\n this.scrim = el(\"div\", {\"class\": \"mac-sheet__scrim\", \"aria-hidden\": \"true\"});\n\n // The panel is the element that actually slides; the root only pins the\n // group to the window top.\n this.panel = el(\"div\", {class: \"mac-sheet__panel\"});\n\n const titleNode = el(\"h2\", {class: \"mac-sheet__title\", id: titleId, text: title});\n\n this.bodyNode = el(\"div\", {class: \"mac-sheet__body\"});\n if (typeof body === \"string\" || typeof body === \"number\") {\n this.bodyNode.textContent = String(body);\n } else if (body) {\n this.bodyNode.appendChild(body);\n }\n\n const actions = el(\"div\", {class: \"mac-sheet__actions\"});\n const cancelButton = new Button({label: cancelLabel, onClick: () => this.cancel()});\n const confirmButton = new Button({\n label: confirmLabel,\n variant: \"primary\",\n onClick: () => this.confirm(),\n });\n actions.append(cancelButton.element, confirmButton.element);\n\n this.panel.append(titleNode, this.bodyNode, actions);\n this.root.append(this.scrim, this.panel);\n\n // A press on the scrim cancels; the panel stops its own presses, so\n // `event.target` can only be the scrim here.\n this.listen(this.scrim, \"mousedown\", () => this.cancel());\n\n // Escape is caught on the document so it works wherever focus sits,\n // including on a control the host injected into the body.\n this.listen(document, \"keydown\", (event) => {\n if (!this.opened || event.key !== \"Escape\") return;\n event.preventDefault();\n this.cancel();\n });\n\n // The focus trap's disposer is owned here rather than only in `close`,\n // so destroying an open sheet still releases the trap and the node.\n this.onDestroy(() => {\n this.releaseFocus?.();\n this.releaseFocus = null;\n this.root.remove();\n });\n }\n\n /** Slide the sheet in. Idempotent. */\n open(): void {\n if (this.closeTimer !== null) {\n window.clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n if (this.opened) return;\n this.opened = true;\n\n document.body.appendChild(this.root);\n this.releaseFocus = trapFocus(this.root);\n\n // Force the closed state to be laid out before the open class lands,\n // otherwise the browser coalesces both styles and nothing animates.\n void this.panel.offsetHeight;\n requestAnimationFrame(() => {\n if (this.opened) this.root.classList.add(\"is-open\");\n });\n }\n\n /** Slide the sheet out and restore focus. Idempotent. */\n close(): void {\n if (!this.opened) return;\n this.opened = false;\n\n this.root.classList.remove(\"is-open\");\n\n this.releaseFocus?.();\n this.releaseFocus = null;\n\n // Stay mounted for the slide-out, then drop out of the body entirely.\n this.closeTimer = window.setTimeout(() => {\n this.closeTimer = null;\n if (!this.opened) this.root.remove();\n }, EXIT_MS);\n }\n\n /** Confirm and close. */\n confirm(): void {\n this.onConfirm?.();\n this.close();\n }\n\n /** Cancel and close. */\n cancel(): void {\n this.onCancel?.();\n this.close();\n }\n\n /** Whether the sheet is on screen. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n /** The body element, for callers that fill it after construction. */\n get content(): HTMLElement {\n return this.bodyNode;\n }\n\n override destroy(): void {\n if (this.closeTimer !== null) {\n window.clearTimeout(this.closeTimer);\n this.closeTimer = null;\n }\n // Releases the listeners and the focus trap registered above.\n super.destroy();\n }\n}\n","import {Component} from \"../component\";\nimport {el} from \"../dom\";\nimport \"./toast.css\";\n\nexport type ToastVariant = \"default\" | \"success\" | \"error\";\n\nexport interface ToastOptions {\n message?: string;\n /** Secondary line under the message. */\n detail?: string;\n /** Auto-dismiss delay in ms; 0 keeps it until dismissed. */\n duration?: number;\n variant?: ToastVariant;\n}\n\n/**\n * The shared container every toast mounts into.\n *\n * Toasts stack, so they cannot each own a fixed-position wrapper: they would\n * overlap. One lazily created stack keeps them in document order and lets the\n * oldest disappear from the top of the column.\n */\nlet stack: HTMLDivElement | null = null;\n\nfunction ensureStack(): HTMLDivElement {\n if (stack && stack.isConnected) return stack;\n\n stack = el(\"div\", {class: \"mac-toast-stack\"});\n document.body.appendChild(stack);\n\n // Drop the container once the last toast leaves, so an idle app has no\n // stray fixed-position node intercepting nothing.\n const observer = new MutationObserver(() => {\n if (stack && stack.childElementCount === 0) {\n stack.remove();\n stack = null;\n observer.disconnect();\n }\n });\n observer.observe(stack, {childList: true});\n\n return stack;\n}\n\n/**\n * A macOS-style toast.\n *\n * Announced through `role=\"status\"` with `aria-live=\"polite\"`, so screen\n * readers read it without stealing focus from whatever the user is doing.\n */\nexport class Toast extends Component<HTMLDivElement> {\n protected readonly root: HTMLDivElement;\n\n private readonly messageNode: HTMLDivElement;\n private readonly detailNode: HTMLDivElement;\n private duration: number;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private visible = false;\n\n constructor(options: ToastOptions = {}) {\n super();\n\n this.duration = options.duration ?? 4000;\n\n const classes = [\"mac-toast\"];\n if (options.variant && options.variant !== \"default\") {\n classes.push(`mac-toast--${options.variant}`);\n }\n\n this.root = el(\"div\", {\n class: classes.join(\" \"),\n role: \"status\",\n \"aria-live\": \"polite\",\n });\n\n this.messageNode = el(\"div\", {\n class: \"mac-toast__message\",\n text: options.message ?? \"\",\n });\n this.detailNode = el(\"div\", {\n class: \"mac-toast__detail\",\n text: options.detail ?? \"\",\n });\n if (!options.detail) this.detailNode.style.display = \"none\";\n\n this.root.append(this.messageNode, this.detailNode);\n }\n\n /** Show the toast, replacing its text. Mounts itself into the shared stack. */\n show(message?: string, detail?: string): void {\n if (message !== undefined) this.messageNode.textContent = message;\n if (detail !== undefined) {\n this.detailNode.textContent = detail;\n this.detailNode.style.display = detail ? \"\" : \"none\";\n }\n\n if (!this.visible) {\n ensureStack().appendChild(this.root);\n this.visible = true;\n // Next frame, so the entrance transition has a start state.\n requestAnimationFrame(() => this.root.classList.add(\"is-open\"));\n }\n\n this.scheduleDismiss();\n }\n\n /** Update the auto-dismiss delay for subsequent shows. */\n setDuration(duration: number): void {\n this.duration = duration;\n }\n\n /** Dismiss immediately. */\n hide(): void {\n this.clearTimer();\n if (!this.visible) return;\n this.visible = false;\n\n this.root.classList.remove(\"is-open\");\n // Remove after the fade so the stack does not jump mid-animation.\n setTimeout(() => {\n if (!this.visible) this.root.remove();\n }, 200);\n }\n\n private scheduleDismiss(): void {\n this.clearTimer();\n if (this.duration <= 0) return;\n this.timer = setTimeout(() => this.hide(), this.duration);\n }\n\n private clearTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n override destroy(): void {\n this.clearTimer();\n this.root.remove();\n super.destroy();\n }\n}\n\n/** Fire-and-forget helper for the common case. */\nexport function showToast(message: string, options: Omit<ToastOptions, \"message\"> = {}): Toast {\n const toast = new Toast({...options, message});\n toast.show();\n return toast;\n}\n","import {Component} from \"../component\";\nimport {el, svgIcon} from \"../dom\";\nimport \"./shell.css\";\n\nexport interface MobileTab {\n /** Stable key. */\n id: string;\n /** Short label under the icon. */\n label: string;\n /** SVG path data for the tab icon. */\n icon: string;\n}\n\nexport interface MobileShellOptions {\n /** Large navigation title shown above the content. */\n title: string;\n /** Tab bar entries. Empty hides the bar (a single-pane screen). */\n tabs?: MobileTab[];\n /** Initially visible tab. */\n selected?: string;\n /** Fired when the tab changes. */\n onTabChange?: (id: string) => void;\n /** Optional trailing action on the navigation bar. */\n action?: HTMLElement;\n}\n\n/**\n * The mobile counterpart of the desktop shell.\n *\n * Deliberately separate from `layout/TitleBar` rather than a media-query variant\n * of it. A phone has no draggable window, no traffic lights and no persistent\n * sidebar; navigation lives in a bottom tab bar, which is the platform-native\n * pattern. Sharing markup between the two would mean every style rule carrying\n * a device exception, which is harder to reason about than two focused modules.\n *\n * Panes are plain containers the host app fills, so this stays layout-only.\n */\nexport class MobileShell extends Component<HTMLElement> {\n protected readonly root: HTMLElement;\n\n private readonly titleNode: HTMLHeadingElement;\n private readonly contentNode: HTMLDivElement;\n private readonly tabBar: HTMLElement | null;\n private readonly onTabChange?: (id: string) => void;\n private currentId: string | null;\n\n constructor(options: MobileShellOptions) {\n super();\n\n const {title, tabs = [], selected, onTabChange, action} = options;\n\n this.currentId = selected ?? tabs[0]?.id ?? null;\n this.onTabChange = onTabChange;\n\n this.root = el(\"div\", {class: \"mac-mobile\"});\n\n // --- Navigation bar -------------------------------------------------\n const navbar = el(\"header\", {class: \"mac-mobile__navbar\"});\n this.titleNode = el(\"h1\", {class: \"mac-mobile__title\", text: title});\n navbar.appendChild(this.titleNode);\n\n if (action) {\n const slot = el(\"div\", {class: \"mac-mobile__action\"});\n slot.appendChild(action);\n navbar.appendChild(slot);\n }\n\n // --- Content region --------------------------------------------------\n this.contentNode = el(\"div\", {class: \"mac-mobile__content\"});\n // --- Tab bar ---------------------------------------------------------\n const tabBar = tabs.length > 0\n ? el(\"nav\", {class: \"mac-mobile__tabbar\", role: \"tablist\"})\n : null;\n this.tabBar = tabBar;\n\n if (tabBar) {\n for (const tab of tabs) {\n const button = el(\"button\", {\n class: `mac-mobile__tab${tab.id === this.currentId ? \" is-selected\" : \"\"}`,\n type: \"button\",\n role: \"tab\",\n \"aria-selected\": String(tab.id === this.currentId),\n dataset: {id: tab.id},\n });\n button.append(svgIcon(tab.icon, 22), el(\"span\", {text: tab.label}));\n tabBar.appendChild(button);\n }\n\n // Delegated: one listener regardless of tab count.\n this.listen(tabBar, \"click\", (event) => {\n const button = (event.target as HTMLElement).closest<HTMLButtonElement>(\n \".mac-mobile__tab\",\n );\n if (!button) return;\n const id = button.dataset.id;\n if (id) this.setSelected(id, true);\n });\n }\n\n this.root.append(navbar, this.contentNode);\n if (tabBar) this.root.appendChild(tabBar);\n }\n\n /** The region host panes are appended to. */\n get content(): HTMLElement {\n return this.contentNode;\n }\n\n /** Update the large title. */\n setTitle(title: string): void {\n if (this.titleNode.textContent === title) return;\n this.titleNode.textContent = title;\n }\n\n /** Switch the active tab. */\n setSelected(id: string, notify = false): void {\n if (this.currentId === id) return;\n this.currentId = id;\n\n if (this.tabBar) {\n for (const node of this.tabBar.querySelectorAll<HTMLButtonElement>(\".mac-mobile__tab\")) {\n const match = node.dataset.id === id;\n node.classList.toggle(\"is-selected\", match);\n node.setAttribute(\"aria-selected\", String(match));\n }\n }\n\n if (notify) this.onTabChange?.(id);\n }\n\n /** The active tab id, or null when the shell has no tabs. */\n get selected(): string | null {\n return this.currentId;\n }\n}\n\n/**\n * Detect whether this build is running inside a mobile Wails shell.\n *\n * Wails reports the host through `runtime.GOOS` on the Go side, but the\n * frontend also needs an answer before any binding round-trip, so this checks\n * the viewport instead: a coarse pointer plus a narrow window is a phone or\n * tablet. The host app can override the decision either way.\n */\nexport function looksLikeMobile(): boolean {\n if (typeof window === \"undefined\") return false;\n const coarse = window.matchMedia(\"(pointer: coarse)\").matches;\n const narrow = window.matchMedia(\"(max-width: 720px)\").matches;\n return coarse && narrow;\n}\n"],"mappings":"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"}