uidex 0.2.1 → 0.3.0

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 (61) hide show
  1. package/README.md +263 -263
  2. package/dist/cli/cli.cjs +3243 -0
  3. package/dist/cli/cli.cjs.map +1 -0
  4. package/dist/cloud/index.cjs +149 -0
  5. package/dist/cloud/index.cjs.map +1 -0
  6. package/dist/cloud/index.d.cts +108 -0
  7. package/dist/cloud/index.d.ts +108 -0
  8. package/dist/cloud/index.js +120 -0
  9. package/dist/cloud/index.js.map +1 -0
  10. package/dist/headless/index.cjs +3580 -0
  11. package/dist/headless/index.cjs.map +1 -0
  12. package/dist/headless/index.d.cts +214 -0
  13. package/dist/headless/index.d.ts +214 -0
  14. package/dist/headless/index.js +3562 -0
  15. package/dist/headless/index.js.map +1 -0
  16. package/dist/index.cjs +7977 -3301
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +898 -108
  19. package/dist/index.d.ts +898 -108
  20. package/dist/index.js +7934 -3270
  21. package/dist/index.js.map +1 -1
  22. package/dist/playwright/index.cjs +164 -24
  23. package/dist/playwright/index.cjs.map +1 -1
  24. package/dist/playwright/index.d.cts +32 -55
  25. package/dist/playwright/index.d.ts +32 -55
  26. package/dist/playwright/index.js +148 -21
  27. package/dist/playwright/index.js.map +1 -1
  28. package/dist/playwright/reporter.cjs +62 -28
  29. package/dist/playwright/reporter.cjs.map +1 -1
  30. package/dist/playwright/reporter.d.cts +24 -12
  31. package/dist/playwright/reporter.d.ts +24 -12
  32. package/dist/playwright/reporter.js +62 -28
  33. package/dist/playwright/reporter.js.map +1 -1
  34. package/dist/react/index.cjs +7970 -3267
  35. package/dist/react/index.cjs.map +1 -1
  36. package/dist/react/index.d.cts +670 -108
  37. package/dist/react/index.d.ts +670 -108
  38. package/dist/react/index.js +8016 -3274
  39. package/dist/react/index.js.map +1 -1
  40. package/dist/scan/index.cjs +3281 -0
  41. package/dist/scan/index.cjs.map +1 -0
  42. package/dist/scan/index.d.cts +373 -0
  43. package/dist/scan/index.d.ts +373 -0
  44. package/dist/scan/index.js +3224 -0
  45. package/dist/scan/index.js.map +1 -0
  46. package/package.json +74 -56
  47. package/templates/claude/audit.md +37 -0
  48. package/templates/claude/rules.md +212 -0
  49. package/claude/audit-command.md +0 -16
  50. package/claude/rules.md +0 -88
  51. package/dist/core/index.cjs +0 -3490
  52. package/dist/core/index.cjs.map +0 -1
  53. package/dist/core/index.d.cts +0 -441
  54. package/dist/core/index.d.ts +0 -441
  55. package/dist/core/index.global.js +0 -3469
  56. package/dist/core/index.global.js.map +0 -1
  57. package/dist/core/index.js +0 -3444
  58. package/dist/core/index.js.map +0 -1
  59. package/dist/core/style.css +0 -971
  60. package/dist/scripts/cli.cjs +0 -1168
  61. package/uidex.schema.json +0 -93
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "uidex",
3
- "version": "0.2.1",
4
- "description": "A framework-agnostic library for highlighting and annotating UI elements",
3
+ "version": "0.3.0",
4
+ "description": "Convention-driven UI element registry and devtools surface for React apps.",
5
5
  "type": "module",
6
- "main": "./dist/index.cjs",
6
+ "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
@@ -12,17 +12,26 @@
12
12
  "import": "./dist/index.js",
13
13
  "require": "./dist/index.cjs"
14
14
  },
15
- "./core": {
16
- "types": "./dist/core/index.d.ts",
17
- "import": "./dist/core/index.js",
18
- "require": "./dist/core/index.cjs"
19
- },
20
- "./core/style.css": "./dist/core/style.css",
21
15
  "./react": {
22
16
  "types": "./dist/react/index.d.ts",
23
17
  "import": "./dist/react/index.js",
24
18
  "require": "./dist/react/index.cjs"
25
19
  },
20
+ "./cloud": {
21
+ "types": "./dist/cloud/index.d.ts",
22
+ "import": "./dist/cloud/index.js",
23
+ "require": "./dist/cloud/index.cjs"
24
+ },
25
+ "./headless": {
26
+ "types": "./dist/headless/index.d.ts",
27
+ "import": "./dist/headless/index.js",
28
+ "require": "./dist/headless/index.cjs"
29
+ },
30
+ "./scan": {
31
+ "types": "./dist/scan/index.d.ts",
32
+ "import": "./dist/scan/index.js",
33
+ "require": "./dist/scan/index.cjs"
34
+ },
26
35
  "./playwright": {
27
36
  "types": "./dist/playwright/index.d.ts",
28
37
  "import": "./dist/playwright/index.js",
@@ -32,34 +41,21 @@
32
41
  "types": "./dist/playwright/reporter.d.ts",
33
42
  "import": "./dist/playwright/reporter.js",
34
43
  "require": "./dist/playwright/reporter.cjs"
35
- },
36
- "./styles.css": "./dist/core/style.css"
44
+ }
37
45
  },
38
- "sideEffects": [
39
- "*.css"
40
- ],
46
+ "sideEffects": false,
41
47
  "files": [
42
48
  "dist",
43
- "claude",
44
- "uidex.schema.json"
49
+ "templates"
45
50
  ],
46
51
  "bin": {
47
- "uidex": "dist/scripts/cli.cjs"
52
+ "uidex": "dist/cli/cli.cjs"
48
53
  },
49
- "scripts": {
50
- "dev": "tsup --watch",
51
- "build": "npm run generate:css && tsup && npm run build:css",
52
- "build:css": "cp src/core/style.css dist/core/style.css",
53
- "generate:css": "tsx scripts/generate-css-text.ts",
54
- "scan": "tsx scripts/cli.ts scan",
55
- "test": "vitest",
56
- "test:run": "vitest run",
57
- "test:coverage": "vitest run --coverage",
58
- "lint": "eslint src/",
59
- "format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
60
- "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"",
61
- "typecheck": "tsc --noEmit",
62
- "prepublishOnly": "npm run build && npm run test:run && npm run typecheck"
54
+ "engines": {
55
+ "node": ">=22"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
63
59
  },
64
60
  "peerDependencies": {
65
61
  "@playwright/test": ">=1.40",
@@ -67,54 +63,76 @@
67
63
  "react-dom": ">=18"
68
64
  },
69
65
  "peerDependenciesMeta": {
70
- "react": {
71
- "optional": true
72
- },
73
- "react-dom": {
74
- "optional": true
75
- },
76
66
  "@playwright/test": {
77
67
  "optional": true
78
68
  }
79
69
  },
70
+ "dependencies": {
71
+ "@clack/prompts": "^1.2.0",
72
+ "@zag-js/combobox": "^1.40.0",
73
+ "@zag-js/core": "^1.40.0",
74
+ "@zag-js/dialog": "^1.40.0",
75
+ "@zag-js/menu": "^1.40.0",
76
+ "@zag-js/popover": "^1.40.0",
77
+ "@zag-js/react": "^1.40.0",
78
+ "@zag-js/scroll-area": "^1.40.0",
79
+ "@zag-js/tabs": "^1.40.0",
80
+ "@zag-js/tooltip": "^1.40.0",
81
+ "@zag-js/types": "^1.40.0",
82
+ "@zag-js/utils": "^1.40.0",
83
+ "clsx": "^2.1.1",
84
+ "lucide": "^1.8.0",
85
+ "lucide-react": "^1.7.0",
86
+ "tailwind-merge": "^3.5.0",
87
+ "xstate": "^5.30.0",
88
+ "zod": "^4.3.6",
89
+ "zustand": "^5.0.2"
90
+ },
80
91
  "devDependencies": {
81
- "@eslint/js": "^9.39.2",
82
92
  "@playwright/test": "^1.58.2",
93
+ "@tailwindcss/cli": "^4.2.2",
83
94
  "@testing-library/jest-dom": "^6.6.3",
84
95
  "@testing-library/react": "^16.1.0",
85
96
  "@types/node": "^22.10.5",
86
- "@types/react": "^18.3.18",
87
- "@types/react-dom": "^18.3.5",
88
- "@typescript-eslint/eslint-plugin": "^8.21.0",
89
- "@typescript-eslint/parser": "^8.21.0",
97
+ "@types/react": "^19.2.14",
98
+ "@types/react-dom": "^19.2.3",
90
99
  "@vitejs/plugin-react": "^4.3.4",
91
- "@vitest/coverage-v8": "^2.1.8",
92
100
  "eslint": "^9.18.0",
93
- "eslint-plugin-react": "^7.37.4",
94
- "eslint-plugin-react-hooks": "^5.1.0",
95
101
  "jsdom": "^26.0.0",
96
- "prettier": "^3.4.2",
97
102
  "react": "^18.3.1",
98
103
  "react-dom": "^18.3.1",
104
+ "tailwindcss": "^4.2.2",
99
105
  "tsup": "^8.3.5",
100
106
  "tsx": "^4.7.0",
101
- "typescript": "^5.7.3",
102
- "typescript-eslint": "^8.54.0",
103
- "vitest": "^2.1.8"
107
+ "typescript": "^5.9.3",
108
+ "vitest": "^2.1.8",
109
+ "@uidex/eslint-config": "0.0.0",
110
+ "@uidex/tsconfig": "0.0.0"
104
111
  },
105
112
  "keywords": [
106
- "annotation",
107
- "highlight",
108
- "overlay",
109
- "component",
113
+ "uidex",
114
+ "devtools",
110
115
  "react",
111
- "vanilla-js",
112
- "framework-agnostic"
116
+ "playwright",
117
+ "feedback",
118
+ "scanner"
113
119
  ],
114
120
  "author": "soel",
115
121
  "license": "MIT",
116
122
  "repository": {
117
123
  "type": "git",
118
124
  "url": "https://github.com/soel/uidex"
125
+ },
126
+ "scripts": {
127
+ "dev": "tsup --watch",
128
+ "build": "pnpm run build:css && tsup && pnpm run check:bundles",
129
+ "build:css": "tailwindcss --input src/styles/tailwind.css --output src/styles/tailwind.built.css",
130
+ "check:bundles": "node scripts/check-bundles.mjs",
131
+ "test": "vitest run",
132
+ "test:watch": "vitest",
133
+ "lint": "eslint src/",
134
+ "typecheck": "tsc --noEmit",
135
+ "views-map": "tsx scripts/extract-views-map.ts",
136
+ "views-map:check": "tsx scripts/extract-views-map.ts --check"
119
137
  }
120
- }
138
+ }
@@ -0,0 +1,37 @@
1
+ Run `npx uidex scan --audit` and fix all reported diagnostics.
2
+
3
+ 1. **`marker-md-ignored`** — legacy `UIDEX_PAGE.md` / `UIDEX_FEATURE.md` files from v1. Migrate their content:
4
+ - Copy the description and acceptance list into a `@uidex page <id>` or `@uidex feature <id>` JSDoc block on the corresponding module (`page.tsx` for pages; the feature's main index/component for features).
5
+ - Each `- [ ]` checkbox becomes one `@acceptance <text>` tag.
6
+ - Delete the `UIDEX_PAGE.md` / `UIDEX_FEATURE.md` file.
7
+
8
+ 2. **`acceptance-uncovered`** — a declared `@acceptance` criterion has no flow exercising it.
9
+ - For widgets: run `npx uidex scaffold widget <id>` to generate a Playwright stub in `e2e/` with one `test()` per criterion; fill in the stubs.
10
+ - For features/pages: write a Playwright spec under `e2e/**` tagged `{ tag: "@uidex:flow" }` that exercises the criterion by calling `uidex('<element-id>').*()` on the relevant elements.
11
+
12
+ 3. **`unknown-reference`** — a flow's `uidex('<id>')` call references an id that isn't in the registry.
13
+ - If the id is a typo, fix the string literal in the spec.
14
+ - If the component was removed, remove the call.
15
+ - If the component lost its `data-uidex`, restore it.
16
+ - Dynamic ids (variables, template literals) are invisible to the scanner. If a literal is flagged, it is actually a literal in the source.
17
+
18
+ 4. **`scope-leak`** — a primitive scoped to `feature:<name>` or `page:<name>` is being imported from outside that scope.
19
+ - Either move the primitive to `src/components/ui/**` so it becomes global, or route the consumer through an appropriate boundary.
20
+
21
+ 5. **`missing-element-annotation`** (INFO) — an interactive `<button>`, `<a>`, `<input>`, `<select>`, or `<textarea>` has no `data-uidex`.
22
+ - Add `data-uidex="<kebab-id>"`. Skip this diagnostic only when the element is purely decorative (e.g., a skip link that never needs automated reference).
23
+
24
+ 6. **`acceptance-orphan`** — an `@acceptance` tag appears on a JSDoc that doesn't declare `@uidex page|feature|widget`.
25
+ - Move the acceptance tags into the correct JSDoc block, or remove them.
26
+
27
+ After fixing, run `npx uidex scan` to regenerate the gen file before re-running the audit.
28
+
29
+ ## Decision rules for primitives
30
+
31
+ Add `data-uidex-primitive="kebab-name"` ONLY when the component lives outside `src/components/ui/**` and meets all of:
32
+
33
+ - Exports a reusable presentational component (not a page, layout, or feature orchestrator).
34
+ - Wraps native elements or other primitives — no business logic or data fetching (`useQuery`, `useMutation`, etc.).
35
+ - Imported by multiple consumers.
36
+
37
+ If it's in `src/components/ui/**`, the scanner auto-promotes it — no attribute needed.
@@ -0,0 +1,212 @@
1
+ # uidex conventions
2
+
3
+ This project uses [uidex](https://github.com/soel/uidex) for UI element tracking and feedback ingestion.
4
+
5
+ ## Entity model
6
+
7
+ Eight entity kinds make up the registry. Most are discovered by convention; annotations only override.
8
+
9
+ | Kind | Source | How discovered |
10
+ | ----------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `element` | Interactive DOM element | `data-uidex="id"` (always manual) |
12
+ | `region` | Structural area | `data-uidex-region="id"` OR HTML5 landmark (`<header>`/`<nav>`/`<main>`/`<aside>`/`<footer>`/`role="region"`) |
13
+ | `widget` | Composite behavioural unit (video player, date picker, autocomplete) | `data-uidex-widget="id"` on the DOM root **AND** `export const uidex = { widget: "id", ... }` on the component module |
14
+ | `primitive` | Reusable presentational component definition | Any file under `src/components/ui/**` (auto-promoted); `data-uidex-primitive="name"` opts in a non-conventional location |
15
+ | `page` | Route-rendered top-level | Auto-derived from your framework's route detection (Next.js / Vite / TanStack Router); `export const uidex = { page: "id", ... }` overrides |
16
+ | `feature` | Cross-cutting capability | Any directory under `src/features/*`; `export const uidex = { feature: "id", ... }` overrides |
17
+ | `flow` | End-to-end user journey | Top-level `test.describe` in `e2e/**` (auto-promoted); `{ tag: "@uidex:flow" }` adds richer metadata; opt out with `export const uidex = { notFlow: true }` |
18
+ | `route` | URL → page mapping | Auto-derived from your framework's router |
19
+
20
+ ## DOM annotations
21
+
22
+ Four attributes. All use kebab-case ids. DOM attributes are the sole surface for multi-per-file kinds (elements, regions, widget roots).
23
+
24
+ ### `data-uidex` — every interactive element
25
+
26
+ ```tsx
27
+ <button data-uidex="save-btn">Save</button>
28
+ <input data-uidex="email-field" />
29
+ <a data-uidex="nav-home" href="/">Home</a>
30
+ ```
31
+
32
+ `uidex scan --audit` flags unannotated `<button>`, `<a>`, `<input>`, `<select>`, `<textarea>` as INFO diagnostics. Rename carefully — ids are referenced by flow tests.
33
+
34
+ ### `data-uidex-region` — structural regions (only when HTML5 doesn't suffice)
35
+
36
+ HTML5 landmarks (`<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>`, `role="region"`) auto-register as regions. Only add `data-uidex-region` on non-semantic `<div>` wrappers you want in the registry:
37
+
38
+ ```tsx
39
+ <div data-uidex-region="settings-form">
40
+ <button data-uidex="save-btn">Save</button>
41
+ </div>
42
+ ```
43
+
44
+ ### `data-uidex-widget` — composite behavioural units
45
+
46
+ Place on the root of a self-contained UI unit with internal state (video player, date picker, rich text editor). The DOM attribute is the runtime boundary; acceptance criteria live on the `export const uidex` at the component definition.
47
+
48
+ ```tsx
49
+ import type { Uidex } from "@/uidex.gen"
50
+
51
+ export const uidex = {
52
+ widget: "video-player",
53
+ acceptance: [
54
+ "Plays/pauses on Space",
55
+ "Scrubber updates as video plays",
56
+ "M toggles mute",
57
+ ],
58
+ } as const satisfies Uidex.Widget
59
+
60
+ export function VideoPlayer() {
61
+ return (
62
+ <div data-uidex-widget="video-player">
63
+ <button data-uidex="play">Play</button>
64
+ <input data-uidex="scrubber" type="range" />
65
+ </div>
66
+ )
67
+ }
68
+ ```
69
+
70
+ **Widget id duplication rule.** The id must appear in **both** places — on `data-uidex-widget="..."` at the DOM root (runtime boundary) **and** on `export const uidex = { widget: "..." }` (scan-time metadata). The scanner cross-validates: a mismatch or one-sided presence is an error.
71
+
72
+ Child `data-uidex` elements are automatically composed onto the widget (via DOM nesting at scan time).
73
+
74
+ ### `data-uidex-primitive` — reusable component definitions
75
+
76
+ Any `.tsx` under `src/components/ui/**` is auto-promoted to a primitive (name = kebab of filename). Add `data-uidex-primitive="name"` only to opt-in a reusable component that lives outside the convention directory.
77
+
78
+ ```tsx
79
+ // src/components/ui/button.tsx — auto-promoted as 'button', no attribute needed
80
+ export function Button({ children, ...props }) {
81
+ return <button {...props}>{children}</button>
82
+ }
83
+ ```
84
+
85
+ ## `export const uidex` — module-scoped authoring surface
86
+
87
+ One named export per module, on the file that **defines** the entity. The RHS must be a plain AST literal — object / array / string / number / boolean literals, optional `as const`, optional `satisfies Uidex.<Kind>`. Identifier references in value position, calls, spreads, conditionals, and template literals with expressions are rejected with an error diagnostic.
88
+
89
+ ```tsx
90
+ // Page
91
+ import type { Uidex } from "@/uidex.gen"
92
+
93
+ export const uidex = {
94
+ page: "org-settings",
95
+ acceptance: [
96
+ "User can update the organization name",
97
+ "Error message is shown if the update fails",
98
+ ],
99
+ features: ["billing"],
100
+ } as const satisfies Uidex.Page
101
+
102
+ export default function SettingsPage() { ... }
103
+ ```
104
+
105
+ Kinds (discriminator fields):
106
+
107
+ | Kind | Discriminator | Other fields |
108
+ | ----------- | --------------------------------- | --------------------------------------------------------------------- |
109
+ | `page` | `page: PageId \| false` | `acceptance?`, `features?`, `widgets?`, `description?` |
110
+ | `feature` | `feature: FeatureId \| false` | `acceptance?`, `description?` |
111
+ | `widget` | `widget: WidgetId` | `acceptance?`, `description?` |
112
+ | `primitive` | `primitive: PrimitiveId \| false` | `description?` |
113
+ | `flow` | `flow: FlowId` | `description?` |
114
+ | opt-out | `notFlow: true` | (no other fields — opts a Playwright spec out of flow auto-promotion) |
115
+
116
+ Setting `page: false` / `feature: false` / `primitive: false` explicitly opts a module out of auto-promotion.
117
+
118
+ ### Typed cross-references
119
+
120
+ `uidex.gen.ts` exports per-kind id unions (`PageId`, `FeatureId`, `WidgetId`, etc.) and a `Uidex` namespace of shape types. `satisfies Uidex.Page` makes `tsc` flag unknown ids at the literal — no `uidex scan --audit` round-trip required:
121
+
122
+ ```tsx
123
+ export const uidex = {
124
+ page: "checkout",
125
+ features: ["billng"], // tsc error: "billng" is not assignable to FeatureId
126
+ } as const satisfies Uidex.Page
127
+ ```
128
+
129
+ ### Precedence
130
+
131
+ ```
132
+ configuration override > export const uidex > DOM attribute > convention
133
+ ```
134
+
135
+ ## Playwright flows
136
+
137
+ End-to-end user journeys are Playwright tests in `e2e/**`. Any top-level `test.describe` auto-promotes to a flow; the `@uidex:flow` tag adds richer metadata. Opt out by adding `export const uidex = { notFlow: true } as const satisfies Uidex.NotFlow` at the top of the spec file.
138
+
139
+ ```ts
140
+ import { expect, test } from "uidex/playwright"
141
+ import type { Uidex } from "@/uidex.gen"
142
+
143
+ export const uidex = {
144
+ flow: "add-and-complete-todo",
145
+ description: "User adds a todo and marks it complete.",
146
+ } as const satisfies Uidex.Flow
147
+
148
+ test.describe("Add and complete a todo", { tag: "@uidex:flow" }, () => {
149
+ test("adds a todo", async ({ uidex }) => {
150
+ await uidex("todo-field").fill("Buy milk")
151
+ await uidex("todo-add").click()
152
+ })
153
+ })
154
+ ```
155
+
156
+ Rules:
157
+
158
+ - One tagged describe per `flow-*.spec.ts`.
159
+ - Component ids inside `uidex(...)` MUST be string literals (static-analysable).
160
+ - `page-*.spec.ts` and `feature-*.spec.ts` scoped tests remain untagged.
161
+
162
+ ## Scanner
163
+
164
+ ```bash
165
+ npx uidex scan # regenerate src/uidex.gen.ts
166
+ npx uidex scan --check # fail non-zero if the committed gen file is stale
167
+ npx uidex scan --audit # validate (--check + --lint; exits non-zero on errors)
168
+ npx uidex scan --json # emit diagnostics as JSON
169
+ npx uidex scaffold widget <id> # emit Playwright stub from declared acceptance
170
+ ```
171
+
172
+ Run `uidex scan` after any `data-uidex*` attribute change, `export const uidex` edit, or new flow. The gen file (`src/uidex.gen.ts` by default) is auto-generated and **committed** — consumers import types from it directly.
173
+
174
+ ### Bundler plugins (optional)
175
+
176
+ Install the matching bundler plugin so `uidex.gen.ts` regenerates on file save:
177
+
178
+ - `@uidex/vite-plugin` — for Vite
179
+ - `@uidex/webpack-plugin` — for raw Webpack
180
+ - `@uidex/next-plugin` — `withUidex(config)` wrapper for Next.js (Webpack transport)
181
+
182
+ ## Configuration
183
+
184
+ `.uidex.json` is flat. Example:
185
+
186
+ ```json
187
+ {
188
+ "sources": [{ "rootDir": "src" }],
189
+ "output": "src/uidex.gen.ts",
190
+ "flows": ["e2e/**/*.spec.ts"],
191
+ "typeMode": "strict"
192
+ }
193
+ ```
194
+
195
+ `typeMode: "strict"` (default) emits literal id unions — `tsc` rejects unknown ids at the `satisfies Uidex.X` site. `typeMode: "loose"` emits `string` — a **temporary migration knob** for repos mid-migration from legacy JSDoc.
196
+
197
+ Multi-source (monorepo) example:
198
+
199
+ ```json
200
+ {
201
+ "sources": [
202
+ { "rootDir": "apps/web/src" },
203
+ { "rootDir": "packages/ui/src", "prefix": "@org/ui" }
204
+ ],
205
+ "output": "apps/web/src/uidex.gen.ts",
206
+ "flows": ["apps/web/e2e/**/*.spec.ts"]
207
+ }
208
+ ```
209
+
210
+ ## Migration from legacy JSDoc
211
+
212
+ JSDoc tags (`@uidex page|feature|widget`, `@acceptance`, `@uidex:not-flow`) are **no longer recognised** — the scanner registers nothing from them. `uidex scan --lint` ships a `legacy-jsdoc` rule that warns on every legacy tag and points at the replacement `export const uidex` form. Set `typeMode: "loose"` in `.uidex.json` during migration to keep `tsc` quiet; flip to `strict` once every JSDoc tag is gone.
@@ -1,16 +0,0 @@
1
- Run `npx uidex scan --check` and fix all reported issues:
2
-
3
- 1. **Uncovered components** (components missing UIDEX_PAGE.md coverage):
4
- - Create a `UIDEX_PAGE.md` in the nearest feature directory
5
- - Include a heading, brief description, and acceptance criteria
6
-
7
- 2. **Orphaned UIDEX_PAGE.md files** (docs with no components underneath):
8
- - If the components were removed, delete the UIDEX_PAGE.md
9
- - If components are missing `data-uidex` attributes, add them
10
-
11
- 3. **Review interactive elements** without `data-uidex` attributes:
12
- - Scan modified files for buttons, inputs, links, and other interactive elements
13
- - Add `data-uidex="kebab-id"` attributes where missing
14
- - Add `/** @uidex id - description */` JSDoc above each annotated element
15
-
16
- After fixing all issues, run `npx uidex scan` to regenerate the component registry.
package/claude/rules.md DELETED
@@ -1,88 +0,0 @@
1
- # uidex conventions
2
-
3
- This project uses [uidex](https://github.com/soel/uidex) for UI element tracking and documentation.
4
-
5
- ## data-uidex attributes
6
-
7
- - Every user-facing interactive element MUST have a `data-uidex="kebab-id"` attribute.
8
- - IDs should be descriptive and kebab-cased: `data-uidex="submit-btn"`, `data-uidex="user-avatar"`.
9
- - When creating new components with interactive elements, always add `data-uidex` attributes.
10
- - When modifying components, preserve existing `data-uidex` attributes. If you rename or remove an element, update or remove its attribute accordingly.
11
-
12
- ## @uidex JSDoc comments
13
-
14
- Add a JSDoc comment above annotated elements describing their purpose:
15
-
16
- ```tsx
17
- /** @uidex submit-btn - Primary action button for form submission */
18
- <button data-uidex="submit-btn">Submit</button>
19
- ```
20
-
21
- Multi-line format for longer descriptions:
22
-
23
- ```tsx
24
- /**
25
- * @uidex user-profile-card
26
- * Displays user avatar, name, and role.
27
- * Shown in the sidebar and team directory.
28
- */
29
- <div data-uidex="user-profile-card">...</div>
30
- ```
31
-
32
- ## UIDEX_PAGE.md files
33
-
34
- Page docs describe a route/view. Components in the directory are automatically associated.
35
-
36
- - Each page directory should have a `UIDEX_PAGE.md` with acceptance criteria.
37
- - When adding components to a directory that lacks a `UIDEX_PAGE.md`, create one.
38
- - When removing all components from a directory, remove its `UIDEX_PAGE.md`.
39
-
40
- ```markdown
41
- # Page Name
42
-
43
- Brief description of the page.
44
-
45
- ## Acceptance
46
- - User can do X
47
- - Y is displayed when Z
48
- ```
49
-
50
- ## UIDEX_FEATURE.md files
51
-
52
- Feature docs describe a cross-cutting feature. Components are explicitly listed via frontmatter.
53
-
54
- - Use `UIDEX_FEATURE.md` for features that span multiple pages (e.g., auth, billing).
55
- - List associated component IDs in the `components:` frontmatter.
56
-
57
- ```markdown
58
- ---
59
- components:
60
- - login-form
61
- - signup-btn
62
- ---
63
- # Feature Name
64
-
65
- Brief description of the feature.
66
-
67
- ## Acceptance
68
- - User can do X
69
- - Y is displayed when Z
70
- ```
71
-
72
- ## Component registry
73
-
74
- For a project-wide view of all components, pages, and features, read the generated registry file (default: `src/uidex.gen.ts`). This file is kept up to date by the scanner.
75
-
76
- ## Scanner
77
-
78
- After adding, removing, or renaming `data-uidex` attributes, run:
79
-
80
- ```bash
81
- npx uidex scan
82
- ```
83
-
84
- To validate that all components have UIDEX_PAGE.md coverage:
85
-
86
- ```bash
87
- npx uidex scan --check
88
- ```