@imfusion/web-ui 0.10.1-dev.6.gf715361a → 0.10.1-dev.60.g7891a716
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.
- package/CHANGELOG.md +159 -0
- package/README.md +8 -32
- package/THIRD_PARTY_NOTICES.md +110 -0
- package/dist/code-NTcddiYk.js +139 -0
- package/dist/components/account-card/account-card.d.ts +9 -26
- package/dist/components/app-shell/app-shell.d.ts +48 -26
- package/dist/components/callout/callout.d.ts +1 -1
- package/dist/components/chip/chip.d.ts +1 -1
- package/dist/components/color-scheme-toggle/color-scheme-preference.d.ts +3 -0
- package/dist/components/color-scheme-toggle/color-scheme-toggle.d.ts +17 -0
- package/dist/components/color-scheme-toggle/color-scheme-toggle.meta.d.ts +2 -0
- package/dist/components/color-scheme-toggle/index.d.ts +2 -0
- package/dist/components/dialog/dialog.d.ts +151 -0
- package/dist/components/dialog/dialog.meta.d.ts +2 -0
- package/dist/components/dialog/index.d.ts +2 -0
- package/dist/components/drawer/drawer.d.ts +1 -1
- package/dist/components/logo/index.d.ts +1 -0
- package/dist/components/logo/product/index.d.ts +1 -0
- package/dist/components/logo/product/marks.d.ts +11 -0
- package/dist/components/logo/product/product.d.ts +17 -0
- package/dist/components/scroll-area/index.d.ts +2 -0
- package/dist/components/scroll-area/scroll-area.d.ts +49 -0
- package/dist/components/scroll-area/scroll-area.meta.d.ts +2 -0
- package/dist/components/select/select.d.ts +18 -4
- package/dist/components/separator/separator.d.ts +4 -2
- package/dist/components/skeleton/skeleton.d.ts +17 -2
- package/dist/components/table/table.d.ts +12 -4
- package/dist/components/toggle/toggle.d.ts +8 -2
- package/dist/components/toggle-group/toggle-group-size.d.ts +3 -0
- package/dist/components/toggle-group/toggle-group.d.ts +7 -3
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/use-disclosure.d.ts +17 -0
- package/dist/icons.js +1 -2
- package/dist/index.d.ts +7 -3
- package/dist/index.js +2705 -2093
- package/dist/integrations/code-highlight.js +2 -2
- package/dist/integrations/frame-selection.js +2 -2
- package/dist/integrations/image-display-options.js +123 -113
- package/dist/{rolldown-runtime-Dy4uBu1J.js → resolve-state-props-DQf9WiGv.js} +12 -1
- package/dist/scroll-area-z9Lf9uIk.js +89 -0
- package/dist/{separator-lzz4A3x_.js → separator-BV2TR8U0.js} +16 -8
- package/dist/slider-Bf274cMe.js +106 -0
- package/dist/style.css +1 -1
- package/dist/{tabs-DRyqtS04.js → tabs-CU46aGud.js} +106 -88
- package/dist/vite/local-web-ui-link.d.ts +17 -4
- package/dist/vite.js +25 -20
- package/dist/web-ui-cli.js +11 -11
- package/docs/user-guide/UsagePatterns.mdx +4 -0
- package/package.json +11 -9
- package/src/docgen/doc.gen.json +2292 -1071
- package/src/llms/install-templates/hooks/baseline-staleness.sh +2 -0
- package/src/llms/install-templates/hooks/stop.sh +7 -2
- package/src/llms/llms.gen.txt +18 -1
- package/src/llms/skills/imf-web-ui-audit/SKILL.md +24 -11
- package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +4 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/data.md +17 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/git.md +5 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +3 -0
- package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +5 -2
- package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +3 -1
- package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +47 -8
- package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +2 -0
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +10 -1
- package/src/llms/skills/imf-web-ui-update/SKILL.md +7 -0
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +47 -26
- package/src/llms/skills/imf-web-ui-ux/references/forms.md +6 -6
- package/src/llms/skills/imf-web-ui-ux/references/usability-heuristics.md +3 -3
- package/src/llms/skills/imf-web-ui-ux/references/visual-design.md +3 -3
- package/src/llms/tokens.gen.json +13 -5
- package/dist/code-BYkr5k55.js +0 -134
- package/dist/slider-Cqpreglu.js +0 -116
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# and warns on drift. Always exits 0 — staleness never blocks a commit.
|
|
5
5
|
PKG=node_modules/@imfusion/web-ui/package.json
|
|
6
6
|
[ -f "$PKG" ] || exit 0
|
|
7
|
+
# A symlinked checkout reports its own dev version.
|
|
8
|
+
[ -L node_modules/@imfusion/web-ui ] && exit 0
|
|
7
9
|
CURRENT=$(node -p "require('./$PKG').version" 2>/dev/null) || exit 0
|
|
8
10
|
|
|
9
11
|
# .claude/skills may be a symlink to .agents/skills; skip markers already read.
|
|
@@ -10,9 +10,14 @@ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
|
|
|
10
10
|
[ -n "$(git status --porcelain -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.css' 2>/dev/null)" ] || exit 0
|
|
11
11
|
# Gate only turns that actually edited files — a tree left dirty by an earlier
|
|
12
12
|
# session must not block a question-answering turn. Edit evidence: Write/Edit
|
|
13
|
-
# tool calls (Claude transcript) or apply_patch calls (Codex rollout)
|
|
13
|
+
# tool calls (Claude transcript) or apply_patch calls (Codex rollout), searched
|
|
14
|
+
# only after the turn's opening entry: the last real user prompt (not a tool
|
|
15
|
+
# result or injected meta message) or the last Codex task_started event. Claude
|
|
16
|
+
# matches only tool_use calls: its transcript also stores the tool definitions,
|
|
17
|
+
# whose "name":"Edit" entries are not edits.
|
|
14
18
|
TRANSCRIPT=$(printf '%s' "$PAYLOAD" | sed -n 's/.*"transcript_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
|
|
15
19
|
[ -f "$TRANSCRIPT" ] || exit 0
|
|
16
|
-
grep -
|
|
20
|
+
TURN_START=$(grep -En '"type":"user"|"type":"task_started"' "$TRANSCRIPT" | grep -Ev '"type":"tool_result"|"isMeta":true' | tail -n 1 | cut -d: -f1)
|
|
21
|
+
tail -n +$(( ${TURN_START:-0} + 1 )) "$TRANSCRIPT" | grep -Eq '"type":"tool_use","id":"[^"]*","name":"(Write|Edit|MultiEdit|NotebookEdit)"|"name":"apply_patch"' || exit 0
|
|
17
22
|
printf '{"decision":"block","reason":"web-ui stop gate: this turn changed source files. Run the project verification (verify:typecheck, verify:lint, and verify:format at minimum), fix what it reports, then finish. If it already ran after the last change, say so and finish."}\n'
|
|
18
23
|
exit 0
|
package/src/llms/llms.gen.txt
CHANGED
|
@@ -11,7 +11,7 @@ props, which always live in the docgen file above.
|
|
|
11
11
|
|
|
12
12
|
## AccountCard
|
|
13
13
|
- category: Display, status: experimental
|
|
14
|
-
|
|
14
|
+
The signed-in person's account row: the avatar in a lead column, the name or email, a quiet line under it, and organization and role below, with a trailing action such as Log out. Also called an ID card or user row; for a bare person badge, use Avatar.
|
|
15
15
|
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .account-card (jq: jq '.account-card' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
16
16
|
|
|
17
17
|
## AppShell
|
|
@@ -73,11 +73,22 @@ Show-and-hide region for secondary content such as FAQs, settings, or details. A
|
|
|
73
73
|
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .collapsible (jq: jq '.collapsible' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
74
74
|
- further reading (usage/anatomy, not props): https://base-ui.com/react/components/collapsible.md
|
|
75
75
|
|
|
76
|
+
## ColorSchemeToggle
|
|
77
|
+
- category: Inputs, status: experimental
|
|
78
|
+
Light, System, and Dark segments that set and remember the page's color scheme. Also called an appearance or theme switch; built on ToggleGroup, which is the choice for any other segmented selection.
|
|
79
|
+
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .color-scheme-toggle (jq: jq '.color-scheme-toggle' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
80
|
+
|
|
76
81
|
## CopyButton
|
|
77
82
|
- category: Buttons, status: stable
|
|
78
83
|
Copies a value to the clipboard and briefly shows confirmation. Also called a copy-to-clipboard button; commonly used with Code.Block.
|
|
79
84
|
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .copy-button (jq: jq '.copy-button' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
80
85
|
|
|
86
|
+
## Dialog
|
|
87
|
+
- category: Layout, status: experimental
|
|
88
|
+
Modal window over a dimmed page for focused tasks, forms, confirmations, or viewers. Also called a modal; use Drawer for a panel from the screen edge and Popover for content anchored to a trigger.
|
|
89
|
+
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .dialog (jq: jq '.dialog' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
90
|
+
- further reading (usage/anatomy, not props): https://base-ui.com/react/components/dialog.md
|
|
91
|
+
|
|
81
92
|
## Drawer
|
|
82
93
|
- category: Layout, status: stable
|
|
83
94
|
Off-canvas panel that slides in from a screen edge for navigation, settings, filters, or focused tasks. Also called a sidebar, side panel, or sheet.
|
|
@@ -173,6 +184,12 @@ Link as a whole row: optional leading icon, label, optional description line, an
|
|
|
173
184
|
Horizontal layout primitive for spacing, aligning, and wrapping children. Also called a horizontal stack or flex row.
|
|
174
185
|
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .row (jq: jq '.row' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
175
186
|
|
|
187
|
+
## ScrollArea
|
|
188
|
+
- category: Layout, status: experimental
|
|
189
|
+
Scrollable region with the library's overlay scrollbar and edge fades, for lists, panels, and dialog bodies. Also called a scroll container; use it instead of a bare overflow: auto.
|
|
190
|
+
- props: node_modules/@imfusion/web-ui/src/docgen/doc.gen.json -> .scroll-area (jq: jq '.scroll-area' node_modules/@imfusion/web-ui/src/docgen/doc.gen.json)
|
|
191
|
+
- further reading (usage/anatomy, not props): https://base-ui.com/react/components/scroll-area.md
|
|
192
|
+
|
|
176
193
|
## Select
|
|
177
194
|
- category: Inputs, status: stable
|
|
178
195
|
Dropdown for choosing a value from a known list. Also called a picker; use it for a labeled, keyboard-accessible option list.
|
|
@@ -23,15 +23,20 @@ it low, and do not let it crowd the sections a consumer has to act on.
|
|
|
23
23
|
|
|
24
24
|
Invoke `imf-web-ui-user-profile` first and explain at its levels throughout.
|
|
25
25
|
|
|
26
|
-
1. Resolve the argument. Bare means `full`; an unknown topic is an error, not a reason to widen the scope.
|
|
26
|
+
1. Resolve the argument. Bare means `full`; an unknown topic is an error, not a reason to widen the scope. Then check that
|
|
27
|
+
`node_modules/@imfusion/web-ui/package.json` exists. Without it, token, installed-version, and installed-type checks can
|
|
28
|
+
only come back Unverified, so tell the human before dispatch and ask whether to install dependencies first or continue
|
|
29
|
+
with those checks unverified.
|
|
27
30
|
2. Enter plan mode unless this audit is being called as verification by another skill or the host has no plan mode.
|
|
28
31
|
3. Read the report template and existing reviewer notes, then dispatch one investigator per in-scope topic. Tell each
|
|
29
|
-
investigator to
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
investigator to inspect statically: `Read` plus the host's file search (`Glob` and `Grep`, or a read-only search tool when
|
|
33
|
+
the host has no `Glob` or `Grep`), with no shell commands or writes. An investigator checks every section in that topic
|
|
34
|
+
across the whole frontend tree and writes nothing; one without any search tool says so and stops instead of auditing only
|
|
35
|
+
the paths it already knows. Once dispatch starts, the host performs no repository inspection: the returned reports are the
|
|
36
|
+
evidence, and the host merges them without reading or searching the files again. Gathering the evidence twice spends
|
|
37
|
+
context on the second pass and reports what the host still remembers. Inspect a topic inline only after its dispatch has
|
|
38
|
+
been tried and refused, and say which attempt failed. A `full` audit also dispatches one marker investigator; a
|
|
39
|
+
named-topic audit gives that search to the topic's investigator. See [Deferred-work markers](#deferred-work-markers).
|
|
35
40
|
4. Merge the evidence into the shared report format in
|
|
36
41
|
[`templates/REPORT.md`](../imf-web-ui-conventions/templates/REPORT.md). Before presenting or writing it, check that every
|
|
37
42
|
Broken, Missing, Deviation, and Unverified entry has a severity, evidence, impact, and next action; do not compact
|
|
@@ -55,7 +60,15 @@ Classify every result as one of these:
|
|
|
55
60
|
|
|
56
61
|
Use the report contract's severity, `path:line` evidence, concrete impact, and smallest next action. Unverified entries use
|
|
57
62
|
that same structure: unavailable runtime evidence, the impact of the unknown, and the check that resolves it. Do not promote
|
|
58
|
-
an optional tool or a working alternative to a missing finding.
|
|
63
|
+
an optional tool or a working alternative to a missing finding. A `vite.config.ts` without `localWebUILink()` registered from
|
|
64
|
+
`@imfusion/web-ui/vite` is Missing. A `tsconfig.json` without the `paths` block from the `tooling` topic is Missing too. Each
|
|
65
|
+
next action is the add from that topic. A Vite config that reads tsconfig `paths` (`resolve.tsconfigPaths` or
|
|
66
|
+
`vite-tsconfig-paths`) is Broken: it loads the React types package as React once Web UI comes from the registry. The fix is
|
|
67
|
+
to drop it and give the `#/` alias through `imports` in `package.json`. A major or minor mismatch between the project and Web
|
|
68
|
+
UI on a package listed under Aligned runtime packages in the `tooling` topic is a Deviation. Report both versions, with
|
|
69
|
+
`npm install <package>@<Web UI's version>` as the fix. A route-local file that the inline rule in the `project-structure`
|
|
70
|
+
topic says belongs in its route file is a Deviation, and so is a route file over 250 lines that inlines parts the rule would
|
|
71
|
+
keep in its `-` folder. Report the line counts, with the move as the fix.
|
|
59
72
|
|
|
60
73
|
## Topics
|
|
61
74
|
|
|
@@ -90,6 +103,6 @@ text; do not run installers or hooks.
|
|
|
90
103
|
|
|
91
104
|
## Safety
|
|
92
105
|
|
|
93
|
-
Use only `Read`,
|
|
94
|
-
linters, tests, builds, hooks, or imported config. Report runtime state as unverified. Investigators inherit
|
|
95
|
-
restriction.
|
|
106
|
+
Use only `Read`, the host's read-only file search, and equivalent static inspection. Do not run Git, shells, Node, npm, npx,
|
|
107
|
+
package scripts, linters, tests, builds, hooks, or imported config. Report runtime state as unverified. Investigators inherit
|
|
108
|
+
this restriction.
|
|
@@ -14,7 +14,7 @@ sections one to one — change a topic's sections and this file follows in the s
|
|
|
14
14
|
- [ ] Stay behind the library
|
|
15
15
|
- [ ] Wrap primitives when the app has a reason to
|
|
16
16
|
- [ ] Lock a field: read-only or disabled
|
|
17
|
-
- [ ] Derive types, don't
|
|
17
|
+
- [ ] Derive types, don't copy them
|
|
18
18
|
- [ ] Integrations own their peers
|
|
19
19
|
- [ ] Styling crosses the boundary through seams
|
|
20
20
|
|
|
@@ -89,6 +89,7 @@ sections one to one — change a topic's sections and this file follows in the s
|
|
|
89
89
|
- [ ] Topic registry and key factory
|
|
90
90
|
- [ ] Options factories and naming
|
|
91
91
|
- [ ] Router context access
|
|
92
|
+
- [ ] Loading states
|
|
92
93
|
- [ ] Automatic invalidation
|
|
93
94
|
- [ ] Testing the data layer
|
|
94
95
|
|
|
@@ -108,7 +109,8 @@ sections one to one — change a topic's sections and this file follows in the s
|
|
|
108
109
|
- [ ] Oxfmt
|
|
109
110
|
- [ ] Oxlint and TypeScript
|
|
110
111
|
- [ ] Pre-commit checks
|
|
111
|
-
- [ ] Vite plugins
|
|
112
|
+
- [ ] Vite plugins, including `localWebUILink()`
|
|
113
|
+
- [ ] Aligned runtime packages
|
|
112
114
|
|
|
113
115
|
## npm project — `npm-project`
|
|
114
116
|
|
|
@@ -70,8 +70,10 @@ Cross-topic invalidation can then use `keys.topic` instead of rebuilding a prefi
|
|
|
70
70
|
|
|
71
71
|
## Options factories and naming
|
|
72
72
|
|
|
73
|
-
Queries use `get*` names such as `getAll`, `getById`, and `getDetails`. Mutations use
|
|
74
|
-
|
|
73
|
+
Queries use `get*` names such as `getAll`, `getById`, and `getDetails`. Mutations use verbs such as `update` or `delete`. A
|
|
74
|
+
name never repeats its topic or adds an `Options` suffix; `context.api.user.getDetails()` already supplies the context. When
|
|
75
|
+
a topic has more than one mutation target, add the sub-resource to the verb: `api.projects.removeMember(projectId)` and
|
|
76
|
+
`api.projects.setMemberPermissions(projectId)`, where a bare `remove` would read as deleting the project.
|
|
75
77
|
|
|
76
78
|
A query factory owns its key and parsing:
|
|
77
79
|
|
|
@@ -102,6 +104,19 @@ not inline those options at the call site.
|
|
|
102
104
|
Put the API object and `QueryClient` in router context. Loaders use `context.api.<topic>.<fn>()`; route components read the
|
|
103
105
|
same context with `Route.useRouteContext()`. Direct imports are for plain utilities and tests outside the route tree.
|
|
104
106
|
|
|
107
|
+
## Loading states
|
|
108
|
+
|
|
109
|
+
Pick the Skeleton part by how the data loads:
|
|
110
|
+
|
|
111
|
+
- Data read with `useSuspenseQuery` inside a Suspense boundary uses `Skeleton.Suspense`, with the bones as its `fallback`.
|
|
112
|
+
Content fades in when the fallback showed; cached data mounts without a fade.
|
|
113
|
+
- Secondary data read with `useQuery` that may arrive later uses `Skeleton.Root` with `loading={isPending}` around the real
|
|
114
|
+
content, which cross-fades when the data lands.
|
|
115
|
+
- A route whose loader blocks navigation until the data is cached shows its `pendingComponent`. The router unmounts it, so
|
|
116
|
+
nothing reveals.
|
|
117
|
+
|
|
118
|
+
A layout that reads suspending data needs its own boundary. Otherwise a suspension blanks the whole layout.
|
|
119
|
+
|
|
105
120
|
## Automatic invalidation
|
|
106
121
|
|
|
107
122
|
Mutation keys start with the topic they change. The shared `MutationCache` invalidates that topic after success by default.
|
|
@@ -7,12 +7,15 @@ Use the repository's tracked configuration and keep verification scopes clear.
|
|
|
7
7
|
Run once in a clone:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
git config core.hooksPath .
|
|
10
|
+
git config core.hooksPath .vite-hooks/_
|
|
11
|
+
vp config --no-agent
|
|
11
12
|
git config pull.rebase true
|
|
12
13
|
git config merge.ff only
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Vite+ manages the hooks. The hook scripts are tracked in `.vite-hooks/`; `vp config` installs the dispatcher into the
|
|
17
|
+
git-ignored `.vite-hooks/_/`. `vp config` leaves a `core.hooksPath` that points elsewhere alone, so set it first. Every clone
|
|
18
|
+
and worktree needs its own dispatcher. Check both with `vp hooks status`; a missing dispatcher skips the hooks silently.
|
|
16
19
|
|
|
17
20
|
## Verify scopes
|
|
18
21
|
|
|
@@ -29,6 +29,9 @@ instead, so component colors cannot resolve against the opposite scheme:
|
|
|
29
29
|
<WebUIProvider colorScheme="dark">
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
To let the user pick Light, System, or Dark, place a `ColorSchemeToggle`. It stores the choice and works only while the
|
|
33
|
+
provider's `colorScheme` is unset.
|
|
34
|
+
|
|
32
35
|
## Symptoms of broken setup
|
|
33
36
|
|
|
34
37
|
- Unstyled components usually mean the `styles.css` import is missing.
|
|
@@ -35,8 +35,11 @@ A route file holds its search schema and `validateSearch`, its loaders, and its
|
|
|
35
35
|
Route-local code that outgrows small in-file components (views, forms, hooks, tests) goes in a sibling folder named `-` plus
|
|
36
36
|
the route file's name: `route.tsx` gets `-route/`, `products.tsx` gets `-products/`. TanStack Router ignores files and
|
|
37
37
|
folders with a leading `-` when it builds the route tree, so the folder never becomes a route. The folder holds the route's
|
|
38
|
-
views with their props types, hooks, `.module.css` files, and tests.
|
|
39
|
-
|
|
38
|
+
views with their props types, hooks, `.module.css` files, and tests.
|
|
39
|
+
|
|
40
|
+
A part used by one route file is inlined into it when it is a component under 50 lines, calls no `use*` hook, has no
|
|
41
|
+
stylesheet or test of its own, and leaves that route file under 250 lines. Every other single-route part goes in the `-`
|
|
42
|
+
folder. The thresholds keep tiny wrappers out of their own files without letting a route file grow into a page of views.
|
|
40
43
|
|
|
41
44
|
`partials/` holds only parts that more than one route uses.
|
|
42
45
|
|
|
@@ -9,7 +9,9 @@ Use native CSS and Web UI's public seams. The library's CSS Modules and token na
|
|
|
9
9
|
- Nest states, pseudo-elements, and child selectors under the root.
|
|
10
10
|
- Write declarations before nested rules; browsers before Chrome 130, Firefox 132, and Safari 18 move later ones above them.
|
|
11
11
|
- Do not nest under a selector list whose selectors differ in specificity. The nested rule takes the highest one.
|
|
12
|
-
- Lift
|
|
12
|
+
- Lift a value into a custom property on the component's root when it repeats within that stylesheet, or when a state or
|
|
13
|
+
variant changes several descendants. A value that only recurs across files stays a literal in each; when those files show
|
|
14
|
+
one design element, a shared component owns it. Never define component values on `:root`.
|
|
13
15
|
- Keep structurally different rules explicit; not every repeated value needs an abstraction.
|
|
14
16
|
|
|
15
17
|
```css
|
|
@@ -49,7 +49,7 @@ from `vite-plus/test`. It needs Node `^22.22.1 || ^24.11 || >=26` and Git 2.32 o
|
|
|
49
49
|
`vp <command>` is a built-in; `vp run <script>` runs a package script.
|
|
50
50
|
|
|
51
51
|
Move an existing Vite, Vitest, ESLint, or Prettier project with `vp migrate --no-hooks` (`vp help migrate` lists its
|
|
52
|
-
options), then check the result against this topic. The hook lives in `.
|
|
52
|
+
options), then check the result against this topic. The hook lives in `.vite-hooks/`, as `git.md` describes.
|
|
53
53
|
|
|
54
54
|
## Oxfmt
|
|
55
55
|
|
|
@@ -101,12 +101,19 @@ export default defineConfig({
|
|
|
101
101
|
});
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
Use strict TypeScript with bundler module resolution, `verbatimModuleSyntax`, unused-value checks, and
|
|
105
|
-
|
|
104
|
+
Use strict TypeScript with bundler module resolution, `verbatimModuleSyntax`, unused-value checks, and `import type` for
|
|
105
|
+
type-only imports. The `#/` alias is a subpath import in `package.json`, which Vite and TypeScript both read without a
|
|
106
|
+
plugin:
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
"imports": {
|
|
110
|
+
"#/*": "./src/*"
|
|
111
|
+
}
|
|
112
|
+
```
|
|
106
113
|
|
|
107
114
|
## Pre-commit checks
|
|
108
115
|
|
|
109
|
-
`.
|
|
116
|
+
`.vite-hooks/pre-commit` runs `vp staged --hide-unstaged --fail-on-changes --revert --diff-filter=ACMRD` with `set -e`. Those
|
|
110
117
|
flags make the checks see only the staged state, include deletion-only commits, fail the commit when a check would change a
|
|
111
118
|
file, and restore the tree afterwards, so the hook never rewrites or stages files. The `staged` block in `vite.config.ts` is
|
|
112
119
|
a function returning path-free commands such as `npm run --silent verify:lint`, so each project-wide check runs once. Use
|
|
@@ -119,10 +126,10 @@ Register Web UI's Vite plugins in the application configuration:
|
|
|
119
126
|
```ts
|
|
120
127
|
import { defineConfig } from "vite-plus";
|
|
121
128
|
import react from "@vitejs/plugin-react";
|
|
122
|
-
import { imfusionBrandAssets, readableCssModuleNames } from "@imfusion/web-ui/vite";
|
|
129
|
+
import { imfusionBrandAssets, localWebUILink, readableCssModuleNames } from "@imfusion/web-ui/vite";
|
|
123
130
|
|
|
124
131
|
export default defineConfig({
|
|
125
|
-
plugins: [react(), imfusionBrandAssets(), readableCssModuleNames({ prefix: "app" })]
|
|
132
|
+
plugins: [react(), imfusionBrandAssets(), readableCssModuleNames({ prefix: "app" }), localWebUILink()]
|
|
126
133
|
});
|
|
127
134
|
```
|
|
128
135
|
|
|
@@ -130,5 +137,37 @@ export default defineConfig({
|
|
|
130
137
|
Register `readableCssModuleNames` in every compiler that processes the app's CSS, including development, Storybook, and
|
|
131
138
|
production. A compiler left out produces different class names and styles that appear to work only in some environments.
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
## Aligned runtime packages
|
|
141
|
+
|
|
142
|
+
Web UI and the app run the same versions of these packages. The `peerDependencies` of
|
|
143
|
+
`node_modules/@imfusion/web-ui/package.json` give the accepted ranges, and its `devDependencies` give the version Web UI
|
|
144
|
+
builds and tests with. A matching major and minor is the rule; a patch difference is fine.
|
|
145
|
+
|
|
146
|
+
- `react`, `react-dom`
|
|
147
|
+
- `@types/react`, `@types/react-dom`
|
|
148
|
+
- `@base-ui/react`, which brings `@floating-ui/*` with it
|
|
149
|
+
- `vite-plus`
|
|
150
|
+
- `vite`, the `npm:@voidzero-dev/vite-plus-core` alias that `vite-plus` provides
|
|
151
|
+
|
|
152
|
+
## Version drift
|
|
153
|
+
|
|
154
|
+
Aligned versions are the primary fix. The plugin and the `paths` block below catch the drift that slips through.
|
|
155
|
+
|
|
156
|
+
`localWebUILink` stays registered for good. It does nothing for an installed package. When `@imfusion/web-ui` is a symlinked
|
|
157
|
+
checkout, it keeps one React in dev, build, and Vitest.
|
|
158
|
+
|
|
159
|
+
Add this to `compilerOptions` in `tsconfig.json`, merged by hand into an existing `paths`. For a symlinked checkout it keeps
|
|
160
|
+
one set of React and Vite types for `tsc`:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
"paths": {
|
|
164
|
+
"react": ["./node_modules/@types/react"],
|
|
165
|
+
"react/*": ["./node_modules/@types/react/*"],
|
|
166
|
+
"vite": ["./node_modules/vite"]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Only TypeScript reads these `paths`. Never let Vite read them: leave `resolve.tsconfigPaths` off and do not add
|
|
171
|
+
`vite-tsconfig-paths`. Either one applies the `react` mapping at runtime, loads the types package as React, and breaks the
|
|
172
|
+
build and tests once `@imfusion/web-ui` is installed from the registry. A symlinked checkout hides the break, because
|
|
173
|
+
`localWebUILink()` dedupes React first.
|
|
@@ -6,6 +6,8 @@ Prefer pure functions, immutable values, and side effects at the edges.
|
|
|
6
6
|
|
|
7
7
|
- Do not use `any`. Accept `unknown` at an untrusted boundary and narrow it.
|
|
8
8
|
- Infer local values; give exported functions explicit return types.
|
|
9
|
+
- TanStack options factories (`queryOptions`, `mutationOptions`, and similar) return their helper's inferred type. Spelling
|
|
10
|
+
it out loses the typed query key (`DataTag`) or repeats the helper's own generics.
|
|
9
11
|
- Model states directly instead of filling a nullable value later.
|
|
10
12
|
- Derive types from their source. Use `z.infer` for schemas and `React.ComponentProps` for Web UI components.
|
|
11
13
|
- Avoid `as`. Keep an assertion only at an untyped third-party boundary where it is the honest boundary.
|
|
@@ -45,10 +45,19 @@ Include these pieces unless the existing project already has them or the user re
|
|
|
45
45
|
- one server-backed current-user query at the route boundary;
|
|
46
46
|
- a minimal `AppShell` in `_app/`, unless the user says the product has no persistent authenticated navigation;
|
|
47
47
|
- an API topic folder for data shown by the first screen;
|
|
48
|
-
- the root provider and library wiring
|
|
48
|
+
- the root provider and library wiring;
|
|
49
|
+
- the Vite plugins below.
|
|
49
50
|
|
|
50
51
|
The plan describes the exact starter files. It does not copy a generic starter project.
|
|
51
52
|
|
|
53
|
+
## Vite plugins
|
|
54
|
+
|
|
55
|
+
Propose a `vite.config.ts` that registers `imfusionBrandAssets()`, `readableCssModuleNames()`, and `localWebUILink()` from
|
|
56
|
+
`@imfusion/web-ui/vite`, next to the plugins the project already has. Register `localWebUILink()` in every project: without a
|
|
57
|
+
symlinked checkout it does nothing. Also propose the `tsconfig.json` `paths` block. Both keep one React and one set of types
|
|
58
|
+
when the package is a symlinked checkout. Vite must not read those `paths`: never propose `resolve.tsconfigPaths` or
|
|
59
|
+
`vite-tsconfig-paths`, and give the `#/` alias through `imports` in `package.json`. The `tooling` topic has the exact config.
|
|
60
|
+
|
|
52
61
|
## Installer steps
|
|
53
62
|
|
|
54
63
|
For `agent-tooling`, list the `npx web-ui install` commands as steps for the human. Read existing registrations first and do
|
|
@@ -96,6 +96,13 @@ changed. A diff shows what moved, not the step order you continue under, so it d
|
|
|
96
96
|
the current step using the updated guidance, keeping the original baseline, user choices, and completed changes in mind.
|
|
97
97
|
Check for newly required steps without repeating completed mutations or expanding the approved scope.
|
|
98
98
|
|
|
99
|
+
## Align the runtime packages
|
|
100
|
+
|
|
101
|
+
After the install, compare the app's installed versions of the packages listed under Aligned runtime packages in the
|
|
102
|
+
`tooling` topic with Web UI's own `package.json` in `node_modules/@imfusion/web-ui`. Report every major or minor mismatch and
|
|
103
|
+
propose `npm install <package>@<Web UI's version>` for each. Make the change as its own commit, in this same update, after
|
|
104
|
+
the base commit and with its own approval.
|
|
105
|
+
|
|
99
106
|
## Verify the base update
|
|
100
107
|
|
|
101
108
|
After every mutating command, compare the status with the preflight snapshot. Stop on an unexpected path or overlapping
|
|
@@ -7,8 +7,8 @@ description:
|
|
|
7
7
|
|
|
8
8
|
# Shape the screen
|
|
9
9
|
|
|
10
|
-
Use this skill for screen-level decisions.
|
|
11
|
-
`imf-web-ui-conventions`.
|
|
10
|
+
Use this skill for screen-level decisions. The patterns below name no components: check which component fits a pattern, and
|
|
11
|
+
its API, in `imf-web-ui-components`. Put code and styling rules in `imf-web-ui-conventions`.
|
|
12
12
|
|
|
13
13
|
Invoke `imf-web-ui-user-profile` first and explain at its levels throughout.
|
|
14
14
|
|
|
@@ -34,33 +34,31 @@ unanswered, make the conservative choice and state the assumption.
|
|
|
34
34
|
## Choose a surface
|
|
35
35
|
|
|
36
36
|
- Use a full page by default.
|
|
37
|
-
- Use
|
|
38
|
-
- Use
|
|
39
|
-
- Use
|
|
40
|
-
- Use
|
|
41
|
-
- Use
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
reason.
|
|
37
|
+
- Use a side panel for a focused task that keeps the current page in context.
|
|
38
|
+
- Use a floating panel anchored to its trigger for small contextual content or a light form.
|
|
39
|
+
- Use a hover or focus hint for a short label only. It must not contain required or interactive information.
|
|
40
|
+
- Use a collapsible region for secondary content inside the page.
|
|
41
|
+
- Use tabs for parallel views of one subject. If users must complete every section, use a flow instead.
|
|
42
|
+
- Use a modal dialog for a decision that must be settled before the page continues, such as a destructive action. If no
|
|
43
|
+
component fits a pattern, raise the gap rather than hand-rolling a replacement without a product reason.
|
|
45
44
|
|
|
46
45
|
## Choose between similar controls
|
|
47
46
|
|
|
48
|
-
-
|
|
49
|
-
- Use
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Use
|
|
54
|
-
- Use
|
|
55
|
-
- Use
|
|
56
|
-
- Use `Slider` when relative position matters more than typing an exact value.
|
|
47
|
+
- An action is a button, navigation is a link, and a label is a chip.
|
|
48
|
+
- Use an inline link inside text or in a row of chips, and a full-row link for a list of resources.
|
|
49
|
+
- Use a plain table for static small data. Pair it with a headless table library for sorting, pagination, or column logic.
|
|
50
|
+
TanStack Table is the recommended choice.
|
|
51
|
+
- Use an in-place status message for persistent status. Keep transient confirmation near the action it confirms.
|
|
52
|
+
- Use a switch for an immediate setting and a checkbox for a submitted choice.
|
|
53
|
+
- Use a select for a known list, and consider a different control when the list is very short or very large.
|
|
54
|
+
- Use a slider when relative position matters more than typing an exact value.
|
|
57
55
|
|
|
58
56
|
## Build the hierarchy
|
|
59
57
|
|
|
60
|
-
- Frame an application with
|
|
61
|
-
-
|
|
58
|
+
- Frame an application with the library's app shell.
|
|
59
|
+
- Arrange content with stack and row layouts, wrap long-form text in a prose container, and use a separator for a real
|
|
62
60
|
division.
|
|
63
|
-
- Use
|
|
61
|
+
- Use the library's typography components for text hierarchy instead of styling raw headings beside them.
|
|
64
62
|
- Give a view one primary action. Make other actions quieter.
|
|
65
63
|
- Choose density from the user's frequency and data volume: frequent work and large data need compact layouts; occasional
|
|
66
64
|
work benefits from more explanation and space.
|
|
@@ -70,14 +68,37 @@ reason.
|
|
|
70
68
|
Plan these states with the loaded view:
|
|
71
69
|
|
|
72
70
|
- **Empty**: say what belongs here and what the user can do next.
|
|
73
|
-
- **Loading**: use
|
|
74
|
-
|
|
71
|
+
- **Loading**: use a spinner or a stable skeleton; keep the frame from jumping. The `imf-web-ui-conventions` data topic says
|
|
72
|
+
which skeleton part fits each way the data loads.
|
|
73
|
+
- **Error**: explain what failed and the next action in an in-place status message near the problem.
|
|
75
74
|
- **Loaded**: the normal view.
|
|
76
75
|
|
|
77
76
|
## Keep custom UI native
|
|
78
77
|
|
|
79
|
-
When
|
|
80
|
-
|
|
78
|
+
When no component covers a piece, compose its primitives and use `--imf-ui-*` tokens. The implementation details are in
|
|
79
|
+
`imf-web-ui-conventions`.
|
|
81
80
|
|
|
82
81
|
For a screen review, read the relevant reference under `references/`: `usability-heuristics.md`, `visual-design.md`, or
|
|
83
82
|
`forms.md`.
|
|
83
|
+
|
|
84
|
+
## Public backdrop handover
|
|
85
|
+
|
|
86
|
+
Public pages share a backdrop with the Keycloak login page: a light column with an animated line mesh, and a brand-colored
|
|
87
|
+
panel. Both sides draw the same geometry, so the cross-origin navigation between them looks seamless. Read the geometry from
|
|
88
|
+
these tokens and never from literals:
|
|
89
|
+
|
|
90
|
+
- `--imf-ui-size-backdrop-edge`: where the brand panel starts on wide landscape screens.
|
|
91
|
+
- `--imf-ui-size-backdrop-band-height`: the height of the light band on top of narrow or portrait screens, with the panel
|
|
92
|
+
below it.
|
|
93
|
+
|
|
94
|
+
The switch between the two layouts is a media query, which a token cannot carry. Use the same breakpoint as the reference
|
|
95
|
+
implementations.
|
|
96
|
+
|
|
97
|
+
The backdrop also paints before React mounts, so the first frame already matches. Put the same two columns on `body` as a
|
|
98
|
+
hard-stop `linear-gradient`, with `min-block-size: 100dvh`. In a single-page app, a marker on `html`
|
|
99
|
+
(`data-backdrop-pending`), set by an inline script in `index.html` for the routes that show the backdrop, gates that
|
|
100
|
+
gradient. The backdrop component removes the marker when it mounts. Generate that script's route list at build time from the
|
|
101
|
+
router's own source, never as a hand-written copy: with TanStack file routes, a small Vite plugin (`transformIndexHtml`)
|
|
102
|
+
reads the folder of backdrop routes and fills a placeholder in `index.html`, so a new route gets the pre-paint without anyone
|
|
103
|
+
touching the script. The ImFusion Portal's public backdrop component and the Keycloak login theme's copy of it are the
|
|
104
|
+
reference implementations.
|
|
@@ -6,9 +6,9 @@ validation. These rules cover structure and feedback.
|
|
|
6
6
|
## Structure
|
|
7
7
|
|
|
8
8
|
- Remove fields that can be derived or asked later.
|
|
9
|
-
- Use one column by default. Put short, inseparable values such as city and postal code
|
|
10
|
-
- Group related fields
|
|
11
|
-
- Use a labeled
|
|
9
|
+
- Use one column by default. Put short, inseparable values such as city and postal code on one row.
|
|
10
|
+
- Group related fields with tight spacing and separate groups with more space.
|
|
11
|
+
- Use a labeled fieldset or heading for each group.
|
|
12
12
|
- Match a field's size to the value it expects.
|
|
13
13
|
|
|
14
14
|
## Labels
|
|
@@ -21,10 +21,10 @@ hints near the field before the user starts typing.
|
|
|
21
21
|
Validate when the user finishes a field, except where live feedback genuinely helps, such as password requirements. Keep the
|
|
22
22
|
error next to the field and use more than color to show it.
|
|
23
23
|
|
|
24
|
-
An error says what is wrong and how to fix it. Keep the user's input. A server or form-level failure belongs in
|
|
25
|
-
near the actions; field-level failures stay with their fields.
|
|
24
|
+
An error says what is wrong and how to fix it. Keep the user's input. A server or form-level failure belongs in an in-place
|
|
25
|
+
status message near the actions; field-level failures stay with their fields.
|
|
26
26
|
|
|
27
27
|
## Submission
|
|
28
28
|
|
|
29
|
-
Use one primary
|
|
29
|
+
Use one primary button whose label names the result, such as `Create project`. While submitting, keep the form visible and
|
|
30
30
|
show progress. A failed submission must leave the user's input on screen.
|
|
@@ -5,12 +5,12 @@ user research.
|
|
|
5
5
|
|
|
6
6
|
1. **Show system status.** Give async actions visible progress and confirmation.
|
|
7
7
|
2. **Use the user's language.** Labels and ordering should match the domain, not an API's private names.
|
|
8
|
-
3. **Provide an exit.**
|
|
8
|
+
3. **Provide an exit.** Side panels and floating panels need clear dismissal; destructive actions need confirmation or undo.
|
|
9
9
|
4. **Be consistent.** Reuse the library's established components and semantics.
|
|
10
10
|
5. **Prevent errors.** Constrain known choices and disable actions that cannot work.
|
|
11
11
|
6. **Support recognition.** Keep relevant options and context visible instead of making users remember them.
|
|
12
12
|
7. **Support frequent users.** Add keyboard shortcuts or dense layouts when the task is frequent and data-heavy.
|
|
13
13
|
8. **Remove decoration without a job.** Every element should support the action or the data.
|
|
14
14
|
9. **Make errors recoverable.** Say what failed and what to do next, near the problem.
|
|
15
|
-
10. **Keep help contextual.** Prefer a short hint beside a control to essential information hidden in a
|
|
16
|
-
page.
|
|
15
|
+
10. **Keep help contextual.** Prefer a short hint beside a control to essential information hidden in a hover hint or
|
|
16
|
+
separate page.
|
|
@@ -7,12 +7,12 @@ Use this when a screen works but still looks wrong. Decide the hierarchy before
|
|
|
7
7
|
Write the intended order of attention: title, primary action, content, and supporting details. Use contrast and scale to make
|
|
8
8
|
that order visible. Keep variation limited; if everything is loud, nothing leads.
|
|
9
9
|
|
|
10
|
-
Reserve saturated status colors for meaning. The primary
|
|
10
|
+
Reserve saturated status colors for meaning. The primary button should be the strongest action treatment on the screen.
|
|
11
11
|
|
|
12
12
|
## Grouping
|
|
13
13
|
|
|
14
|
-
Use proximity first: tight gaps inside a group, larger gaps between groups.
|
|
15
|
-
|
|
14
|
+
Use proximity first: tight gaps inside a group, larger gaps between groups. Spacing values encode this relationship. Use a
|
|
15
|
+
shared card or bordered region only when spacing alone does not make the relationship clear.
|
|
16
16
|
|
|
17
17
|
Similarity helps related elements stay recognisable across a layout. It supports proximity; it does not replace it.
|
|
18
18
|
|
package/src/llms/tokens.gen.json
CHANGED
|
@@ -477,7 +477,7 @@
|
|
|
477
477
|
},
|
|
478
478
|
{
|
|
479
479
|
"name": "--imf-ui-font-family-mono",
|
|
480
|
-
"value": "\"
|
|
480
|
+
"value": "\"Paper Mono\", ui-monospace, monospace"
|
|
481
481
|
},
|
|
482
482
|
{
|
|
483
483
|
"name": "--imf-ui-font-family-sans",
|
|
@@ -493,7 +493,7 @@
|
|
|
493
493
|
},
|
|
494
494
|
{
|
|
495
495
|
"name": "--imf-ui-font-letterspacing-brand",
|
|
496
|
-
"value": "-0.
|
|
496
|
+
"value": "clamp(-0.035rem, -0.0081rem - 0.0224vw, -0.0131rem)"
|
|
497
497
|
},
|
|
498
498
|
{
|
|
499
499
|
"name": "--imf-ui-font-letterspacing-display",
|
|
@@ -501,7 +501,7 @@
|
|
|
501
501
|
},
|
|
502
502
|
{
|
|
503
503
|
"name": "--imf-ui-font-letterspacing-hero",
|
|
504
|
-
"value": "-0.
|
|
504
|
+
"value": "clamp(-0.225rem, -0.0312rem - 0.1615vw, -0.0675rem)"
|
|
505
505
|
},
|
|
506
506
|
{
|
|
507
507
|
"name": "--imf-ui-font-letterspacing-loose",
|
|
@@ -689,7 +689,7 @@
|
|
|
689
689
|
},
|
|
690
690
|
{
|
|
691
691
|
"name": "--imf-ui-font-weight-strong",
|
|
692
|
-
"value": "
|
|
692
|
+
"value": "500"
|
|
693
693
|
}
|
|
694
694
|
],
|
|
695
695
|
"inner-*": [
|
|
@@ -913,6 +913,14 @@
|
|
|
913
913
|
"name": "--imf-ui-size-9",
|
|
914
914
|
"value": "4rem"
|
|
915
915
|
},
|
|
916
|
+
{
|
|
917
|
+
"name": "--imf-ui-size-backdrop-band-height",
|
|
918
|
+
"value": "30dvh"
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
"name": "--imf-ui-size-backdrop-edge",
|
|
922
|
+
"value": "38%"
|
|
923
|
+
},
|
|
916
924
|
{
|
|
917
925
|
"name": "--imf-ui-size-content-1",
|
|
918
926
|
"value": "20ch"
|
|
@@ -947,5 +955,5 @@
|
|
|
947
955
|
}
|
|
948
956
|
]
|
|
949
957
|
},
|
|
950
|
-
"count":
|
|
958
|
+
"count": 233
|
|
951
959
|
}
|