@imfusion/web-ui 0.5.1-dev.2.gf11bbed1 → 0.5.1-dev.21.g2accd17d
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/README.md +120 -32
- package/bin/install.js +343 -0
- package/bin/install.test.ts +166 -0
- package/dist/build/vite-css-module-names/index.d.ts +20 -0
- package/dist/build/vite-css-module-names.js +17 -0
- package/dist/components/logo/logo.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/style.css +1 -1
- package/package.json +34 -24
- package/src/docgen/doc.gen.json +1 -1
- package/src/llms/skills/imf-web-ui/SKILL.md +15 -11
- package/src/llms/skills/imf-web-ui-agent-setup/SKILL.md +71 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/baseline-staleness.sh +17 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/post-tool-use.sh +21 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/session-start.sh +4 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/hooks/user-prompt-submit.sh +4 -0
- package/src/llms/skills/imf-web-ui-agent-setup/templates/settings.json +37 -0
- package/src/llms/skills/imf-web-ui-components/SKILL.md +1 -1
- package/src/llms/skills/imf-web-ui-frontend-conventions/SKILL.md +45 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/assets.md +23 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/class-names.md +42 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/components.md +63 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/data.md +196 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/docs-structure.md +44 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/git.md +33 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/library-boundary.md +37 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/npm-project.md +57 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/project-structure.md +21 -0
- package/src/llms/skills/{imf-web-ui-frontend-patterns/references/react-patterns.md → imf-web-ui-frontend-conventions/references/react.md} +26 -12
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/stack.md +39 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/styling.md +91 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/testing.md +16 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/tooling.md +76 -0
- package/src/llms/skills/imf-web-ui-frontend-conventions/references/typescript.md +45 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/SKILL.md +89 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/templates/AGENTS.md +34 -0
- package/src/llms/skills/imf-web-ui-frontend-setup/templates/README.md +27 -0
- package/src/llms/skills/imf-web-ui-library-setup/SKILL.md +36 -0
- package/src/llms/skills/imf-web-ui-update/SKILL.md +119 -0
- package/src/llms/skills/imf-web-ui-ux/SKILL.md +4 -4
- package/src/llms/skills/imf-web-ui-ux/references/forms.md +1 -1
- package/bin/install-skill.js +0 -180
- package/src/llms/skills/imf-web-ui-frontend-patterns/SKILL.md +0 -93
- package/src/llms/skills/imf-web-ui-frontend-patterns/references/code-conventions.md +0 -133
- package/src/llms/skills/imf-web-ui-imfusion-frontend-setup/SKILL.md +0 -201
- package/src/llms/skills/imf-web-ui-setup/SKILL.md +0 -57
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: imf-web-ui-frontend-patterns
|
|
3
|
-
description:
|
|
4
|
-
"Raise the quality of frontend code written around @imfusion/web-ui — including quickly vibe-coded frontends. Library
|
|
5
|
-
boundary contract (tokens, CSS layers, type derivation), component roles, state placement, effects discipline, code
|
|
6
|
-
conventions (TypeScript, naming, file organisation, testing), and stack defaults. Load when writing wrapper components,
|
|
7
|
-
custom UI, styling beyond the defaults, adding new files to a consumer app, or choosing a routing, data-fetching, form, or
|
|
8
|
-
table library."
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# imf-web-ui-frontend-patterns
|
|
12
|
-
|
|
13
|
-
One guard, once: **if the host project already has a convention — a styling system, a state library, a folder shape — the
|
|
14
|
-
project wins.** These defaults fill vacuums. They are not a license to refactor a consumer codebase toward this document.
|
|
15
|
-
|
|
16
|
-
Everything else below is how to build.
|
|
17
|
-
|
|
18
|
-
## Stay behind the library
|
|
19
|
-
|
|
20
|
-
Never import Base UI (or any other upstream this library wraps) directly — no upstream stylesheets, no upstream components,
|
|
21
|
-
even when upstream docs show it that way. Everything a component needs ships in `@imfusion/web-ui`. If the library is missing
|
|
22
|
-
something upstream has, report the gap (see `imf-web-ui-components`); don't reach around it.
|
|
23
|
-
|
|
24
|
-
## Style through the sanctioned seams
|
|
25
|
-
|
|
26
|
-
All library styles live in the `imf-ui.components` CSS layer, so **any plain selector you write wins** — that's the whole
|
|
27
|
-
override contract:
|
|
28
|
-
|
|
29
|
-
- Target the stable hooks: `data-imf-ui-component` attributes and your own classes/wrappers.
|
|
30
|
-
- Never target the library's internal class names — they are generated and change without notice.
|
|
31
|
-
- Never `!important` — if you think you need it, you're targeting the wrong thing.
|
|
32
|
-
|
|
33
|
-
## Build custom UI from tokens
|
|
34
|
-
|
|
35
|
-
Anything you build that the library doesn't cover — a stat widget, a custom panel — uses `--imf-ui-*` variables for color,
|
|
36
|
-
spacing, radius, and type instead of hardcoded values. That's what makes custom UI look native next to library components,
|
|
37
|
-
and what keeps it correct when the theme changes. A hex code or a magic `px` next to a concept the tokens already name is a
|
|
38
|
-
defect.
|
|
39
|
-
|
|
40
|
-
## Derive types, don't import them
|
|
41
|
-
|
|
42
|
-
Prop types come from the components themselves: `React.ComponentProps<typeof Button>`. The library deliberately exports no
|
|
43
|
-
`Props` types — don't look for them, and don't re-declare prop shapes by hand.
|
|
44
|
-
|
|
45
|
-
## Integrations own their peers
|
|
46
|
-
|
|
47
|
-
Components under `@imfusion/web-ui/integrations/*` depend on optional peers (e.g. `@tanstack/highlight` for `CodeHighlight`).
|
|
48
|
-
Add the peer explicitly to the consumer's `package.json` — never rely on hoisting.
|
|
49
|
-
|
|
50
|
-
## React patterns
|
|
51
|
-
|
|
52
|
-
The full treatment — component roles with an example, state placement, effects discipline, and the react.dev sources to
|
|
53
|
-
consult while building — lives in [references/react-patterns.md](references/react-patterns.md). Read it before writing new
|
|
54
|
-
screens or wrappers. The core in one breath:
|
|
55
|
-
|
|
56
|
-
- **Three roles.** Dumb components own how things look, layout components own arrangement, smart containers own data and
|
|
57
|
-
logic. Styling never lives in containers.
|
|
58
|
-
- **State lives where its truth lives.** URL → query cache → context → store → local state; walk the list, stop at the first
|
|
59
|
-
match.
|
|
60
|
-
- **Effects are a last resort**, and always extracted into purpose-named hooks.
|
|
61
|
-
- **Compose, don't configure.** If a component's prop list reads like a settings page, it wanted to be two or three
|
|
62
|
-
components.
|
|
63
|
-
|
|
64
|
-
## Everything else about the code
|
|
65
|
-
|
|
66
|
-
TypeScript, naming, where files go, and what's worth testing live in
|
|
67
|
-
[references/code-conventions.md](references/code-conventions.md). Read it when you're adding files rather than editing
|
|
68
|
-
existing ones — that's when these choices get made and then inherited by everything after.
|
|
69
|
-
|
|
70
|
-
The split between the two references: `react-patterns.md` covers **writing React** — component roles, where state lives,
|
|
71
|
-
effects discipline, composition. `code-conventions.md` covers **the code around it** — TypeScript, JS style, naming, file
|
|
72
|
-
layout, testing. Starting a new feature usually wants both.
|
|
73
|
-
|
|
74
|
-
## Stack defaults
|
|
75
|
-
|
|
76
|
-
TanStack is the default for the tooling around web-ui, whether the app is greenfield or you're adding one screen to something
|
|
77
|
-
that already exists. The ones you'll reach for most: **Router** (URL state, type-safe search params), **Query** (server
|
|
78
|
-
state), **Form** (form state), and **Table** for data grids, which you pair with web-ui's styled `Table` parts
|
|
79
|
-
(`Table.SortableHeaderCell` carries the sort glue). The suite goes wider than those four — check what exists before adding a
|
|
80
|
-
non-TanStack dependency. This is the stack the state ladder assumes.
|
|
81
|
-
|
|
82
|
-
`useState` is the right tool for local UI state, and most of it is local: whether a panel is open, which tab is active, a
|
|
83
|
-
draft value being typed, a hover flag. Keep those in the component and don't reach for a library.
|
|
84
|
-
|
|
85
|
-
The line is what the state is _for_, not how much of it there is. A library owns the layer once you find yourself rebuilding
|
|
86
|
-
what it does: validation timing and cross-field rules (**Form**), caching and refetching (**Query**), URL as the source of
|
|
87
|
-
truth (**Router**), sorting and pagination over rows (**Table**). web-ui ships none of that logic, and that absence is not an
|
|
88
|
-
argument for writing it yourself. Adding the library mid-project is normal and cheap; unpicking a hand-rolled version of it
|
|
89
|
-
later is not.
|
|
90
|
-
|
|
91
|
-
For best practices and patterns within any of these libraries, go to the library's own guidance rather than working from
|
|
92
|
-
memory: `npx @tanstack/cli` for docs. Where a project has wired up `@tanstack/intent`, use it to reach the Agent Skills its
|
|
93
|
-
TanStack dependencies ship, and read those too.
|
|
@@ -1,133 +0,0 @@
|
|
|
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.
|
|
@@ -1,201 +0,0 @@
|
|
|
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,57 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: imf-web-ui-setup
|
|
3
|
-
description:
|
|
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."
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# imf-web-ui-setup
|
|
10
|
-
|
|
11
|
-
This is library wiring: the styles import and the provider. It applies to anyone using `@imfusion/web-ui`.
|
|
12
|
-
|
|
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.
|
|
17
|
-
|
|
18
|
-
Every consumer entry point needs exactly two lines, in this order:
|
|
19
|
-
|
|
20
|
-
```tsx
|
|
21
|
-
import "@imfusion/web-ui/styles.css";
|
|
22
|
-
import { WebUIProvider, Button } from "@imfusion/web-ui";
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Wrap the app root in `<WebUIProvider>` once. Components rendered outside it won't have the theme/CSS-variable context they
|
|
26
|
-
expect.
|
|
27
|
-
|
|
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.
|
|
31
|
-
|
|
32
|
-
## Dependency-shipped skills
|
|
33
|
-
|
|
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.
|
|
37
|
-
|
|
38
|
-
Setting it up is the project's own call, not something web-ui does on its behalf. Point it out:
|
|
39
|
-
|
|
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.
|
|
42
|
-
|
|
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.
|
|
47
|
-
|
|
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.
|
|
50
|
-
|
|
51
|
-
## Symptoms of a broken setup
|
|
52
|
-
|
|
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`.
|