entasis 0.8.0 → 0.9.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 (27) hide show
  1. package/README.md +7 -1
  2. package/dist/components/AppShell/appShell.theme.js +17 -2
  3. package/dist/components/Form/Form/form.state.svelte.d.ts +4 -0
  4. package/dist/components/Form/Form/visibility.d.ts +2 -0
  5. package/dist/components/Form/MultiStepForm/multiStepForm.state.svelte.d.ts +4 -0
  6. package/dist/components/Form/Select/Select.svelte +28 -2
  7. package/dist/components/Form/Select/select.align.d.ts +55 -0
  8. package/dist/components/Form/Select/select.align.js +41 -0
  9. package/dist/components/Form/Select/select.mcp.d.ts +1 -1
  10. package/dist/components/Form/Select/select.mcp.js +7 -3
  11. package/dist/components/Form/Select/select.props.d.ts +8 -0
  12. package/dist/components/Form/Select/select.state.svelte.d.ts +24 -0
  13. package/dist/components/Form/Select/select.state.svelte.js +111 -1
  14. package/dist/components/PageShell/pageShell.theme.js +2 -2
  15. package/dist/components/Popover/Popover.svelte +4 -0
  16. package/dist/components/Popover/popover.mcp.d.ts +1 -1
  17. package/dist/components/Popover/popover.mcp.js +1 -0
  18. package/dist/components/Popover/popover.props.d.ts +16 -0
  19. package/dist/components/Popover/popover.state.svelte.d.ts +1 -1
  20. package/dist/components/Popover/popover.state.svelte.js +13 -0
  21. package/dist/components/Sidebar/Sidebar.svelte +6 -1
  22. package/dist/components/Sidebar/sidebar.mcp.d.ts +1 -1
  23. package/dist/components/Sidebar/sidebar.mcp.js +1 -1
  24. package/dist/components/Sidebar/sidebar.theme.d.ts +12 -0
  25. package/dist/components/Sidebar/sidebar.theme.js +27 -3
  26. package/dist/generated/componentMcpRegistry.d.ts +3 -3
  27. package/package.json +3 -3
@@ -25,7 +25,7 @@ export declare const componentMcpRegistry: {
25
25
  readonly stack: "\n# Stack\n\nStack arranges arbitrary content along one flex axis. Use the `orientation` prop to switch\nbetween horizontal and vertical layout, and `align` / `justify` for cross-axis and main-axis\nalignment.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n import { Stack } from '../components/Stack/index.ts';\n</script>\n```\n\n## Usage\n\n```svelte\n<Stack gap=\"xl\" padding=\"xl\">\n <h2>Account</h2>\n <Stack orientation=\"horizontal\" align=\"center\" gap=\"md\" wrap=\"wrap\">\n <span>Profile</span>\n <span>Security</span>\n </Stack>\n</Stack>\n```\n\n## Responsive props\n\n`orientation`, `gap`, `align`, `justify` and `wrap` each take a plain value or a\nper-breakpoint record:\n\n```svelte\n<Stack orientation={{ md: 'horizontal' }} gap={{ xs: 'sm', lg: 'xl' }} align=\"center\">\n <span>Filters</span>\n <span>Results</span>\n</Stack>\n```\n\nThe breakpoints measure the stack's OWN width — `xs` base, `sm` 36rem, `md` 42rem,\n`lg` 56rem, `xl` 72rem — not the viewport's, so the same stack is `xs` in a narrow sidebar\nand `lg` full-bleed on the same page. The nearest defined key at or below the stack's width\nwins, and below the narrowest key the prop's default applies: `{ md: 'horizontal' }` is\nvertical at `xs` and `sm`. A prop falls back to its default only when it is `undefined`\nor `null`. A function form must be deterministic in its argument — it is called once per\nbreakpoint. There is no measurement and no JS: the five values ship as custom\nproperties and container queries pick one, so server-rendered markup is already laid out.\n\n## Props\n\n- `orientation`: `'horizontal' | 'vertical'` — flex direction (default: `'vertical'`).\n- `align`: cross-axis alignment — `start | center | end | stretch` (default: `'stretch'`).\n- `justify`: main-axis alignment — `start | center | end | between | around | evenly` (default: `'start'`).\n- `gap`, `padding`, `paddingInline`, and `paddingBlock` accept\n `none | xs | sm | md | lg | xl`.\n- `paddingInline` and `paddingBlock` override `padding` on their axis.\n- `width`, `height`, `maxWidth`, and `minHeight` accept CSS strings or pixel numbers.\n- `wrap` accepts `nowrap | wrap | wrap-reverse`.\n- `scrollable` enables native `overflow: auto`.\n- `as` changes the semantic HTML element without changing layout behavior:\n `div | span | section | article | aside | main | nav | header | footer | form | fieldset`.\n Lists are not among them — the root always wraps its children in one layout `<div>`, which\n `<ul>` and `<ol>` do not admit. Write the list yourself and put a stack inside an `<li>`.\n\n## Structure\n\nStack renders two elements: the root (`data-slot=\"stack\"`) is the box the host sizes and the\ncontainer the breakpoints are measured against — it takes `as`, `class`, `style`, the size\nprops, the padding and `scrollable` — and a single layout child (`data-slot=\"stack-layout\"`)\ncarries the flex line. The `inner` theme slot styles that child.\n\nStack forwards common semantic HTML attributes and Svelte attachments to the root element.\nPrefer parent-owned `gap` over child margins. The internal `micro` and `layout-*` tokens are\nreserved for component recipes and must not be used in generated interfaces.\n";
26
26
  readonly 'app-shell': "\n# AppShell Component\n\nConvenience wrapper for the common application layout: Sidebar owns navigation,\nresponsive drawer behavior, the application wall, and variant surfaces, while PageShell\nowns the page header, document-flow content, footer, and route-level injection. AppShell\nforwards one shared variant to Sidebar and composes PageShell inside it.\nAppShell establishes a dynamic viewport-height minimum while letting the document own\nvertical scrolling. The desktop sidebar remains sticky independently of page content.\n\nUse AppShell when every route follows the same sidebar + page shell structure. Use\nSidebar and PageShell directly when the frame needs custom composition.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { AppShell, type AppShellSidebarProps } from 'entasis/app-shell';\n\timport { houseIcon } from 'entasis/icons/house';\n\n\tconst sidebar: AppShellSidebarProps = {\n\t\tcollapsible: 'icon',\n\t\trail: true,\n\t\titems: [\n\t\t\t{\n\t\t\t\tlabel: 'Workspace',\n\t\t\t\titems: [{ label: 'Home', href: '/', icon: houseIcon, isActive: true }]\n\t\t\t}\n\t\t]\n\t};\n</script>\n\n<AppShell variant=\"framed\" {sidebar} title=\"Dashboard\" subtitle=\"Operational overview\">\n\t{#snippet children({ sidebar })}\n\t\t<button type=\"button\" onclick={sidebar.toggle}>Toggle sidebar</button>\n\t{/snippet}\n</AppShell>\n```\n\n## Route-Level Injection\n\nAppShell renders PageShell internally, so child pages can use the PageShell context API:\n\n```svelte\n<script lang=\"ts\">\n\timport { setPageShell } from 'entasis/page-shell';\n\n\tsetPageShell({\n\t\ttitle: 'Insights',\n\t\tsubtitle: 'Revenue and retention',\n\t\tfooter: pageFooter\n\t});\n</script>\n\n{#snippet pageFooter()}\n\t<span>Synced just now</span>\n{/snippet}\n```\n\n## Props\n\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Shared shell treatment forwarded to Sidebar and PageShell chrome. Admin chrome uses the Sidebar canvas surface; floating chrome uses detached raised, rounded surfaces. `framed` draws one rounded card (`rounded-xl` + `raised-1`, so the border and elevation come from the elevation engine) around both the sidebar and the page; the Sidebar's own `framed` variant paints its navigation well as `surface-recessed`, an inset of that card, and the page header sits on the page surface.\n- **sidebar**: AppShellSidebarProps - Sidebar props except `children`, `mode`, `frame`, and `variant`.\n Configure Sidebar `size` and `density` independently inside this object.\n- **eyebrow**: string | Snippet<[PageShellApi]> - Small metadata above the PageShell title.\n- **breadcrumbs**: BreadcrumbItem[] | Snippet<[AppShellApi]> - PageShell breadcrumbs.\n- **breadcrumbsMaxItems**: number - Maximum visible breadcrumb items before ellipsis. Defaults to 4.\n- **back**: PageShellAction | Snippet<[AppShellApi]> - Back affordance before breadcrumbs or eyebrow.\n- **title**: string | Snippet<[PageShellApi]> - Default PageShell title.\n- **subtitle**: string | Snippet<[PageShellApi]> - Default PageShell subtitle.\n- **header**: Snippet<[PageShellApi]> - Custom PageShell header.\n- **headerActions**: Snippet<[AppShellApi]> | PageShellAction[] - Actions in the default PageShell header. Use an array for standard Button props, or a snippet when the action needs sidebar/page-shell API access.\n- **footer**: Snippet<[PageShellApi]> - PageShell footer.\n- **footerActions**: Snippet<[AppShellApi]> | PageShellAction[] - PageShell footer actions.\n- **children**: Snippet<[AppShellApi]> - Main content, with `pageShell` and `sidebar` APIs.\n- **contentPadding**: 'none' | 'small' | 'normal' | 'large' - PageShell content padding preset.\n- **contentWidth**: 'full' | 'narrow' | 'normal' | 'wide' | 'prose' - PageShell content width preset.\n- **pageShellTheme**: PageShellThemeProps - PageShell theme overrides.\n- **theme**: AppShellThemeProps - AppShell `root`, `frame` and `page` surface-token overrides. Sidebar owns the wall and shell geometry.\n\n## Accessibility\n\nAppShell delegates navigation semantics to Sidebar and page landmarks to PageShell.\nUse string `title` for the default `h1`, or preserve heading semantics when replacing\nthe PageShell header with a custom snippet.\n";
27
27
  readonly 'page-shell': "\n# PageShell Component\n\nContent shell for pages rendered inside an application frame. PageShell provides a\nsticky header and footer, document-flow content, title/subtitle props, and a context\nAPI for child routes to inject shell content. Scrolling stays on the document by default,\nso browser navigation and scroll restoration keep their native behavior.\n\nUse PageShell inside `Sidebar.children` when Sidebar owns navigation and responsive\ndrawer behavior.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { PageShell, type PageShellAction } from 'entasis/page-shell';\n\timport { downloadSimpleIcon } from 'entasis/icons/downloadSimple';\n\n\tconst headerActions = [\n\t\t{\n\t\t\tcontent: 'Export',\n\t\t\tcolor: 'primary',\n\t\t\tprefix: downloadSimpleIcon\n\t\t}\n\t] satisfies PageShellAction[];\n</script>\n\n<PageShell title=\"Insights\" subtitle=\"Live account health\" {headerActions}>\n\t{#snippet footer()}\n\t\t<span>Updated just now</span>\n\t{/snippet}\n\n\t{#snippet children()}\n\t\t<section class=\"p-6\">Page content</section>\n\t{/snippet}\n</PageShell>\n```\n\n## Route-Level Injection\n\nChild pages can set header and footer content through context. Use `setPageShell`\nduring component initialization for automatic cleanup.\n\n```svelte\n<script lang=\"ts\">\n\timport { setPageShell } from 'entasis/page-shell';\n\n\tsetPageShell({\n\t\ttitle: 'Revenue',\n\t\tsubtitle: 'Segment breakdown',\n\t\theaderActions: revenueActions,\n\t\tfooter: revenueFooter\n\t});\n</script>\n\n{#snippet revenueActions()}\n\t<button type=\"button\">Refresh</button>\n{/snippet}\n\n{#snippet revenueFooter()}\n\t<span>Synced 2 minutes ago</span>\n{/snippet}\n```\n\n## Props\n\n- **eyebrow**: string | Snippet<[PageShellApi]> - Small metadata above the title. Ignored when breadcrumbs are set.\n- **breadcrumbs**: BreadcrumbItem[] | Snippet<[PageShellApi]> - Default-header breadcrumbs.\n- **breadcrumbsMaxItems**: number - Maximum visible breadcrumb items before ellipsis. Defaults to 4.\n- **back**: PageShellAction | Snippet<[PageShellApi]> - Back affordance before breadcrumbs or eyebrow.\n- **title**: string | Snippet<[PageShellApi]> - Default header title.\n- **subtitle**: string | Snippet<[PageShellApi]> - Default header subtitle.\n- **header**: Snippet<[PageShellApi]> - Custom sticky header content.\n- **headerActions**: Snippet<[PageShellApi]> | PageShellAction[] - Actions on the right side of the default header. Use an array for standard Button props, or a snippet when the action needs shell API access.\n- **footer**: Snippet<[PageShellApi]> - Custom sticky footer content.\n- **footerActions**: Snippet<[PageShellApi]> | PageShellAction[] - Actions on the right side of the sticky footer.\n\t- **children**: Snippet<[PageShellApi]> - Page content rendered in normal document flow.\n\t- **contentPadding**: 'none' | 'small' | 'normal' | 'large' - Padding applied to the content inner wrapper.\n\t- **contentWidth**: 'full' | 'narrow' | 'normal' | 'wide' | 'prose' - Max-width preset for the content inner wrapper.\n\t- **actionOverflow**: 'auto' | 'never' - Mobile overflow behavior for action arrays.\n\t- **mobileActionCount**: 0 | 1 | 2 - Number of action-array buttons kept inline on mobile.\n\t- **label**: string - Accessible name for the page's `main` landmark, applied as aria-label.\n\t- **theme**: PageShellThemeProps - Per-instance theme overrides.\n\n## API\n\n- **usePageShell()** returns the current PageShell API and throws when no PageShell exists.\n- **setPageShell(config)** registers a scoped config override and removes it on component destroy.\n- **api.set(config)** pushes a manual override and returns a cleanup function.\n- **api.setFooterActions(actions)** pushes scoped page footer actions.\n- **api.reset()** clears all scoped overrides.\n\n## Header Action Arrays\n\n```svelte\n<script lang=\"ts\">\n\timport { PageShell, type PageShellAction } from 'entasis/page-shell';\n\timport { arrowClockwiseIcon } from 'entasis/icons/arrowClockwise';\n\n\tconst headerActions = [\n\t\t{\n\t\t\tlabel: 'Refresh',\n\t\t\tsquared: true,\n\t\t\tvariant: 'outline',\n\t\t\tprefix: arrowClockwiseIcon\n\t\t},\n\t\t{\n\t\t\tcontent: 'Create report',\n\t\t\tcolor: 'primary'\n\t\t}\n\t] satisfies PageShellAction[];\n</script>\n\n<PageShell title=\"Reports\" {headerActions}>\n\t{#snippet children()}\n\t\tPage content\n\t{/snippet}\n</PageShell>\n```\n\n## Content Presets And Footer Actions\n\n```svelte\n<PageShell\n\teyebrow=\"Settings\"\n\ttitle=\"Billing profile\"\n\tcontentPadding=\"normal\"\n\tcontentWidth=\"narrow\"\n\tfooterActions={[\n\t\t{ content: 'Cancel', variant: 'outline' },\n\t\t{ content: 'Save changes', color: 'primary' }\n\t]}\n>\n\t{#snippet footer()}\n\t\t<span>2 unsaved changes</span>\n\t{/snippet}\n\n\t{#snippet children()}\n\t\t<form>...</form>\n\t{/snippet}\n</PageShell>\n```\n\n## Accessibility\n\nPageShell renders semantic `header`, `main`, and `footer` regions. The title is an\n`h1` when provided as a string. Custom snippets are responsible for preserving\nequivalent semantics when replacing the default header.\n";
28
- readonly sidebar: "\n# Sidebar Component\n\nSidebar navigation with data-driven groups, icon collapse, mobile drawer behavior,\nrecursive tree groups, header/footer rows, search, actions, and snippet escape hatches.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n\timport { houseIcon } from 'entasis/icons/house';\n\timport { gearIcon } from 'entasis/icons/gear';\n\n\tconst items: SidebarGroup[] = [\n\t\t{\n\t\t\tlabel: 'Workspace',\n\t\t\titems: [\n\t\t\t\t{ label: 'Dashboard', href: '/', icon: houseIcon, isActive: true },\n\t\t\t\t{ label: 'Settings', href: '/settings', icon: gearIcon }\n\t\t\t]\n\t\t}\n\t];\n</script>\n\n<Sidebar items={items}>\n\t{#snippet children({ toggle })}\n\t\t<header>\n\t\t\t<button type=\"button\" onclick={toggle}>Toggle</button>\n\t\t</header>\n\t\t<main>Page content</main>\n\t{/snippet}\n</Sidebar>\n```\n\n## AI-Safe Usage Contract\n\n1. Use `items` for normal navigation. Use `content` only when data-driven rows cannot express the layout.\n2. Use local Entasis icon snippets such as `houseIcon`, not Lucide component constructors.\n3. Use `MenuItem[]` from `entasis/menu` for `menu` and action dropdowns.\n4. Do not combine `menu` with `href` or `onclick` on the same row; use `action` for a trailing row menu.\n5. Keep `children`, `header`, `content`, `footer`, `banner`, and action snippets pure; they receive `SidebarApi`.\n6. Use `collapsible=\"icon\"` for icon rail behavior, `collapsible=\"offcanvas\"` for hidden desktop panels, and `collapsible=\"none\"` for fixed sidebars. Icon collapse automatically falls back to offcanvas when any data-driven row lacks an icon.\n7. Offcanvas sidebars reveal over the content from the screen edge by default when hidden; set `edgeReveal={false}` to disable that. A revealed hidden sidebar keeps its resize handle and dismisses through a small rectangular pointer tolerance.\n8. Set `keyboardShortcut={false}` when embedding Sidebar inside another shortcut-heavy surface.\n9. Sidebar owns navigation, resize mechanics, the lower application wall, and variant surface geometry. AppShell forwards its variant and composes PageShell inside that surface.\n10. Use `size` for typography, icon scale, and item height. Use `density` independently for section padding, gaps, and submenu spacing.\n11. Use `activityBar` for a persistent icon rail outside the panel (section switching, workspaces). Every item needs an `icon` and a `label`; the label is the accessible name and the tooltip. It is layout mode only: `mode=\"panel\"` renders the navigation panel alone. The rail does not change the panel by itself: keep the selected rail item in state from `onSelect`, mark it `isActive`, and either derive the Sidebar's `items` from it or, for an animated switch, give each rail item a view in `views` and set `view` from it.\n12. Use `expandOnHover` only with `collapsible=\"icon\"`. It is a temporary peek, not a toggle: the persisted collapsed state never changes, while the peeked panel renders with expanded semantics.\n13. Use `views` when the panel's contents change as the user moves through the app: sections switched from the activity bar or the route, and nested menus that open inside the panel. Key each view, set `parent` on nested ones, open them with a row's `view`, and drive `view` from state or the URL. Do not hand-animate `items` swaps.\n\n## Data Model\n\n### SidebarGroup\n- **label**: string - Group label, hidden in icon-collapsed mode.\n- **items**: SidebarMenuEntry[] - Menu rows.\n- **tree**: SidebarTreeNode[] - Recursive tree rows instead of menu items.\n- **action**: SidebarMenuActionDescriptor | SidebarMenuActionDescriptor[] | Snippet<[SidebarApi]> - Top-right group actions. Pass an array to pin several affordances (a `+` and a drag handle) to one group header; each renders as its own icon-only ghost button.\n- **collapsible**: boolean - Makes the group label a toggle.\n- **defaultOpen**: boolean - Initial collapsible group state.\n- **separator**: boolean - Divider before the group.\n\n### SidebarMenuEntry\n- **label**: string - Visible row label.\n- **icon**: SidebarIcon - Entasis icon snippet or string.\n- **iconColor**: Colors - Role tint for the leading icon, applied through `data-color`.\n- **iconVariant**: 'bare' | 'tile' - Leading icon treatment. `tile` paints a rounded square (`bg-color-muted text-color-muted-readable`) around the glyph, so per-project colour chips come from the role scale instead of hand-built markup.\n- **href**: string - Render as an anchor. Mutually exclusive with menu.\n- **onclick**: (event: MouseEvent) => void - Native click handler for button or anchor rows. Mutually exclusive with menu.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **disabled**: boolean - Disables button rows and marks anchor rows disabled.\n- **badge**: string | number - Trailing count/status, hidden in icon mode.\n- **tooltip**: string - Entasis Tooltip content in icon mode. Defaults to label.\n- **items**: SidebarMenuSubEntry[] - Inline nested menu.\n- **collapsible**: boolean - Set false for an always-open submenu.\n- **defaultOpen**: boolean - Initial nested menu state.\n- **menu**: MenuItem[] - Popup menu opened from the full row. Mutually exclusive with href/onclick.\n- **view**: string - Key of the `views` entry the row opens, sliding it in; the row shows a trailing chevron. Mutually exclusive with href, menu and items.\n- **action**: SidebarMenuActionDescriptor | Snippet<[SidebarApi]> - Hover/focus trailing action.\n\n### SidebarView\nOne named panel content in `views`.\n- **label**: string - The view's name, shown on the back row of the views nested under it.\n- **parent**: string - Key of the view this one is nested under. A nested view opens with a back row to its parent (named \"Back, <parent label>\"), and on mobile a swipe toward the inline end goes back. A view without `parent` is a top-level section.\n- **items**: SidebarGroup[] / **content**: Snippet<[SidebarApi]> - The view's body.\n- **headerButton**, **search**, **headerMenu**, **header**, **footerButton**, **footerMenu**, **footer** - Header and footer props for this view. Each one left undefined comes from the parent view, then from the Sidebar's own prop; `null` removes an inherited one.\n\nChanging the view slides the two views side by side, like pages: a deeper view comes in from the inline end while the old one leaves to the start, a shallower one slides back the other way, and between views at the same depth (sections) the later one in `views` counts as forward. When both views get every header and footer prop from the same place, the header and footer stay still and only the menu slides; when a view changes any of them, the whole panel slides as one page. `api.view` reads the current view and `api.setView(key)` changes it from a snippet.\n\n### SidebarActivityBar\nIcon rail pinned to the outer edge of the sidebar, visible in every display state.\n- **items**: SidebarActivityBarItem[] - Items rendered from the top.\n- **footerItems**: SidebarActivityBarItem[] - Items pinned to the end of the column.\n- **header** / **footer**: Snippet - Custom content before the first item and after the pinned ones.\n- **width**: string (default '3rem') - Column thickness, published as `--sidebar-width-activity`.\n- **label**: string - Accessible name for the column landmark. Set it whenever the panel also renders navigation.\n- **onSelect**: ({ item, index }) => void - Fires after an item is activated. `index` counts `items` then `footerItems`.\n\n### SidebarActivityBarItem\n- **icon**: SidebarIcon (required) - Icon rendered in the square.\n- **label**: string (required) - Accessible name and default tooltip; the square shows no text.\n- **id**: string - Stable render key.\n- **href** / **target** / **rel** - Render an anchor instead of a button.\n- **onclick**: (event: MouseEvent) => void - Native click handler.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **badge**: string | number | Snippet - Corner badge pinned to the outer top corner. An empty string renders a bare dot. A string or number badge joins the accessible name (`\"Alerts, 3\"`); a dot and a Snippet badge are decorative, so put their meaning in `label`.\n- **disabled**: boolean - Blocks activation and skips the item during keyboard navigation.\n- **tooltip**: string | false - Tooltip override; `false` suppresses it.\n\n### SidebarMenuButtonItem\nUse for `headerButton`, `footerButton`, or direct `<SidebarMenuButton />` rows.\n- **icon**: SidebarIcon - Leading logo/icon.\n- **avatar**: { src?: string; alt?: string; fallback?: string } - Leading avatar.\n- **variant**: 'default' | 'brand' | 'compact'.\n- **title**: string - Primary text.\n- **subtitle**: string - Secondary text.\n- **trailing**: SidebarIcon | false | SidebarMenuActionDescriptor - Trailing content. An icon is decorative; an action descriptor (`{ icon, label, onclick }`, the same shape as a group action) renders its own icon-only ghost button beside the row, so a workspace card can carry its own collapse control without a custom `header` snippet. Descriptor handlers are `onclick(event, api)`, so `api.toggle()` is reachable.\n- **href** / **onclick** / **menu** - Choose link, button, or popup behavior. `onclick(event, api)` receives the SidebarApi beside the event, so `api.toggle()` is reachable from the row itself.\n- **menuIconClass**: string - Class override for option icons inside the popup menu.\n\n## Props\n\n### State\n- **open**: boolean (bindable, default true) - Desktop expanded state.\n- **defaultOpen**: boolean (default true) - Initial desktop state when `open` is omitted.\n- **onOpenChange**: (open: boolean) => void - Called once for a library-originated desktop state change. Repeated requests and parent prop updates stay silent.\n- **onDisplayStateChange**: (state: SidebarDisplayState) => void - Called once for a library-originated semantic display-state change.\n- **view**: string (bindable) - Key of the view on screen when `views` is set. Defaults to `defaultView`, then the first view.\n- **defaultView**: string - Initial view when `view` is omitted.\n- **onViewChange**: (view: string) => void - Called once for a library-originated view change: a view row, a back row, a swipe. Parent prop updates stay silent; with a route-driven `view`, navigate here.\n- **api.displayState**: 'expanded' | 'collapsed' | 'hidden' - Semantic desktop state; hidden means closed offcanvas. A hover peek does not change it.\n- **api.isPeeking**: boolean - True while a hover peek renders the collapsed panel at full width. Read it alongside `displayState` when a snippet hides content in icon mode.\n- **keyboardShortcut**: string | false (default 'b') - Ctrl/Cmd shortcut key.\n\n### Layout\n- **side**: 'left' | 'right' - Desktop and mobile side.\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Sidebar geometry. `framed` is the admin geometry for a sidebar hosted inside a raised card (AppShell variant framed), with a `surface-recessed` well. `admin` renders the conventional full-height navigation column; `inset` integrates navigation into the lower wall with an inset content surface; `split` renders detached sidebar and content surfaces.\n- **size**: 'small' | 'normal' | 'large' (default 'normal') - Typography, icon, avatar, badge, leading-media, item-height, and search-height scale.\n- **iconSize**: 'small' | 'normal' | 'large' - Icon and leading-media scale inside the panel on its own; defaults to `size`. Menu rows, sub rows, group labels and the header button follow it; the activity bar keeps `size`.\n- **activeVariant**: 'soft' | 'outline' | 'solid' (default 'soft') - How active rows are painted. `soft` is the shared selected recipe (`selectedSoft`: `bg-selected-muted text-selected-muted-readable`), `solid` its loud counterpart (`selectedSolid`: `bg-selected text-selected-contrast`), `outline` a bordered surface card (`bg-surface border border-neutral-muted text-neutral`) that reads as a raised card on a tinted well. Each row carries the choice as `data-active-variant`, so no descendant selector is needed to restyle selection.\n- **density**: 'compact' | 'normal' | 'comfortable' (default 'normal') - Section padding, group padding, gaps, horizontal inset, and submenu spacing.\n- **collapsible**: 'offcanvas' | 'icon' | 'none' - Collapse behavior. Icon mode requires icons on every data-driven row and otherwise resolves to offcanvas.\n- **collapseIcon**: DisclosureIndicator — 'chevron' | 'plus-minus' | 'none' (default 'chevron') - Disclosure indicator drawn on collapsible menu rows. 'none' renders no indicator.\n- **mode**: 'layout' | 'panel' - Full resizing layout or only the visible navigation panel.\n- **frame**: 'viewport' | 'contained' - Standalone Sidebar positioning. Viewport mode uses Theme's dynamic window-height token; contained mode fills a positioned parent.\n- **width**: string - Expanded width.\n- **widthIcon**: string - Icon-collapsed width.\n- **widthMobile**: string - Mobile drawer width.\n- **rail**: boolean | 'line' | 'thumb' - Edge toggle rail. `true` keeps the thin line style; `thumb` renders a short visible handle with the same full-height hitbox. The appearance is preserved when the rail shares the resize control.\n- **activityBar**: SidebarActivityBar - Icon rail pinned outside the panel, in layout mode only (`mode=\"panel\"` renders the panel alone and ignores it). It never slides off screen: only the panel takes the offcanvas offset, and the reserved layout column is the panel width plus the rail width. It wears the surface of the variant's panel: a hairline column on the canvas for `admin`, the recessed well for `framed`, borderless on the canvas for `inset`, and a card of its own (the panel's radius, edge and gutter) for `floating` and `split`. On mobile it renders as a horizontal row at the top of the drawer.\n- **expandOnHover**: boolean (default false) - With `collapsible=\"icon\"`, hovering or focusing into the collapsed panel expands it to `width` over the page (`data-peek=\"true\"`) while the reserved column stays at `widthIcon`, so page content does not reflow. The persisted collapsed state is untouched, and the peeked panel renders exactly like an expanded one: group headers, badges, search, inline submenus, and inline tree branches all come back, and the rail or resize handle travels to its inner edge.\n- **edgeReveal**: boolean (default true) - Pointer/focus edge preview for hidden offcanvas sidebars. Hover reveal overlays content, remains resizable when configured, and re-hides after the pointer leaves its small rectangular tolerance. Dragging the sidebar closed suppresses immediate hover reopening until the pointer leaves the edge trigger; toggle/click opens persistently.\n- **resizable**: boolean | SidebarResizableOptions - Enables pointer and keyboard resizing while expanded, icon-collapsed, or temporarily edge-revealed. By default, collapse requires dragging 75% of `minWidth` beyond the minimum; override `collapseThreshold` for a custom boundary. Use `storageKey` to restore and persist the expanded width across sessions.\n - `onWidthChange({ width, isUserInteraction })` reports every expanded-width change with one named payload: continuously while the user resizes (`isUserInteraction: true`) and once when a stored width is restored (`isUserInteraction: false`).\n\n### Content\n- **items**: SidebarGroup[] - Data-driven body navigation.\n- **views**: Record<string, SidebarView> - Named panel contents, one on screen at a time, replacing `items` and `content`. See SidebarView.\n- **headerButton** / **footerButton**: SidebarMenuButtonItem - Sticky large rows.\n- **search**: SidebarSearch - Header search input; use its native `oninput` handler.\n- **headerMenu** / **footerMenu**: SidebarMenuEntry[] - Sticky quick menus.\n- Menu entries and nested entries accept `size: 'small' | 'normal' | 'large'` for row geometry.\n- **header**, **content**, **footer**, **children**, **banner**: Snippet<[SidebarApi]> - Escape hatches. The `header` snippet renders **first** in the header region, above `headerButton`, `search` and `headerMenu`.\n\n### Styling\n- **class**: string - Classes applied to the Sidebar root.\n- **theme**: SidebarThemeProps - Semantic part overrides such as `panel`, `header`, `nav`, `footer`, menu, search, rail, and mobile drawer parts. Every part, with the element it lands on, its variants and their default classes, is listed in the entasis skill's `theme-parts/sidebar.md`; the registry key is `sidebar`.\n\n## Motion\n\n- **motion** theme slot: the y-axis slide shared by collapsible groups, inline submenus, and\n tree branches. Takes `in` / `out` slide params plus a `duration` / `easing` motion token.\n- Ladder: `<Theme components={{ sidebar: { motion } }}>` → `setSidebarTheme({ motion })` →\n `theme.motion`. Reduced motion collapses it to 0.\n- The `view` variant (`part: 'view'`) is the pager between views: `in.x` / `out.x` is how far a view travels (default `'100%'`: the two views slide side by side like pages) while it fades between `opacity` (default 0) and 1, at the `slow` duration on the `enter` easing. The back swipe moves the same layers the view change would, with the same travel and opacity. Reduced motion makes the switch instant while a swipe still follows the finger.\n\n## Restyle recipes\n\nThe five asks that come up first, as the override to copy. Each one is rendered and asserted by\n`sidebar-recipes.svelte.test.ts`, so it cannot drift from the component. One rule behind them: a\ndefault written under a variant prefix (`data-[active-variant=solid]:data-active:bg-selected`,\n`data-[side=left]:border-r`) is only replaced by an override carrying the same prefixes; an\nunprefixed class coexists with it and loses on specificity. `theme-parts/sidebar.md` shows every\ndefault verbatim, prefixes included.\n\n- **Dark panel.** `dark` scopes the dark theme's variables to the panel, so every neutral ink inside\n flips with it (every theme also emits its variables on `.<name>`, so the class scopes the dark theme to one subtree); needs a theme named `dark`, which the documented setup declares.\n `theme={{ panel: { base: 'dark bg-slate-900' }, mobilePanel: { base: 'dark bg-slate-900' } }}`\n- **Active row in your colour.** `activeVariant=\"solid\"` plus the same prefix chain as the default:\n `theme={{ menuButton: { base: 'rounded-full', activeVariant: { solid: 'data-[active-variant=solid]:data-active:bg-indigo-600 data-[active-variant=solid]:data-active:text-white' } } }}`\n- **No hover change.** The overlay is a `::before` and the default also brightens the ink on hover:\n `theme={{ menuButton: { base: 'state-layer-none hover:text-inherit' }, subButton: { base: 'state-layer-none hover:text-neutral/70' } }}`\n- **Flat panel.** The edge is a prefixed `border-r` / `border-l` on the admin variant, the shadow a\n `raised-*` on the floating ones:\n `theme={{ panel: { base: 'raised-none', variant: { admin: 'data-[side=left]:border-r-0 data-[side=right]:border-l-0' } } }}`\n- **Row height.** A plain height beats the compound `h-control-*`: `theme={{ menuButton: { base: 'h-11' } }}`\n- **Bigger icons.** A prop, not a theme: `iconSize=\"large\"`.\n\n## Accessibility\n\n- The body navigation renders inside a named `<nav>` landmark (`data-sidebar=\"nav\"`), so assistive tech can jump straight to it and tell it apart from the activity bar's own landmark.\n- Active links set `aria-current=\"page\"`.\n- Collapsible rows and groups set `aria-expanded`.\n- Disabled buttons use `disabled`; disabled links omit `href`, use `aria-disabled` and `tabindex=-1`, and block activation.\n- Mobile drawer includes a backdrop button labelled \"Close Sidebar\".\n- Icon-collapsed rows keep their labels mounted and visually fade them, preserving accessible names and stable icon geometry.\n- Search, group controls, actions, and nested rows become inert before collapse can remove or hide them; focus returns to the owning visible row.\n- Nested groups, tree branches, and inline submenus use reversible height transitions for open, close, and sidebar-collapse changes.\n- Tree roots use menu-row styling and nested tree nodes use submenu-row styling. In desktop icon mode, root folders open a PopupMenu and descendants remain navigable through recursive Menu submenu popovers; root leaves retain direct navigation and tooltips.\n- When `rail` and `resizable` are both enabled, one edge control owns click-to-toggle, drag resize, and keyboard resize without overlapping hitboxes.\n- Hidden offcanvas sidebars keep that combined edge control while temporarily revealed. Resizing does not pin the sidebar open; leaving the panel, trigger, and handle tolerance re-hides it without discarding the configured width.\n- Both peeks (edge reveal and `expandOnHover`) stay open while focus is inside the panel or while an overlay opened from inside it is open, including nested submenus. They release about 120ms after the pointer, focus, and every such overlay are gone.\n- The activity bar is its own `<nav>` landmark with a `<ul>` of items, a roving tabindex, and ArrowUp/ArrowDown/Home/End navigation that loops and skips disabled items. Tab lands on the `isActive` item. Each square takes its accessible name from `label`, with a string or number `badge` appended to it, and shows `label` as a tooltip on hover and focus.\n- A hidden offcanvas panel is `inert`, so Tab never lands in a panel parked off screen; a peek makes it interactive again.\n- Views: when focus sat in the view being replaced, it lands on the back row after going deeper and on the row that opened the view after coming back. A view change from outside the panel (a route) leaves focus where it is. The back swipe is a shortcut; the back row stays the accessible path.\n\n## Notes\n\n- Dropdown menus use Entasis `PopupMenu` and `MenuItem[]`.\n- The component uses semantic Entasis tokens. Do not add shadcn `sidebar-*` color tokens.\n- Snippet icons from `entasis/icons/*` are the preferred icon format.\n";
28
+ readonly sidebar: "\n# Sidebar Component\n\nSidebar navigation with data-driven groups, icon collapse, mobile drawer behavior,\nrecursive tree groups, header/footer rows, search, actions, and snippet escape hatches.\n\n## Import\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Sidebar, type SidebarGroup } from 'entasis/sidebar';\n\timport { houseIcon } from 'entasis/icons/house';\n\timport { gearIcon } from 'entasis/icons/gear';\n\n\tconst items: SidebarGroup[] = [\n\t\t{\n\t\t\tlabel: 'Workspace',\n\t\t\titems: [\n\t\t\t\t{ label: 'Dashboard', href: '/', icon: houseIcon, isActive: true },\n\t\t\t\t{ label: 'Settings', href: '/settings', icon: gearIcon }\n\t\t\t]\n\t\t}\n\t];\n</script>\n\n<Sidebar items={items}>\n\t{#snippet children({ toggle })}\n\t\t<header>\n\t\t\t<button type=\"button\" onclick={toggle}>Toggle</button>\n\t\t</header>\n\t\t<main>Page content</main>\n\t{/snippet}\n</Sidebar>\n```\n\n## AI-Safe Usage Contract\n\n1. Use `items` for normal navigation. Use `content` only when data-driven rows cannot express the layout.\n2. Use local Entasis icon snippets such as `houseIcon`, not Lucide component constructors.\n3. Use `MenuItem[]` from `entasis/menu` for `menu` and action dropdowns.\n4. Do not combine `menu` with `href` or `onclick` on the same row; use `action` for a trailing row menu.\n5. Keep `children`, `header`, `content`, `footer`, `banner`, and action snippets pure; they receive `SidebarApi`.\n6. Use `collapsible=\"icon\"` for icon rail behavior, `collapsible=\"offcanvas\"` for hidden desktop panels, and `collapsible=\"none\"` for fixed sidebars. Icon collapse automatically falls back to offcanvas when any data-driven row lacks an icon.\n7. Offcanvas sidebars reveal over the content from the screen edge by default when hidden; set `edgeReveal={false}` to disable that. A revealed hidden sidebar keeps its resize handle and dismisses through a small rectangular pointer tolerance.\n8. Set `keyboardShortcut={false}` when embedding Sidebar inside another shortcut-heavy surface.\n9. Sidebar owns navigation, resize mechanics, the lower application wall, and variant surface geometry. AppShell forwards its variant and composes PageShell inside that surface.\n10. Use `size` for typography, icon scale, and item height. Use `density` independently for section padding, gaps, and submenu spacing.\n11. Use `activityBar` for a persistent icon rail outside the panel (section switching, workspaces). Every item needs an `icon` and a `label`; the label is the accessible name and the tooltip. It is layout mode only: `mode=\"panel\"` renders the navigation panel alone. The rail does not change the panel by itself: keep the selected rail item in state from `onSelect`, mark it `isActive`, and either derive the Sidebar's `items` from it or, for an animated switch, give each rail item a view in `views` and set `view` from it.\n12. Use `expandOnHover` only with `collapsible=\"icon\"`. It is a temporary peek, not a toggle: the persisted collapsed state never changes, while the peeked panel renders with expanded semantics.\n13. Use `views` when the panel's contents change as the user moves through the app: sections switched from the activity bar or the route, and nested menus that open inside the panel. Key each view, set `parent` on nested ones, open them with a row's `view`, and drive `view` from state or the URL. Do not hand-animate `items` swaps.\n\n## Data Model\n\n### SidebarGroup\n- **label**: string - Group label, hidden in icon-collapsed mode.\n- **items**: SidebarMenuEntry[] - Menu rows.\n- **tree**: SidebarTreeNode[] - Recursive tree rows instead of menu items.\n- **action**: SidebarMenuActionDescriptor | SidebarMenuActionDescriptor[] | Snippet<[SidebarApi]> - Top-right group actions. Pass an array to pin several affordances (a `+` and a drag handle) to one group header; each renders as its own icon-only ghost button.\n- **collapsible**: boolean - Makes the group label a toggle.\n- **defaultOpen**: boolean - Initial collapsible group state.\n- **separator**: boolean - Divider before the group.\n\n### SidebarMenuEntry\n- **label**: string - Visible row label.\n- **icon**: SidebarIcon - Entasis icon snippet or string.\n- **iconColor**: Colors - Role tint for the leading icon, applied through `data-color`.\n- **iconVariant**: 'bare' | 'tile' - Leading icon treatment. `tile` paints a rounded square (`bg-color-muted text-color-muted-readable`) around the glyph, so per-project colour chips come from the role scale instead of hand-built markup.\n- **href**: string - Render as an anchor. Mutually exclusive with menu.\n- **onclick**: (event: MouseEvent) => void - Native click handler for button or anchor rows. Mutually exclusive with menu.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **disabled**: boolean - Disables button rows and marks anchor rows disabled.\n- **badge**: string | number - Trailing count/status, hidden in icon mode.\n- **tooltip**: string - Entasis Tooltip content in icon mode. Defaults to label.\n- **items**: SidebarMenuSubEntry[] - Inline nested menu.\n- **collapsible**: boolean - Set false for an always-open submenu.\n- **defaultOpen**: boolean - Initial nested menu state.\n- **menu**: MenuItem[] - Popup menu opened from the full row. Mutually exclusive with href/onclick.\n- **view**: string - Key of the `views` entry the row opens, sliding it in; the row shows a trailing chevron. Mutually exclusive with href, menu and items.\n- **action**: SidebarMenuActionDescriptor | Snippet<[SidebarApi]> - Hover/focus trailing action.\n\n### SidebarView\nOne named panel content in `views`.\n- **label**: string - The view's name, shown on the back row of the views nested under it.\n- **parent**: string - Key of the view this one is nested under. A nested view opens with a back row to its parent (named \"Back, <parent label>\"), and on mobile a swipe toward the inline end goes back. A view without `parent` is a top-level section.\n- **items**: SidebarGroup[] / **content**: Snippet<[SidebarApi]> - The view's body.\n- **headerButton**, **search**, **headerMenu**, **header**, **footerButton**, **footerMenu**, **footer** - Header and footer props for this view. Each one left undefined comes from the parent view, then from the Sidebar's own prop; `null` removes an inherited one.\n\nChanging the view slides the two views side by side, like pages: a deeper view comes in from the inline end while the old one leaves to the start, a shallower one slides back the other way, and between views at the same depth (sections) the later one in `views` counts as forward. When both views get every header and footer prop from the same place, the header and footer stay still and only the menu slides; when a view changes any of them, the whole panel slides as one page. `api.view` reads the current view and `api.setView(key)` changes it from a snippet.\n\n### SidebarActivityBar\nIcon rail pinned to the outer edge of the sidebar, visible in every display state.\n- **items**: SidebarActivityBarItem[] - Items rendered from the top.\n- **footerItems**: SidebarActivityBarItem[] - Items pinned to the end of the column.\n- **header** / **footer**: Snippet - Custom content before the first item and after the pinned ones.\n- **width**: string (default '3rem') - Column thickness, published as `--sidebar-width-activity`.\n- **label**: string - Accessible name for the column landmark. Set it whenever the panel also renders navigation.\n- **onSelect**: ({ item, index }) => void - Fires after an item is activated. `index` counts `items` then `footerItems`.\n\n### SidebarActivityBarItem\n- **icon**: SidebarIcon (required) - Icon rendered in the square.\n- **label**: string (required) - Accessible name and default tooltip; the square shows no text.\n- **id**: string - Stable render key.\n- **href** / **target** / **rel** - Render an anchor instead of a button.\n- **onclick**: (event: MouseEvent) => void - Native click handler.\n- **isActive**: boolean - Adds active styling and `aria-current=\"page\"`.\n- **badge**: string | number | Snippet - Corner badge pinned to the outer top corner. An empty string renders a bare dot. A string or number badge joins the accessible name (`\"Alerts, 3\"`); a dot and a Snippet badge are decorative, so put their meaning in `label`.\n- **disabled**: boolean - Blocks activation and skips the item during keyboard navigation.\n- **tooltip**: string | false - Tooltip override; `false` suppresses it.\n\n### SidebarMenuButtonItem\nUse for `headerButton`, `footerButton`, or direct `<SidebarMenuButton />` rows.\n- **icon**: SidebarIcon - Leading logo/icon.\n- **avatar**: { src?: string; alt?: string; fallback?: string } - Leading avatar.\n- **variant**: 'default' | 'brand' | 'compact'.\n- **title**: string - Primary text.\n- **subtitle**: string - Secondary text.\n- **trailing**: SidebarIcon | false | SidebarMenuActionDescriptor - Trailing content. An icon is decorative; an action descriptor (`{ icon, label, onclick }`, the same shape as a group action) renders its own icon-only ghost button beside the row, so a workspace card can carry its own collapse control without a custom `header` snippet. Descriptor handlers are `onclick(event, api)`, so `api.toggle()` is reachable.\n- **href** / **onclick** / **menu** - Choose link, button, or popup behavior. `onclick(event, api)` receives the SidebarApi beside the event, so `api.toggle()` is reachable from the row itself.\n- **menuIconClass**: string - Class override for option icons inside the popup menu.\n\n## Props\n\n### State\n- **open**: boolean (bindable, default true) - Desktop expanded state.\n- **defaultOpen**: boolean (default true) - Initial desktop state when `open` is omitted.\n- **onOpenChange**: (open: boolean) => void - Called once for a library-originated desktop state change. Repeated requests and parent prop updates stay silent.\n- **onDisplayStateChange**: (state: SidebarDisplayState) => void - Called once for a library-originated semantic display-state change.\n- **view**: string (bindable) - Key of the view on screen when `views` is set. Defaults to `defaultView`, then the first view.\n- **defaultView**: string - Initial view when `view` is omitted.\n- **onViewChange**: (view: string) => void - Called once for a library-originated view change: a view row, a back row, a swipe. Parent prop updates stay silent; with a route-driven `view`, navigate here.\n- **api.displayState**: 'expanded' | 'collapsed' | 'hidden' - Semantic desktop state; hidden means closed offcanvas. A hover peek does not change it.\n- **api.isPeeking**: boolean - True while a hover peek renders the collapsed panel at full width. Read it alongside `displayState` when a snippet hides content in icon mode.\n- **keyboardShortcut**: string | false (default 'b') - Ctrl/Cmd shortcut key.\n\n### Layout\n- **side**: 'left' | 'right' - Desktop and mobile side.\n- **variant**: 'admin' | 'floating' | 'inset' | 'split' | 'framed' - Sidebar geometry. `framed` is the admin geometry for a sidebar hosted inside a raised card (AppShell variant framed), with a `surface-recessed` well. `admin` renders the conventional full-height navigation column; `inset` integrates navigation into the lower wall with an inset content surface; `split` renders detached sidebar and content surfaces.\n- **size**: 'small' | 'normal' | 'large' (default 'normal') - Typography, icon, avatar, badge, leading-media, item-height, and search-height scale.\n- **iconSize**: 'small' | 'normal' | 'large' - Icon and leading-media scale inside the panel on its own; defaults to `size`. Menu rows, sub rows, group labels and the header button follow it; the activity bar keeps `size`.\n- **activeVariant**: 'soft' | 'outline' | 'solid' (default 'soft') - How active rows are painted. `soft` is the shared selected recipe (`selectedSoft`: `bg-selected-muted text-selected-muted-readable`), `solid` its loud counterpart (`selectedSolid`: `bg-selected text-selected-contrast`), `outline` a bordered surface card (`bg-surface border border-neutral-muted text-neutral`) that reads as a raised card on a tinted well. Each row carries the choice as `data-active-variant`, so no descendant selector is needed to restyle selection.\n- **density**: 'compact' | 'normal' | 'comfortable' (default 'normal') - Section padding, group padding, gaps, horizontal inset, and submenu spacing.\n- **collapsible**: 'offcanvas' | 'icon' | 'none' - Collapse behavior. Icon mode requires icons on every data-driven row and otherwise resolves to offcanvas.\n- **collapseIcon**: DisclosureIndicator — 'chevron' | 'plus-minus' | 'none' (default 'chevron') - Disclosure indicator drawn on collapsible menu rows. 'none' renders no indicator.\n- **mode**: 'layout' | 'panel' - Full resizing layout or only the visible navigation panel.\n- **frame**: 'viewport' | 'contained' - Standalone Sidebar positioning. Viewport mode uses Theme's dynamic window-height token; contained mode fills a positioned parent.\n- **width**: string - Expanded width.\n- **widthIcon**: string - Icon-collapsed width.\n- **widthMobile**: string - Mobile drawer width.\n- **rail**: boolean | 'line' | 'thumb' - Edge toggle rail. `true` keeps the thin line style; `thumb` renders a short visible handle with the same full-height hitbox. The appearance is preserved when the rail shares the resize control.\n- **activityBar**: SidebarActivityBar - Icon rail pinned outside the panel, in layout mode only (`mode=\"panel\"` renders the panel alone and ignores it). It never slides off screen: only the panel takes the offcanvas offset, and the reserved layout column is the panel width plus the rail width. It wears the surface of the variant's panel: a hairline column on the canvas for `admin`, the recessed well for `framed`, borderless on the canvas for `inset`, and a card of its own (the panel's radius, edge and gutter) for `floating` and `split`. With `inset` and `split`, hiding the panel normally lets the page drop its frame and fill the edge; with an activity bar the rail is still beside the page, so the page keeps its framed form (padding, rounding, AppShell's border and shadow), and a `split` page takes the gutter the panel used to supply. The wrapper carries `data-page-flush` and the `main` part a `flush` variant only when the page does fill the edge. On mobile it renders as a horizontal row at the top of the drawer.\n- **expandOnHover**: boolean (default false) - With `collapsible=\"icon\"`, hovering or focusing into the collapsed panel expands it to `width` over the page (`data-peek=\"true\"`) while the reserved column stays at `widthIcon`, so page content does not reflow. The persisted collapsed state is untouched, and the peeked panel renders exactly like an expanded one: group headers, badges, search, inline submenus, and inline tree branches all come back, and the rail or resize handle travels to its inner edge.\n- **edgeReveal**: boolean (default true) - Pointer/focus edge preview for hidden offcanvas sidebars. Hover reveal overlays content, remains resizable when configured, and re-hides after the pointer leaves its small rectangular tolerance. Dragging the sidebar closed suppresses immediate hover reopening until the pointer leaves the edge trigger; toggle/click opens persistently.\n- **resizable**: boolean | SidebarResizableOptions - Enables pointer and keyboard resizing while expanded, icon-collapsed, or temporarily edge-revealed. By default, collapse requires dragging 75% of `minWidth` beyond the minimum; override `collapseThreshold` for a custom boundary. Use `storageKey` to restore and persist the expanded width across sessions.\n - `onWidthChange({ width, isUserInteraction })` reports every expanded-width change with one named payload: continuously while the user resizes (`isUserInteraction: true`) and once when a stored width is restored (`isUserInteraction: false`).\n\n### Content\n- **items**: SidebarGroup[] - Data-driven body navigation.\n- **views**: Record<string, SidebarView> - Named panel contents, one on screen at a time, replacing `items` and `content`. See SidebarView.\n- **headerButton** / **footerButton**: SidebarMenuButtonItem - Sticky large rows.\n- **search**: SidebarSearch - Header search input; use its native `oninput` handler.\n- **headerMenu** / **footerMenu**: SidebarMenuEntry[] - Sticky quick menus.\n- Menu entries and nested entries accept `size: 'small' | 'normal' | 'large'` for row geometry.\n- **header**, **content**, **footer**, **children**, **banner**: Snippet<[SidebarApi]> - Escape hatches. The `header` snippet renders **first** in the header region, above `headerButton`, `search` and `headerMenu`.\n\n### Styling\n- **class**: string - Classes applied to the Sidebar root.\n- **theme**: SidebarThemeProps - Semantic part overrides such as `panel`, `header`, `nav`, `footer`, menu, search, rail, and mobile drawer parts. Every part, with the element it lands on, its variants and their default classes, is listed in the entasis skill's `theme-parts/sidebar.md`; the registry key is `sidebar`.\n\n## Motion\n\n- **motion** theme slot: the y-axis slide shared by collapsible groups, inline submenus, and\n tree branches. Takes `in` / `out` slide params plus a `duration` / `easing` motion token.\n- Ladder: `<Theme components={{ sidebar: { motion } }}>` → `setSidebarTheme({ motion })` →\n `theme.motion`. Reduced motion collapses it to 0.\n- The `view` variant (`part: 'view'`) is the pager between views: `in.x` / `out.x` is how far a view travels (default `'100%'`: the two views slide side by side like pages) while it fades between `opacity` (default 0) and 1, at the `slow` duration on the `enter` easing. The back swipe moves the same layers the view change would, with the same travel and opacity. Reduced motion makes the switch instant while a swipe still follows the finger.\n\n## Restyle recipes\n\nThe five asks that come up first, as the override to copy. Each one is rendered and asserted by\n`sidebar-recipes.svelte.test.ts`, so it cannot drift from the component. One rule behind them: a\ndefault written under a variant prefix (`data-[active-variant=solid]:data-active:bg-selected`,\n`data-[side=left]:border-r`) is only replaced by an override carrying the same prefixes; an\nunprefixed class coexists with it and loses on specificity. `theme-parts/sidebar.md` shows every\ndefault verbatim, prefixes included.\n\n- **Dark panel.** `dark` scopes the dark theme's variables to the panel, so every neutral ink inside\n flips with it (every theme also emits its variables on `.<name>`, so the class scopes the dark theme to one subtree); needs a theme named `dark`, which the documented setup declares.\n `theme={{ panel: { base: 'dark bg-slate-900' }, mobilePanel: { base: 'dark bg-slate-900' } }}`\n- **Active row in your colour.** `activeVariant=\"solid\"` plus the same prefix chain as the default:\n `theme={{ menuButton: { base: 'rounded-full', activeVariant: { solid: 'data-[active-variant=solid]:data-active:bg-indigo-600 data-[active-variant=solid]:data-active:text-white' } } }}`\n- **No hover change.** The overlay is a `::before` and the default also brightens the ink on hover:\n `theme={{ menuButton: { base: 'state-layer-none hover:text-inherit' }, subButton: { base: 'state-layer-none hover:text-neutral/70' } }}`\n- **Flat panel.** The edge is a prefixed `border-r` / `border-l` on the admin variant, the shadow a\n `raised-*` on the floating ones:\n `theme={{ panel: { base: 'raised-none', variant: { admin: 'data-[side=left]:border-r-0 data-[side=right]:border-l-0' } } }}`\n- **Row height.** A plain height beats the compound `h-control-*`: `theme={{ menuButton: { base: 'h-11' } }}`\n- **Bigger icons.** A prop, not a theme: `iconSize=\"large\"`.\n\n## Accessibility\n\n- The body navigation renders inside a named `<nav>` landmark (`data-sidebar=\"nav\"`), so assistive tech can jump straight to it and tell it apart from the activity bar's own landmark.\n- Active links set `aria-current=\"page\"`.\n- Collapsible rows and groups set `aria-expanded`.\n- Disabled buttons use `disabled`; disabled links omit `href`, use `aria-disabled` and `tabindex=-1`, and block activation.\n- Mobile drawer includes a backdrop button labelled \"Close Sidebar\".\n- Icon-collapsed rows keep their labels mounted and visually fade them, preserving accessible names and stable icon geometry.\n- Search, group controls, actions, and nested rows become inert before collapse can remove or hide them; focus returns to the owning visible row.\n- Nested groups, tree branches, and inline submenus use reversible height transitions for open, close, and sidebar-collapse changes.\n- Tree roots use menu-row styling and nested tree nodes use submenu-row styling. In desktop icon mode, root folders open a PopupMenu and descendants remain navigable through recursive Menu submenu popovers; root leaves retain direct navigation and tooltips.\n- When `rail` and `resizable` are both enabled, one edge control owns click-to-toggle, drag resize, and keyboard resize without overlapping hitboxes.\n- Hidden offcanvas sidebars keep that combined edge control while temporarily revealed. Resizing does not pin the sidebar open; leaving the panel, trigger, and handle tolerance re-hides it without discarding the configured width.\n- Both peeks (edge reveal and `expandOnHover`) stay open while focus is inside the panel or while an overlay opened from inside it is open, including nested submenus. They release about 120ms after the pointer, focus, and every such overlay are gone.\n- The activity bar is its own `<nav>` landmark with a `<ul>` of items, a roving tabindex, and ArrowUp/ArrowDown/Home/End navigation that loops and skips disabled items. Tab lands on the `isActive` item. Each square takes its accessible name from `label`, with a string or number `badge` appended to it, and shows `label` as a tooltip on hover and focus.\n- A hidden offcanvas panel is `inert`, so Tab never lands in a panel parked off screen; a peek makes it interactive again.\n- Views: when focus sat in the view being replaced, it lands on the back row after going deeper and on the row that opened the view after coming back. A view change from outside the panel (a route) leaves focus where it is. The back swipe is a shortcut; the back row stays the accessible path.\n\n## Notes\n\n- Dropdown menus use Entasis `PopupMenu` and `MenuItem[]`.\n- The component uses semantic Entasis tokens. Do not add shadcn `sidebar-*` color tokens.\n- Snippet icons from `entasis/icons/*` are the preferred icon format.\n";
29
29
  readonly button: "\n# Button Component\n\nThe Button component is a flexible and customizable button element that supports various variants, sizes, colors, and interactive states.\n\n## Basic Usage\n\n```svelte\n<Button>Click me</Button>\n<Button variant=\"outline\">Outline Button</Button>\n<Button color=\"primary\" size=\"large\">Large Primary Button</Button>\n```\n\n## Props\n\n### Core Props\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' (default: 'solid')\n - solid: Filled background with color\n - outline: Transparent background with colored border\n - soft: Muted color background\n - ghost: Transparent background with a transient state layer on hover and press\n - link: Text-only styling with underline on hover\n\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' (default: 'neutral')\n - Determines the color scheme of the button\n\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: 28px height, smaller padding and text\n - normal: 32px height, standard padding\n - large: 36px height, larger padding and text\n\n### Layout Props\n- **fullWidth**: boolean (default: false) - Makes button take full width of container\n- **squared**: boolean - Makes button square (aspect-ratio 1:1), auto-determined if only prefix/suffix is provided\n- **disabled**: boolean (default: false) - Disables button interaction\n- **loading**: boolean (default: false) - Shows the Theme-configured loading spinner and disables interaction\n\n### State Props\nDescribe the meaning; the Button writes the ARIA. Never pass an aria-* attribute to a Button.\n- **pressed**: boolean - Toggle state of a button that stays on or off (a bold button in a toolbar, a \"show password\" eye). Rendered as aria-pressed\n- **selected**: boolean - Chosen state of a button acting as one option among several (a tab, a listbox option). Rendered as aria-selected\n- **expanded**: boolean - Whether the surface this button opens is showing. Rendered as aria-expanded. A entasis surface (Popover, PopupMenu, Select, Combobox) sets this on its own trigger, so pass it only for a surface you open yourself\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true - What the surface this button opens contains. Rendered as aria-haspopup, and likewise set by a entasis surface on its own trigger\n\n### Link Props\n- **href**: string - Makes button render as anchor tag\n- **target**: string - Link target (e.g., \"_blank\")\n- **rel**: string - Link relationship\n\n### Event Props\n- **onclick**: (event: MouseEvent) => void - Native click event handler\n- **onpointerenter**: (event: PointerEvent) => void - Native pointer enter event handler\n- **onpointerleave**: (event: PointerEvent) => void - Native pointer leave event handler\n\n### Content Props (Slots)\n- **children**: Snippet - Main button content\n- **prefix**: Snippet - Content before main text (typically icons)\n- **suffix**: Snippet - Content after main text (typically icons)\n\n### Advanced Props\n- **label**: string - Accessible label applied as aria-label on the root element (required for icon-only buttons)\n- **ref**: HTMLElement - Reference to the button element\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\nThe button follows this DOM structure:\n```\n<Button>\n\t<Prefix /> <!-- Optional prefix content -->\n\t<Children /> <!-- Main button content -->\n\t<Suffix /> <!-- Optional suffix content -->\n</Button>\n```\n\n## Examples\n\n### Basic Buttons\n```svelte\n<Button>Default Button</Button>\n<Button variant=\"outline\" color=\"primary\">Primary Outline</Button>\n<Button variant=\"soft\" color=\"danger\">Soft Danger</Button>\n<Button variant=\"ghost\">Ghost Button</Button>\n<Button variant=\"link\">Link Button</Button>\n```\n\n### With Icons\n```svelte\n<Button>\n\t{#snippet prefix()}\n\t\t{@render icon()}\n\t{/snippet}\n\tAdd Item\n</Button>\n\n<Button squared>\n\t{#snippet prefix()}\n\t\t{@render icon()}\n\t{/snippet}\n</Button>\n```\n\n### Interactive States\n```svelte\n<Button loading>Loading...</Button>\n<Button disabled>Disabled</Button>\n<Button fullWidth>Full Width Button</Button>\n```\n\n### As Link\n```svelte\n<Button href=\"/dashboard\" target=\"_blank\">Go to Dashboard</Button>\n```\n\n### With Event Handlers\n```svelte\n<script lang=\"ts\">\n\tfunction handleClick(event: MouseEvent) {\n\t\tconsole.log('Clicked:', event.currentTarget);\n\t}\n</script>\n\n<Button onclick={handleClick}>\n\tClick\n</Button>\n```\n\n### Custom Styling\n```svelte\n<Button class=\"lift-4 border-2\" color=\"primary\" variant=\"outline\">\n\tCustom Styled\n</Button>\n```\n\n## Accessibility\n\n- Automatically sets appropriate ARIA roles (button/link)\n- Supports keyboard navigation\n- Disabled state prevents interaction\n- Loading state provides visual feedback\n\n## Notes\n\n- When `href` is provided, renders as `<a>` tag, otherwise `<button>`\n- `squared` is automatically determined when only prefix or suffix is provided without children\n- All event handlers respect disabled state\n- Icon sizing is automatically adjusted based on button size\n\n## Theme Customization\n\nThe Button component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button container styles\n- **prefix**: Styles for prefix content (icons before text)\n- **suffix**: Styles for suffix content (icons after text)\n\n### Theme Type Definition\n\n```typescript\nimport type { ButtonThemeProps } from 'entasis/button';\n\n// Example theme customization\nconst customTheme: ButtonThemeProps = {\n root: {\n base: 'custom-base-classes',\n size: {\n small: 'custom-small-classes',\n normal: 'custom-normal-classes',\n large: 'custom-large-classes'\n },\n color: {\n primary: 'bg-blue-500 text-white',\n danger: 'bg-red-500 text-white'\n },\n variant: {\n solid: 'bg-color text-color-contrast',\n outline: 'border-2 border-color'\n }\n },\n prefix: {\n size: {\n small: 'w-3 h-3',\n normal: 'w-4 h-4',\n large: 'w-5 h-5'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all buttons\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls padding, text size, and height\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' - Visual style variant\n - loading: boolean - Loading state styling\n - disabled: boolean - Disabled state styling\n - squared: boolean - Square button styling\n - fullWidth: boolean - Full width styling\n\n**prefix**:\n- base: Base classes for prefix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on button size\n\n**suffix**:\n- base: Base classes for suffix content\n- Variants:\n - size: 'small' | 'normal' | 'large' - Icon size based on button size\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Button \n theme={{\n root: {\n base: 'rounded-full lift-4',\n size: {\n large: 'px-8 py-4 text-xl'\n }\n }\n }}\n>\n Custom Styled Button\n</Button>\n```\n\n**Color Variant Customization**:\n```svelte\n<Button \n color=\"primary\"\n theme={{\n root: {\n color: {\n primary: 'state-layer bg-linear-to-r from-blue-500 to-purple-500'\n }\n }\n }}\n>\n Gradient Button\n</Button>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setButtonTheme } from '../components/Button/index.ts';\n \n setButtonTheme({\n root: {\n variant: {\n solid: 'state-layer bg-color text-color-contrast lift-3 hover:lift-4 transition-shadow',\n outline: 'state-layer border-2 border-color'\n }\n },\n prefix: {\n size: {\n normal: 'w-5 h-5'\n }\n }\n });\n</script>\n```\n";
30
30
  readonly 'button-group': "\n# ButtonGroup Component\n\nThe ButtonGroup component displays a collection of related buttons as a cohesive group with shared styling properties.\n\n## Basic Usage\n\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'First' },\n\t\t{ children: 'Second' },\n\t\t{ children: 'Third' }\n\t]}\n/>\n```\n\n## Props\n\n### Core Props\n- **items**: Array<ButtonProps> (required) - Array of button configurations\n - Each button can have all standard Button component props\n\n### Shared Button Props\n- **size**: 'small' | 'normal' | 'large' - Applied to all buttons in the group\n- **color**: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Shared color for all buttons\n- **variant**: 'solid' | 'outline' | 'soft' | 'ghost' | 'link' - Shared variant for all buttons\n- **disabled**: boolean - Disables all buttons in the group\n\n### Styling Props\n- **class**: string - Additional CSS classes for the group container\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<ButtonGroup>\n\t<Button />\n\t<Button />\n\t<Button />\n</ButtonGroup>\n```\n\n## Examples\n\n### Basic Button Group\n```svelte\n<ButtonGroup \n\titems={[\n\t\t{ children: 'Left' },\n\t\t{ children: 'Center' },\n\t\t{ children: 'Right' }\n\t]}\n/>\n```\n\n### With Shared Styling\n```svelte\n<ButtonGroup \n\tsize=\"large\"\n\tcolor=\"primary\"\n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' },\n\t\t{ children: 'Option 3' }\n\t]}\n/>\n```\n\n### With Icons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textAlignLeftIcon } from 'entasis/icons/textAlignLeft';\n\timport { textAlignCenterIcon } from 'entasis/icons/textAlignCenter';\n\timport { textAlignRightIcon } from 'entasis/icons/textAlignRight';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tprefix: textAlignLeftIcon,\n\t\t\tchildren: 'Left' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignCenterIcon,\n\t\t\tchildren: 'Center' \n\t\t},\n\t\t{ \n\t\t\tprefix: textAlignRightIcon,\n\t\t\tchildren: 'Right' \n\t\t}\n\t]}\n/>\n```\n\n### With Individual Click Handlers\n```svelte\n<script>\n\tfunction handleOption(option) {\n\t\tconsole.log(`Selected: ${option}`);\n\t}\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Save',\n\t\t\tonclick: () => handleOption('save')\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Cancel',\n\t\t\tonclick: () => handleOption('cancel')\n\t\t}\n\t]}\n/>\n```\n\n### Disabled Group\n```svelte\n<ButtonGroup \n\tdisabled\n\titems={[\n\t\t{ children: 'Option 1' },\n\t\t{ children: 'Option 2' }\n\t]}\n/>\n```\n\n### Icon Only Buttons\n```svelte\n<script lang=\"ts\">\n\timport { ButtonGroup } from 'entasis/button-group';\n\timport { textBIcon } from 'entasis/icons/textB';\n\timport { textItalicIcon } from 'entasis/icons/textItalic';\n\timport { textUnderlineIcon } from 'entasis/icons/textUnderline';\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textBIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textItalicIcon\n\t\t},\n\t\t{ \n\t\t\tsquared: true,\n\t\t\tprefix: textUnderlineIcon\n\t\t}\n\t]}\n/>\n```\n\n### Mixed Button States\n```svelte\n<ButtonGroup \n\tvariant=\"outline\"\n\titems={[\n\t\t{ children: 'Active', color: 'primary' },\n\t\t{ children: 'Default', color: 'neutral' },\n\t\t{ children: 'Disabled', disabled: true }\n\t]}\n/>\n```\n\n### Segmented Control\n```svelte\n<script>\n\tlet selected = $state('week');\n</script>\n\n<ButtonGroup \n\titems={[\n\t\t{ \n\t\t\tchildren: 'Day',\n\t\t\tvariant: selected === 'day' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'day'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Week',\n\t\t\tvariant: selected === 'week' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'week'\n\t\t},\n\t\t{ \n\t\t\tchildren: 'Month',\n\t\t\tvariant: selected === 'month' ? 'solid' : 'ghost',\n\t\t\tonclick: () => selected = 'month'\n\t\t}\n\t]}\n/>\n```\n\n## Styling\n\nButtonGroup automatically:\n- Removes border-radius from middle buttons\n- Adjusts borders to prevent double borders\n- Creates a cohesive, connected appearance\n- Maintains consistent spacing\n\n## Accessibility\n\n- Each button maintains full keyboard accessibility\n- Focus styles are preserved\n- Disabled state cascades properly\n- Screen readers announce each button individually\n\n## Notes\n\n- Individual button props override shared props\n- Buttons are rendered in the order provided\n- The group container can be styled with the `class` prop\n- All Button component features are supported for individual buttons\n\n## Theme Customization\n\nThe ButtonGroup component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main button group container styles\n\n### Available Variants\n\n**root**:\n- base: Base classes for button group container (handles border radius and border connections between buttons)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Custom Group Styling**:\n```svelte\n<ButtonGroup \n items={buttons}\n theme={{\n root: {\n base: 'flex items-center gap-0 border-2 border-primary rounded-lg overflow-hidden'\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setButtonGroupTheme } from '../components/ButtonGroup/index.ts';\n \n setButtonGroupTheme({\n root: {\n base: 'flex items-center first-child:rounded-r-none last-child:rounded-l-none'\n }\n });\n</script>\n```\n";
31
31
  readonly 'segmented-control': "\n# SegmentedControl\n\nA compact single-selection input for switching between a small set of mutually exclusive modes. It uses radiogroup/radio semantics and an animated indicator that slides and resizes to the selected item.\n\n## Basic usage\n\n```svelte\n<script lang=\"ts\">\n import { SegmentedControl } from '../components/SegmentedControl/index.ts';\n import { gridFourIcon } from 'entasis/icons/gridFour';\n import { listIcon } from 'entasis/icons/list';\n\n const items = [\n { value: 'grid', label: 'Grid', icon: gridFourIcon },\n { value: 'list', label: 'List', icon: listIcon }\n ] as const;\n\n let value = $state<(typeof items)[number]['value']>('grid');\n</script>\n\n<SegmentedControl {items} bind:value />\n```\n\n## Props\n\n- **items**: `readonly SegmentedControlItem[]` — required options with unique `value` fields.\n- **value**: `string` — bindable selected value; defaults to the first enabled item.\n- **defaultValue**: `string` — initial selected value when value is omitted.\n- **onValueChange**: `(value: string) => void` — called once after user interaction changes the value.\n- **item**: `Snippet<[SegmentedControlItem]>` — replaces the default item renderer.\n- **size**: `'small' | 'normal' | 'large'` — defaults to `'normal'`.\n- **color**: semantic color — controls the selected pill and focus ring; defaults to `'neutral'`.\n- **variant**: `'normal' | 'pill'` — controls corner radius; defaults to the moderately rounded `'normal'` shape.\n- **disabled**: `boolean` — disables the full control.\n- **label**: `string` — accessible name for the radiogroup; defaults to the catalog's \"Segmented control\".\n- **class**: additional root classes.\n- **theme**: component theme overrides.\n\n## Item shape\n\n```ts\ntype SegmentedControlItem<Value extends string = string> = {\n value: Value;\n label?: string | Snippet;\n icon?: string | Snippet;\n disabled?: boolean;\n};\n```\n\nThe default renderer displays `icon`, then `label`. `label` is also the segment's accessible name: a string is painted and spoken, a snippet names the segment through its own content, and an icon-only segment (no `label`) falls back to its `value`.\n\n## Custom item renderer\n\n```svelte\n<SegmentedControl {items} bind:value>\n {#snippet item(option)}\n <span>{option.label}</span>\n <span>{option.count}</span>\n {/snippet}\n</SegmentedControl>\n```\n\nExtra fields on item objects remain available to the snippet through generic inference.\n\n## Shapes\n\n`variant=\"normal\"` uses a moderately rounded track and segments. Use `variant=\"pill\"` for the fully rounded stadium shape.\n\n```svelte\n<SegmentedControl {items} bind:value />\n<SegmentedControl {items} bind:value variant=\"pill\" />\n```\n\n## Keyboard behavior\n\n- Tab enters on the selected item, or the first enabled item when no value matches.\n- Arrow Left/Right moves and selects, wrapping at the ends.\n- Home/End selects the first/last enabled item.\n- Disabled items are skipped.\n\nEach item has an invisible, size-aware pointer target that extends beyond the visual track: 36px for small, 44px for normal, and 48px for large. Adjacent targets meet at the midpoint of the configured gap instead of overlapping.\n\nUse SegmentedControl for mode or value selection. Use Tabbar when changing navigational views with tab semantics.\n";
@@ -54,7 +54,7 @@ export declare const componentMcpRegistry: {
54
54
  readonly 'radio-input': "\n# RadioInput Component\n\nThe RadioInput component provides a group of radio buttons for single-selection from multiple options.\n\n## Basic Usage\n\n```svelte\n<script>\n\tlet selected = $state('');\n</script>\n\n<RadioInput \n\tlabel=\"Choose a plan\"\n\tbind:value={selected}\n\titems={[\n\t\t{ value: 'free', label: 'Free' },\n\t\t{ value: 'pro', label: 'Pro' },\n\t\t{ value: 'enterprise', label: 'Enterprise' }\n\t]}\n/>\n```\n\n## Props\n\nExtends all Field component props plus:\n\n### Core Props\n- **value**: string (bindable) - Selected value\n- **items**: Array<{ value: string, label: string, description?: string, disabled?: boolean }> (required)\n\n### Layout Props\n- **mode**: 'normal' | 'card' (default: 'normal') - Visual layout style of the options\n\n### Field Props (inherited)\n- **label**: string | Snippet - Field label\n- **description**: string | Snippet - Helper text for the entire group\n- **error**: string - Error message\n- **required**: boolean - Mark as required\n- **disabled**: boolean - Disable all options\n- **size**: 'small' | 'normal' | 'large'\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: RadioInputThemeProps & FieldThemeProps - Custom theme overrides\n\n## Structure\n\n```\n<Field>\n\t<Label />\n\t<Description />\n\t<RadioGroup mode=\"...\">\n\t\t<Radio>\n\t\t\t<RadioIndicator />\n\t\t\t<RadioLabel />\n\t\t\t<RadioDescription />\n\t\t</Radio>\n\t\t<Radio>...</Radio>\n\t</RadioGroup>\n\t<Error />\n</Field>\n```\n\n## Examples\n\n### Basic Radio Group\n```svelte\n<script>\n\tlet plan = $state('pro');\n</script>\n\n<RadioInput \n\tlabel=\"Select a plan\"\n\tbind:value={plan}\n\titems={[\n\t\t{ value: 'free', label: 'Free' },\n\t\t{ value: 'pro', label: 'Pro' },\n\t\t{ value: 'enterprise', label: 'Enterprise' }\n\t]}\n/>\n```\n\n### With Descriptions\n```svelte\n<RadioInput \n\tlabel=\"Subscription\"\n\tbind:value={subscription}\n\titems={[\n\t\t{ \n\t\t\tvalue: 'monthly', \n\t\t\tlabel: 'Monthly',\n\t\t\tdescription: '$10/month, cancel anytime'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'yearly', \n\t\t\tlabel: 'Yearly',\n\t\t\tdescription: '$100/year, save $20'\n\t\t}\n\t]}\n/>\n```\n\n### Horizontal Layout\n```svelte\n<RadioInput \n\tlabel=\"Gender\"\n\tbind:value={gender}\n\ttheme={{ inputContainer: { base: 'grid-cols-3' } }}\n\titems={[\n\t\t{ value: 'male', label: 'Male' },\n\t\t{ value: 'female', label: 'Female' },\n\t\t{ value: 'other', label: 'Other' }\n\t]}\n/>\n```\n\n### Required Field\n```svelte\n<RadioInput \n\tlabel=\"Shipping Method\"\n\tbind:value={shipping}\n\trequired\n\titems={[\n\t\t{ value: 'standard', label: 'Standard (5-7 days)' },\n\t\t{ value: 'express', label: 'Express (2-3 days)' },\n\t\t{ value: 'overnight', label: 'Overnight' }\n\t]}\n/>\n```\n\n### With Disabled Options\n```svelte\n<RadioInput \n\tlabel=\"Seat Selection\"\n\tbind:value={seat}\n\titems={[\n\t\t{ value: 'window', label: 'Window' },\n\t\t{ value: 'aisle', label: 'Aisle' },\n\t\t{ value: 'middle', label: 'Middle', disabled: true }\n\t]}\n/>\n```\n\n### All Disabled\n```svelte\n<RadioInput \n\tlabel=\"Account Type\"\n\tvalue=\"premium\"\n\tdisabled\n\titems={[\n\t\t{ value: 'free', label: 'Free' },\n\t\t{ value: 'premium', label: 'Premium' }\n\t]}\n/>\n```\n\n### Payment Method Selector\n```svelte\n<script>\n\tlet paymentMethod = $state('card');\n</script>\n\n<RadioInput \n\tlabel=\"Payment Method\"\n\tbind:value={paymentMethod}\n\trequired\n\titems={[\n\t\t{ \n\t\t\tvalue: 'card', \n\t\t\tlabel: 'Credit/Debit Card',\n\t\t\tdescription: 'Visa, Mastercard, Amex'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'paypal', \n\t\t\tlabel: 'PayPal',\n\t\t\tdescription: 'Pay with your PayPal account'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'bank', \n\t\t\tlabel: 'Bank Transfer',\n\t\t\tdescription: '2-3 business days processing'\n\t\t}\n\t]}\n/>\n\n{#if paymentMethod === 'card'}\n\t<TextInput label=\"Card Number\" />\n\t<TextInput label=\"CVV\" />\n{:else if paymentMethod === 'paypal'}\n\t<TextInput type=\"email\" label=\"PayPal Email\" />\n{/if}\n```\n\n### Size Options\n```svelte\n<RadioInput \n\tlabel=\"T-Shirt Size\"\n\tbind:value={size}\n\trequired\n\titems={[\n\t\t{ value: 'xs', label: 'XS' },\n\t\t{ value: 's', label: 'S' },\n\t\t{ value: 'm', label: 'M' },\n\t\t{ value: 'l', label: 'L' },\n\t\t{ value: 'xl', label: 'XL' }\n\t]}\n/>\n```\n\n### Delivery Options\n```svelte\n<script>\n\tlet delivery = $state('');\n\t\n\tconst deliveryOptions = [\n\t\t{ \n\t\t\tvalue: 'pickup', \n\t\t\tlabel: 'Store Pickup',\n\t\t\tdescription: 'Free - Ready in 2 hours'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'standard', \n\t\t\tlabel: 'Standard Delivery',\n\t\t\tdescription: '$5.99 - 5-7 business days'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'express', \n\t\t\tlabel: 'Express Delivery',\n\t\t\tdescription: '$12.99 - 2-3 business days'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'overnight', \n\t\t\tlabel: 'Overnight',\n\t\t\tdescription: '$24.99 - Next day delivery'\n\t\t}\n\t];\n</script>\n\n<RadioInput \n\tlabel=\"Delivery Method\"\n\tbind:value={delivery}\n\titems={deliveryOptions}\n\trequired\n/>\n```\n\n### Survey Question\n```svelte\n<RadioInput \n\tlabel=\"How satisfied are you with our service?\"\n\tbind:value={satisfaction}\n\trequired\n\titems={[\n\t\t{ value: '5', label: 'Very Satisfied' },\n\t\t{ value: '4', label: 'Satisfied' },\n\t\t{ value: '3', label: 'Neutral' },\n\t\t{ value: '2', label: 'Dissatisfied' },\n\t\t{ value: '1', label: 'Very Dissatisfied' }\n\t]}\n/>\n```\n\n### Settings Radio Group\n```svelte\n<RadioInput \n\tlabel=\"Notification Frequency\"\n\tbind:value={frequency}\n\tdescription=\"Choose how often you want to receive notifications\"\n\titems={[\n\t\t{ \n\t\t\tvalue: 'realtime', \n\t\t\tlabel: 'Real-time',\n\t\t\tdescription: 'Instant notifications'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'hourly', \n\t\t\tlabel: 'Hourly',\n\t\t\tdescription: 'Digest every hour'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'daily', \n\t\t\tlabel: 'Daily',\n\t\t\tdescription: 'Once per day'\n\t\t},\n\t\t{ \n\t\t\tvalue: 'never', \n\t\t\tlabel: 'Never',\n\t\t\tdescription: 'Disable notifications'\n\t\t}\n\t]}\n/>\n```\n\n## Validation\n\nRadioInput automatically validates:\n- **required**: A value must be selected\n\n## Keyboard Navigation\n\n- **Arrow keys**: Navigate between options\n- **Space**: Select focused option\n- **Tab**: Move focus to next element\n\n## Accessibility\n\n- Proper radio group semantics\n- ARIA attributes for required state\n- Keyboard navigation support\n- Focus management\n- Screen reader announcements\n- Disabled options communicated properly\n\n## Notes\n\n- Only one option can be selected at a time\n- Each option can have its own description\n- Individual options can be disabled\n- Supports both vertical and horizontal layouts\n- Works seamlessly with Form component\n\n## Theme Customization\n\nThe RadioInput component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main container styles\n- **radiosInputContainer**: Options grid container styles\n- **radiosInputItem**: Individual radio wrapper styles\n- **radiosInputItemTrack**: Radio button track/background styles\n- **radiosInputItemThumb**: Radio button indicator styles\n- **radiosInputItemLabel**: Label text styles\n- **radiosInputItemDescription**: Description text styles\n- **radiosInputItemIcon**: Icon styling\n\n### Available Variants\n\n**root**:\n- base: Base classes for main container\n- Variants:\n - mode: 'card' | 'normal' - Layout mode\n\n**radiosInputContainer**:\n- base: Base classes for options grid container\n- Variants:\n - mode: 'card' | 'normal' - Grid layout based on mode\n - disabled: boolean - Disabled state styling\n\n**radiosInputItem**:\n- base: Base classes for individual radio wrapper\n- Variants:\n - mode: 'card' | 'normal' - Item styling based on mode\n - checked: boolean - Selected state styling\n - disabled: boolean - Disabled state styling\n\n**radiosInputItemTrack**:\n- base: Base classes for radio track/background\n- Variants:\n - checked: boolean - Track styling when checked\n - mode: 'card' | 'normal' - Mode-based styling\n - disabled: boolean - Disabled state styling\n\n**radiosInputItemThumb**:\n- base: Base classes for radio indicator\n- Variants:\n - checked: boolean - Visibility and styling when checked\n - mode: 'card' | 'normal' - Mode-based styling\n - disabled: boolean - Disabled state styling\n\n**radiosInputItemLabel**:\n- base: Base classes for label text\n\n**radiosInputItemDescription**:\n- base: Base classes for description text\n\n**radiosInputItemIcon**:\n- base: Base classes for icon styling\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<RadioInput \n label=\"Choose Option\"\n bind:value={value}\n items={items}\n theme={{\n // The option grid lays itself out against its OWN width, not the viewport: declare the\n // container on the root and query it on the grid, so the columns follow the field's host\n // (a sidebar, a split pane, a dialog) instead of the device.\n root: { base: '@container' },\n radiosInputContainer: {\n base: 'grid-cols-1 @md:grid-cols-3 gap-lg'\n },\n radiosInputItem: {\n mode: {\n card: 'rounded-lg border-2'\n }\n }\n }}\n/>\n```\n\n**Card Mode Customization**:\n```svelte\n<RadioInput \n mode=\"card\"\n label=\"Plan\"\n bind:value={plan}\n items={items}\n theme={{\n radiosInputItem: {\n checked: {\n true: 'ring-2 ring-primary bg-primary/10'\n },\n mode: {\n card: 'rounded-xl raised-3 hover:raised-4'\n }\n },\n radiosInputItemThumb: {\n checked: {\n true: 'bg-primary scale-[60%]'\n }\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setRadioInputTheme } from '../components/Form/RadioInput/index.ts';\n \n setRadioInputTheme({\n root: { base: '@container' },\n radiosInputContainer: {\n base: 'gap-lg',\n mode: {\n card: 'grid-cols-1 @2xl:grid-cols-2'\n }\n },\n radiosInputItem: {\n mode: {\n card: 'rounded-lg transition-all'\n }\n }\n });\n</script>\n```\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
55
55
  readonly 'rating-input': "\n# RatingInput Component\n\nThe RatingInput component is a star rating field with optional half-star support, a configurable star count, and full RTL support. Its value is a number.\n\n## Basic Usage\n\n```svelte\n<script>\n\tlet rating = $state(3);\n</script>\n\n<RatingInput label=\"Rating\" bind:value={rating} />\n```\n\n## Props\n\nExtends all Field component props plus:\n\n### Core Props\n- **value**: number | null (bindable, default null) - Current rating; null (or 0) means \"no rating\"\n- **max**: number (default: 5) - Number of stars, which is also the maximum value\n- **halfSteps**: boolean (default: false) - When true the value snaps to 0.5 increments (half stars)\n- **readonly**: boolean (default: false) - Displays the value without allowing interaction (aria-readonly=true); still shows the stars\n- **clearable**: boolean (default: true) - When true, clicking the exact current value clears it back to null\n- **dir**: 'ltr' | 'rtl' - Reading direction override; inherits the ambient direction when omitted\n- **color**: Colors (default: 'warning') - Color of the filled stars (the classic gold/amber star)\n\n### Content Props (Slots)\n- **star**: Snippet<[{ index, fraction, layer }]> (optional) - Custom star icon, forwarded to the underlying Rating display component. Rendered twice per star: once for the muted outline (layer: 'base') and once for the colored fill overlay (layer: 'fill'). See the Rating component docs for an example.\n\n### Field Props (inherited)\n- **label**: string | Snippet - Field label\n- **description**: string | Snippet - Helper text\n- **required**: boolean - Mark as required (a required rating must be at least half a star)\n- **disabled**: boolean - Disable input\n- **size**: 'small' | 'normal' | 'large' - Star size\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n- **i18n**: Partial<Messages> - Per-instance i18n overrides\n\n## Examples\n\n### Basic Rating (integer)\n```svelte\n<RatingInput label=\"Rating\" bind:value={rating} max={5} />\n```\n\n### Half Steps\n```svelte\n<RatingInput label=\"Rating\" bind:value={rating} halfSteps />\n```\n\n### Half Steps in RTL (fills from the right)\n```svelte\n<RatingInput label=\"Rating\" bind:value={rating} halfSteps dir=\"rtl\" />\n```\n\n### Custom Star Count\n```svelte\n<RatingInput label=\"Score\" bind:value={score} max={10} />\n```\n\n### Readonly\n```svelte\n<RatingInput label=\"Average\" value={4.5} halfSteps readonly />\n```\n\n### Disabled\n```svelte\n<RatingInput label=\"Rating\" value={3} disabled />\n```\n\n### Required (inside a Form)\n```svelte\n<Form inputs={{ rating: { type: 'rating', label: 'Rating', required: true } }} />\n```\n\n## Keyboard Interactions\n\nThe rating row is a single tab stop with `role=\"slider\"`.\n\n- **Arrow Right / Arrow Up**: Increase the value by the step (0.5 when halfSteps, else 1)\n- **Arrow Left / Arrow Down**: Decrease the value by the step\n- **Home**: Clear the value (sets it to null)\n- **End**: Set the value to max\n\nValue semantics do NOT flip in RTL: Arrow Right always increases the numeric value. Only the visual fill is mirrored.\n\n## Accessibility\n\n- The row container carries `role=\"slider\"` and is the single focusable element (`tabindex` 0 when interactive, -1 when readonly/disabled)\n- ARIA: `aria-valuemin={0}`, `aria-valuemax={max}`, `aria-valuenow`, `aria-valuetext`, `aria-orientation=\"horizontal\"`, `aria-readonly`, `aria-disabled`\n- Individual stars are `aria-hidden` and not focusable; the pointer selects a value and half-star hits are computed from the pointer position within each star\n- Clicking a star focuses the slider container (native focus fixup), so arrow keys work immediately after a pointer selection and `focused` becomes true — same as clicking a text input\n\n## RTL\n\nWhen the effective direction is RTL (via the `dir` prop or the inherited ambient direction), the stars render right-to-left and the fill grows from the right. The fill uses the CSS logical property `inset-inline-start`, so the visual mirroring is automatic. Note again: the numeric value is unaffected by direction — only the visual fill mirrors.\n\n## Half Steps\n\nWith `halfSteps`, the left half of a star (in the reading direction) selects n-0.5 and the right half selects n. The fill is rendered with an overflow-hidden clip whose width is the star's fill fraction, giving exact 0.5 (and arbitrary partial) fills.\n\n## Theme Customization\n\nThe RatingInput component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **container**: The flex row of stars (gap + focus ring)\n- **star**: The fixed-size box wrapping one star (cursor-pointer when interactive)\n- **starBase**: The outline (empty) star color\n- **starFill**: The filled star, driven by the `color` variant\n\n### Available Variants\n\n**container**:\n- size: 'small' | 'normal' | 'large' - Gap between stars\n- disabled: boolean - Disabled state styling\n\n**star**:\n- size: 'small' | 'normal' | 'large' - Star box size\n- interactive: boolean - cursor-pointer when interactive\n\n**starFill**:\n- color: Colors - Fill color of the star (defaults to 'warning')\n\n### Global Theme Setting\n\nThe star rendering lives in the `Rating` display component, so the theme is shared: set it once and both `Rating` and `RatingInput` pick it up.\n\n```svelte\n<script>\n\timport { setRatingTheme } from 'entasis/rating-input'; // also exported from 'entasis/rating'\n\n\tsetRatingTheme({\n\t\tstarFill: {\n\t\t\tcolor: {\n\t\t\t\tprimary: 'text-primary'\n\t\t\t}\n\t\t}\n\t});\n</script>\n```\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
56
56
  readonly 'rich-text-input': "\n# RichTextInput Component\n\nRichTextInput is a markdown rich text editor for AI-style composition. It supports inline and block formatting, trigger-based suggestions, token insertion, and a bindable markdown value.\n\n## Requires\n\nRichTextInput is built on Lexical. Those packages are optional peer dependencies of entasis, so install them alongside it:\n\n`pnpm add lexical @lexical/history @lexical/link @lexical/list @lexical/markdown @lexical/rich-text @lexical/selection @lexical/utils`\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { RichTextInput, type RichTextInputTriggers } from 'entasis/rich-text-input';\n\n\tlet value = $state('');\n\n\tconst triggers: RichTextInputTriggers = {\n\t\t'/': {\n\t\t\ttitle: 'Commands',\n\t\t\ttokenKind: 'command',\n\t\t\titems: [{ id: 'summarize', label: '/summarize', kind: 'command' }]\n\t\t}\n\t};\n</script>\n\n<RichTextInput bind:value {triggers} toolbar=\"both\" />\n```\n\n## Standalone Usage\n\n```svelte\n<RichTextInput standalone toolbar=\"hover\" placeholder=\"Prompt...\" />\n```\n\n## Height\n\n```svelte\n<RichTextInput placeholder=\"Grows with content by default...\" />\n<RichTextInput maxHeight={320} placeholder=\"Capped at 320px...\" />\n```\n\n## Form Usage\n\n```svelte\n<Form\n\tinputs={{\n\t\tprompt: {\n\t\t\ttype: 'rich-text',\n\t\t\tlabel: 'Prompt',\n\t\t\tplaceholder: 'Write a prompt...',\n\t\t\ttoolbar: 'fixed'\n\t\t}\n\t}}\n/>\n```\n\n## Props\n\n### Value\n- **value**: string - Bindable markdown value.\n- **name**: string - Hidden input name for native form submission; Form supplies this from the field key.\n- **id**: string - Applied to the editable root.\n- **ref**: HTMLDivElement | null - Bindable editable root reference.\n- **label**, **description**, **helper**, **errors**, **required**, **onValidate** - Standard Field props.\n- **placeholder**: string (default: \"Ask anything...\") - Placeholder and textbox aria-label.\n- **disabled**: boolean (default: false) - Disables editing and suggestions.\n- **standalone**: boolean (default: false) - Renders only the rich text editor chrome without the Field wrapper or input surface.\n- **maxHeight**: number | string | false (default: false) - False lets the editor grow with content; a number/string caps the editor height.\n\n### Suggestions\n- **triggers**: RichTextInputTriggers - Trigger configuration keyed by characters such as '/', '@', or '$'.\n- **onSuggestionOpen**: (payload) => void - Called when a suggestion menu opens.\n- **onSuggestionClose**: (payload) => void - Called when a suggestion menu closes.\n- **onSuggestionQueryChange**: (payload) => void - Called when the active query changes.\n- **onSuggestionHighlightChange**: (payload) => void - Called when highlighted suggestion changes.\n\n### Formatting\n- **toolbar**: 'hover' | 'fixed' | 'both' | 'none' (default: 'hover') - 'hover' shows controls for selected text, 'fixed' pins controls above the editor, 'both' enables both, and 'none' hides formatting controls.\n- **formats**: RichTextInputFormat[] - Allowed formatting controls.\n- **toolbarClass**: string - Extra classes on the fixed toolbar wrapper.\n\n### Events and Methods\n- **onValueChange**: (payload: RichTextInputChange) => void - Receives markdown, tokens, and empty state.\n- **submitShortcut**: 'enter' | 'shift-enter' | 'command-enter' | 'none' - Shortcut that calls onSubmitShortcut.\n- **onSubmitShortcut**: (event: KeyboardEvent) => void - Submit shortcut callback.\n- **focus()**, **clear()**, **insertText(text)**, **insertItem(trigger, item)**, **insertToken(token)** are exported component methods.\n\n## Accessibility\n\n- The editable root uses role=\"textbox\" and aria-multiline.\n- Fixed and selected-text formatting controls use ToggleMenu toolbar semantics and roving focus.\n- Block styles use radio semantics, list controls toggle on and off, and command buttons do not expose pressed state.\n- The hover toolbar uses SelectionMenu's direct ToggleMenu pass-through for range containment, selection preservation, and Popover positioning.\n- Suggestion menus use the existing Command and Popover primitives.\n";
57
- readonly select: "\n# Select Component\n\nA custom (non-native) dropdown selection field: a combobox trigger opening a listbox popover,\nwith full keyboard navigation, grouped options, and Field/Form integration.\n\n## Basic Usage\n\n```svelte\n<Select\n\tlabel=\"Country\"\n\tbind:value={country}\n\titems={[\n\t\t{ value: 'us', label: 'United States' },\n\t\t{ value: 'uk', label: 'United Kingdom' },\n\t\t{ value: 'ca', label: 'Canada' }\n\t]}\n/>\n```\n\n## Grouped options\n\nFlat options and `{ label, items }` groups can be mixed freely; separators render between groups.\n\n```svelte\n<Select\n\tlabel=\"Timezone\"\n\tbind:value={tz}\n\titems={[\n\t\t{ label: 'Europe', items: [{ value: 'paris', label: 'Paris' }, { value: 'berlin', label: 'Berlin' }] },\n\t\t{ label: 'America', items: [{ value: 'nyc', label: 'New York' }, { value: 'la', label: 'Los Angeles' }] }\n\t]}\n/>\n```\n\n## Props\n\nExtends all Field component props plus:\n\n### Core Props\n- **value**: string (bindable) - Selected value\n- **items**: (SelectOption | SelectOptionGroup)[] - Flat `{ value, label, disabled? }` options and/or `{ label, items }` groups\n- **placeholder**: string (default: 'Select an option') - Trigger text when no selection\n- **separators**: boolean (default: true) - Render separators between consecutive groups\n\n### Field Props (inherited)\n- **label**: string | Snippet - Field label, and the trigger's accessible name; without it the trigger falls back to the placeholder\n- **description**: string | Snippet - Helper text\n- **required**: boolean - Mark as required\n- **disabled**: boolean - Disable the trigger\n- **size**: 'small' | 'normal' | 'large' - Trigger and dropdown size\n- **density**: 'compact' | 'normal' | 'comfortable' (default: 'normal') - Spacing density forwarded to the dropdown option rows (paddings, gaps, min-height)\n- **name**: string - Form field name; also renders a hidden input for native form posts\n\n### Bindable Props\n- **value**: string - Selected value\n- **errors**: string[] - Validation errors\n- **focused**: boolean - Trigger focus state\n\n### Callbacks\n- **onValueChange**: (value) => void - Fires when the selection changes\n- **onValidate**: (value) => string[] | boolean - Custom validation\n\n### Advanced Props\n- **theme**: SelectThemeProps - Theme overrides (input, inputContainer, value, triggerIcon, content, group, groupLabel, item, itemIndicator, separator)\n\n## Keyboard\n\n- Closed: ArrowDown / ArrowUp / Enter / Space open the dropdown, anchored on the selected option\n- Open: ArrowDown / ArrowUp move the highlight (wrap-around), Home / End jump, Enter / Space select, Escape closes, Tab closes and moves focus on\n- Type-ahead: typing letters while the trigger has focus moves the highlight to the next option whose label starts with the typed text\n- Disabled options are skipped by the highlight\n\n## Accessibility\n\nARIA 1.2 select-only combobox pattern: the trigger is a `role=\"combobox\"` button with\n`aria-haspopup=\"listbox\"`, `aria-expanded`, and `aria-controls`; DOM focus stays on the\ntrigger while `aria-activedescendant` tracks the highlighted `role=\"option\"` (virtual focus).\nThe selected option shows a check indicator and `aria-selected`.\n\nThe trigger always has an accessible name: `label` names it through the Field label (a string\nlabel as a `<label for>`, a snippet through `aria-labelledby`), and without one the trigger\nfalls back to `placeholder`, then to the catalog's \"Select an option\". Pass `label` whenever\nan unlabelled select sits in a toolbar or filter row, so the name says which control it is\nrather than repeating the placeholder.\n\n## Notes\n\n- Selection re-focuses the trigger (matches native select behavior)\n- Clicking outside closes via trigger blur; option rows prevent mousedown so the click can land\n- The dropdown scrolls beyond ~240px (ScrollArea); the highlight scrolls into view on keyboard nav\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
57
+ readonly select: "\n# Select Component\n\nA custom (non-native) selection field: a combobox trigger opening a listbox popover, with full\nkeyboard navigation, grouped options, and Field/Form integration. Like a native select (and\nRadix's item-aligned or Base UI's `alignItemWithTrigger` position), the listbox opens over the\ntrigger with the selected option sitting on the value.\n\n## Basic Usage\n\n```svelte\n<Select\n\tlabel=\"Country\"\n\tbind:value={country}\n\titems={[\n\t\t{ value: 'us', label: 'United States' },\n\t\t{ value: 'uk', label: 'United Kingdom' },\n\t\t{ value: 'ca', label: 'Canada' }\n\t]}\n/>\n```\n\n## Grouped options\n\nFlat options and `{ label, items }` groups can be mixed freely; separators render between groups.\n\n```svelte\n<Select\n\tlabel=\"Timezone\"\n\tbind:value={tz}\n\titems={[\n\t\t{ label: 'Europe', items: [{ value: 'paris', label: 'Paris' }, { value: 'berlin', label: 'Berlin' }] },\n\t\t{ label: 'America', items: [{ value: 'nyc', label: 'New York' }, { value: 'la', label: 'Los Angeles' }] }\n\t]}\n/>\n```\n\n## Props\n\nExtends all Field component props plus:\n\n### Core Props\n- **value**: string (bindable) - Selected value\n- **items**: (SelectOption | SelectOptionGroup)[] - Flat `{ value, label, disabled? }` options and/or `{ label, items }` groups\n- **placeholder**: string (default: 'Select an option') - Trigger text when no selection\n- **separators**: boolean (default: true) - Render separators between consecutive groups\n- **alignItemWithTrigger**: boolean (default: true) - Open over the trigger with the selected option (the first enabled one when nothing is selected) on the trigger's middle and its text on the value text. A list taller than the viewport is capped 8px from the edges and pre-scrolled so the option stays on the trigger; a trigger too close to the bottom for four rows gives up the alignment and the panel rises into view. Wheel and touch scrolling outside the panel are blocked while it is open, so the trigger cannot move away from it. `false` opens a dropdown below the trigger instead, capped at ~240px\n\n### Field Props (inherited)\n- **label**: string | Snippet - Field label, and the trigger's accessible name; without it the trigger falls back to the placeholder\n- **description**: string | Snippet - Helper text\n- **required**: boolean - Mark as required\n- **disabled**: boolean - Disable the trigger\n- **size**: 'small' | 'normal' | 'large' - Trigger and dropdown size\n- **density**: 'compact' | 'normal' | 'comfortable' (default: 'normal') - Spacing density forwarded to the dropdown option rows (paddings, gaps, min-height)\n- **name**: string - Form field name; also renders a hidden input for native form posts\n\n### Bindable Props\n- **value**: string - Selected value\n- **errors**: string[] - Validation errors\n- **focused**: boolean - Trigger focus state\n\n### Callbacks\n- **onValueChange**: (value) => void - Fires when the selection changes\n- **onValidate**: (value) => string[] | boolean - Custom validation\n\n### Advanced Props\n- **theme**: SelectThemeProps - Theme overrides (input, inputContainer, value, triggerIcon, content, group, groupLabel, item, itemIndicator, separator)\n\n## Keyboard\n\n- Closed: ArrowDown / ArrowUp / Enter / Space open the dropdown, anchored on the selected option\n- Open: ArrowDown / ArrowUp move the highlight (wrap-around), Home / End jump, Enter / Space select, Escape closes, Tab closes and moves focus on\n- Type-ahead: typing letters while the trigger has focus moves the highlight to the next option whose label starts with the typed text\n- Disabled options are skipped by the highlight\n\n## Accessibility\n\nARIA 1.2 select-only combobox pattern: the trigger is a `role=\"combobox\"` button with\n`aria-haspopup=\"listbox\"`, `aria-expanded`, and `aria-controls`; DOM focus stays on the\ntrigger while `aria-activedescendant` tracks the highlighted `role=\"option\"` (virtual focus).\nThe selected option shows a check indicator and `aria-selected`.\n\nThe trigger always has an accessible name: `label` names it through the Field label (a string\nlabel as a `<label for>`, a snippet through `aria-labelledby`), and without one the trigger\nfalls back to `placeholder`, then to the catalog's \"Select an option\". Pass `label` whenever\nan unlabelled select sits in a toolbar or filter row, so the name says which control it is\nrather than repeating the placeholder.\n\n## Notes\n\n- Selection re-focuses the trigger (matches native select behavior)\n- Clicking outside closes via trigger blur; option rows prevent mousedown so the click can land\n- The list scrolls (ScrollArea) beyond the viewport when item-aligned, beyond ~240px as a dropdown; the highlight scrolls into view on keyboard nav\n- The panel covers the trigger while open, so clicking outside (not the trigger) closes it, as with a native select\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
58
58
  readonly slider: "\n# Slider Component\n\nThe Slider component is a Field-based numeric control for scalar values, range tuples, vertical layouts, multi-thumb values, and draggable selected ranges. It renders ARIA slider thumbs instead of relying on a native range input so every mode shares the same state machine and theme parts.\n\n## Basic Usage\n\n```svelte\n<script>\n\tlet volume = $state(40);\n</script>\n\n<Slider label=\"Volume\" bind:value={volume} min={0} max={100} step={5} showValue />\n```\n\n## Range Usage\n\n```svelte\n<script>\n\tlet budget = $state([25, 75]);\n</script>\n\n<Slider\n\tmode=\"range\"\n\tlabel=\"Budget\"\n\tbind:value={budget}\n\tmin={0}\n\tmax={100}\n\tstep={5}\n\tminStepsBetweenThumbs={2}\n\tdragRange\n\tshowValue\n\tformatValue={(value) => `$${value}k`}\n/>\n```\n\n## Props\n\nExtends all Field props plus:\n\n### Core Props\n- **value**: number | number[] | null (bindable) - Current scalar or multi-thumb value\n- **mode**: 'single' | 'range' (default: 'single') - Range mode defaults to two thumbs\n- **variant**: 'default' | 'thick' | 'contained' (default: 'default') - Visual track style; `thick` renders a heavier track with an inset pill thumb, while `contained` places the label and optional value inside an input-like rail\n- **thumbs**: number - Number of thumbs when the value is missing or scalar\n- **min**: number (default: 0) - Minimum selectable value\n- **max**: number (default: 100) - Maximum selectable value\n- **step**: number (default: 1) - Pointer and keyboard increment\n- **minStepsBetweenThumbs**: number (default: 0) - Minimum spacing between neighboring thumbs, in step units\n- **orientation**: 'horizontal' | 'vertical' (default: 'horizontal') - Track direction\n- **dragRange**: boolean (default: false) - Allows dragging the selected range segment as a unit\n- **color**: semantic color (default: 'primary') - Filled track and thumb color\n\n### Display Props\n- **showValue**: boolean (default: false) - Shows value chips beside the track\n- **formatValue**: (value: number, index: number, values: number[]) => string - Formats thumb labels and aria-valuetext\n- **marks**: Array<{ value: number; label?: string | Snippet }> - Optional tick marks along the track\n- **thumbLabels**: string[] - Accessible labels for individual thumbs\n- **i18n**: Partial<Messages> - Per-instance i18n overrides\n\n### Field Props\n- **label**: string | Snippet - Field label\n- **description**: string | Snippet - Helper text\n- **required**: boolean - Validates that a value is present\n- **disabled**: boolean - Disables pointer and keyboard interaction\n- **size**: 'small' | 'normal' | 'large' - Control size\n- **errors**: string[] | boolean (bindable) - Validation errors\n- **focused**: boolean (bindable) - Focus state\n- **onValueChange**: (value: number | number[]) => void - Called when value changes\n- **onValidate**: (value: number | number[]) => string[] | boolean - Custom validation\n\n### Slots\n- **prefix**: Snippet - Content before the slider\n- **suffix**: Snippet - Content after the slider\n- **valueLabel**: Snippet<SliderValuePayload> - Custom per-thumb value label\n- **rangeLabel**: Snippet<SliderRangePayload> - Custom selected range label\n\n### Styling Props\n- **class**: string - Classes for the field root\n- **theme**: SliderThemeProps & FieldThemeProps - Theme overrides\n\n## Form Integration\n\nUse `type: 'slider'` for scalar values and `type: 'slider-range'` for submitted number arrays.\n\n```svelte\n<Form\n\tinputs={{\n\t\tvolume: {\n\t\t\ttype: 'slider',\n\t\t\tlabel: 'Volume',\n\t\t\tvalue: 35,\n\t\t\tmin: 0,\n\t\t\tmax: 100,\n\t\t\tstep: 5,\n\t\t\tshowValue: true\n\t\t},\n\t\tcomfortBand: {\n\t\t\ttype: 'slider-range',\n\t\t\tlabel: 'Comfort band',\n\t\t\tvalue: [18, 24],\n\t\t\tmin: 12,\n\t\t\tmax: 32,\n\t\t\tdragRange: true,\n\t\t\tshowValue: true\n\t\t}\n\t}}\n/>\n```\n\n## Structure\n\n```\n<Field>\n\t<Label />\n\t<InputContainer>\n\t\t<Prefix />\n\t\t<SliderRoot>\n\t\t\t<HiddenInput />\n\t\t\t<Track role=\"group\">\n\t\t\t\t<ScreenReaderInstructions />\n\t\t\t\t<TrackBackground />\n\t\t\t\t<SelectedRange />\n\t\t\t\t<Thumb role=\"slider\" />\n\t\t\t\t<Marks />\n\t\t\t</Track>\n\t\t\t<ValueLabels />\n\t\t</SliderRoot>\n\t\t<Suffix />\n\t</InputContainer>\n\t<Description />\n\t<Error />\n</Field>\n```\n\n## Examples\n\n### Vertical\n```svelte\n<Slider\n\torientation=\"vertical\"\n\tlabel=\"Output\"\n\tbind:value={output}\n\tmin={0}\n\tmax={100}\n\tstep={10}\n\tshowValue\n/>\n```\n\n### Thick Variant\n```svelte\n<Slider\n\tvariant=\"thick\"\n\tsize=\"normal\"\n\tlabel=\"Temperature\"\n\tbind:value={temperature}\n\tmin={16}\n\tmax={30}\n\tstep={1}\n\tshowValue\n/>\n```\n\n### Contained Variant\n\n`contained` is intended for compact settings panels. It keeps the native Field label association while rendering the label inside the rail. Add `showValue` to place the formatted value at the opposite edge.\n\n```svelte\n<Slider\n variant=\"contained\"\n label=\"Background glow\"\n bind:value={glow}\n min={0}\n max={3}\n step={0.1}\n showValue\n formatValue={(value) => value.toFixed(1)}\n/>\n```\n\n### Three Thumbs\n```svelte\n<Slider\n\tlabel=\"Distribution\"\n\tbind:value={distribution}\n\tmin={0}\n\tmax={100}\n\tstep={5}\n\tthumbs={3}\n\tshowValue\n/>\n```\n\n### Custom Range Label\n```svelte\n<Slider mode=\"range\" bind:value={range} showValue>\n\t{#snippet rangeLabel(payload)}\n\t\t<span>{payload.startValue} - {payload.endValue}</span>\n\t{/snippet}\n</Slider>\n```\n\n## Accessibility\n\n- Each thumb is a button with `role=\"slider\"`\n- Single-thumb sliders use the visible Field label when present; multi-thumb sliders add per-thumb labels such as \"Budget minimum\" and \"Budget maximum\"\n- The track is exposed as a labelled group when the label is plain text, with hidden keyboard instructions referenced by `aria-describedby`\n- Thumb values expose `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext`, and `aria-orientation`\n- Multi-thumb `aria-valuemin`/`aria-valuemax` reflect each thumb's current movement bounds, including minimum thumb spacing\n- Keyboard support: Arrow keys move by one step, Shift+Arrow and PageUp/PageDown move by ten steps, Home/End jump to bounds\n- ArrowLeft / ArrowRight follow the writing direction: in RTL, ArrowLeft increases and ArrowRight decreases (vertical sliders are unaffected)\n- Multi-thumb sliders enforce ordering and optional minimum thumb spacing\n- Disabled state blocks pointer, keyboard, and selected-range dragging\n\n## Theme Customization\n\nTheme parts:\n- **inputContainer**: inherited Field input container wrapper\n- **root**: Slider root layout\n- **control**: Track and value-label layout\n- **track**: Pointer surface and color carrier\n- **trackBackground**: Unselected track\n- **range**: Selected range segment\n- **thumb**: Individual slider thumb button\n- **thumbHitbox**: Invisible thick-variant thumb pointer target\n- **thumbVisual**: Visible thumb shape\n- **valueLabels**: Value chip group\n- **valueLabel**: Individual value chip\n- **containedLabel**: Label rendered inside the contained rail\n- **containedTicks**: Decorative scale wrapper for the contained rail\n- **containedTick**: Individual decorative scale line\n- **marks**: Mark container\n- **mark**: Individual mark positioning wrapper\n- **markDot**: Mark dot\n- **markLabel**: Mark text\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
59
59
  readonly switch: "\n# Switch Component\n\nThe Switch component is a toggle control for boolean settings, providing a visual on/off state with smooth animations.\n\n## Basic Usage\n\n```svelte\n<script>\n\tlet enabled = $state(false);\n</script>\n\n<Switch label=\"Enable notifications\" bind:value={enabled} />\n```\n\n## Props\n\nExtends all Field component props plus:\n\n### Core Props\n- **value**: boolean | null (bindable) - Toggle state (bind with `bind:value`)\n- **defaultValue**: boolean | null (default: null) - Initial value when value is omitted\n\n### Field Props (inherited)\n- **label**: string | Snippet - Field label\n- **description**: string | Snippet - Helper text\n- **error**: string - Error message\n- **required**: boolean - Mark as required\n- **disabled**: boolean - Disable toggle\n- **size**: 'small' | 'normal' | 'large' - Switch size\n\n### Event Props\n- **onValueChange**: (value: boolean) => void - Called when toggled\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<Field>\n\t<InputContainer>\n\t\t<SwitchToggle>\n\t\t\t<SwitchThumb />\n\t\t</SwitchToggle>\n\t\t<Label />\n\t</InputContainer>\n\t<Description />\n\t<Error />\n</Field>\n```\n\n## Examples\n\n### Basic Switch\n```svelte\n<script>\n\tlet notifications = $state(false);\n</script>\n\n<Switch \n\tlabel=\"Enable Notifications\"\n\tbind:value={notifications}\n/>\n```\n\n### With Description\n```svelte\n<Switch \n\tlabel=\"Auto-save\"\n\tdescription=\"Automatically save your work every 5 minutes\"\n\tbind:value={autoSave}\n/>\n```\n\n### Required Switch\n```svelte\n<Switch \n\tlabel=\"I agree to the terms and conditions\"\n\tbind:value={agreedToTerms}\n\trequired\n/>\n```\n\n### Disabled Switch\n```svelte\n<Switch \n\tlabel=\"Premium Feature\"\n\tvalue={false}\n\tdisabled\n\tdescription=\"Upgrade to unlock this feature\"\n/>\n```\n\n### Different Sizes\n```svelte\n<Switch size=\"small\" label=\"Small Switch\" bind:value={val1} />\n<Switch size=\"normal\" label=\"Normal Switch\" bind:value={val2} />\n<Switch size=\"large\" label=\"Large Switch\" bind:value={val3} />\n```\n\n### With Change Handler\n```svelte\n<script>\n\tfunction handleToggle(checked) {\n\t\tconsole.log('Switch toggled:', checked);\n\t\t// Save to API, etc.\n\t}\n</script>\n\n<Switch \n\tlabel=\"Dark Mode\"\n\tbind:value={darkMode}\n\tonValueChange={handleToggle}\n/>\n```\n\n### Settings Panel\n```svelte\n<script>\n\tlet settings = $state({\n\t\tnotifications: true,\n\t\temailUpdates: false,\n\t\tautoPlay: false,\n\t\tshowPreview: true\n\t});\n</script>\n\n<div class=\"space-y-4\">\n\t<Switch \n\t\tlabel=\"Push Notifications\"\n\t\tdescription=\"Receive notifications on your device\"\n\t\tbind:value={settings.notifications}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Email Updates\"\n\t\tdescription=\"Get weekly email summaries\"\n\t\tbind:value={settings.emailUpdates}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Auto-play Videos\"\n\t\tdescription=\"Videos start playing automatically\"\n\t\tbind:value={settings.autoPlay}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Show Preview\"\n\t\tdescription=\"Display content previews\"\n\t\tbind:value={settings.showPreview}\n\t/>\n</div>\n```\n\n### Privacy Settings\n```svelte\n<form>\n\t<Heading size=\"h3\">Privacy Settings</Heading>\n\t\n\t<Switch \n\t\tlabel=\"Profile Visibility\"\n\t\tdescription=\"Make your profile visible to other users\"\n\t\tbind:value={privacy.profileVisible}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Show Email\"\n\t\tdescription=\"Display your email on your profile\"\n\t\tbind:value={privacy.showEmail}\n\t\tdisabled={!privacy.profileVisible}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Activity Status\"\n\t\tdescription=\"Show when you're online\"\n\t\tbind:value={privacy.showActivity}\n\t/>\n\t\n\t<Button type=\"submit\">Save Settings</Button>\n</form>\n```\n\n### Feature Toggles\n```svelte\n<script>\n\tlet features = $state({\n\t\texperimental: false,\n\t\tbeta: false,\n\t\tanalytics: true\n\t});\n\t\n\t$effect(() => {\n\t\tif (features.experimental) {\n\t\t\tconsole.log('Experimental features enabled');\n\t\t}\n\t});\n</script>\n\n<div class=\"panel\">\n\t<Heading size=\"h4\">Feature Flags</Heading>\n\t\n\t<Switch \n\t\tlabel=\"Experimental Features\"\n\t\tdescription=\"⚠️ Use at your own risk\"\n\t\tbind:value={features.experimental}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Beta Features\"\n\t\tdescription=\"Try new features before they're released\"\n\t\tbind:value={features.beta}\n\t/>\n\t\n\t<Switch \n\t\tlabel=\"Analytics\"\n\t\tdescription=\"Help us improve by sharing usage data\"\n\t\tbind:value={features.analytics}\n\t/>\n</div>\n```\n\n### Conditional Content\n```svelte\n<script>\n\tlet advancedMode = $state(false);\n</script>\n\n<Switch \n\tlabel=\"Advanced Mode\"\n\tdescription=\"Show advanced options\"\n\tbind:value={advancedMode}\n/>\n\n{#if advancedMode}\n\t<div class=\"advanced-options\">\n\t\t<!-- Advanced settings here -->\n\t</div>\n{/if}\n```\n\n### Form Integration\n```svelte\n<Form \n\tinputs={{\n\t\tname: { type: 'text', label: 'Name', required: true },\n\t\tnotifications: { \n\t\t\ttype: 'switch', \n\t\t\tlabel: 'Enable Notifications',\n\t\t\tdescription: 'Receive important updates'\n\t\t},\n\t\tmarketing: { \n\t\t\ttype: 'switch', \n\t\t\tlabel: 'Marketing Emails'\n\t\t}\n\t}}\n\tonSubmit={handleSubmit}\n/>\n```\n\n## Validation\n\nSwitch validates:\n- **required**: Must be checked (useful for terms acceptance)\n\n## Accessibility\n\n- Proper ARIA roles and attributes\n- Keyboard accessible (Space/Enter to toggle)\n- Focus states clearly visible\n- Label clickable to toggle\n- State changes announced to screen readers\n- Disabled state communicated properly\n\n## Notes\n\n- Smooth animated transitions between states\n- Visual feedback on hover and focus\n- Works standalone or within forms\n- Thumb slides with smooth animation\n- Toggle background changes color based on state\n- Supports all Field component features\n\n## Theme Customization\n\nThe Switch component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **toggle**: Switch toggle track styles\n- **thumb**: Switch thumb/knob styles\n- **inputContainer**: Container wrapper styles\n\n### Available Variants\n\n**toggle**:\n- base: Base classes for the toggle track\n- Variants:\n - checked: boolean - Background and border color based on checked state\n - size: 'small' | 'normal' | 'large' - Toggle dimensions (height and width)\n - disabled: boolean - Disabled state styling\n\n**thumb**:\n- base: Base classes for the thumb/knob\n- Variants:\n - checked: boolean - Thumb position and styling based on checked state\n - size: 'small' | 'normal' | 'large' - Thumb dimensions\n\n**inputContainer**:\n- base: Base classes for the container wrapper\n- Variants:\n - size: 'small' | 'normal' | 'large' - Gap spacing between label and toggle\n - disabled: boolean - Disabled state styling\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Switch \n label=\"Enable Feature\"\n bind:value={enabled}\n theme={{\n toggle: {\n base: 'rounded-full transition-all',\n checked: {\n true: 'bg-green-500 border-green-600'\n },\n size: {\n normal: 'h-6 w-11'\n }\n },\n thumb: {\n size: {\n normal: 'h-5 w-5'\n }\n }\n }}\n/>\n```\n\n**Custom Colors**:\n```svelte\n<Switch \n label=\"Custom Switch\"\n bind:value={checked}\n theme={{\n toggle: {\n checked: {\n true: 'bg-purple-500 border-purple-600',\n false: 'bg-gray-300 border-gray-400'\n }\n },\n thumb: {\n checked: {\n true: 'bg-white border-purple-500',\n false: 'bg-white border-gray-400'\n }\n }\n }}\n/>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setSwitchTheme } from '../components/Form/Switch/index.ts';\n \n setSwitchTheme({\n toggle: {\n base: 'transition-all duration-300',\n checked: {\n true: 'bg-primary border-primary lift-3'\n },\n size: {\n normal: 'h-6 w-12'\n }\n },\n thumb: {\n base: 'lift-4',\n size: {\n normal: 'h-5 w-5'\n }\n }\n });\n</script>\n```\n";
60
60
  readonly 'tag-group': "\n# TagGroup Component\n\nTagGroup renders a finite set of selectable tags as chips. It is a form input for compact category, filter, or preference selection.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { TagGroup } from 'entasis/tag-group';\n\n\tlet value = $state('news');\n</script>\n\n<TagGroup\n\tlabel=\"Category\"\n\tbind:value\n\titems={[\n\t\t{ value: 'news', label: 'News' },\n\t\t{ value: 'travel', label: 'Travel' },\n\t\t{ value: 'gaming', label: 'Gaming' }\n\t]}\n/>\n```\n\n## Multiple Selection\n\n```svelte\n<script lang=\"ts\">\n\tlet selected = $state(['news', 'gaming']);\n</script>\n\n<TagGroup\n\tmultiple\n\tlabel=\"Topics\"\n\tbind:value={selected}\n\titems={[\n\t\t{ value: 'news', label: 'News' },\n\t\t{ value: 'travel', label: 'Travel' },\n\t\t{ value: 'gaming', label: 'Gaming' }\n\t]}\n/>\n```\n\n## Props\n\n### Core Props\n- **items**: TagGroupOption[] - Options rendered as selectable chips.\n- **value**: string | string[] | null - Bindable selection. Single mode writes string|null; multiple mode writes string[].\n- **defaultValue**: string | string[] | null - Initial selection used only when `value` is omitted.\n- **multiple**: boolean (default: false) - Enables selecting multiple tags.\n- **label**: Slot - Field label.\n- **name**: string - Form field name.\n- **required**: boolean (default: false) - Marks selection as required.\n- **disabled**: boolean (default: false) - Disables all tag buttons.\n\n### Option Props\n- **value**: string - Unique selection value.\n- **label**: string | Snippet - Visible chip label.\n- **icon**: string | Snippet - Optional icon rendered before the label.\n- **disabled**: boolean - Disables one option.\n- **color**: Colors - Per-option chip color override.\n- **variant**: 'solid' | 'outline' | 'soft' - Per-option chip variant override.\n\n### Style Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Controls chip density.\n- **color**: Colors (default: 'primary') - Selected chip color.\n- **unselectedColor**: Colors (default: 'neutral') - Unselected chip color.\n- **selectedVariant**: 'solid' | 'outline' | 'soft' (default: 'soft') - Selected chip variant. The selected fill itself comes from the shared soft selected recipe on the item class.\n- **unselectedVariant**: 'solid' | 'outline' | 'soft' (default: 'outline') - Unselected chip variant. Outline by default so an unselected tag stays distinct from the soft selected fill at the neutral role.\n- **theme**: TagGroupThemeProps - Theme overrides for TagGroup and Field parts.\n- **chipTheme**: ChipThemeProps - Theme overrides forwarded to each Chip.\n\n### Events\n- **onValueChange**: (value: string | string[] | null) => void - Called when normalized selection changes.\n- **onValidate**: (value: string | string[] | null) => string[] | boolean - Custom validation.\n\n## Accessibility\n\n- Uses native button controls for each selectable chip.\n- Buttons expose aria-pressed for selected state.\n- The field is rendered as a fieldset through the shared Field wrapper.\n- Hidden inputs mirror selected values for native form submission.\n\n## State contract\n\n- **value**: current bindable editable value.\n- **defaultValue**: initial value used only when `value` is omitted.\n- **onValueChange**: called with the new value when the component changes it.\n\n";
@@ -109,7 +109,7 @@ export declare const componentMcpRegistry: {
109
109
  readonly 'hover-card': "\n# HoverCard Component\n\nHoverCard previews supplemental content when a trigger is hovered or focused. It composes Popover for positioning and dismissal with Card for the visible content surface.\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { HoverCard } from 'entasis/hover-card';\n</script>\n\n<HoverCard\n\ttrigger={{ content: 'Hover @entasis', variant: 'link' }}\n\ttitle=\"@entasis\"\n\tdescription=\"Composable Svelte UI components.\"\n>\n\t<p>Preview content shown on hover or focus.</p>\n</HoverCard>\n```\n\n## Props\n\n### Core Props\n- **id**: string - Stable id for the underlying popover root.\n- **open**: boolean - Bindable open state.\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided.\n- **trigger**: string | Snippet<[HoverCardPayload]> | ButtonProps - Trigger content. ButtonProps render a Entasis Button.\n- **children**: string | Snippet<[HoverCardPayload]> - Main card content.\n- **title**: string | Snippet<[HoverCardPayload]> - Card title slot.\n- **description**: string | Snippet<[HoverCardPayload]> - Card description slot.\n- **footer**: string | Snippet<[HoverCardPayload]> - Card footer slot.\n\n### Behavior Props\n- **position**: Placement (default: 'top') - Preferred placement relative to the trigger.\n- **offset**: number (default: 8) - Gap between trigger and card.\n- **delay**: number (default: 150) - Delay before opening on hover or focus.\n- **closeDelay**: number (default: 100) - Delay before closing after pointer/focus leaves.\n- **openOnFocus**: boolean (default: true) - Opens when focus enters the trigger or card.\n- **openOnClick**: boolean (default: false) - Toggles on trigger click, useful for touch fallbacks.\n- **disabled**: boolean (default: false) - Prevents opening and disables Button triggers.\n\n### Dismissal and Transition Props\n- **closeOnEscape**: boolean (default: true) - Escape closes the hover card.\n- **closeOnClickOutside**: boolean (default: true) - Outside clicks close the hover card.\n- **directedTransition**: boolean (default: true) - Transition direction follows placement.\n- **transition**: ResponsiveProps<FSOProps> - Popover transition override.\n\n### Styling Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Controls Popover panel and Card sizing.\n- **density**: 'compact' | 'normal' | 'comfortable' (default: 'normal') - Controls inner Card padding and spacing.\n- **class**: string - Extra classes on the inner Card.\n- **triggerClass**: string - Extra classes on the trigger wrapper.\n- **popover**: Props forwarded to the transparent Popover panel as one object - `{ class, theme }`.\n- **card**: Props forwarded to the inner Card as one object - `{ color, variant, theme }` (defaults: color 'neutral', variant 'solid').\n- **showBorders**: boolean (default: false) - Card section borders.\n- **theme**: HoverCardThemeProps - Theme overrides for HoverCard wrapper parts.\n\n### Callbacks\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change.\n- **onAfterOpen**: (payload: HoverCardPayload) => void - Called after the open transition finishes.\n- **onAfterClose**: (payload: HoverCardPayload) => void - Called after the close transition finishes.\n\n## Examples\n\n### Delays\n```svelte\n<HoverCard delay={300} closeDelay={200} trigger=\"Hover\">\n\tContent\n</HoverCard>\n```\n\n### Custom Trigger\n```svelte\n<HoverCard position=\"right\">\n\t{#snippet trigger(hoverCard)}\n\t\t<button aria-expanded={hoverCard.isOpen}>Preview</button>\n\t{/snippet}\n\n\tPreview content\n</HoverCard>\n```\n\n## Accessibility\n\n- Opens on pointer hover and keyboard focus by default.\n- Escape and outside click dismissal are delegated to Popover.\n- Button triggers receive aria-haspopup, aria-expanded, and aria-controls.\n- HoverCard is best for supplemental previews; primary content should remain reachable without hover.\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) — a small lift plus scale on `fast` / `enter`.\n- Resolved by HoverCard and handed to the underlying Popover, replacing the popover preset.\n- Ladder: `<Theme components={{ 'hover-card': { motion } }}>` → `setHoverCardTheme({ motion })`\n → `theme.motion` → the `transition` prop. Reduced motion collapses it to 0.\n";
110
110
  readonly 'link-preview': "\n# LinkPreview Component\n\nLinkPreview renders an anchor trigger with a HoverCard preview that loads link metadata asynchronously. It shows Skeleton placeholders while loading and displays title, description, site name, Open Graph image, and favicon when available.\n\n## Import\n\n```svelte\n<script>\n\timport { LinkPreview } from 'entasis/link-preview';\n</script>\n```\n\n## Basic Usage\n\n```svelte\n<LinkPreview href=\"https://svelte.dev\">Svelte</LinkPreview>\n```\n\nBy default, LinkPreview requests `/api/link-metadata?url=<href>` when the card opens. Browser-only fetching of arbitrary links is not reliable because most sites block cross-origin HTML reads, so applications should provide a server endpoint or a custom `fetchMetadata` function.\n\n## With Preloaded Metadata\n\n```svelte\n<LinkPreview\n\thref=\"https://entasis.dev\"\n\tmetadata={{\n\t\ttitle: 'Entasis',\n\t\tdescription: 'Configuration-first Svelte components.',\n\t\tsiteName: 'Entasis',\n\t\tfavicon: '/favicon.png'\n\t}}\n>\n\tEntasis\n</LinkPreview>\n```\n\n## Custom Fetcher\n\n```svelte\n<script>\n\tconst fetchMetadata = async (href, signal) => {\n\t\tconst response = await fetch(`/api/preview?href=${encodeURIComponent(href)}`, { signal });\n\t\tif (!response.ok) throw new Error('Preview unavailable');\n\t\treturn response.json();\n\t};\n</script>\n\n<LinkPreview href=\"https://example.com\" {fetchMetadata}>Example</LinkPreview>\n```\n\n## Props\n\n- **href**: string - URL opened by the trigger link and requested by the metadata loader.\n- **id**: string - Stable DOM id for the underlying HoverCard; falls back to a generated id.\n- **open**: boolean - Bindable open state.\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided.\n- **children**: string | Snippet<[LinkPreviewPayload]> - Trigger anchor content.\n- **metadata**: LinkPreviewMetadata - Preloaded metadata; skips network loading.\n- **fetchMetadata**: (href, signal) => Promise<LinkPreviewMetadata> - Custom async loader.\n- **metadataEndpoint**: string | (href) => string - Endpoint used when fetchMetadata is not provided. String endpoints receive ?url=<href>.\n- **prefetch**: boolean - Load metadata on mount instead of waiting for open.\n- **target**: string - Trigger anchor target.\n- **rel**: string - Trigger anchor rel. Defaults to noopener noreferrer for target=\"_blank\".\n- **fallbackTitle**: string - Title shown when metadata has no title.\n- **imageAlt**: string - Alt text for the preview image.\n- **showUrl**: boolean - Whether to show the URL line.\n- **loadingLabel**: string - Accessible label for the loading region.\n- **errorLabel**: string - Heading shown when metadata loading fails.\n- **position**: Popover placement - Preferred card placement.\n- **offset**: number - Gap between trigger and card.\n- **delay**: number - Open delay in milliseconds.\n- **closeDelay**: number - Close delay in milliseconds.\n- **openOnFocus**: boolean - Open when focus enters trigger or card.\n- **openOnClick**: boolean - Toggle card on click before navigation.\n- **closeOnEscape**: boolean - Close on Escape.\n- **closeOnClickOutside**: boolean - Close when clicking outside.\n- **directedTransition**: boolean - Use placement-aware transitions.\n- **transition**: object - Popover transition overrides.\n- **size**: 'small' | 'normal' | 'large' - Preview card size.\n- **disabled**: boolean - Disable opening and link navigation.\n- **class**: string - Trigger anchor classes.\n- **card**: Props forwarded to the inner Card as one object - `{ class, color, variant, theme }` (defaults: color 'neutral', variant 'solid').\n- **popover**: Props forwarded to the Popover panel as one object - `{ class, theme }`.\n- **showBorders**: boolean - Show Card section borders.\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change.\n- **onAfterOpen**: (payload) => void - Called after open transition.\n- **onAfterClose**: (payload) => void - Called after close transition.\n- **onLoad**: (payload) => void - Called after metadata loads.\n- **onError**: (error) => void - Called after metadata loading fails.\n- **theme**: LinkPreviewThemeProps - LinkPreview theme overrides.\n- **hoverCardTheme**: HoverCardThemeProps - HoverCard wrapper theme overrides.\n\n## Metadata Shape\n\n```ts\ntype LinkPreviewMetadata = {\n\turl?: string;\n\ttitle?: string;\n\tdescription?: string;\n\tsiteName?: string;\n\timage?: string;\n\tfavicon?: string;\n};\n```\n\n## Endpoint Contract\n\nThe default endpoint should return JSON matching LinkPreviewMetadata. Non-2xx responses should return a JSON object with a `message` string when possible.\n\n## Accessibility\n\n- The trigger remains a real anchor, so the destination is reachable without hover.\n- Loading and error states use role=\"status\".\n- A disabled LinkPreview removes the anchor href and prevents hover opening.\n\n## Theme Parts\n\n- **trigger** - Anchor trigger.\n- **card** - HoverCard surface classes.\n- **content** - Preview content wrapper.\n- **media** - Image container.\n- **image** - Preview image.\n- **body** - Metadata text stack.\n- **header** - Favicon and site row.\n- **favicon** - Favicon image.\n- **site** - Site name text.\n- **title** - Preview title.\n- **description** - Preview description.\n- **url** - URL display line.\n- **loading** - Skeleton stack.\n- **error** - Error state container.\n\n## Motion\n\n- LinkPreview has no preset of its own: it forwards `transition` (now a plain `FSOProps`,\n responsive) to HoverCard, whose **motion** slot owns the preset.\n- Retune it with `<Theme components={{ 'hover-card': { motion } }}>` or\n `setHoverCardTheme({ motion })`; the `transition` prop still wins per instance.\n";
111
111
  readonly overlay: "\n# Overlay Component\n\nOverlay layers concise content and actions over bounded media or another visual surface. Place it as the direct first child of a container; the component automatically establishes that parent as its positioning context, so no attachment or wrapper component is required.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Overlay } from 'entasis/overlay';\n</script>\n\n<div class=\"aspect-video overflow-hidden rounded-lg\">\n\t<Overlay\n\t\ttitle=\"Design system foundations\"\n\t\tdescription=\"A practical tour of tokens, primitives, and composition.\"\n\t\tactions={[{ content: 'Open gallery', color: 'neutral', variant: 'soft' }]}\n\t/>\n\t<img src=\"/cover.jpg\" alt=\"Coastal landscape\" class=\"size-full object-cover\" />\n</div>\n```\n\n## Props\n\n- **position**: 'fill' | 'top' | 'bottom' (default: 'fill') - Places the content vertically. Fill uses a surface-wide dark scrim; top and bottom use content-sized black-to-transparent gradients.\n- **align**: 'start' | 'center' | 'end' (default: 'center') - Horizontal content and text alignment.\n- **showOn**: 'always' | 'hover' | 'focus' (default: 'always') - Reveal condition. Hover also reveals for focus-within so actions remain keyboard accessible.\n- **open**: boolean (default: true) - Enables or hides the overlay while preserving its reveal transition.\n- **defaultOpen**: boolean (default: true) - Initial state when open is not provided.\n- **onOpenChange**: (open: boolean) => void - Reserved for library-requested state changes.\n- **onAfterOpen**: () => void - Called after the open transition finishes.\n- **onAfterClose**: () => void - Called after the close transition finishes.\n- **scrim**: boolean (default: true) - Toggles the dark fill or directional gradient.\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Scales padding, gaps, and typography.\n- **actions**: OverlayAction[] - Button props plus a content string, rendered as a wrapping action row.\n- **ref**: HTMLDivElement | null - Bindable root reference.\n- **class**: string - Additional root classes.\n- **theme**: OverlayThemeProps - Theme overrides.\n\n## Slots\n\n- **title**: Main overlay heading.\n- **description**: Supporting text.\n- **content**: Additional body content between the description and actions.\n- **children**: Fully custom composition replacing title, description, content, and actions.\n\nAll named content slots accept a string or snippet.\n\n## Placement\n\nThe overlay must be the direct first child of the surface it covers:\n\n```svelte\n<div class=\"overflow-hidden rounded-lg\">\n\t<Overlay position=\"bottom\" title=\"Golden hour\" />\n\t<img src=\"/photo.jpg\" alt=\"Golden hour over a valley\" />\n</div>\n```\n\nThe parent receives a zero-specificity relative positioning context and isolated stacking context through CSS `:has()`. An explicit parent positioning utility or inline style still takes precedence.\n\nTop and bottom content enters from its corresponding edge while the scrim fades in place. Fill content only fades. Motion is disabled when the user requests reduced motion.\n\n## Reveal on Hover or Focus\n\n```svelte\n<div class=\"aspect-video overflow-hidden rounded-lg\">\n\t<Overlay\n\t\tshowOn=\"hover\"\n\t\tposition=\"bottom\"\n\t\ttitle=\"Mountain archive\"\n\t\tactions={[{ content: 'View collection', color: 'neutral', variant: 'soft' }]}\n\t/>\n\t<img src=\"/mountain.jpg\" alt=\"Snow-covered mountain\" />\n</div>\n```\n\n## Accessibility\n\n- The overlay is semantically neutral; supplied buttons and links keep their native semantics.\n- Hover-revealed content also appears when focus enters the surface or its actions.\n- Setting open to false makes the overlay inert and hides it from assistive technology.\n- Images and video beneath the overlay still require their own accessible labels or alternatives.\n\n## Theme Parts\n\n- **root**: Absolute overlay, reveal state, vertical placement, and clipping.\n- **scrim**: Fill or directional gradient.\n- **content**: Padding and horizontal alignment.\n- **header**: Title and description stack.\n- **title**: Heading typography.\n- **description**: Supporting text.\n- **body**: Additional content slot.\n- **actions**: Button row.\n";
112
- readonly popover: "\n# Popover Component\n\nThe Popover component displays floating content positioned relative to a trigger element. It's ideal for tooltips, dropdown menus, and contextual information.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\t\t\n</script>\n// By default Popover comes with a button that triggers them, no need to define a callback and a $state\n<Popover trigger={{ content: \"Toggle Popover\" }}>\n\tPopover content here\n</Popover>\n```\n\n## Props\n\n### Core Props\n- **open**: boolean (bindable) - Controls popover visibility (optional when using trigger prop)\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided\n- **ref**: HTMLElement | null - Reference element to position popover against (optional when using trigger prop)\n- **id**: string - Unique identifier\n\n### Layout Props\n- **position**: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' (default: 'bottom')\n - Determines where popover appears relative to trigger\n- **offset**: number - Distance in pixels from the reference element\n- **fitTrigger**: boolean (default: false) - Whether popover should match the width of the trigger element\n- **mobileSheet**: boolean (default: false) - On mobile viewports (<768px), render as a bottom sheet instead of an anchored floating panel\n- **inline**: boolean (default: false) - Render the panel in normal document flow where the component sits instead of portaling to the viewport-fixed layer: no floating-ui positioning, no scroll lock, no outside-press dismissal (Escape still closes it). Same panel classes and motion, and the trigger still toggles it. Wins over `mobileSheet`\n- **mobileSheetSizeTransition**: boolean (default: true) - Whether mobile-sheet panels animate intrinsic size changes\n\n### Event Props\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change\n- **onAfterOpen**: (payload: PopoverState) => void - Called after the open transition finishes\n- **onAfterClose**: (payload: PopoverState) => void - Called after the close transition finishes\n\n### Slot Props\n- **children**: Snippet<[PopoverState]> - Popover content\n- **trigger**: Snippet<[PopoverState]> | (ButtonProps & { content?: string }) | false - Trigger element\n - Pass a snippet function for custom trigger: `{#snippet trigger(popover)}...</snippet>`\n - Pass button props object for default button: `trigger={{ content: \"Click Me\", color: \"primary\" }}`\n - Pass `false` to disable trigger (use with external ref)\n\n### Interaction Props\n- **openOnHover**: boolean (default: false) - Open on mouse hover\n- **openOnClick**: boolean (default: true) - Open on click\n- **delay**: number (default: 100) - Delay in ms before opening on hover\n- **closeOnEscape**: boolean (default: true) - Close on Escape. Only the topmost open layer closes, so Escape inside a nested popover leaves its parent open\n- **closeOnClickOutside**: boolean (default: true) - Close on an outside press. A press dismisses this popover and every layer stacked above it, but never the layer that was pressed\n- **closeOnMouseLeave**: boolean (default: false) - Close when the pointer leaves the hover safe area. The safe area is the trigger, the panel, and a prediction cone toward the panel, so a diagonal move to the panel keeps it open.\n- **debugSafeArea**: boolean (default: false) - Show hover safe-area overlays. Trigger/panel rectangles render in blue; the prediction cone toward the panel renders in orange.\n\n### Focus & ARIA Props\n- **focusOnOpen**: 'first' | 'container' | false (default: false) - Where focus goes when the panel opens: `'first'` moves it to an `[autofocus]` / `[data-autofocus]` target or the first tabbable control, `'container'` focuses the panel itself, `false` keeps it on the trigger. Focus always returns to the trigger when the popover closes (Escape or outside press)\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true (default: 'dialog') - Value of `aria-haspopup` on the trigger, describing what the panel contains. `aria-expanded` and `aria-controls` are managed automatically alongside it, for the built-in Button trigger and for a snippet trigger using `{@attach popover.reference}`\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover focusOnOpen=\"first\" haspopup=\"listbox\" trigger={{ content: 'Pick one' }}>\n\t<ul role=\"listbox\"><li role=\"option\" tabindex=\"0\">First</li></ul>\n</Popover>\n```\n\n### Visual Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Compact popover size\n - normal: Standard popover size\n - large: Larger popover size\n- **transition**: TransitionConfig - Custom transition animation\n- **directedTransition**: boolean (default: true) - Transition direction follows position\n\n### Behavior Props\n- **lockScroll**: boolean (default: true) - Lock body scroll when open\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<PopoverTrigger>\n\t<Trigger />\n</PopoverTrigger>\n\n<PopoverContent>\n\t<Children />\n</PopoverContent>\n```\n\n## Examples\n\n### More Examples\n\n### With a custom trigger snippet\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<!-- {@attach popover.reference} anchors the panel to the element and keeps\n aria-haspopup / aria-expanded / aria-controls in sync on it -->\n<Popover>\n\t{#snippet trigger(popover)}\n\t\t<Button onclick={() => popover.toggle()} {@attach popover.reference}>Open</Button>\n\t{/snippet}\n\t\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### With button props\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{\n\t\tcontent: \"Click Me\",\n\t\tcolor: \"secondary\",\n\t\tsize: \"small\"\n\t}}\n>\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### Different Positions\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Top -->\n<Popover position=\"top\" trigger={{ content: \"Top\" }}>\n\tTop popover\n</Popover>\n\n<!-- Bottom -->\n<Popover position=\"bottom\" trigger={{ content: \"Bottom\" }}>\n\tBottom popover\n</Popover>\n\n<!-- Left -->\n<Popover position=\"left\" trigger={{ content: \"Left\" }}>\n\tLeft popover\n</Popover>\n\n<!-- Right -->\n<Popover position=\"right\" trigger={{ content: \"Right\" }}>\n\tRight popover\n</Popover>\n```\n\n### Advanced Example\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<Popover position=\"bottom-start\" trigger={{ content: \"Menu\" }}>\n\t<div class=\"flex flex-col gap-1\">\n\t\t<Button variant=\"ghost\" fullWidth>Profile</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Settings</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Logout</Button>\n\t</div>\n</Popover>\n```\n\n### Open on Hover\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\topenOnClick={false}\n\tdelay={200}\n>\n\tHover content\n</Popover>\n```\n\n### With Custom Offset\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tposition=\"bottom\"\n\toffset={20}\n>\n\t20px away from trigger\n</Popover>\n```\n\n### Fit Trigger Width\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Click Me\" }}\n\tfitTrigger\n>\n\tPopover matches trigger width\n</Popover>\n```\n\n### Inline (static, in flow)\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Open in place, no portal: useful for docs, visual tests, or an always-visible panel -->\n<Popover inline open trigger={false}>\n\t<p>Rendered where the component sits.</p>\n</Popover>\n\n<!-- The trigger still toggles an inline panel -->\n<Popover inline trigger={{ content: 'Toggle' }}>\n\t<p>Expands below the trigger, in the flow.</p>\n</Popover>\n```\n\n### Mobile Bottom Sheet\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover\n\ttrigger={{ content: \"Open filters\" }}\n\tposition=\"bottom\"\n\tmobileSheet\n>\n\tFilters content\n</Popover>\n```\n\n### Different Sizes\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Small -->\n<Popover size=\"small\" trigger={{ content: \"Small\" }}>\n\tSmall popover\n</Popover>\n\n<!-- Large -->\n<Popover size=\"large\" trigger={{ content: \"Large\" }}>\n\tLarge popover with more content\n</Popover>\n```\n\n### Close on Mouse Leave\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\tcloseOnMouseLeave\n>\n\tCloses when you move outside the rectangle tolerance\n</Popover>\n```\n\n### With Lifecycle Hooks\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tonAfterOpen={(payload) => console.log('Popover opened', payload)}\n\tonAfterClose={(payload) => console.log('Popover closed', payload)}\n>\n\tWatch the console\n</Popover>\n```\n\n### User Card Popover with External Ref\n\n```svelte\n<script lang=\"ts\">\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n\timport { Avatar } from 'entasis/avatar';\n\n\tlet avatarRef = $state<HTMLElement | null>(null);\n\tlet open = $state(false);\n\tconst user = { name: 'John Doe', email: 'john@example.com' };\n</script>\n\n<button type=\"button\" bind:this={avatarRef} onclick={() => (open = !open)}>\n\t<Avatar name={user.name} />\n</button>\n\n<Popover bind:open ref={avatarRef} position=\"bottom\">\n\t<div class=\"p-4\">\n\t\t<h3>{user.name}</h3>\n\t\t<p>{user.email}</p>\n\t\t<Button fullWidth>View Profile</Button>\n\t</div>\n</Popover>\n```\n\n## State Management\n\nThe Popover component uses a `PopoverState` instance that is passed to all slot snippets. This state object provides:\n\n- **id**: string - Popover identifier\n- **size**: Size - Current popover size\n- **position**: Placement - Current popover position\n- **offset**: number - Current offset value\n- **open()**: () => void - Method to open the popover\n- **close()**: () => void - Method to close the popover\n- **toggle()**: () => void - Method to toggle the popover\n- **reference**: attachment for a custom trigger element (`{@attach popover.reference}`); anchors the panel to it and keeps its `aria-haspopup`, `aria-expanded`, and `aria-controls` in sync\n\n## Accessibility\n\n- The trigger carries `aria-haspopup` (from `haspopup`), `aria-expanded`, and `aria-controls` — automatically for the built-in Button and for a snippet trigger using `{@attach popover.reference}`\n- `focusOnOpen` decides where focus lands on open; focus returns to the trigger on close, whether by Escape or an outside press\n- Non-modal: Tab is not contained and the page is not made inert (use Dialog for that)\n- Escape closes only the topmost open layer; an outside press dismisses every layer stacked above the one pressed. Popovers, menus, and dialogs share one layer stack\n- Keyboard navigation support\n\n## Notes\n\n- Popover is positioned using floating-ui, except with `inline`, where the document lays the panel out\n- Automatically adjusts position to stay in viewport\n- Multiple popovers can be stacked\n- Scroll locking prevents background scroll (when enabled)\n- Transitions animate based on position direction\n\n## Theme Customization\n\nThe Popover component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **motion**: Open/close transition preset, keyed by `mode` (a floating panel scales, the mobile\n sheet slides up). Takes `in` / `out` FSO params plus a `duration` / `easing` motion token;\n the `transition` prop wins over it\n- **popover**: Main popover container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { PopoverThemeProps } from 'entasis/popover';\n\n// Example theme customization\nconst customTheme: PopoverThemeProps = {\n popover: {\n base: 'z-[+50] fixed bg-surface-floating text-neutral w-fit rounded-xl raised isolate h-fit',\n size: {\n small: 'max-w-3xs w-full p-2',\n normal: 'max-w-xs w-full p-3',\n large: 'max-w-sm w-full p-4'\n }\n }\n};\n```\n\n### Available Variants\n\n**popover**:\n- base: Base classes applied to all popovers\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls max-width, width, and padding\n - mode: 'floating' | 'inline' | 'mobileSheet' - Set from `inline` / `mobileSheet`; `root` positions the wrapper (fixed, in flow, or full-screen sheet)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Popover \n trigger={{ content: \"Click Me\" }}\n theme={{\n popover: {\n base: 'rounded-xl lift-5 border-2 border-primary',\n size: {\n normal: 'max-w-md p-4'\n }\n }\n }}\n>\n Custom styled popover content\n</Popover>\n```\n\n**Size Customization**:\n```svelte\n<Popover \n size=\"large\"\n trigger={{ content: \"Large Popover\" }}\n theme={{\n popover: {\n size: {\n large: 'max-w-lg p-6'\n }\n }\n }}\n>\n Large popover with more padding\n</Popover>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setPopoverTheme } from '../components/Popover/index.ts';\n \n setPopoverTheme({\n popover: {\n base: 'rounded-lg lift-5 backdrop-blur-sm bg-white/95',\n size: {\n normal: 'max-w-sm p-4'\n }\n }\n });\n</script>\n```\n";
112
+ readonly popover: "\n# Popover Component\n\nThe Popover component displays floating content positioned relative to a trigger element. It's ideal for tooltips, dropdown menus, and contextual information.\n\n## Basic Usage\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\t\t\n</script>\n// By default Popover comes with a button that triggers them, no need to define a callback and a $state\n<Popover trigger={{ content: \"Toggle Popover\" }}>\n\tPopover content here\n</Popover>\n```\n\n## Props\n\n### Core Props\n- **open**: boolean (bindable) - Controls popover visibility (optional when using trigger prop)\n- **defaultOpen**: boolean (default: false) - Initial state when open is not provided\n- **ref**: HTMLElement | null - Reference element to position popover against (optional when using trigger prop)\n- **id**: string - Unique identifier\n\n### Layout Props\n- **position**: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end' (default: 'bottom')\n - Determines where popover appears relative to trigger\n- **offset**: number - Distance in pixels from the reference element\n- **fitTrigger**: boolean (default: false) - Whether popover should match the width of the trigger element\n- **positionPanel**: ({ panel, reference }) => { x: number; y: number; minWidth?: number } | null - Place the panel yourself instead of floating-ui: return its viewport coordinates (the panel is `position: fixed`) and optionally a `minWidth` in px that replaces `fitTrigger`'s, or `null` to fall back to `position`. Called when the panel mounts and whenever floating-ui would reposition it. Select uses it to open over its trigger\n- **mobileSheet**: boolean (default: false) - On mobile viewports (<768px), render as a bottom sheet instead of an anchored floating panel\n- **inline**: boolean (default: false) - Render the panel in normal document flow where the component sits instead of portaling to the viewport-fixed layer: no floating-ui positioning, no scroll lock, no outside-press dismissal (Escape still closes it). Same panel classes and motion, and the trigger still toggles it. Wins over `mobileSheet`\n- **mobileSheetSizeTransition**: boolean (default: true) - Whether mobile-sheet panels animate intrinsic size changes\n\n### Event Props\n- **onOpenChange**: (open: boolean) => void - Called once for each library-requested state change\n- **onAfterOpen**: (payload: PopoverState) => void - Called after the open transition finishes\n- **onAfterClose**: (payload: PopoverState) => void - Called after the close transition finishes\n\n### Slot Props\n- **children**: Snippet<[PopoverState]> - Popover content\n- **trigger**: Snippet<[PopoverState]> | (ButtonProps & { content?: string }) | false - Trigger element\n - Pass a snippet function for custom trigger: `{#snippet trigger(popover)}...</snippet>`\n - Pass button props object for default button: `trigger={{ content: \"Click Me\", color: \"primary\" }}`\n - Pass `false` to disable trigger (use with external ref)\n\n### Interaction Props\n- **openOnHover**: boolean (default: false) - Open on mouse hover\n- **openOnClick**: boolean (default: true) - Open on click\n- **delay**: number (default: 100) - Delay in ms before opening on hover\n- **closeOnEscape**: boolean (default: true) - Close on Escape. Only the topmost open layer closes, so Escape inside a nested popover leaves its parent open\n- **closeOnClickOutside**: boolean (default: true) - Close on an outside press. A press dismisses this popover and every layer stacked above it, but never the layer that was pressed\n- **closeOnMouseLeave**: boolean (default: false) - Close when the pointer leaves the hover safe area. The safe area is the trigger, the panel, and a prediction cone toward the panel, so a diagonal move to the panel keeps it open.\n- **debugSafeArea**: boolean (default: false) - Show hover safe-area overlays. Trigger/panel rectangles render in blue; the prediction cone toward the panel renders in orange.\n\n### Focus & ARIA Props\n- **focusOnOpen**: 'first' | 'container' | false (default: false) - Where focus goes when the panel opens: `'first'` moves it to an `[autofocus]` / `[data-autofocus]` target or the first tabbable control, `'container'` focuses the panel itself, `false` keeps it on the trigger. Focus always returns to the trigger when the popover closes (Escape or outside press)\n- **haspopup**: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | true (default: 'dialog') - Value of `aria-haspopup` on the trigger, describing what the panel contains. `aria-expanded` and `aria-controls` are managed automatically alongside it, for the built-in Button trigger and for a snippet trigger using `{@attach popover.reference}`\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover focusOnOpen=\"first\" haspopup=\"listbox\" trigger={{ content: 'Pick one' }}>\n\t<ul role=\"listbox\"><li role=\"option\" tabindex=\"0\">First</li></ul>\n</Popover>\n```\n\n### Visual Props\n- **size**: 'small' | 'normal' | 'large' (default: 'normal')\n - small: Compact popover size\n - normal: Standard popover size\n - large: Larger popover size\n- **transition**: TransitionConfig - Custom transition animation\n- **directedTransition**: boolean (default: true) - Transition direction follows position\n\n### Behavior Props\n- **lockScroll**: boolean (default: true) - Lock body scroll when open\n\n### Styling Props\n- **class**: string - Additional CSS classes\n- **theme**: ComponentTheme - Custom theme overrides\n\n## Structure\n\n```\n<PopoverTrigger>\n\t<Trigger />\n</PopoverTrigger>\n\n<PopoverContent>\n\t<Children />\n</PopoverContent>\n```\n\n## Examples\n\n### More Examples\n\n### With a custom trigger snippet\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<!-- {@attach popover.reference} anchors the panel to the element and keeps\n aria-haspopup / aria-expanded / aria-controls in sync on it -->\n<Popover>\n\t{#snippet trigger(popover)}\n\t\t<Button onclick={() => popover.toggle()} {@attach popover.reference}>Open</Button>\n\t{/snippet}\n\t\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### With button props\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{\n\t\tcontent: \"Click Me\",\n\t\tcolor: \"secondary\",\n\t\tsize: \"small\"\n\t}}\n>\n\t<p>This is a popover!</p>\n</Popover>\n```\n\n### Different Positions\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Top -->\n<Popover position=\"top\" trigger={{ content: \"Top\" }}>\n\tTop popover\n</Popover>\n\n<!-- Bottom -->\n<Popover position=\"bottom\" trigger={{ content: \"Bottom\" }}>\n\tBottom popover\n</Popover>\n\n<!-- Left -->\n<Popover position=\"left\" trigger={{ content: \"Left\" }}>\n\tLeft popover\n</Popover>\n\n<!-- Right -->\n<Popover position=\"right\" trigger={{ content: \"Right\" }}>\n\tRight popover\n</Popover>\n```\n\n### Advanced Example\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n</script>\n\n<Popover position=\"bottom-start\" trigger={{ content: \"Menu\" }}>\n\t<div class=\"flex flex-col gap-1\">\n\t\t<Button variant=\"ghost\" fullWidth>Profile</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Settings</Button>\n\t\t<Button variant=\"ghost\" fullWidth>Logout</Button>\n\t</div>\n</Popover>\n```\n\n### Open on Hover\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\topenOnClick={false}\n\tdelay={200}\n>\n\tHover content\n</Popover>\n```\n\n### With Custom Offset\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tposition=\"bottom\"\n\toffset={20}\n>\n\t20px away from trigger\n</Popover>\n```\n\n### Fit Trigger Width\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Click Me\" }}\n\tfitTrigger\n>\n\tPopover matches trigger width\n</Popover>\n```\n\n### Inline (static, in flow)\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Open in place, no portal: useful for docs, visual tests, or an always-visible panel -->\n<Popover inline open trigger={false}>\n\t<p>Rendered where the component sits.</p>\n</Popover>\n\n<!-- The trigger still toggles an inline panel -->\n<Popover inline trigger={{ content: 'Toggle' }}>\n\t<p>Expands below the trigger, in the flow.</p>\n</Popover>\n```\n\n### Mobile Bottom Sheet\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover\n\ttrigger={{ content: \"Open filters\" }}\n\tposition=\"bottom\"\n\tmobileSheet\n>\n\tFilters content\n</Popover>\n```\n\n### Different Sizes\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<!-- Small -->\n<Popover size=\"small\" trigger={{ content: \"Small\" }}>\n\tSmall popover\n</Popover>\n\n<!-- Large -->\n<Popover size=\"large\" trigger={{ content: \"Large\" }}>\n\tLarge popover with more content\n</Popover>\n```\n\n### Close on Mouse Leave\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Hover Me\" }}\n\topenOnHover\n\tcloseOnMouseLeave\n>\n\tCloses when you move outside the rectangle tolerance\n</Popover>\n```\n\n### With Lifecycle Hooks\n\n```svelte\n<script>\n\timport { Popover } from 'entasis/popover';\n</script>\n\n<Popover \n\ttrigger={{ content: \"Trigger\" }}\n\tonAfterOpen={(payload) => console.log('Popover opened', payload)}\n\tonAfterClose={(payload) => console.log('Popover closed', payload)}\n>\n\tWatch the console\n</Popover>\n```\n\n### User Card Popover with External Ref\n\n```svelte\n<script lang=\"ts\">\n\timport { Popover } from 'entasis/popover';\n\timport { Button } from 'entasis/button';\n\timport { Avatar } from 'entasis/avatar';\n\n\tlet avatarRef = $state<HTMLElement | null>(null);\n\tlet open = $state(false);\n\tconst user = { name: 'John Doe', email: 'john@example.com' };\n</script>\n\n<button type=\"button\" bind:this={avatarRef} onclick={() => (open = !open)}>\n\t<Avatar name={user.name} />\n</button>\n\n<Popover bind:open ref={avatarRef} position=\"bottom\">\n\t<div class=\"p-4\">\n\t\t<h3>{user.name}</h3>\n\t\t<p>{user.email}</p>\n\t\t<Button fullWidth>View Profile</Button>\n\t</div>\n</Popover>\n```\n\n## State Management\n\nThe Popover component uses a `PopoverState` instance that is passed to all slot snippets. This state object provides:\n\n- **id**: string - Popover identifier\n- **size**: Size - Current popover size\n- **position**: Placement - Current popover position\n- **offset**: number - Current offset value\n- **open()**: () => void - Method to open the popover\n- **close()**: () => void - Method to close the popover\n- **toggle()**: () => void - Method to toggle the popover\n- **reference**: attachment for a custom trigger element (`{@attach popover.reference}`); anchors the panel to it and keeps its `aria-haspopup`, `aria-expanded`, and `aria-controls` in sync\n\n## Accessibility\n\n- The trigger carries `aria-haspopup` (from `haspopup`), `aria-expanded`, and `aria-controls` — automatically for the built-in Button and for a snippet trigger using `{@attach popover.reference}`\n- `focusOnOpen` decides where focus lands on open; focus returns to the trigger on close, whether by Escape or an outside press\n- Non-modal: Tab is not contained and the page is not made inert (use Dialog for that)\n- Escape closes only the topmost open layer; an outside press dismisses every layer stacked above the one pressed. Popovers, menus, and dialogs share one layer stack\n- Keyboard navigation support\n\n## Notes\n\n- Popover is positioned using floating-ui, except with `inline`, where the document lays the panel out\n- Automatically adjusts position to stay in viewport\n- Multiple popovers can be stacked\n- Scroll locking prevents background scroll (when enabled)\n- Transitions animate based on position direction\n\n## Theme Customization\n\nThe Popover component uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **motion**: Open/close transition preset, keyed by `mode` (a floating panel scales, the mobile\n sheet slides up). Takes `in` / `out` FSO params plus a `duration` / `easing` motion token;\n the `transition` prop wins over it\n- **popover**: Main popover container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { PopoverThemeProps } from 'entasis/popover';\n\n// Example theme customization\nconst customTheme: PopoverThemeProps = {\n popover: {\n base: 'z-[+50] fixed bg-surface-floating text-neutral w-fit rounded-xl raised isolate h-fit',\n size: {\n small: 'max-w-3xs w-full p-2',\n normal: 'max-w-xs w-full p-3',\n large: 'max-w-sm w-full p-4'\n }\n }\n};\n```\n\n### Available Variants\n\n**popover**:\n- base: Base classes applied to all popovers\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls max-width, width, and padding\n - mode: 'floating' | 'inline' | 'mobileSheet' - Set from `inline` / `mobileSheet`; `root` positions the wrapper (fixed, in flow, or full-screen sheet)\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<Popover \n trigger={{ content: \"Click Me\" }}\n theme={{\n popover: {\n base: 'rounded-xl lift-5 border-2 border-primary',\n size: {\n normal: 'max-w-md p-4'\n }\n }\n }}\n>\n Custom styled popover content\n</Popover>\n```\n\n**Size Customization**:\n```svelte\n<Popover \n size=\"large\"\n trigger={{ content: \"Large Popover\" }}\n theme={{\n popover: {\n size: {\n large: 'max-w-lg p-6'\n }\n }\n }}\n>\n Large popover with more padding\n</Popover>\n```\n\n**Global Theme Setting**:\n```svelte\n<script>\n import { setPopoverTheme } from '../components/Popover/index.ts';\n \n setPopoverTheme({\n popover: {\n base: 'rounded-lg lift-5 backdrop-blur-sm bg-white/95',\n size: {\n normal: 'max-w-sm p-4'\n }\n }\n });\n</script>\n```\n";
113
113
  readonly tooltip: "\n# Tooltip\n\nContextual information shown on hover or keyboard focus. Two forms share one surface:\n\n- `<Tooltip>` — a component with a `trigger` prop, like every other overlay.\n- `tooltip()` — the underlying attachment, for elements you already render yourself.\n\nBoth are rendered by the single tooltip surface that `<Theme>` mounts (`TooltipHost`).\n\n## Basic Usage\n\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Click to submit\" trigger={{ content: 'Submit', variant: 'outline' }} />\n```\n\n## Props\n\n- **content**: string | Snippet (required) - Tooltip body\n- **trigger**: Snippet<[Attachment<HTMLElement>]> | ButtonProps & { content?: string } (required) -\n A snippet receives the tooltip attachment and spreads it on its own element; Button props render\n a Button carrying it\n- **open**: boolean (default: false) - Shows the tooltip without hover or focus; bindable\n- **defaultOpen**: boolean (default: false) - Initial open state when `open` is not provided\n- **onOpenChange**: (open: boolean) => void - Called whenever the tooltip becomes visible or hidden,\n hover and focus included\n- **position**: Placement (default: 'top') - Tooltip position relative to the trigger\n - Options: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end'\n- **size**: 'small' | 'normal' | 'large' (default: 'normal') - Visual size\n- **color**: Colors (default: 'neutral') - Color theme\n- **variant**: 'solid' | 'outline' | 'soft' (default: 'solid') - Visual style matching Chip\n- **delay**: number (default: 400) - Delay in ms before showing tooltip; zero shows immediately\n- **offset**: number - Distance from the trigger in pixels\n- **class**: string - Additional CSS classes\n- **transition**: FSOProps - Custom transition configuration\n- **theme**: TooltipThemeProps - Per-instance theme overrides\n- **onAfterOpen**: () => void - Callback after the opening transition completes\n- **onAfterClose**: () => void - Callback after the closing transition completes\n\nEvery prop except `trigger`, `open`, `defaultOpen` and `onOpenChange` is also an option of the\n`tooltip()` attachment.\n\n## Examples\n\n### Button Trigger\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"This is helpful information\"\n\ttrigger={{ content: 'Hover me', variant: 'outline', color: 'neutral' }}\n/>\n```\n\n### Snippet Trigger\n```svelte\n<script lang=\"ts\">\n\timport type { Attachment } from 'svelte/attachments';\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet helpTrigger(attach: Attachment<HTMLElement>)}\n\t<span class=\"underline\" {@attach attach}>What is this?</span>\n{/snippet}\n\n<Tooltip content=\"Anchored to any element you like\" trigger={helpTrigger} />\n```\n\n### Forced Open\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<!-- Useful for docs, screenshots and visual tests -->\n<Tooltip open content=\"Always visible\" trigger={{ content: 'Anchor', variant: 'outline' }} />\n```\n\n### Different Positions\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Top tooltip\" position=\"top\" trigger={{ content: 'Top' }} />\n<Tooltip content=\"Bottom tooltip\" position=\"bottom\" trigger={{ content: 'Bottom' }} />\n<Tooltip content=\"Left tooltip\" position=\"left\" trigger={{ content: 'Left' }} />\n<Tooltip content=\"Right tooltip\" position=\"right\" trigger={{ content: 'Right' }} />\n```\n\n### Different Colors\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Success!\" color=\"success\" trigger={{ content: 'Success' }} />\n<Tooltip content=\"Warning!\" color=\"warning\" trigger={{ content: 'Warning' }} />\n<Tooltip content=\"Error!\" color=\"danger\" trigger={{ content: 'Error' }} />\n```\n\n### Custom Delay\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Quick tooltip\" delay={100} trigger={{ content: 'Quick (100ms)' }} />\n<Tooltip content=\"Slow tooltip\" delay={1000} trigger={{ content: 'Slow (1000ms)' }} />\n```\n\n### Different Sizes and Variants\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip content=\"Small tooltip\" size=\"small\" trigger={{ content: 'Small' }} />\n<Tooltip content=\"Large tooltip\" size=\"large\" trigger={{ content: 'Large' }} />\n<Tooltip content=\"Outlined tooltip\" variant=\"outline\" trigger={{ content: 'Outline' }} />\n<Tooltip content=\"Soft tooltip\" variant=\"soft\" trigger={{ content: 'Soft' }} />\n```\n\n### With Snippet Content\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n{#snippet richContent()}\n\t<div class=\"p-2\">\n\t\t<strong>Pro Tip</strong>\n\t\t<p class=\"text-sm\">Use Ctrl+S to save</p>\n\t</div>\n{/snippet}\n\n<Tooltip content={richContent} trigger={{ content: 'Keyboard Shortcuts' }} />\n```\n\n### With Callbacks\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Tracked tooltip\"\n\tonAfterOpen={() => console.log('Tooltip opened')}\n\tonAfterClose={() => console.log('Tooltip closed')}\n\ttrigger={{ content: 'Track me' }}\n/>\n```\n\n## The tooltip() attachment\n\nUse the attachment when the element already exists in your markup — icons, table cells, list rows,\ndisabled wrappers — or inside another component's internals.\n\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button {@attach tooltip({ content: 'Click to submit' })}> Submit </button>\n\n<!-- On icons or any non-interactive element -->\n<span {@attach tooltip({ content: 'More information', position: 'right' })}> ⓘ </span>\n\n<!-- Disabled elements do not fire events, so wrap them -->\n<span {@attach tooltip({ content: 'Feature coming soon' })}>\n\t<button disabled>Disabled Button</button>\n</span>\n```\n\nThe `<Tooltip>` component hands this same attachment to a snippet trigger, so the two forms are\ninterchangeable.\n\n## Accessibility\n\n- Shows on pointer hover and on keyboard focus (`focusin` / `focusout` on the trigger element), so\n attach it to focusable elements for keyboard users\n- Dismissed on mouse leave or blur\n- Non-interactive (cannot be clicked)\n- Renders with `role=\"tooltip\"` and sets `aria-describedby` on the trigger while visible (any previous value is restored on hide)\n- Does not block content behind it\n\n## Notes\n\n- Only one tooltip shows at a time; an `open` tooltip hands the surface over when another tooltip is\n hovered and reports that through `onOpenChange`\n- Automatically positions to stay in viewport using Floating UI\n- Uses smart delay: subsequent tooltips show instantly if within 400ms of previous\n- Brief content only (use Popover for interactive content)\n- The surface is a singleton (`TooltipHost`) rendered by `<Theme>`\n- Does not lock scroll or trap focus\n\n## Theme Customization\n\nThe Tooltip uses a theme object that can be customized using the `theme` prop or by setting a global theme.\n\n### Theme Structure\n\nThe theme object contains the following parts:\n- **root**: Main tooltip container styles\n\n### Theme Type Definition\n\n```typescript\nimport type { TooltipThemeProps } from 'entasis/tooltip';\n\n// Example theme customization\nconst customTheme: TooltipThemeProps = {\n root: {\n base: 'inline-flex w-fit items-center rounded-full border font-medium',\n size: {\n small: 'h-5 px-2 text-xs',\n normal: 'h-6 px-2.5 text-xs',\n large: 'h-7 px-3 text-sm'\n },\n\tcolor: {\n\t neutral: 'bg-neutral text-neutral-contrast',\n primary: 'bg-primary text-primary-contrast',\n danger: 'bg-danger text-danger-contrast',\n success: 'bg-success text-success-contrast',\n warning: 'bg-warning text-warning-contrast',\n\t info: 'bg-info text-info-contrast'\n\t},\n\tvariant: {\n\t solid: 'bg-color text-color-contrast',\n\t outline: 'border-color bg-transparent text-color-readable',\n\t soft: 'bg-color-muted text-color-muted-readable'\n }\n }\n};\n```\n\n### Available Variants\n\n**root**:\n- base: Base classes applied to all tooltips\n- Variants:\n - size: 'small' | 'normal' | 'large' - Controls text size and padding\n - color: 'primary' | 'secondary' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' - Color scheme\n - variant: 'solid' | 'outline' | 'soft' - Matches Chip's visual variants\n\n### Usage Examples\n\n**Basic Theme Override**:\n```svelte\n<script lang=\"ts\">\n\timport { Tooltip } from 'entasis/tooltip';\n</script>\n\n<Tooltip\n\tcontent=\"Custom tooltip\"\n\ttheme={{ root: { base: 'rounded-lg lift-4 border-2', size: { normal: 'px-3 py-2 text-sm' } } }}\n\ttrigger={{ content: 'Hover me' }}\n/>\n```\n\n**Color Customization**:\n```svelte\n<script lang=\"ts\">\n\timport { tooltip } from 'entasis/tooltip';\n</script>\n\n<button\n\t{@attach tooltip({\n\t\tcontent: 'Success!',\n\t\tcolor: 'success',\n\t\ttheme: { root: { color: { success: 'bg-green-500 text-white lift-3' } } }\n\t})}\n>\n\tSuccess Tooltip\n</button>\n```\n\n**Global Theme Setting**:\n```svelte\n<script lang=\"ts\">\n\timport { setTooltipTheme } from 'entasis/tooltip';\n\n\tsetTooltipTheme({\n\t\troot: {\n\t\t\tbase: 'rounded-md lift-4 backdrop-blur-sm',\n\t\t\tsize: { normal: 'px-3 py-1.5 text-sm' },\n\t\t\tcolor: { neutral: 'bg-gray-900 text-white', primary: 'bg-blue-500 text-white' }\n\t\t}\n\t});\n</script>\n```\n\n## Motion\n\n- **motion** theme slot: one preset (no variants) — a short fade-and-rise, `duration: 'fast'`.\n- Resolved by the tooltip surface and handed to the underlying Popover, so it replaces the\n popover preset.\n- Ladder: `<Theme components={{ tooltip: { motion } }}>` → `setTooltipTheme({ motion })` →\n `theme.motion` → the tooltip's `transition` option. Reduced motion collapses it to 0.\n- The tooltip surface is a singleton rendered by `<Theme>`, so a `setTooltipTheme` call\n made *below* `<Theme>` never reaches it. Call it at or above the `<Theme>` boundary, or\n use the `<Theme components={{ tooltip }}>` registry, which always applies.\n";
114
114
  readonly 'audio-player': "\n# AudioPlayer Component\n\nAudioPlayer is a native HTML5 audio player with Entasis chrome. It can render a\nwaveform or track seek/progress surface and composes controls from Button, Tooltip,\nPopover, and the shared Slider primitive.\n\n## Import\n\n```ts\nimport { AudioPlayer } from 'entasis/audio-player';\n```\n\n## Core Props\n\n- **src**: string - Single audio source URL.\n- **sources**: AudioPlayerSource[] - Multiple native source candidates.\n- **title**: string - Track title.\n- **label**: string - Accessible player label; falls back to `title`.\n- **artist**: string - Secondary metadata line.\n- **artwork**: string | false - Optional artwork image URL. Omitted artwork renders no fallback.\n- **variant**: 'waveform' | 'track' - Primary progress surface.\n- **layout**: 'block' | 'inline' - Controls/progress arrangement. Block stacks the seek surface below the header; inline places controls and seek on the same row when space allows.\n- **color**: Colors - Theme color for controls and progress fill.\n- **waveform**: number[] - Amplitude samples from 0 to 1. When omitted, samples are generated from the selected audio source when possible.\n- **waveformVariant**: 'centered' | 'histogram' - Waveform visual mode.\n- **waveformBars**: number - Number of bars rendered after resampling.\n- **controls**: AudioPlayerControl[] - Toggle play, seek, time, volume, loop, download.\n- **header**: Snippet<[AudioPlayerState]> - Replaces the full default header row.\n- **controlsSlot**: Snippet<[AudioPlayerState]> - Replaces the default controls area.\n- **leading**: Snippet<[AudioPlayerState]> - Renders before default metadata.\n- **trailing**: Snippet<[AudioPlayerState]> - Renders after default controls.\n- **seek**: Snippet<[AudioPlayerState]> - Replaces the waveform or track seek surface.\n- **download**: boolean | string - true uses the selected source, string uses that href.\n- **theme**: AudioPlayerThemeProps - Per-instance theme overrides.\n\n## Notes\n\nThe waveform and track surfaces are both seek inputs with an invisible range hitbox.\nWhen no waveform samples are provided, the component fetches and decodes the selected\naudio source in the browser to generate peak samples. While generation is pending, or\nif fetch/decode is unavailable, it uses a deterministic fallback waveform from source\nand metadata. Generation failures are reported through onError.\n";
115
115
  readonly carousel: "\n# Carousel Component\n\nCarousel renders a scrollable collection from items. Its public data contract matches Stepper and Tabs: pass items, then render one generated slide with children({ carousel, item, index }).\n\nThe component owns the direct slide wrappers. Do not put a consumer-owned repeated slide loop directly inside Carousel.\n\nThe chrome is a FOOTER ROW under the slides, never an overlay: pagination fills the leading side, the prev/next pair sits at the trailing end — including with pagination={false}, where the arrows are the row's only child and still sit trailing. By default the pagination is a progress line whose fill is the fraction of the scrollable range already scrolled.\n\n## Basic Usage\n\n<script lang=\"ts\">\n\timport { Carousel } from 'entasis/carousel';\n\n\tconst items = [\n\t\t{ title: 'Signal', description: 'Collect the first insight.' },\n\t\t{ title: 'Orbit', description: 'Review the second item.' },\n\t\t{ title: 'Focus', description: 'Finish with the third item.' }\n\t];\n</script>\n\n<Carousel items={items} navigationButton={{ color: 'primary' }} pagination={{ color: 'primary' }}>\n\t{#snippet children({ item, index, carousel })}\n\t\t<article>\n\t\t\t<p>Slide {index + 1}</p>\n\t\t\t<h3>{item.title}</h3>\n\t\t\t<p>{item.description}</p>\n\t\t\t<button onclick={() => carousel.next()}>Next</button>\n\t\t</article>\n\t{/snippet}\n</Carousel>\n\n## Core Props\n\n- items: Item[] - the collection used to generate one slide per item.\n- children: Snippet<[CarouselRenderPayload<Item>]> - renders content inside each generated slide wrapper.\n- layout: ResponsiveProps<number> - number of slides visible. Default: 1.\n- gaps: ResponsiveProps<number> - gap in pixels between generated slides. Default: 20.\n- partialDelta: ResponsiveProps<number> - pixels to reveal from the adjacent slide. Default: 0.\n- dragFree: boolean - disables strict snap behavior when true.\n- navigationButton: object, snippet or false - built-in prev/next controls, custom controls, or no buttons. The object takes color and size: 'small' | 'normal' | 'large' (default 'normal'). Default: { color: 'neutral' }.\n- pagination: object, snippet or false - built-in pagination, custom pagination, or none. The object takes variant: 'line' | 'dots', color and size. Default: { variant: 'line' }. 'line' is a presentational progress bar (role=\"progressbar\", not clickable); 'dots' renders one clickable dot per page.\n\nWhen pagination and navigationButton are both false no footer is rendered and the track is scrolled by drag, wheel and the keyboard.\n- class: string - classes for the root container.\n- theme: CarouselThemeProps - theme overrides for public parts.\n\nThose three props take the shared ResponsiveProps shape: a plain number used at every width (gaps={16}) or a record keyed by breakpoint (layout={{ xs: 1, md: 2 }}). In the record form xs is the base — there is no default key — and the nearest defined key at or below the active width wins, so { xs: 1, md: 2 } shows two slides from md up. A key you leave unset below the narrowest one falls back to the prop default.\n\nThe xs / sm / md / lg / xl keys are the CAROUSEL's own width, not the viewport's: sm from 36rem, md from 42rem, lg from 56rem, xl from 72rem of carousel width, with xs below that. These are the shared container breakpoints exported from entasis/theme, so sm means the same box width in Carousel, Grid and Stack. A 360px carousel in a sidebar of a wide page is xs; the same carousel run full-bleed is xl. Give the carousel a width that fills its host (the default root is w-full) so it can measure itself.\n\n## Render Payload\n\nchildren receives:\n\n- carousel: CarouselState - state and navigation helpers.\n- item: Item - the current item, preserving the caller's item shape.\n- index: number - zero-based generated slide index.\n\nUse item fields directly. For example, if items contains { title, image }, item.title and item.image are typed inside the snippet.\n\n## Responsive Example\n\n<Carousel\n\titems={items}\n\tlayout={{ xs: 1, sm: 2, lg: 3 }}\n\tgaps={{ xs: 16, lg: 24 }}\n\tpartialDelta={48}\n\tnavigationButton={{ color: 'primary' }}\n\tpagination={{ color: 'primary' }}\n>\n\t{#snippet children({ item })}\n\t\t<div class=\"rounded bg-surface p-6\">\n\t\t\t<h3>{item.title}</h3>\n\t\t\t<p>{item.description}</p>\n\t\t</div>\n\t{/snippet}\n</Carousel>\n\n## Custom Navigation\n\nThe snippet is rendered once per direction inside the footer's trailing slot, so it needs no positioning of its own.\n\n<Carousel items={items} pagination={{ color: 'primary' }}>\n\t{#snippet navigationButton(carousel, attributes, direction)}\n\t\t<button\n\t\t\t{...attributes}\n\t\t\tonclick={() => direction === 'prev' ? carousel.prev() : carousel.next()}\n\t\t>\n\t\t\t{direction === 'prev' ? 'Previous' : 'Next'}\n\t\t</button>\n\t{/snippet}\n\n\t{#snippet children({ item })}\n\t\t<div>{item.title}</div>\n\t{/snippet}\n</Carousel>\n\n## Custom Pagination\n\n<Carousel items={items} navigationButton={{ color: 'primary' }}>\n\t{#snippet pagination(carousel, dotItems)}\n\t\t<div class=\"flex justify-center gap-2\">\n\t\t\t{#each dotItems as dot, index}\n\t\t\t\t<button {...dot.attributes}>\n\t\t\t\t\t<span class=\"sr-only\">Slide {index + 1}</span>\n\t\t\t\t</button>\n\t\t\t{/each}\n\t\t</div>\n\t{/snippet}\n\n\t{#snippet children({ item })}\n\t\t<div>{item.title}</div>\n\t{/snippet}\n</Carousel>\n\n## CarouselState\n\n- currentSlide - currently visible slide.\n- lastSlideInView - last visible slide in the viewport.\n- canScrollNext - whether next navigation is possible.\n- canScrollPrev - whether previous navigation is possible.\n- sortedSlides - generated slide records in DOM order.\n- dots - pagination dot records with active state and attributes.\n- progress - 0..1 fraction of the scrollable range already scrolled; the progress line's fill width. 0 on the server.\n- scrollRange - the track's scrollable distance (scrollWidth - clientWidth).\n- breakpoint - active breakpoint, resolved from the carousel's own measured width.\n- resolvedLayout - active slides-per-view value.\n- resolvedGaps - active gap value.\n- next(count?) - move forward by count slides, defaulting to the active layout size.\n- prev(count?) - move backward by count slides, defaulting to the active layout size.\n- nextButton and prevButton - attributes for custom button composition.\n\n## Keyboard and Accessibility\n\n- The slider track is a focusable landmark: role=\"region\", aria-roledescription=\"carousel\", tabindex=\"0\"; each slide is labelled \"Slide n of m\".\n- With the track focused, ArrowRight / ArrowLeft move to the next / previous slide (mirrored in RTL), Home and End jump to the first and last slide.\n- Built-in dots mark the active slide with aria-current (not aria-selected); custom dots receive the same attributes through dot.attributes.\n- The progress line is presentational: role=\"progressbar\" with aria-valuenow/min/max and an i18n accessible name. It is not focusable and not clickable.\n\n## Theme Parts\n\n- root - outer carousel container.\n- slider - scrollable track.\n- slide - generated direct slide wrapper.\n- footer - the chrome row under the slider.\n- progress - the progress line's recessed track.\n- progressFill - the progress line's coloured fill.\n- navigation - the prev/next pair's container.\n- navigationButton - built-in previous and next buttons.\n- dots - built-in dots container.\n- dot - built-in dot button.\n\nUse the slide theme part to style every generated wrapper consistently:\n\n<Carousel\n\titems={items}\n\ttheme={{ slide: { base: 'rounded-lg bg-surface p-4' } }}\n>\n\t{#snippet children({ item })}\n\t\t<h3>{item.title}</h3>\n\t{/snippet}\n</Carousel>\n";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entasis",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "dev": "vite dev",
@@ -135,7 +135,7 @@
135
135
  "@maskito/kit": "^5.4.0",
136
136
  "@pierre/diffs": "^1.4.2",
137
137
  "@pierre/trees": "1.0.0-beta.6",
138
- "@tanstack/highlight": "0.1.0",
138
+ "@tanstack/highlight": "1.0.0",
139
139
  "@tanstack/svelte-virtual": "3.13.38",
140
140
  "@tanstack/table-core": "9.2.4",
141
141
  "cn": "0.3.0",
@@ -145,7 +145,7 @@
145
145
  "lightgallery": "^2.9.0",
146
146
  "melt": "^0.44.0",
147
147
  "runed": "^0.37.1",
148
- "svelte-streamdown": "^4.2.0",
148
+ "svelte-streamdown": "^4.2.1",
149
149
  "svelte-themes": "^2.0.10",
150
150
  "valibot": "^1.5.0"
151
151
  },