@imfusion/web-ui 0.10.1-dev.6.gf715361a → 0.10.1-dev.61.g9d1000b8

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 (74) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/README.md +8 -32
  3. package/THIRD_PARTY_NOTICES.md +110 -0
  4. package/dist/code-NTcddiYk.js +139 -0
  5. package/dist/components/account-card/account-card.d.ts +9 -26
  6. package/dist/components/app-shell/app-shell.d.ts +48 -26
  7. package/dist/components/callout/callout.d.ts +1 -1
  8. package/dist/components/chip/chip.d.ts +1 -1
  9. package/dist/components/color-scheme-toggle/color-scheme-preference.d.ts +3 -0
  10. package/dist/components/color-scheme-toggle/color-scheme-toggle.d.ts +17 -0
  11. package/dist/components/color-scheme-toggle/color-scheme-toggle.meta.d.ts +2 -0
  12. package/dist/components/color-scheme-toggle/index.d.ts +2 -0
  13. package/dist/components/dialog/dialog.d.ts +151 -0
  14. package/dist/components/dialog/dialog.meta.d.ts +2 -0
  15. package/dist/components/dialog/index.d.ts +2 -0
  16. package/dist/components/drawer/drawer.d.ts +1 -1
  17. package/dist/components/logo/index.d.ts +1 -0
  18. package/dist/components/logo/product/index.d.ts +1 -0
  19. package/dist/components/logo/product/marks.d.ts +11 -0
  20. package/dist/components/logo/product/product.d.ts +17 -0
  21. package/dist/components/scroll-area/index.d.ts +2 -0
  22. package/dist/components/scroll-area/scroll-area.d.ts +49 -0
  23. package/dist/components/scroll-area/scroll-area.meta.d.ts +2 -0
  24. package/dist/components/select/select.d.ts +18 -4
  25. package/dist/components/separator/separator.d.ts +4 -2
  26. package/dist/components/skeleton/skeleton.d.ts +17 -2
  27. package/dist/components/table/table.d.ts +12 -4
  28. package/dist/components/toggle/toggle.d.ts +8 -2
  29. package/dist/components/toggle-group/toggle-group-size.d.ts +3 -0
  30. package/dist/components/toggle-group/toggle-group.d.ts +7 -3
  31. package/dist/hooks/index.d.ts +2 -0
  32. package/dist/hooks/use-disclosure.d.ts +17 -0
  33. package/dist/icons.js +1 -2
  34. package/dist/index.d.ts +7 -3
  35. package/dist/index.js +2705 -2093
  36. package/dist/integrations/code-highlight.js +2 -2
  37. package/dist/integrations/frame-selection.js +2 -2
  38. package/dist/integrations/image-display-options.js +123 -113
  39. package/dist/{rolldown-runtime-Dy4uBu1J.js → resolve-state-props-DQf9WiGv.js} +12 -1
  40. package/dist/scroll-area-z9Lf9uIk.js +89 -0
  41. package/dist/{separator-lzz4A3x_.js → separator-BV2TR8U0.js} +16 -8
  42. package/dist/slider-Bf274cMe.js +106 -0
  43. package/dist/style.css +1 -1
  44. package/dist/{tabs-DRyqtS04.js → tabs-CU46aGud.js} +106 -88
  45. package/dist/vite/local-web-ui-link.d.ts +17 -4
  46. package/dist/vite.js +25 -20
  47. package/dist/web-ui-cli.js +155 -141
  48. package/docs/user-guide/AgentTooling.mdx +4 -4
  49. package/docs/user-guide/UsagePatterns.mdx +4 -0
  50. package/package.json +11 -9
  51. package/src/docgen/doc.gen.json +2292 -1071
  52. package/src/llms/install-templates/hooks/session-start.sh +14 -0
  53. package/src/llms/install-templates/hooks/stop.sh +7 -2
  54. package/src/llms/llms.gen.txt +18 -1
  55. package/src/llms/skills/imf-web-ui-audit/SKILL.md +28 -15
  56. package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +4 -2
  57. package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +8 -9
  58. package/src/llms/skills/imf-web-ui-conventions/topics/data.md +17 -2
  59. package/src/llms/skills/imf-web-ui-conventions/topics/git.md +8 -5
  60. package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +3 -0
  61. package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +5 -2
  62. package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +3 -1
  63. package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +47 -8
  64. package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +2 -0
  65. package/src/llms/skills/imf-web-ui-setup/SKILL.md +10 -1
  66. package/src/llms/skills/imf-web-ui-update/SKILL.md +12 -5
  67. package/src/llms/skills/imf-web-ui-ux/SKILL.md +47 -26
  68. package/src/llms/skills/imf-web-ui-ux/references/forms.md +6 -6
  69. package/src/llms/skills/imf-web-ui-ux/references/usability-heuristics.md +3 -3
  70. package/src/llms/skills/imf-web-ui-ux/references/visual-design.md +3 -3
  71. package/src/llms/tokens.gen.json +13 -5
  72. package/dist/code-BYkr5k55.js +0 -134
  73. package/dist/slider-Cqpreglu.js +0 -116
  74. package/src/llms/install-templates/hooks/baseline-staleness.sh +0 -24
package/CHANGELOG.md CHANGED
@@ -2,14 +2,181 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ### Highlights
6
+
7
+ #### Paper Mono is the mono font
8
+
9
+ [Paper Mono](https://paper.design/mono) replaces CoFo Sans Mono as the default `--imf-ui-font-family-mono`. It is licensed
10
+ under the SIL Open Font License, ships with the package, and covers every weight from 100 to 800, where CoFo had
11
+ only Regular. Mono text sets a little wider than before, so check tight labels in your layouts.
12
+
13
+ Mono text follows three weights: 600 for titles; 500 for control labels, buttons, tabs, and table headers; 400 for eyebrows,
14
+ metadata, values, and code. `Select`, `Slider`, and `Meter` labels, the `AppShell` nav item label, the `Popover` and
15
+ `Tooltip` triggers, the `Popover` close button, and the `Toast` close and action buttons move to 500. The `Typo` and
16
+ `Popover` eyebrows move to 400, and so does `Code`, which inherited the surrounding weight before: inline code in `Typo.P`
17
+ rendered at 300.
18
+
19
+ [`CodeHighlight`](src/integrations/code-highlight/code-highlight.mdx) uses the weights as well: keywords, JSX and HTML tags,
20
+ and Markdown headings at 600, functions and types at 500, comments at 300. The
21
+ [Weights](src/integrations/code-highlight/code-highlight.stories.tsx#Weights) story shows every tier in one snippet. A weight
22
+ never changes the character width, so columns stay aligned. CoFo Sans Mono still ships, so setting
23
+ `--imf-ui-font-family-mono` back to it restores the previous face; weights of 600 and up then render as synthesized bold.
24
+
25
+ ![Paper Mono in a highlighted TSX snippet, light](docs/assets/changelog/0.11.0-paper-mono-light.webp)
26
+ ![Paper Mono in a highlighted TSX snippet, dark](docs/assets/changelog/0.11.0-paper-mono-dark.webp)
27
+
28
+ ### Breaking
29
+
30
+ - **Breaking:** `AppShell.Main`, the AppShell aside drawer, and `Drawer.Content` scroll in a `ScrollArea` and pad their
31
+ content inside it ([WEBSDK-247](https://imfusion.atlassian.net/browse/WEBSDK-247)). — Nikolaus Brunner <brunner@imfusion.com>
32
+ - `AppShell.Main` stays the scrolling element and pads with `--imf-ui-appshell-padding`; a `padding` set on it adds to
33
+ that.
34
+ - `Drawer.Content` pads with `--imf-ui-drawer-content-padding`; a `padding`, `gap`, or grid set on it applies outside the
35
+ padded layer and does not reach the children.
36
+ - The aside drawer pads its content inside the scroll area; a `padding` on `AppShell.Aside` in drawer mode adds to it.
37
+ - **Breaking:** The AppShell navbar drawer opens from the inline start instead of the inline end. — Nikolaus Brunner <brunner@imfusion.com>
38
+ - Consumer CSS that places something against the navbar drawer's right edge moves to its left edge.
39
+ - **Breaking:** `AppShell.Navbar` renders its own logo row and account foot, and stacks its children as nav items with its own spacing. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
40
+ - Pass the brand as `logo` and the account card as `account`, and drop the `AppShell.Section`s that held them. Omit `logo` to use the default ImFusion logo; `logo={null}` leaves the row empty.
41
+ - **Breaking:** The current `AppShell.NavItem` has no background surface; its label is underlined and the other items are dimmed. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
42
+ - Consumer CSS that styled the item's background or `--navitem-current` moves to `--imf-ui-appshell-navitem-dim` and the underline properties.
43
+ - **Breaking:** `--imf-ui-appshell-padding` runs from 1.25rem to 3rem at `2.75cqi`, and `--imf-ui-appshell-navbar-width` adds one gutter. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
44
+ - Pages that tuned their padding against the old values re-check them. Set either property on `AppShell.Root` to restore the old numbers.
45
+ - **Breaking:** The aside pads with `--imf-ui-appshell-padding` on every side instead of `--imf-ui-size-6` above and below. — Nikolaus Brunner <brunner@imfusion.com>
46
+ - Set `--imf-ui-appshell-aside-padding: var(--imf-ui-size-6) var(--imf-ui-appshell-padding)` to restore the old block padding.
47
+ - **Breaking:** `AccountCard` is a compact row: the avatar in a lead column, the text beside it, and the organization and role stacked under the subtitle without a rule. `AccountCard.Avatar` defaults to `size="sm"`. [Account card](src/components/account-card/account-card.mdx) — Nikolaus Brunner <brunner@imfusion.com>
48
+ - Pass `size="md"` to keep the larger avatar. Consumer CSS that styled the large title or the foot rule re-checks against the row.
49
+ - **Breaking:** `AccountCard` is no longer a link, and `AccountCard.Link` and `AccountCard.Group` are removed. [Account card](src/components/account-card/account-card.mdx) — Nikolaus Brunner <brunner@imfusion.com>
50
+ - Move the Link's children straight into `AccountCard.Root`. Wire account navigation to a button in `AccountCard.Action` or a nav item instead of the row.
51
+ - An account switcher built on `AccountCard.Group` has no replacement yet; keep the old version until one ships.
52
+ - **Breaking:** An `AccountCard` in the `AppShell.Navbar` foot lines up with the nav items and no longer takes the 8px inset. — Nikolaus Brunner <brunner@imfusion.com>
53
+ - Pass the card as `account`; a card kept in a Section keeps its own padding.
54
+ - **Breaking:** The shell renders the mobile bar itself: the navbar's logo and the toggle for its drawer. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
55
+ - Remove `AppShell.Burger` and a logo of your own from your `AppShell.Header`; the Burger is no longer exported, and phones would show two logos.
56
+ - **Breaking:** The header takes the navbar's `variant`: beside a `branded` navbar it fills with the primary color and its text is on-color. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
57
+ - Remove a header color set to match the branded navbar. A custom surface still goes in `--imf-ui-appshell-header-bg`, which is unset by default and falls back to the navbar variant's surface.
58
+ - **Breaking:** `AppShell.Header` is a flex row, and it wraps its children in a container that aligns them to the inline end. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
59
+ - Give your own row `width: 100%` to span the header.
60
+ - **Breaking:** `npx web-ui install hooks` removes `baseline-staleness.sh`, and the `SessionStart` hook reports installed
61
+ skills that differ from the package copy in `node_modules` instead. — Nikolaus Brunner <brunner@imfusion.com>
62
+ - Remove the `.agents/hooks/imf-web-ui/baseline-staleness.sh` line from the project's pre-commit hook before running
63
+ `install hooks`; a pre-commit that calls the missing script fails every commit.
64
+ - Keep `.agents/skills/imf-web-ui*` out of the project's formatter, or the reformatted skills read as stale.
65
+
66
+ ### Added
67
+
68
+ - `--imf-ui-size-backdrop-edge` (`38%`) and `--imf-ui-size-backdrop-band-height` (`30dvh`) are the public backdrop's geometry tokens. The `imf-web-ui-ux` skill describes the handover. — Nikolaus Brunner <brunner@imfusion.com>
69
+ - `ProductLogo` renders the ImFusion Labels, Suite, and SDK product logos on the `Logo` sizes and color variants. The name is solid by default and cut out of the plate with `cutout`. [Product](src/components/logo/product/product.stories.tsx#Default) — Nikolaus Brunner <brunner@imfusion.com>
70
+ - `imf-web-ui-update` proposes and `imf-web-ui-audit` reports aligning the core runtime packages (React, Base UI, Vite+) with the versions Web UI builds with. — Nikolaus Brunner <brunner@imfusion.com>
71
+ - `localWebUILink()` and a `paths` block in `tsconfig.json` let an app run against a symlinked web-ui checkout with one React and one set of types. `imf-web-ui-setup` proposes both and `imf-web-ui-audit` reports a missing one. — Nikolaus Brunner <brunner@imfusion.com>
72
+ - The shell's navigation toggle sets `aria-controls` from the navbar, and reads the `id` the navbar generates. A consumer `id` on the Navbar is what it points at. — Nikolaus Brunner <brunner@imfusion.com>
73
+ - `Separator` takes `tone="minor"` for a neutral line, a quiet break inside a page; the default stays primary. — Nikolaus Brunner <brunner@imfusion.com>
74
+ - `Select.ItemDescription` sets a muted second line under an item's text, such as what a role allows; the trigger shows only the item text, and the list keeps the trigger's width so descriptions wrap. — Nikolaus Brunner <brunner@imfusion.com>
75
+ - `Table.Root` takes `density` (`compact`, `comfortable`, `spacious`) for the cell padding. — Nikolaus Brunner <brunner@imfusion.com>
76
+ - `Table.HeaderCell` and `Table.Cell` take `action` for a column of buttons: it shrinks to its content, so the data columns keep the width. — Nikolaus Brunner <brunner@imfusion.com>
77
+ - [`useDisclosure`](src/hooks/use-disclosure.stories.tsx#UseDisclosure) keeps an open state that works controlled or uncontrolled, and returns `open`, `setOpen`, and `toggle`. — Nikolaus Brunner <brunner@imfusion.com>
78
+ - `AppShell.Navbar` and `AppShell.Aside` take `defaultOpen` and keep their own open state when `open` is omitted, and close an uncontrolled drawer when they dock. A controlled `open` is never changed by docking. — Nikolaus Brunner <brunner@imfusion.com>
79
+ - An `AppShell.Aside` opens from the consumer's state, such as a row selection, through `open` and `onOpenChange`. — Nikolaus Brunner <brunner@imfusion.com>
80
+ - `AppShell.Main` content is a flex column at least as tall as Main, so a child with `flex: 1` fills the rest. — Nikolaus Brunner <brunner@imfusion.com>
81
+ - `AppShell.Aside` content wraps long words such as emails and IDs inside a narrow panel, in the docked aside and the drawer. — Nikolaus Brunner <brunner@imfusion.com>
82
+ - An `AppShell.NavItem` followed inside the navbar drawer closes it, unless its `onClick` calls `event.preventDefault()`. — Nikolaus Brunner <brunner@imfusion.com>
83
+ - [`ColorSchemeToggle`](src/components/color-scheme-toggle/color-scheme-toggle.mdx) lets the user pick Light, System, or
84
+ Dark. It stores the choice through `useColorScheme` and needs no props; `label`, `lightLabel`, `systemLabel`, and
85
+ `darkLabel` replace the English text — Nikolaus Brunner <brunner@imfusion.com>
86
+ - [`ToggleGroup`](src/components/toggle-group/toggle-group.mdx) and `Toggle` take a `size` of `sm` or `md`. A small group
87
+ has no inset or gap, a 1px border, and 1.5rem toggles; a toggle takes its group's size unless it sets its own
88
+ — Nikolaus Brunner <brunner@imfusion.com>
89
+ - `Skeleton.Suspense` takes a `fallback` of bones, a `label`, and the content. It is a Suspense boundary whose fallback is a
90
+ busy `Skeleton.Root`; when the content mounts after the fallback showed, it fades in, and content that was
91
+ ready at mount appears without a fade. Use it for data loaded through Suspense, and `Skeleton.Root` for data loaded with
92
+ `useQuery` — Nikolaus Brunner <brunner@imfusion.com>
93
+ - `ScrollArea` is a scrolling region with a slim primary-colored thumb that floats over the content and shows while the
94
+ area is hovered or scrolled, and a fade at each edge with more content behind it. `ScrollArea.Auto` wraps children with
95
+ the scrollbars for `orientation` (`vertical`, `horizontal`, `both`, or `none`); the parts compose by hand for anything else
96
+ ([WEBSDK-247](https://imfusion.atlassian.net/browse/WEBSDK-247)) — Nikolaus Brunner <brunner@imfusion.com>
97
+ - `Dialog` is a modal window over a dimmed page, adapted from Base UI with all its parts. `Dialog.Header` takes a
98
+ `variant` (`main`, `support`, `minor`, or `primary` with light text); `Dialog.Title` renders as `Typo.H2` and
99
+ `Dialog.Description` as `Typo.P`. `Dialog.Body` scrolls in a `ScrollArea` while header and footer stay in view.
100
+ `Dialog.Popup` comes in `sm`, `md`, `lg`, and `full`, and stops at 40rem high (`--imf-ui-dialog-max-height`) except in
101
+ `full`. `Dialog.Status` adds a tinted status column like `Card.Status`, on top of the width the size sets.
102
+ `Dialog.Simple` builds the whole dialog from props, and `Dialog.Inline` opens it from a term inside running text. Both
103
+ show the × close button only without `actions`; with them, the footer is the way out
104
+ ([WEBSDK-247](https://imfusion.atlassian.net/browse/WEBSDK-247)) — Nikolaus Brunner <brunner@imfusion.com>
105
+ - Storybook — Nikolaus Brunner <brunner@imfusion.com>
106
+ - The token drawer offers Paper Mono as a mono font.
107
+ - Docs links open a single story when they name its export after the stories file:
108
+ `[Inline](src/components/code/code.stories.tsx#Inline)`.
109
+ - `withBreakHints` is exported. It inserts a break before each `@` and after each `.` in an email's string children, so the address wraps at its seams. [Account card](src/components/account-card/account-card.mdx) — Nikolaus Brunner <brunner@imfusion.com>
110
+ - `--imf-ui-accountcard-lead` and `--imf-ui-accountcard-padding-inline` set an `AccountCard`'s avatar column and inline padding, so a container can line it up with its own rows. [Account card](src/components/account-card/account-card.mdx) — Nikolaus Brunner <brunner@imfusion.com>
111
+ - `AppShell.Navbar` takes `logo` and `account`; the logo defaults to the ImFusion logo and lines up with the baseline of Main's first heading. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
112
+ - `--imf-ui-appshell-heading-*`, `--imf-ui-appshell-navitem-dim`, `--imf-ui-appshell-navitem-underline-thickness`, and `--imf-ui-appshell-navitem-underline-offset` tune the logo row's strut and the NavItem underline. [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
113
+ - `AppShell.Aside` shows a close button in its drawer below `lg`, labelled by `closeLabel` (`Close panel` by default). [AppShell](src/components/app-shell/app-shell.mdx) — Nikolaus Brunner <brunner@imfusion.com>
114
+ - `Select.Root` takes a `width` of `auto`, `wide`, or `full`: `auto` keeps the 10rem trigger minimum, `wide` raises it to
115
+ 15rem, and `full` fills the parent. `--imf-ui-select-trigger-min-width` on `Select.Root` sets the minimum from CSS.
116
+ [Select](src/components/select/select.mdx) — Nikolaus Brunner <brunner@imfusion.com>
117
+
5
118
  ### Changed
6
119
 
120
+ - The `project-structure` topic sets when a single-route part is inlined into its route file: a component under 50 lines, with no `use*` hook, no stylesheet or test of its own, leaving the route file under 250 lines. `imf-web-ui-audit` reports a breach as a Deviation. — Nikolaus Brunner <brunner@imfusion.com>
121
+ - The `Meter` track is translucent ink (8% `fg-main`), so it stays visible on tinted surfaces such as a selected table row. — Nikolaus Brunner <brunner@imfusion.com>
122
+ - `Table` reads lighter: the header has no band, its mono labels are muted with a 1px rule under them, body rows have no rules, and cells get more vertical padding. A selected row sits one neutral step darker than a hovered row, in place of the primary tint. — Nikolaus Brunner <brunner@imfusion.com>
123
+ - [`AppShell.Header`](src/components/app-shell/app-shell.mdx) is a page row that shows at every width, below the shell's bar on mobile. — Nikolaus Brunner <brunner@imfusion.com>
124
+ - The header pads its inline sides with `--imf-ui-appshell-padding`, so its content lines up with Main's.
125
+ - AppShell — Nikolaus Brunner <brunner@imfusion.com>
126
+ - The navbar rail, `AppShell.Main`, and the aside share `--imf-ui-appshell-padding` as their inline gutter, so their text lines up with no consumer CSS. [Docs](src/components/app-shell/app-shell.mdx)
127
+ - `--imf-ui-appshell-navbar-padding` defaults to `--imf-ui-appshell-padding`, and `--imf-ui-appshell-aside-padding` defaults to `--imf-ui-appshell-padding`. Both stay overridable.
7
128
  - Agent skills — Nikolaus Brunner <brunner@imfusion.com>
129
+ - `npx web-ui install skills` writes no `.imf-web-ui-skill-version.json` marker into each skill and removes existing ones.
130
+ Both install commands read the harness back from the Claude skill links and the installer's hook registrations, and
131
+ take it from an old marker when one is still there.
132
+ - `imf-web-ui-audit` checks that `@imfusion/web-ui` is installed before it dispatches, and asks whether to install first,
133
+ since token, version, and type checks need the installed package. Its investigators search with whatever read-only
134
+ file search the host offers.
135
+ - The `imf-web-ui-conventions` audit checklist lists the "Derive types, don't copy them" and "Loading states" sections
136
+ under their current topic headings.
8
137
  - `imf-web-ui-conventions` has query factories own a numeric `staleTime` chosen by how fast other people change the data.
9
138
  Loaders call `queryClient.query(options)` without an override, and `"static"` no longer appears in factories or
10
139
  `defaultOptions`, where it blocked invalidation refetches.
11
140
  - `imf-web-ui-conventions` puts a route's own views, forms, hooks, and tests in a `-<file-name>/` folder beside the route
12
141
  file, and reserves `partials/` for parts that more than one route uses.
142
+ - `imf-web-ui-conventions` lifts a value into a custom property on the component's root only when it repeats within that
143
+ stylesheet or a state or variant changes several descendants. A value that only recurs across files stays a literal in
144
+ each, and a shared component owns it when those files show one design element.
145
+ - `imf-web-ui-conventions` exempts TanStack options factories (`queryOptions`, `mutationOptions`) from the explicit return
146
+ type on exported functions, since spelling it out loses the typed query key.
147
+ - `imf-web-ui-conventions` keeps the Git hooks in `.vite-hooks/` and installs the Vite+ dispatcher with `vp config` from
148
+ `git:config`.
149
+ - `imf-web-ui-conventions` names the Skeleton part for each way data loads, and says a layout that reads suspending data
150
+ needs its own boundary. `imf-web-ui-ux` points to that rule from its loading state.
151
+ - `imf-web-ui-conventions` lets a mutation name the sub-resource after its verb when a topic has more than one target,
152
+ as in `api.projects.removeMember(projectId)`; a name still never repeats its topic.
153
+ - CI and verification — Nikolaus Brunner <brunner@imfusion.com>
154
+ - The Git hooks live in `.vite-hooks/` and run through the Vite+ hook dispatcher, which `npm run git:config` installs.
155
+ Existing clones and worktrees rerun `npm run git:config`; until then their hooks do not run.
156
+ - `engines.node` is `^22.22.1 || ^24.11 || >=26`, the range Vite+ supports. Node 24.0 to 24.10 and Node 25 fall
157
+ outside it.
158
+ - The main column, header, and footer snap when the aside docks or undocks — Nikolaus Brunner <brunner@imfusion.com>
159
+ - Components ([WEBSDK-247](https://imfusion.atlassian.net/browse/WEBSDK-247)) — Nikolaus Brunner <brunner@imfusion.com>
160
+ - The AppShell navbar drawer, `Code`, and `ImageDisplayOptions` scroll in a `ScrollArea`, with its overlay thumb and edge
161
+ fades.
162
+ - Removed default padding from `Popover.Trigger` such that it keeps a neutral style.
163
+ - A ghost `Button` or `IconButton` hovers by fading its label or glyph, with no fill or edge, on main and oncolor surfaces alike, instead of inverting to a solid fill. `Dialog.Close` and the `AppShell.Aside` drawer close follow. [Button](src/components/button/button.mdx) — Nikolaus Brunner <brunner@imfusion.com>
164
+ - `Typo.UnorderedList` marks its items with a short primary-coloured dash on the main, support, and minor variants, and with a dash in the text colour on the others. It sets `role="list"` by default. [Typo](src/components/typo/typo.mdx) ([WEBSDK-270](https://imfusion.atlassian.net/browse/WEBSDK-270)) — Nikolaus Brunner <brunner@imfusion.com>
165
+
166
+ ### Fixed
167
+
168
+ - `Typo.Hero`, `Typo.H3`, `Typo.H4`, `Typo.Lead`, and `Typo.Blockquote` no longer crowd their letters: `--imf-ui-font-letterspacing-hero` and `--imf-ui-font-letterspacing-brand` scale with the viewport, from about -0.0225em and -0.01em at 360px to -0.025em and -0.02em at 1920px, so pairs like "rt" and "ty" stay apart — Nikolaus Brunner <brunner@imfusion.com>
169
+ - The `Select` popup sits 4px below its trigger by default (`sideOffset` on `Select.Positioner`) and casts a hard shadow (`--imf-ui-shadow-4`). — Nikolaus Brunner <brunner@imfusion.com>
170
+ - An empty `Select.ItemIndicator` draws the `Check` icon in the item's text colour, so it stays visible on the highlighted item. Base UI's fallback was a `✔️` emoji in its own colour. — Nikolaus Brunner <brunner@imfusion.com>
171
+ - The `tooling` topic, `imf-web-ui-setup` and `imf-web-ui-audit` keep tsconfig `paths` away from Vite: `resolve.tsconfigPaths` or `vite-tsconfig-paths` loaded the React types package as React and broke a build that installs Web UI from the registry. The `#/` alias comes from `imports` in `package.json`. — Nikolaus Brunner <brunner@imfusion.com>
172
+ - The skill staleness hook stays quiet when `@imfusion/web-ui` is a symlinked checkout, whose version never matches the installed skills. — Nikolaus Brunner <brunner@imfusion.com>
173
+ - `Drawer.Popup` draws no focus ring around the whole panel when it takes focus on open; the controls inside keep theirs. — Nikolaus Brunner <brunner@imfusion.com>
174
+ - `Chip` stays on one line within its container's width and ends in an ellipsis when the label doesn't fit. [Chip](src/components/chip/chip.mdx) — Nikolaus Brunner <brunner@imfusion.com>
175
+ - Only the navbar drawer's own close shows while it is open; the header burger steps back until the drawer closes — Nikolaus Brunner <brunner@imfusion.com>
176
+ - `Skeleton.Root` fades only its own content in when loading ends; the rest of the page stays still, and no `AbortError` reaches the console — Nikolaus Brunner <brunner@imfusion.com>
177
+ - `AppShell`'s docked aside no longer leaves `AppShell.Main` up to 13px under it at 1024px: Main ends where the aside begins at every docked width — Nikolaus Brunner <brunner@imfusion.com>
178
+ - The Stop hook's verify gate blocks only a turn that edited files. An edit from an earlier turn no longer blocks later turns that only answer questions while the tree stays dirty — Nikolaus Brunner <brunner@imfusion.com>
179
+ - `imf-web-ui-ux` describes patterns and points to `imf-web-ui-components` for which components exist, instead of claiming which ones do not — Nikolaus Brunner <brunner@imfusion.com>
13
180
 
14
181
  ---
15
182
 
package/README.md CHANGED
@@ -65,8 +65,10 @@ npm install
65
65
  npm run git:config
66
66
  ```
67
67
 
68
- `git:config` sets the tracked hooks path, enables rebase pulls, and requires fast-forward merges. Every checkout and worktree
69
- needs its own `node_modules`; the hooks stop with "run npm ci" without one.
68
+ The hooks are tracked in `.vite-hooks/`. `git:config` installs the Vite+ hook dispatcher into the git-ignored
69
+ `.vite-hooks/_/` and points the hooks path at it, enables rebase pulls, and requires fast-forward merges. Every checkout and
70
+ worktree needs its own `node_modules` and dispatcher, so run `npm ci` and `npm run git:config` in each; without the
71
+ dispatcher the hooks do not run at all.
70
72
 
71
73
  The pre-commit hook runs `vp staged` against the staged state: it lints and format-checks the repository, checks that every
72
74
  generated `*.gen.*` file matches its sources (`npm run verify:generated`), and runs the pinned-dependency, Knip, type-check,
@@ -99,34 +101,8 @@ The main development skills are:
99
101
  | `/web-ui-dev-teardown-worktree` | Remove a finished worktree. |
100
102
  | `/web-ui-dev-mcp` | Recover one optional MCP capability. |
101
103
 
102
- ### Work on it from a consumer app
104
+ ### Try a change in a consumer app
103
105
 
104
- To change the library while running an app that uses it, point the app's dependency at your checkout:
105
-
106
- ```json
107
- "@imfusion/web-ui": "file:../web-ui"
108
- ```
109
-
110
- Run `npm install` in the app, then keep `npm run dev:lib` running here. The package resolves to `dist/`, so the app resolves
111
- nothing until the first watch build has finished, and sees each later change once the rebuild has written it.
112
-
113
- npm links the checkout instead of copying it. Without help, the app then loads two copies of React, its own and the one in
114
- this checkout's `node_modules`, and every hook fails with "Invalid hook call". Register `localWebUILink` in the app's Vite
115
- config to prevent that:
116
-
117
- ```ts
118
- import { localWebUILink } from "@imfusion/web-ui/vite";
119
-
120
- export default defineConfig({
121
- plugins: [react(), localWebUILink()]
122
- });
123
- ```
124
-
125
- It dedupes every peer dependency of the library, including `@imfusion/sdk` and `@imfusion/sdk-react`. For an installed
126
- package it does nothing, so it can stay in the config.
127
-
128
- It covers the browser in development and in the build. Server rendering, including React Router prerendering, still loads
129
- dependencies through Node, which picks the checkout's React. Base UI's `use-sync-external-store` dependency is CommonJS, so
130
- Popover, Select, and other stateful parts can still fail on the server.
131
-
132
- Change the dependency back before you commit the app.
106
+ Use the maintainer skill `imfg-web-ui-live-link`. The app needs `localWebUILink()` in its `vite.config.ts` and the `paths`
107
+ block from the `tooling` topic of `imf-web-ui-conventions` in its `tsconfig.json`, on top of matching React, Base UI, and
108
+ Vite+ versions.
@@ -66,3 +66,113 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
66
66
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
67
67
  SOFTWARE.
68
68
  ```
69
+
70
+ ## Paper Mono
71
+
72
+ - Project: [Paper Mono](https://github.com/paper-design/paper-mono)
73
+ - License: [SIL Open Font License 1.1](https://github.com/paper-design/paper-mono/blob/v1.000/LICENSE.txt)
74
+
75
+ The Paper Mono font shipped with `@imfusion/web-ui` is unmodified.
76
+
77
+ The following license text applies only to Paper Mono and is reproduced verbatim:
78
+
79
+ ```text
80
+ Paper Mono
81
+ Copyright (c) 2025 Lost Coast Labs, Inc. (Paper Design)
82
+
83
+ Based on Geist Mono
84
+ Copyright (c) 2023 Vercel, in collaboration with basement.studio
85
+
86
+ -----------------------------------------------------------
87
+
88
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
89
+ This license is copied below, and is also available with a FAQ at:
90
+ http://scripts.sil.org/OFL
91
+
92
+ -----------------------------------------------------------
93
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
94
+ -----------------------------------------------------------
95
+
96
+ PREAMBLE
97
+ The goals of the Open Font License (OFL) are to stimulate worldwide
98
+ development of collaborative font projects, to support the font creation
99
+ efforts of academic and linguistic communities, and to provide a free and
100
+ open framework in which fonts may be shared and improved in partnership
101
+ with others.
102
+
103
+ The OFL allows the licensed fonts to be used, studied, modified and
104
+ redistributed freely as long as they are not sold by themselves. The
105
+ fonts, including any derivative works, can be bundled, embedded,
106
+ redistributed and/or sold with any software provided that any reserved
107
+ names are not used by derivative works. The fonts and derivatives,
108
+ however, cannot be released under any other type of license. The
109
+ requirement for fonts to remain under this license does not apply
110
+ to any document created using the fonts or their derivatives.
111
+
112
+ DEFINITIONS
113
+ "Font Software" refers to the set of files released by the Copyright
114
+ Holder(s) under this license and clearly marked as such. This may
115
+ include source files, build scripts and documentation.
116
+
117
+ "Reserved Font Name" refers to any names specified as such after the
118
+ copyright statement(s).
119
+
120
+ "Original Version" refers to the collection of Font Software components as
121
+ distributed by the Copyright Holder(s).
122
+
123
+ "Modified Version" refers to any derivative made by adding to, deleting,
124
+ or substituting -- in part or in whole -- any of the components of the
125
+ Original Version, by changing formats or by porting the Font Software to a
126
+ new environment.
127
+
128
+ "Author" refers to any designer, engineer, programmer, technical
129
+ writer or other person who contributed to the Font Software.
130
+
131
+ PERMISSION AND CONDITIONS
132
+ Permission is hereby granted, free of charge, to any person obtaining
133
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
134
+ redistribute, and sell modified and unmodified copies of the Font
135
+ Software, subject to the following conditions:
136
+
137
+ 1) Neither the Font Software nor any of its individual components,
138
+ in Original or Modified Versions, may be sold by itself.
139
+
140
+ 2) Original or Modified Versions of the Font Software may be bundled,
141
+ redistributed and/or sold with any software, provided that each copy
142
+ contains the above copyright notice and this license. These can be
143
+ included either as stand-alone text files, human-readable headers or
144
+ in the appropriate machine-readable metadata fields within text or
145
+ binary files as long as those fields can be easily viewed by the user.
146
+
147
+ 3) No Modified Version of the Font Software may use the Reserved Font
148
+ Name(s) unless explicit written permission is granted by the corresponding
149
+ Copyright Holder. This restriction only applies to the primary font name as
150
+ presented to the users.
151
+
152
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
153
+ Software shall not be used to promote, endorse or advertise any
154
+ Modified Version, except to acknowledge the contribution(s) of the
155
+ Copyright Holder(s) and the Author(s) or with their explicit written
156
+ permission.
157
+
158
+ 5) The Font Software, modified or unmodified, in part or in whole,
159
+ must be distributed entirely under this license, and must not be
160
+ distributed under any other license. The requirement for fonts to
161
+ remain under this license does not apply to any document created
162
+ using the Font Software.
163
+
164
+ TERMINATION
165
+ This license becomes null and void if any of the above conditions are
166
+ not met.
167
+
168
+ DISCLAIMER
169
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
170
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
171
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
172
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
173
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
174
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
175
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
176
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
177
+ OTHER DEALINGS IN THE FONT SOFTWARE.
178
+ ```
@@ -0,0 +1,139 @@
1
+ import { r as e } from "./resolve-state-props-DQf9WiGv.js";
2
+ import { Xo as t, ja as n, pM as r } from "./icons-BsK_o-Cb.js";
3
+ import { t as i } from "./meta-V_KyLeGn.js";
4
+ import { a, i as o, n as s, o as c, s as l } from "./scroll-area-z9Lf9uIk.js";
5
+ import { cx as u } from "class-variance-authority";
6
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
7
+ import { useCallback as m, useEffect as h, useRef as g, useState as _ } from "react";
8
+ import { mergeProps as v } from "@base-ui/react/merge-props";
9
+ import { useRender as y } from "@base-ui/react/use-render";
10
+ //#region src/hooks/use-clipboard.ts
11
+ function b({ timeout: e = 1500 } = {}) {
12
+ let [t, n] = _(!1), r = g(void 0);
13
+ return h(() => () => clearTimeout(r.current), []), {
14
+ copied: t,
15
+ copy: m((t) => {
16
+ navigator.clipboard.writeText(t).then(() => {
17
+ n(!0), clearTimeout(r.current), r.current = setTimeout(() => n(!1), e);
18
+ });
19
+ }, [e])
20
+ };
21
+ }
22
+ var x = {
23
+ block: "imf-ui-code-module-block",
24
+ copy: "imf-ui-code-module-copy",
25
+ inline: "imf-ui-code-module-inline",
26
+ inlineCopy: "imf-ui-code-module-inlineCopy",
27
+ inlineLink: "imf-ui-code-module-inlineLink",
28
+ language: "imf-ui-code-module-language",
29
+ pre: "imf-ui-code-module-pre",
30
+ separator: "imf-ui-code-module-separator"
31
+ }, S = /* @__PURE__ */ e({
32
+ Block: () => T,
33
+ Inline: () => C,
34
+ InlineLink: () => w
35
+ });
36
+ function C({ copyable: e = !1, children: i, render: a, ...o }) {
37
+ let { copied: s, copy: c } = b(), l = g(null), u = s ? n : t;
38
+ return y({
39
+ defaultTagName: "code",
40
+ render: a,
41
+ props: {
42
+ ...v(o, {
43
+ className: x.inline,
44
+ children: /* @__PURE__ */ p(d, { children: [
45
+ /* @__PURE__ */ f("span", {
46
+ ref: l,
47
+ children: i
48
+ }),
49
+ e && /* @__PURE__ */ f("span", { className: x.separator }),
50
+ e && /* @__PURE__ */ f("button", {
51
+ type: "button",
52
+ className: x.inlineCopy,
53
+ "data-copied": s || void 0,
54
+ "aria-label": s ? "Copied" : "Copy",
55
+ onClick: () => c(l.current?.textContent ?? ""),
56
+ children: /* @__PURE__ */ f(r, {
57
+ glyph: u,
58
+ size: 12,
59
+ strokeWidth: 2,
60
+ "aria-hidden": !0
61
+ })
62
+ })
63
+ ] })
64
+ }),
65
+ "data-imf-ui-component": "Code.Inline"
66
+ }
67
+ });
68
+ }
69
+ C.displayName = "Code.Inline";
70
+ function w({ kind: e = "external", ...t }) {
71
+ let n = e === "external" ? {
72
+ target: "_blank",
73
+ rel: "noreferrer"
74
+ } : {};
75
+ return y({
76
+ defaultTagName: "a",
77
+ render: t.render,
78
+ props: {
79
+ ...v(n, t, {
80
+ className: u(x.inline, x.inlineLink, t.className),
81
+ children: t.children
82
+ }),
83
+ "data-imf-ui-component": "Code.InlineLink",
84
+ "data-kind": e
85
+ }
86
+ });
87
+ }
88
+ w.displayName = "Code.InlineLink";
89
+ function T({ className: e, language: i, hideCopy: d = !1, children: m, ...h }) {
90
+ let { copied: _, copy: v } = b(), y = g(null), S = _ ? n : t;
91
+ return /* @__PURE__ */ p("div", {
92
+ "data-imf-ui-component": "Code.Block",
93
+ className: u(x.block, e),
94
+ children: [
95
+ i != null && /* @__PURE__ */ f("span", {
96
+ className: x.language,
97
+ children: i
98
+ }),
99
+ !d && /* @__PURE__ */ f("button", {
100
+ type: "button",
101
+ className: x.copy,
102
+ "data-copied": _ || void 0,
103
+ "aria-label": _ ? "Copied" : "Copy",
104
+ onClick: () => v(y.current?.textContent ?? ""),
105
+ children: /* @__PURE__ */ f(r, {
106
+ glyph: S,
107
+ size: 12,
108
+ strokeWidth: 2,
109
+ "aria-hidden": !0
110
+ })
111
+ }),
112
+ /* @__PURE__ */ p(o, { children: [/* @__PURE__ */ f(l, { children: /* @__PURE__ */ f(s, { children: /* @__PURE__ */ f("pre", {
113
+ ...h,
114
+ className: x.pre,
115
+ children: /* @__PURE__ */ f("code", {
116
+ ref: y,
117
+ children: m
118
+ })
119
+ }) }) }), /* @__PURE__ */ f(a, {
120
+ orientation: "horizontal",
121
+ children: /* @__PURE__ */ f(c, {})
122
+ })] })
123
+ ]
124
+ });
125
+ }
126
+ T.displayName = "Code.Block";
127
+ //#endregion
128
+ //#region src/components/code/code.meta.ts
129
+ var E = {
130
+ name: "Code",
131
+ category: i.display,
132
+ status: "stable",
133
+ namespace: !0,
134
+ description: "Displays source code inline or in a block, with optional language labels and copying. Also called a code snippet or code block.",
135
+ origin: "native",
136
+ internalDependencies: ["ScrollArea"]
137
+ };
138
+ //#endregion
139
+ export { S as a, w as i, T as n, b as o, C as r, E as t };
@@ -1,4 +1,3 @@
1
- import { useRender } from '@base-ui/react/use-render';
2
1
  import { VariantProps } from 'class-variance-authority';
3
2
  import * as AvatarParts from '../avatar/avatar';
4
3
  declare const root: (props?: ({
@@ -6,8 +5,9 @@ declare const root: (props?: ({
6
5
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
6
  type RootProps = React.ComponentProps<"div"> & VariantProps<typeof root>;
8
7
  /**
9
- * AccountCard.Root — the signed-in person's card: `AccountCard.Link` and, in its top row, `AccountCard.Action`. The
10
- * action sits beside the link, not inside it, since a link cannot contain a button.
8
+ * AccountCard.Root — the signed-in person's row, holding `AccountCard.Avatar`, `AccountCard.Title`, `AccountCard.Subtitle`,
9
+ * `AccountCard.Description`, `AccountCard.Role`, and, at its top right, `AccountCard.Action`. The row itself is not
10
+ * interactive; the action's buttons are.
11
11
  */
12
12
  declare function Root({
13
13
  /** Explicit saturated-surface context; this is never inferred from an ancestor. */
@@ -15,15 +15,6 @@ context, ...props }: RootProps): import("react/jsx-runtime").JSX.Element;
15
15
  declare namespace Root {
16
16
  var displayName: string;
17
17
  }
18
- /**
19
- * AccountCard.Link — the link to the account, holding `AccountCard.Avatar`, `AccountCard.Title`,
20
- * `AccountCard.Subtitle`, `AccountCard.Description`, and `AccountCard.Role`. Pass a router link through `render`. Its
21
- * accessible name is its text; the avatar stays decorative.
22
- */
23
- declare function Link({ render, ...props }: useRender.ComponentProps<"a">): import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>>;
24
- declare namespace Link {
25
- var displayName: string;
26
- }
27
18
  type AvatarProps = React.ComponentProps<"span"> & {
28
19
  /** The person's photo. Until it loads, and when it fails, the initials or the person glyph show instead. */
29
20
  src?: string;
@@ -39,8 +30,8 @@ declare namespace Avatar {
39
30
  var displayName: string;
40
31
  }
41
32
  /**
42
- * AccountCard.Title — the account's name, or the email's local part until a name exists. Set large; text wraps before
43
- * an `@` and after a dot.
33
+ * AccountCard.Title — the account's name, or the email's local part until a name exists. Text wraps before an `@` and
34
+ * after a dot.
44
35
  */
45
36
  declare function Title({ children, ...props }: React.ComponentProps<"span">): import("react/jsx-runtime").JSX.Element;
46
37
  declare namespace Title {
@@ -54,30 +45,22 @@ declare function Subtitle({ children, ...props }: React.ComponentProps<"span">):
54
45
  declare namespace Subtitle {
55
46
  var displayName: string;
56
47
  }
57
- /** AccountCard.Description — the organization, under a rule at the foot of the card. */
48
+ /** AccountCard.Description — the organization, set apart below the subtitle. */
58
49
  declare function Description(props: React.ComponentProps<"span">): import("react/jsx-runtime").JSX.Element;
59
50
  declare namespace Description {
60
51
  var displayName: string;
61
52
  }
62
- /** AccountCard.Role — a mono uppercase label beside the description, such as the person's role. */
53
+ /** AccountCard.Role — a mono uppercase label under the description, such as the person's role. */
63
54
  declare function Role(props: React.ComponentProps<"span">): import("react/jsx-runtime").JSX.Element;
64
55
  declare namespace Role {
65
56
  var displayName: string;
66
57
  }
67
58
  /**
68
- * AccountCard.Action — a slot in the card's top row, beside the link, such as a ghost icon `Button` that logs out. The
59
+ * AccountCard.Action — the card's top-right slot for ghost `IconButton`s, such as one that logs out. The
69
60
  * button carries its own `aria-label` and its own `context`.
70
61
  */
71
62
  declare function Action(props: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
72
63
  declare namespace Action {
73
64
  var displayName: string;
74
65
  }
75
- /**
76
- * AccountCard.Group — a list of cards, such as accounts to switch between. Inside it a link with `aria-current="page"`
77
- * shows as current; a standalone card is always the signed-in account and shows no current state.
78
- */
79
- declare function Group(props: React.ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
80
- declare namespace Group {
81
- var displayName: string;
82
- }
83
- export { Root, Link, Avatar, Title, Subtitle, Description, Role, Action, Group };
66
+ export { Root, Avatar, Title, Subtitle, Description, Role, Action };