@imfusion/web-ui 0.5.1-dev.48.g27701f90 → 0.5.1-dev.5.gb4de52d7

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 (69) hide show
  1. package/README.md +55 -156
  2. package/bin/install-skill.js +180 -0
  3. package/dist/index.d.ts +0 -2
  4. package/dist/index.js +4317 -4956
  5. package/dist/integrations/code-highlight/highlighter.d.ts +3 -32
  6. package/dist/integrations/code-highlight.js +48 -188
  7. package/dist/integrations/image-display-options.js +1 -1
  8. package/dist/style.css +1 -1
  9. package/dist/{tabs-CVp_SgBl.js → tabs-DqBFSqq6.js} +1 -1
  10. package/package.json +25 -39
  11. package/src/docgen/doc.gen.json +0 -327
  12. package/src/llms/llms.gen.txt +0 -12
  13. package/src/llms/skills/imf-web-ui/SKILL.md +12 -13
  14. package/src/llms/skills/imf-web-ui-components/SKILL.md +1 -2
  15. package/src/llms/skills/imf-web-ui-frontend-patterns/SKILL.md +93 -0
  16. package/src/llms/skills/imf-web-ui-frontend-patterns/references/code-conventions.md +133 -0
  17. package/src/llms/skills/imf-web-ui-frontend-patterns/references/react-patterns.md +94 -0
  18. package/src/llms/skills/imf-web-ui-imfusion-frontend-setup/SKILL.md +201 -0
  19. package/src/llms/skills/imf-web-ui-setup/SKILL.md +37 -67
  20. package/src/llms/skills/imf-web-ui-ux/SKILL.md +4 -4
  21. package/src/llms/skills/imf-web-ui-ux/references/forms.md +2 -2
  22. package/bin/install.js +0 -428
  23. package/bin/install.test.ts +0 -329
  24. package/dist/build/vite-css-module-names/index.d.ts +0 -20
  25. package/dist/build/vite-css-module-names.js +0 -17
  26. package/dist/components/field/field.d.ts +0 -104
  27. package/dist/components/field/field.meta.d.ts +0 -2
  28. package/dist/components/field/index.d.ts +0 -2
  29. package/dist/components/fieldset/fieldset.d.ts +0 -29
  30. package/dist/components/fieldset/fieldset.meta.d.ts +0 -2
  31. package/dist/components/fieldset/index.d.ts +0 -2
  32. package/dist/integrations/code-highlight/language-patterns.d.ts +0 -7
  33. package/dist/integrations/code-highlight/languages/cmake.d.ts +0 -1
  34. package/dist/integrations/code-highlight/languages/cpp.d.ts +0 -1
  35. package/dist/integrations/code-highlight/languages/python.d.ts +0 -1
  36. package/dist/llms/gen-tokens.d.ts +0 -7
  37. package/src/llms/install-templates/AGENTS.md +0 -34
  38. package/src/llms/install-templates/codex-hooks.json +0 -44
  39. package/src/llms/install-templates/hooks/baseline-staleness.sh +0 -17
  40. package/src/llms/install-templates/hooks/session-start.sh +0 -5
  41. package/src/llms/install-templates/hooks/stop.sh +0 -18
  42. package/src/llms/install-templates/hooks/subagent-start.sh +0 -5
  43. package/src/llms/install-templates/hooks/user-prompt-submit.sh +0 -5
  44. package/src/llms/install-templates/settings.json +0 -45
  45. package/src/llms/skills/imf-web-ui-audit/SKILL.md +0 -119
  46. package/src/llms/skills/imf-web-ui-conventions/SKILL.md +0 -57
  47. package/src/llms/skills/imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md +0 -141
  48. package/src/llms/skills/imf-web-ui-conventions/templates/REPORT.md +0 -45
  49. package/src/llms/skills/imf-web-ui-conventions/topics/agent-tooling.md +0 -82
  50. package/src/llms/skills/imf-web-ui-conventions/topics/assets.md +0 -27
  51. package/src/llms/skills/imf-web-ui-conventions/topics/authentication.md +0 -65
  52. package/src/llms/skills/imf-web-ui-conventions/topics/class-names.md +0 -50
  53. package/src/llms/skills/imf-web-ui-conventions/topics/components.md +0 -101
  54. package/src/llms/skills/imf-web-ui-conventions/topics/data.md +0 -221
  55. package/src/llms/skills/imf-web-ui-conventions/topics/docs-structure.md +0 -40
  56. package/src/llms/skills/imf-web-ui-conventions/topics/git.md +0 -34
  57. package/src/llms/skills/imf-web-ui-conventions/topics/library-boundary.md +0 -33
  58. package/src/llms/skills/imf-web-ui-conventions/topics/library-setup.md +0 -26
  59. package/src/llms/skills/imf-web-ui-conventions/topics/npm-project.md +0 -53
  60. package/src/llms/skills/imf-web-ui-conventions/topics/project-structure.md +0 -44
  61. package/src/llms/skills/imf-web-ui-conventions/topics/react.md +0 -109
  62. package/src/llms/skills/imf-web-ui-conventions/topics/styling.md +0 -88
  63. package/src/llms/skills/imf-web-ui-conventions/topics/testing.md +0 -25
  64. package/src/llms/skills/imf-web-ui-conventions/topics/tokens.md +0 -7
  65. package/src/llms/skills/imf-web-ui-conventions/topics/tooling.md +0 -116
  66. package/src/llms/skills/imf-web-ui-conventions/topics/typescript.md +0 -73
  67. package/src/llms/skills/imf-web-ui-conventions/topics/validation.md +0 -62
  68. package/src/llms/skills/imf-web-ui-update/SKILL.md +0 -157
  69. package/src/llms/tokens.gen.json +0 -887
@@ -0,0 +1,133 @@
1
+ # Code conventions
2
+
3
+ The ImFusion defaults for everyday code around `@imfusion/web-ui` — the parts that aren't React-specific. TypeScript, file
4
+ organisation, naming, and testing. React component structure lives in [react-patterns.md](react-patterns.md); the two are
5
+ read together when starting new code.
6
+
7
+ These fill vacuums. Where the host project has already decided, the project wins.
8
+
9
+ ## TypeScript
10
+
11
+ - **`any` is forbidden.** `unknown` at a boundary you genuinely can't type, narrowed before use. An `any` that silences an
12
+ error moves the failure from compile time to runtime, which is the opposite of the trade you wanted.
13
+ - **Lean on inference for locals; annotate the contract.** Restating a type the compiler already knows inside a function body
14
+ is a second thing to keep in sync. An **explicit return type on an exported function is worth writing**: it's the promise
15
+ the module makes, it stops an internal refactor silently widening the public shape, and it makes the error surface at the
16
+ function rather than at every call site.
17
+ - **No temporal coupling.** Don't initialise to `null` and fill the value in later — model the states instead, so "not loaded
18
+ yet" and "loaded, empty" aren't the same value.
19
+ - **Avoid `as`.** A type assertion tells the compiler to stop checking exactly where checking is worth most. Fix the type.
20
+ Assertions at an untyped third-party boundary are the honest exception; keep them at the boundary, not spread through call
21
+ sites.
22
+
23
+ **Derive types, don't duplicate them.** One source of truth, everything else follows from it:
24
+
25
+ ```ts
26
+ const sizes = ["sm", "md", "lg"] as const;
27
+ type Size = (typeof sizes)[number];
28
+
29
+ const labels: Record<Size, string> = { sm: "S", md: "M", lg: "L" }; // compiler breaks if `sizes` changes
30
+ ```
31
+
32
+ The same rule crosses the library boundary: prop types come from the components themselves
33
+ (`React.ComponentProps<typeof Button>`), never re-declared by hand.
34
+
35
+ **Function signatures.** One or two positional arguments read fine. At three or more, take a single object and destructure —
36
+ call sites stop depending on argument order, and adding a parameter stops being a breaking change.
37
+
38
+ ## Expressions over statements
39
+
40
+ Reach for the array methods before the loop. `map`, `filter`, `find`, `some`, `every`, `flatMap`, `reduce` — each names what
41
+ it's doing, where a `for` loop makes you read the body to find out.
42
+
43
+ ```ts
44
+ // The name is the documentation
45
+ const activeNames = users.filter(u => u.isActive).map(u => u.name);
46
+
47
+ // vs. a loop you have to read to understand
48
+ const activeNames = [];
49
+ for (const u of users) {
50
+ if (u.isActive) activeNames.push(u.name);
51
+ }
52
+ ```
53
+
54
+ The deeper reason is mutation: the method chain produces a new value, so nothing else can observe a half-built array. Prefer
55
+ spreads and `structuredClone` over in-place edits, and `toSorted`/`toReversed` over `sort`/`reverse`, which mutate their
56
+ receiver and have surprised everyone at least once.
57
+
58
+ Two honest exceptions: a genuine early exit (`for` with `break` beats `find` returning a sentinel) and a hot loop over
59
+ thousands of items where the intermediate arrays actually measure. Neither is the common case, so reach for the method first
60
+ and justify the loop.
61
+
62
+ Keep the chain flat. Three or four steps read well; ten want intermediate named constants, and a `reduce` doing four things
63
+ at once wants to be a loop after all.
64
+
65
+ ## Naming
66
+
67
+ - Say what it is, not what it is made of. `useUserQuery`, not `useUserHook`. `retryDelay`, not `num`.
68
+ - Booleans read as assertions: `isOpen`, `hasAccess`, `canSubmit`. A boolean called `status` will end up holding a string.
69
+ - Handlers are `onX` as props, `handleX` as implementations — the prop names the event, the function names the response.
70
+ - Match the vocabulary the product and the API already use. Inventing a synonym for a term the backend already named costs a
71
+ translation step on every read.
72
+
73
+ ## File and folder organisation
74
+
75
+ Kebab-case throughout, folders and files.
76
+
77
+ ```
78
+ src/
79
+ routes/ # TanStack Router file-based routes; routing only
80
+ api/<topic>/ # <topic>.ts (queries/mutations), query-key.ts, types.ts
81
+ components/ # grouped by kind of component — layouts/, primitives/, or a domain name
82
+ http/ # client, error normalisation
83
+ lib/ # framework-free helpers
84
+ ```
85
+
86
+ `api/` groups by topic: a query lives next to its key factory and its types, so a query key is never spelled out at a call
87
+ site. Routes compose and don't fetch inline. Transport concerns live in `http/` and nowhere else.
88
+
89
+ `components/` groups by kind — a layout component under `layouts/`, not beside a domain widget. Flat is fine while there are
90
+ few; let the grouping follow what the project has rather than imposing it up front. The kinds worth separating are the
91
+ component roles in [react-patterns.md](react-patterns.md): dumb components, layout components, smart containers.
92
+
93
+ A component gets a folder once it has more than one file, with an `index.ts` that only re-exports:
94
+
95
+ ```
96
+ components/data-table/
97
+ data-table.tsx
98
+ data-table-row.tsx
99
+ data-table.module.css
100
+ index.ts
101
+ ```
102
+
103
+ Colocate tests, styles, and types with their subject. A file you have to hunt for in a parallel tree gets edited less
104
+ carefully.
105
+
106
+ ## Styling
107
+
108
+ **CSS Modules by default**, colocated as `<component>.module.css`. No CSS-in-JS, no utility-class framework. Compose from
109
+ `--imf-ui-*` tokens so custom UI stays consistent with library components and follows the theme; the override contract (CSS
110
+ layers, `data-imf-ui-component`, never the library's generated class names) is in the parent skill.
111
+
112
+ `imf-web-ui-imfusion-frontend-setup` sets up or audits this structure on an ImFusion project.
113
+
114
+ ## Testing
115
+
116
+ Test the **decisions**, not the rendering.
117
+
118
+ - **Pure logic gets unit tests** — pricing, permissions, date math, parsing, reducers. These are cheap, fast, and the place
119
+ bugs actually hide.
120
+ - **Presentational components generally don't.** A component that maps props onto web-ui primitives has no logic of its own;
121
+ asserting that it rendered a `<Button>` tests React, not your code.
122
+ - **Behaviour a user performs gets an interaction test** — a form that validates, a flow with steps. Test it through the
123
+ interface the user has (roles, labels, visible text), not through internals.
124
+ - **Extract the decision out of a hook and test that.** A hook whose interesting part is a plain function is easier to test
125
+ as a plain function than through a render harness. A hook that only wraps a browser API has no decision to extract.
126
+
127
+ The measure isn't coverage percentage. It's whether a test failing tells you something you didn't already know.
128
+
129
+ ## Formatting and linting
130
+
131
+ Don't argue about it in review — the tooling decides, and it runs before the commit lands. A formatter, a linter, and a
132
+ pre-commit hook wired so none of them is optional. On an ImFusion project, `imf-web-ui-imfusion-frontend-setup` carries the
133
+ baseline and the setup steps.
@@ -0,0 +1,94 @@
1
+ # React patterns
2
+
3
+ The house defaults for the React code around `@imfusion/web-ui`, in full. The links throughout are for **you, the agent**:
4
+ consult them while building — they are the authoritative source when a case here is ambiguous. Hand them to the human only if
5
+ asked.
6
+
7
+ ## Component roles
8
+
9
+ Dumb/smart separation is standard React practice (it traces back to Dan Abramov's
10
+ ["Presentational and Container Components"](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) and
11
+ survives in [Thinking in React](https://react.dev/learn/thinking-in-react)). The house version has three roles:
12
+
13
+ - **Dumb components** own how things _look_. They style and compose library primitives, receive plain data and callbacks as
14
+ props, and know nothing about fetching, routing, or business logic. All non-layout styling lives here — and only here.
15
+ - **Layout components** own _arrangement_ — and nothing else. `Stack`- and `Row`-based wrappers with token gaps, a page grid,
16
+ a section frame. They exist because smart containers are styleless: when a container needs two panels side by side, that
17
+ arrangement is a layout component, not an inline style.
18
+ - **Smart containers** own how things _work_. Routes (or explicit container components) fetch data, hold orchestration logic,
19
+ and wire the other two together. Zero styling — the moment a container wants CSS, extract a layout component.
20
+
21
+ ```tsx
22
+ // Dumb — renders what it's given
23
+ function UserCard({ name, role, onEdit }: { name: string; role: string; onEdit: () => void }) {
24
+ return (
25
+ <Card.Root>
26
+ <Card.Content>
27
+ <Typo>{name}</Typo>
28
+ <Chip>{role}</Chip>
29
+ </Card.Content>
30
+ <Card.Footer>
31
+ <Button onClick={onEdit}>Edit</Button>
32
+ </Card.Footer>
33
+ </Card.Root>
34
+ );
35
+ }
36
+
37
+ // Smart — knows where data comes from, renders the dumb component
38
+ function UserCardContainer({ userId }: { userId: string }) {
39
+ const { data } = useUserQuery(userId);
40
+ const openEditor = useEditorNavigation(userId);
41
+ return <UserCard name={data.name} role={data.role} onEdit={openEditor} />;
42
+ }
43
+ ```
44
+
45
+ Why it matters here: dumb components are the layer where `@imfusion/web-ui` lives. Keeping them free of logic keeps every
46
+ screen restylable, testable with plain props, and resilient to library updates. The one web-ui-specific addition: wrap
47
+ `experimental` components (marked in the identity index) in a dumb component once per app even if you add nothing yet — a
48
+ breaking upstream change then lands in one file instead of every call site.
49
+
50
+ ## Compose, don't configure
51
+
52
+ Build screen-level pieces by composing primitives (`Stack`, `Row`, `Card`, your dumb components) rather than growing one
53
+ component with a dozen boolean props. If a component's prop list reads like a settings page, it wanted to be two or three
54
+ components. When state must be shared between siblings, lift it to the nearest common parent —
55
+ [Sharing State Between Components](https://react.dev/learn/sharing-state-between-components) — rather than syncing copies.
56
+
57
+ ## Put state where its truth lives
58
+
59
+ Work down this list and stop at the first match:
60
+
61
+ 1. **Shareable via URL?** (filters, sort, pagination, active tab) → router search params. Back button and copied links are UX
62
+ features you get for free.
63
+ 2. **Comes from an API?** → the data-fetching layer's cache (e.g. TanStack Query). Never copy server data into `useState` —
64
+ that's how stale-UI bugs are born.
65
+ 3. **Scoped to a subtree, resets on leave?** (wizard progress) → React context.
66
+ 4. **App-wide and persistent?** → a client store, and only now.
67
+ 5. **Local to one component?** (input value, open/closed) → `useState`.
68
+
69
+ Most frontends need far less of tier 4 than they think; tiers 1–2 usually dissolve the "we need a store" instinct. For
70
+ structuring the state itself, [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) is the
71
+ reference — especially its rules on avoiding redundant and duplicated state.
72
+
73
+ ## Effects: last resort, and named
74
+
75
+ Before writing `useEffect`, check: derived values belong in render (or `useMemo`), responses to user actions belong in the
76
+ event handler, and server synchronization belongs in the data-fetching layer. Effects are for synchronizing with systems
77
+ _outside_ React. The definitive catalog of effect misuses — read it before every effect you're tempted to write — is
78
+ [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect).
79
+
80
+ When an effect is genuinely needed, extract it into a custom hook named for its purpose — `useSyncedScroll`,
81
+ `useDocumentTitle`, `useHotkey` — never an anonymous `useEffect` block inline in a component. The name documents intent, the
82
+ hook isolates the dependency array, and the component body stays declarative. Pattern reference:
83
+ [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks).
84
+
85
+ ## Reading list
86
+
87
+ Consult while building; each is the authority for its topic:
88
+
89
+ - [Thinking in React](https://react.dev/learn/thinking-in-react) — decomposition and one-way data flow
90
+ - [Keeping Components Pure](https://react.dev/learn/keeping-components-pure) — why dumb components stay dumb
91
+ - [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) — shaping state without duplication
92
+ - [Sharing State Between Components](https://react.dev/learn/sharing-state-between-components) — lifting state
93
+ - [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect) — the effect misuse catalog
94
+ - [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) — named effects live here
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: imf-web-ui-imfusion-frontend-setup
3
+ description:
4
+ "Set up or audit an ImFusion frontend's project tooling: the stack, package.json scripts, formatting, linting, typecheck,
5
+ staged-file and pre-commit hooks, verification scopes, dependency pinning, tsconfig, folder structure. House conventions,
6
+ not industry standards. Load when starting a new ImFusion frontend, or when asked what an existing one's setup is missing.
7
+ Not for adding one config file on request — that's just the edit. Not for wiring the library itself (imf-web-ui-setup)."
8
+ ---
9
+
10
+ # imf-web-ui-imfusion-frontend-setup
11
+
12
+ ImFusion house conventions, **not** industry standards. Report as "missing against the ImFusion baseline", never "against
13
+ best practice". Only apply to an ImFusion frontend.
14
+
15
+ Two modes, same list:
16
+
17
+ - **New project** — work down the list and set each piece up.
18
+ - **Existing project** — audit. Read the repo (don't ask what it has), report present / missing / broken, change nothing
19
+ until the human picks. An established repo is where a forgotten piece hides.
20
+
21
+ **The project wins.** Where the repo already decided, that stands. Report what's _absent_; a working convention you'd have
22
+ chosen differently is not a finding.
23
+
24
+ ## The stack
25
+
26
+ | Concern | Tool | Notes |
27
+ | ------------ | --------------------------------------------------------- | ----------------------------------------------------------------------------------- |
28
+ | Format | [Prettier](https://prettier.io) | Values below are shared across repos |
29
+ | Lint | [ESLint](https://eslint.org) flat config | `--cache --max-warnings=0` |
30
+ | Types | `tsc --noEmit` | Own script, own CI step |
31
+ | Staged files | [lint-staged](https://github.com/lint-staged/lint-staged) | [nano-staged](https://github.com/usmanyunusov/nano-staged) is a drop-in alternative |
32
+ | Routing | [TanStack Router](https://tanstack.com/router) | File-based, `src/routes/` |
33
+ | Server state | [TanStack Query](https://tanstack.com/query) | Query keys colocated per API topic |
34
+ | Client state | [TanStack Store](https://tanstack.com/store) | Only for state that isn't URL- or server-owned |
35
+ | Styling | CSS Modules | Colocated `<component>.module.css` |
36
+ | Dead code | [knip](https://knipjs.dev) | Needs per-repo config |
37
+ | Build | [Vite](https://vite.dev) | |
38
+ | Test | [Vitest](https://vitest.dev) | |
39
+
40
+ Either staged-file runner is fine; lint-staged is the larger project and the safer default when one misbehaves.
41
+
42
+ ### Devtools come with the library
43
+
44
+ Every TanStack library that ships a devtools package gets it as a dev dependency alongside the library itself, mounted in
45
+ development only. Router is a given in any ImFusion frontend, so `@tanstack/react-router-devtools` is a given too; Query's
46
+ goes in when Query does, and so on. Look for a `-devtools` sibling whenever you add a TanStack dependency rather than working
47
+ from a fixed list — the set grows, and not every library has one yet (Store doesn't). Once a project has several,
48
+ `@tanstack/devtools` hosts them in one panel.
49
+
50
+ ## package.json
51
+
52
+ `"type": "module"`, `"private": true`. Scripts — these names, in every repo:
53
+
54
+ | Script | Runs |
55
+ | ------------------ | -------------------------------------------------------- |
56
+ | `dev` | dev server |
57
+ | `build` | production build |
58
+ | `verify:lint` | `eslint . --cache --max-warnings=0` |
59
+ | `verify:format` | `prettier --check .` |
60
+ | `verify:typecheck` | `tsc --noEmit` |
61
+ | `verify:tests` | `vitest run` |
62
+ | `verify:staged` | staged-file subset, called by the pre-commit hook |
63
+ | `verify:full` | every check above plus the build; what CI runs |
64
+ | `format` | `prettier --write .` |
65
+ | `lint:fix` | `eslint . --cache --fix` |
66
+ | `git:config` | `git config core.hooksPath …` + `pull.rebase`/`merge.ff` |
67
+
68
+ **Every check is `verify:*`.** One namespace, so "what can I run to check this?" is answered by tab-completion. Write-mode
69
+ scripts keep tool names — `format` and `lint:fix` change files, which isn't verifying. Same name, same meaning, every repo.
70
+
71
+ **Git config is an explicit command.** `git:config` holds the real command, greppable and run by hand after cloning, and the
72
+ README's setup steps name it. `ignore-scripts=true` disables the root package's own lifecycle scripts alongside its
73
+ dependencies', so setup that matters is a command someone runs, not a hook that fires on install.
74
+
75
+ **Dependencies pinned exactly.** No `^`, `~`, or `latest`, in `dependencies` and `devDependencies` alike. A check script in
76
+ the verify chain enforces it, but that only catches drift after it lands — `save-exact=true` in `.npmrc` stops `npm install`
77
+ reintroducing ranges in the first place.
78
+
79
+ ## Config
80
+
81
+ **Prettier** — config file shape is free (`.prettierrc`, `prettier.config.ts`); the values are not:
82
+
83
+ ```
84
+ printWidth: 125 tabWidth: 2 useTabs: false trailingComma: "none"
85
+ arrowParens: "avoid" semi: true singleQuote: false proseWrap: "always"
86
+ ```
87
+
88
+ No config file at all means Prettier runs on defaults — flag it, the values silently differ.
89
+
90
+ **ESLint** — flat config (`eslint.config.ts`), `strictTypeChecked` + `stylisticTypeChecked` with `projectService: true`, `as`
91
+ and `!` banned outside tests, and `.gitignore` as the ignore source (`includeIgnoreFile` from `@eslint/compat`) so ignores
92
+ aren't maintained twice.
93
+
94
+ **tsconfig** — defaults:
95
+
96
+ ```jsonc
97
+ {
98
+ "compilerOptions": {
99
+ "strict": true,
100
+ "moduleResolution": "bundler",
101
+ "verbatimModuleSyntax": true, // import type stays import type
102
+ "noUnusedLocals": true,
103
+ "noUnusedParameters": true,
104
+ "noFallthroughCasesInSwitch": true,
105
+ "noUncheckedSideEffectImports": true,
106
+ "skipLibCheck": true,
107
+ "paths": { "#/*": ["./src/*"] }
108
+ }
109
+ }
110
+ ```
111
+
112
+ The alias is always `#/` → `src/`. `#` is Node's own subpath-import prefix, so it resolves without a bundler-specific
113
+ convention, and it can't collide with an npm scope the way `@/` does.
114
+
115
+ **Staged files** — runner config applying eslint `--fix` and prettier `--write` to staged files only.
116
+
117
+ **Pre-commit** — the hook installs itself via `prepare` → `git:config`, which sets `core.hooksPath` to a tracked directory
118
+ plus `pull.rebase true` and `merge.ff only`, so history strategy doesn't depend on personal git config. Two silent failure
119
+ modes: no `prepare` at all (hooks exist only on the machine that ran `git config` by hand), and `core.hooksPath` pointing at
120
+ a directory that doesn't exist. Check config **and** directory.
121
+
122
+ **Verify scopes** — two blocking, one advisory:
123
+
124
+ - **staged** — `verify:staged`, called by the pre-commit hook: lint, format, restage. Fast. A passing commit is not CI green.
125
+ - **full** — `verify:full`: the build plus every `verify:*` check. What CI runs.
126
+ - **files** — optional post-edit agent hook. Advisory, never exits non-zero, so a mid-flight refactor can't trap the agent.
127
+
128
+ One script owns each scope's step list; npm scripts and hooks only launch them. Name by depth, not by occasion — a name like
129
+ `preflight` needs explaining and invites a second, near-identical script beside it. Two of those drift, and the drift reads
130
+ as "passes locally, fails in CI".
131
+
132
+ **Node pinning** — `.nvmrc` or `engines.node`. Not a personal version manager's config; that pins it for you alone.
133
+
134
+ ## Folder structure
135
+
136
+ ```
137
+ src/
138
+ routes/ # TanStack Router file-based routes; nothing but routing
139
+ api/<topic>/ # one folder per API topic
140
+ <topic>.ts # queries/mutations
141
+ query-key.ts # key factory
142
+ types.ts # request/response types
143
+ components/ # see below
144
+ http/ # client, error normalisation — the only transport-aware place
145
+ lib/ # framework-free helpers
146
+ ```
147
+
148
+ Everything is kebab-case, folders and files alike.
149
+
150
+ **Inside `components/`, group by what kind of component it is** — `layouts/`, `primitives/`, `forms/`, or a domain name. Not
151
+ a hard rule: a handful of components reads fine flat, and the grouping should follow what the project actually has rather
152
+ than a structure imposed up front. But most codebases grow past flat, and a clear layout component belongs under `layouts/`
153
+ rather than beside a domain widget.
154
+
155
+ ```
156
+ components/
157
+ page-header/ # flat is fine
158
+ layouts/
159
+ page-shell/
160
+ data-table/ # a component with sub-component files
161
+ data-table.tsx
162
+ data-table-row.tsx
163
+ data-table.module.css
164
+ index.ts
165
+ ```
166
+
167
+ A component gets a folder when it has more than one file — sub-components, styles, tests. Single-file components can stay
168
+ single files. The folder's `index.ts` only re-exports, so imports read `#/components/data-table` and the inside can be
169
+ restructured without touching call sites.
170
+
171
+ Routes stay thin: they compose, they don't fetch inline. `api/<topic>/` holds the query and its key factory together so a key
172
+ is never spelled out at a call site. Anything transport-level (base client, error normalisation) lives in `http/` and nowhere
173
+ else.
174
+
175
+ ## Styling
176
+
177
+ CSS Modules by default, colocated as `<component>.module.css` next to the component. No CSS-in-JS, no utility-class
178
+ framework.
179
+
180
+ On a project using `@imfusion/web-ui`, style through the sanctioned seams — `--imf-ui-*` tokens and `data-imf-ui-component`
181
+ attributes, never the library's generated class names. `imf-web-ui-frontend-patterns` covers that contract.
182
+
183
+ If more than one tool compiles the CSS (app build plus Storybook), the generated class-name pattern must be defined **once**
184
+ and imported by both, or the same source file gets different class names in each and styles silently don't apply.
185
+ `build/css-modules-config.ts` in web-ui is the reference shape; pick your own prefix.
186
+
187
+ ## Optional
188
+
189
+ Recommend when the shape calls for it; absence is not a finding.
190
+
191
+ - **knip** — once several people delete things independently.
192
+ - **`ignore-scripts=true` in `.npmrc`** — blocks most supply-chain worm payloads; costs an explicit `npm rebuild` for native
193
+ deps.
194
+ - **`eslint-plugin-jsx-a11y`** — anything user-facing.
195
+
196
+ Out of scope, project-specific: CI, env and secrets, error tracking, deploy, dependency updates.
197
+
198
+ ## Not this skill
199
+
200
+ - Library wiring (styles import, provider) → `imf-web-ui-setup`
201
+ - Code conventions (TypeScript, naming, testing) → `imf-web-ui-frontend-patterns`, `references/code-conventions.md`
@@ -1,87 +1,57 @@
1
1
  ---
2
2
  name: imf-web-ui-setup
3
3
  description:
4
- "Plan and, after explicit approval, bootstrap an ImFusion frontend against the conventions baseline: library setup,
5
- tooling, git, npm project, authentication, project structure, docs structure, data, testing, or agent tooling. Inspect
6
- statically first and write only the approved files. Use imf-web-ui-audit for read-only health checks or topics with no
7
- setup action."
8
- argument-hint: "[full|library-setup|tooling|git|npm-project|authentication|project-structure|docs-structure|data|testing|agent-tooling]"
9
- allowed-tools: Read Glob Grep Write
4
+ "One-time wiring of a consumer project: the @imfusion/web-ui styles import and WebUIProvider wrapper. Also covers what to
5
+ say about the Agent Skills a dependency ships. Load when installing the library for the first time, or when components
6
+ render unstyled or without theme context."
10
7
  ---
11
8
 
12
9
  # imf-web-ui-setup
13
10
 
14
- You are the frontend bootstrap planner. Inspect the repository statically, read the applicable `imf-web-ui-conventions`
15
- topics, and prepare a complete proposal for every file you would create or change. The project wins where it already has a
16
- working convention. This skill is plan-first: enter the host's plan mode before presenting the proposal, and do not create a
17
- report file as a substitute for the host plan.
11
+ This is library wiring: the styles import and the provider. It applies to anyone using `@imfusion/web-ui`.
18
12
 
19
- ## Workflow
13
+ If the project is an **ImFusion** frontend and this is first-time setup, mention once that
14
+ `imf-web-ui-imfusion-frontend-setup` sets up or audits the repo's tooling (formatting, linting, hooks, scripts) against the
15
+ ImFusion baseline, and let the human decide. Offer it; never run it uninvited, and don't raise it again if they pass — the
16
+ library works fine without any of it.
20
17
 
21
- 1. Resolve the argument, not the prose. Bare means `full`; a topic argument selects one supported setup topic. A prompt that
22
- merely _mentions_ one area ("wire up the tooling", "get the project set up") is still a `full` run — the words describe a
23
- starting point, not a scope that excludes the rest. For a greenfield `full` run the application boundary (router, route
24
- groups, current-user query, AppShell) is part of the deliverable even when the prompt never names it; propose it and let
25
- the human remove it, never silently defer it as "out of scope." For an unsupported topic argument, list the available
26
- setup topics and point the human to `imf-web-ui-audit` for a read-only report.
27
- 2. Enter the host's plan mode. If it is not already active, use the host plan-mode control before inspecting and planning.
28
- 3. Read the selected convention topics and inspect the repository with `Read`, `Glob`, and `Grep` only.
29
- 4. Build the complete proposal in the host plan from the shared report contract at
30
- [`../imf-web-ui-conventions/templates/REPORT.md`](../imf-web-ui-conventions/templates/REPORT.md). Include the concrete
31
- content of every file the approved setup would create or change, with repository evidence for each decision.
32
- 5. A greenfield `full` setup proposes the whole starter skeleton, not just tooling — tooling without the application boundary
33
- is an unfinished setup. Include, as concrete files in the plan:
34
- - the TanStack Router **package added to `package.json`** (with its router devtools as a dev dependency), not just
35
- imported — code that imports an uninstalled package is an incomplete setup
36
- ([project-structure.md](../imf-web-ui-conventions/topics/project-structure.md));
37
- - pathless `_public/` and `_app/` route groups behind one server-backed current-user boundary
38
- ([authentication.md](../imf-web-ui-conventions/topics/authentication.md));
39
- - the minimal branded `AppShell` in `_app/` by default — omit the shell only when the human explicitly says the product
40
- has no persistent authenticated navigation (ask first), but keep the `_app/` boundary either way;
41
- - a per-topic `api/` folder for any data the first screen shows, never an inline fixture in the component
42
- ([data.md](../imf-web-ui-conventions/topics/data.md)).
18
+ Every consumer entry point needs exactly two lines, in this order:
43
19
 
44
- The plan documents this starter shape as concrete file content; it does not copy a full external starter template.
45
- Existing projects keep their working choices — propose only the gaps.
20
+ ```tsx
21
+ import "@imfusion/web-ui/styles.css";
22
+ import { WebUIProvider, Button } from "@imfusion/web-ui";
23
+ ```
46
24
 
47
- 6. Keep the plan as the approval gate. After the host approves and exits plan mode, write only the listed files. Complete any
48
- immediately requested bootstrap work covered by that approved plan, then invoke `imf-web-ui-audit full` as a verification
49
- step and review the findings it reports back before declaring the setup complete. The audit stays out of plan mode here —
50
- the human has just left it to let this work happen.
25
+ Wrap the app root in `<WebUIProvider>` once. Components rendered outside it won't have the theme/CSS-variable context they
26
+ expect.
51
27
 
52
- Installer and hook runs stay with the human: an `agent-tooling` proposal lists the `npx web-ui-install` commands as human
53
- steps, applies the judgment from the topic (read existing registrations first, never stack a hook on a covered event), and
54
- includes the topic's Codex trust follow-up whenever hooks are part of the plan.
28
+ Never import a Base UI (or other upstream) stylesheet or component directly — everything a web-ui component needs is already
29
+ inside `styles.css` and the package's own exports; reaching around web-ui to the upstream library is always wrong, even if
30
+ the upstream docs show it that way.
55
31
 
56
- ## Checklist
32
+ ## Dependency-shipped skills
57
33
 
58
- The post-implementation audit uses the shared
59
- [convention audit checklist](../imf-web-ui-conventions/templates/AUDIT_CHECKLIST.md) to verify the complete baseline, not
60
- only the setup topic selected at the start.
34
+ Some libraries ship Agent Skills inside their npm package; TanStack does across much of the suite.
35
+ [`@tanstack/intent`](https://github.com/TanStack/intent) is the CLI that surfaces them — an agent holding a dependency but
36
+ not its guidance writes plausible code against a half-remembered API.
61
37
 
62
- ## Safety
38
+ Setting it up is the project's own call, not something web-ui does on its behalf. Point it out:
63
39
 
64
- Use only static inspection: `Read`, `Glob`, and `Grep`. Do not use a shell or invoke Node, npm, npx, package scripts, hooks,
65
- config imports, linters, tests, builds, Git commands, project binaries, installers, or package managers. Read config as text
66
- and report runtime or machine-local state that cannot be established statically as unverified. After approval, `Write` is
67
- limited to the files listed in the approved report; anything needing execution goes in the plan for the human.
40
+ > This project has TanStack dependencies that ship their own Agent Skills. `@tanstack/intent` can make them reachable — worth
41
+ > a look if you want your agent working from the library's own guidance.
68
42
 
69
- ## Setup topics
43
+ Intent offers two things: a fenced instructions block in `AGENTS.md`, and a `PreToolUse` hook that blocks an edit until a
44
+ matching skill has been read. The house preference is both — the block alone is advice an agent can walk past. The hook
45
+ refuses every edit while no matching skill is loadable, so a project adopting it wants the current docs open; that sequencing
46
+ belongs to whoever runs it.
70
47
 
71
- `full` covers every topic below. A one-topic run assesses only that topic.
48
+ Whatever the project decides, guidance you didn't read is not guidance you have: use `npx @tanstack/cli` for TanStack docs,
49
+ and never guess at a skill name.
72
50
 
73
- | Topic | Assess and plan |
74
- | ------------------- | ------------------------------------------------------------------------------ |
75
- | `library-setup` | styles import, `WebUIProvider`, and library package wiring |
76
- | `tooling` | dependency selection, devtools, Prettier, ESLint, TypeScript, and verification |
77
- | `git` | tracked hooks, verification scopes, and staleness wiring |
78
- | `npm-project` | package metadata, scripts, pins, npm, and Node configuration |
79
- | `authentication` | current-user query, public/app guards, login, and logout |
80
- | `project-structure` | source tree, route groups, optional app shell, naming, and placement |
81
- | `docs-structure` | README, AGENTS, docs index, and content boundaries |
82
- | `data` | transport, schemas, query/mutation options, keys, and invalidation |
83
- | `testing` | test boundaries and verification coverage |
84
- | `agent-tooling` | vendored skills, AGENTS fence, lifecycle hooks, registrations, and staleness |
51
+ ## Symptoms of a broken setup
85
52
 
86
- Setup has no file-creation action for `react`, `typescript`, `class-names`, `validation`, `components`, `styling`, `assets`,
87
- `library-boundary`, or `tokens`; select those in `imf-web-ui-audit`.
53
+ - **Components render but look unstyled** — the `styles.css` import is missing from the entry point.
54
+ - **Components render but ignore the theme (wrong colors, no CSS variables resolving)** — they're mounted outside
55
+ `<WebUIProvider>`.
56
+ - **An integration component throws on import** — its optional peer dependency isn't installed; check the component's
57
+ description in the docgen index (`imf-web-ui-components`) for which peer to add to your `package.json`.
@@ -11,7 +11,7 @@ description:
11
11
  Most teams consuming `@imfusion/web-ui` don't have a designer on call. This skill stands in: it encodes the library authors'
12
12
  UX experience — the whole experience of a screen, its visual design, and the usability where both meet. Follow it by default;
13
13
  deviate when the product has a real reason to. Code-level patterns (tokens, layers, wrappers) live in
14
- `imf-web-ui-conventions`; project wiring lives in the `library-setup` topic of `imf-web-ui-conventions`.
14
+ `imf-web-ui-frontend-patterns`; project wiring lives in `imf-web-ui-setup`.
15
15
 
16
16
  Component names below are real — verify any API against the docgen index (`imf-web-ui-components`) before use. Never invent a
17
17
  component this library doesn't ship.
@@ -82,13 +82,13 @@ Every screen ships four states, not one:
82
82
 
83
83
  Custom UI the library doesn't cover should be indistinguishable from library UI: build it from `--imf-ui-*` tokens and
84
84
  compose it with library primitives. The goal lives here; the mechanics (tokens, layers, wrappers) live in
85
- `imf-web-ui-conventions`.
85
+ `imf-web-ui-frontend-patterns`.
86
86
 
87
87
  ## Experimental components
88
88
 
89
89
  The identity index marks each component `stable` or `experimental`. Experimental ones are fine to use, but expect API
90
- movement across releases — prefer wrapping them once (see `imf-web-ui-conventions`) so a breaking change lands in one file,
91
- not forty call sites.
90
+ movement across releases — prefer wrapping them once (see `imf-web-ui-frontend-patterns`) so a breaking change lands in one
91
+ file, not forty call sites.
92
92
 
93
93
  ## Deep dives
94
94
 
@@ -8,8 +8,8 @@ non-compliant ones. Read before building any form beyond two fields.
8
8
 
9
9
  The library ships the controls (`Input`, `Select`, `Checkbox`, `Switch`, `Slider`) but no form or field wrapper, so labels,
10
10
  grouping, and where errors appear are composed by you. That's exactly where these rules apply. Composing the markup is not
11
- the same as owning the state: form state and validation belong to a form library (see `imf-web-ui-conventions`), and these
12
- rules govern how its errors get presented.
11
+ the same as owning the state: form state and validation belong to a form library (see `imf-web-ui-frontend-patterns`), and
12
+ these rules govern how its errors get presented.
13
13
 
14
14
  ## Structure
15
15