uidex 0.2.4 → 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 (67) hide show
  1. package/README.md +253 -353
  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 +6902 -9801
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +901 -146
  19. package/dist/index.d.ts +901 -146
  20. package/dist/index.js +6896 -9805
  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 +30 -53
  25. package/dist/playwright/index.d.ts +30 -53
  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 +6936 -9808
  35. package/dist/react/index.cjs.map +1 -1
  36. package/dist/react/index.d.cts +673 -146
  37. package/dist/react/index.d.ts +673 -146
  38. package/dist/react/index.js +6980 -9811
  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 +71 -65
  47. package/templates/claude/audit.md +37 -0
  48. package/templates/claude/rules.md +212 -0
  49. package/claude/audit-command.md +0 -46
  50. package/claude/rules.md +0 -167
  51. package/dist/api/index.cjs +0 -254
  52. package/dist/api/index.cjs.map +0 -1
  53. package/dist/api/index.d.cts +0 -236
  54. package/dist/api/index.d.ts +0 -236
  55. package/dist/api/index.js +0 -226
  56. package/dist/api/index.js.map +0 -1
  57. package/dist/core/index.cjs +0 -11045
  58. package/dist/core/index.cjs.map +0 -1
  59. package/dist/core/index.d.cts +0 -424
  60. package/dist/core/index.d.ts +0 -424
  61. package/dist/core/index.global.js +0 -66516
  62. package/dist/core/index.global.js.map +0 -1
  63. package/dist/core/index.js +0 -10995
  64. package/dist/core/index.js.map +0 -1
  65. package/dist/core/style.css +0 -1529
  66. package/dist/scripts/cli.cjs +0 -3904
  67. package/uidex.schema.json +0 -93
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "uidex",
3
- "version": "0.2.4",
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,40 +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
- "./api": {
37
- "types": "./dist/api/index.d.ts",
38
- "import": "./dist/api/index.js",
39
- "require": "./dist/api/index.cjs"
40
- },
41
- "./styles.css": "./dist/core/style.css"
44
+ }
42
45
  },
43
- "sideEffects": [
44
- "*.css"
45
- ],
46
+ "sideEffects": false,
46
47
  "files": [
47
48
  "dist",
48
- "claude",
49
- "uidex.schema.json"
49
+ "templates"
50
50
  ],
51
51
  "bin": {
52
- "uidex": "dist/scripts/cli.cjs"
52
+ "uidex": "dist/cli/cli.cjs"
53
53
  },
54
- "scripts": {
55
- "dev": "tsup --watch",
56
- "build": "npm run build:tailwind && npm run generate:css && tsup && npm run build:css",
57
- "build:tailwind": "tailwindcss --input src/ui/tailwind.css --output src/ui/tailwind.out.css",
58
- "build:css": "cp src/core/style.css dist/core/style.css",
59
- "generate:css": "tsx scripts/generate-css-text.ts",
60
- "scan": "tsx scripts/cli.ts scan",
61
- "test": "vitest",
62
- "test:run": "vitest run",
63
- "test:coverage": "vitest run --coverage",
64
- "lint": "eslint src/",
65
- "format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
66
- "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"",
67
- "typecheck": "tsc --noEmit",
68
- "prepublishOnly": "npm run build && npm run test:run && npm run typecheck"
54
+ "engines": {
55
+ "node": ">=22"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
69
59
  },
70
60
  "peerDependencies": {
71
61
  "@playwright/test": ">=1.40",
@@ -77,44 +67,55 @@
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
- "@phosphor-icons/react": "^2.1.10",
83
92
  "@playwright/test": "^1.58.2",
84
93
  "@tailwindcss/cli": "^4.2.2",
85
94
  "@testing-library/jest-dom": "^6.6.3",
86
95
  "@testing-library/react": "^16.1.0",
87
96
  "@types/node": "^22.10.5",
88
- "@types/react": "^18.3.18",
89
- "@types/react-dom": "^18.3.5",
90
- "@typescript-eslint/eslint-plugin": "^8.21.0",
91
- "@typescript-eslint/parser": "^8.21.0",
97
+ "@types/react": "^19.2.14",
98
+ "@types/react-dom": "^19.2.3",
92
99
  "@vitejs/plugin-react": "^4.3.4",
93
- "@vitest/coverage-v8": "^2.1.8",
94
100
  "eslint": "^9.18.0",
95
- "eslint-plugin-react": "^7.37.4",
96
- "eslint-plugin-react-hooks": "^5.1.0",
97
101
  "jsdom": "^26.0.0",
98
- "prettier": "^3.4.2",
99
102
  "react": "^18.3.1",
100
103
  "react-dom": "^18.3.1",
101
- "shadcn": "^4.1.2",
102
104
  "tailwindcss": "^4.2.2",
103
105
  "tsup": "^8.3.5",
104
106
  "tsx": "^4.7.0",
105
- "tw-animate-css": "^1.4.0",
106
- "typescript": "^5.7.3",
107
- "typescript-eslint": "^8.54.0",
108
- "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"
109
111
  },
110
112
  "keywords": [
111
- "annotation",
112
- "highlight",
113
- "overlay",
114
- "component",
113
+ "uidex",
114
+ "devtools",
115
115
  "react",
116
- "vanilla-js",
117
- "framework-agnostic"
116
+ "playwright",
117
+ "feedback",
118
+ "scanner"
118
119
  ],
119
120
  "author": "soel",
120
121
  "license": "MIT",
@@ -122,11 +123,16 @@
122
123
  "type": "git",
123
124
  "url": "https://github.com/soel/uidex"
124
125
  },
125
- "dependencies": {
126
- "@base-ui/react": "^1.3.0",
127
- "class-variance-authority": "^0.7.1",
128
- "clsx": "^2.1.1",
129
- "lucide-react": "^1.7.0",
130
- "tailwind-merge": "^3.5.0"
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"
131
137
  }
132
- }
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,46 +0,0 @@
1
- Run `npx uidex scan --audit` and fix all reported issues:
2
-
3
- 1. **Uncovered components** (components missing doc coverage):
4
- - Create a `UIDEX_PAGE.md` in the component's directory for page-specific components
5
- - For cross-cutting components (auth, billing, search, etc.), create or update a `UIDEX_FEATURE.md` in `features/<name>/` and list the component ID in the `components:` frontmatter
6
- - Include a `# Title`, `> description` blockquote, and `## Acceptance` with `- [ ]` checkboxes
7
-
8
- 2. **Route directories missing UIDEX_PAGE.md** (route with `page.tsx` but no doc):
9
- - Create a `UIDEX_PAGE.md` in the route directory
10
- - Without its own doc, the route's components get absorbed by a parent page doc
11
-
12
- 3. **Orphaned UIDEX_PAGE.md files** (docs with no components underneath):
13
- - If the components were removed, delete the UIDEX_PAGE.md
14
- - If components are missing annotations, add them
15
-
16
- 4. **Orphaned UIDEX_FEATURE.md files** (features referencing no known components):
17
- - Update the `components:` frontmatter to reference valid component IDs
18
- - If the feature is no longer relevant, delete it
19
-
20
- 5. **Missing descriptions** (pages/features without a `>` blockquote description):
21
- - Add a `> description` blockquote immediately after the `# Title` heading
22
-
23
- 6. **Missing block annotations** (structural regions):
24
- - Add `data-uidex-block="kebab-id"` to page root wrappers and section containers
25
- - Blocks do NOT need JSDoc comments
26
-
27
- 7. **Missing interactive annotations**:
28
- - Add `data-uidex="kebab-id"` to buttons, inputs, links, and other interactive elements
29
- - Optionally add `/** @uidex id - description */` JSDoc above interactive elements
30
-
31
- 8. **Primitive annotations** — use `data-uidex-primitive="kebab-name"` when a component is a **reusable presentational building block**, not a consumer/page component. Apply when ALL of these are true:
32
- - The file **exports** a component (not a page or layout)
33
- - The component **wraps native HTML elements or other primitives** (buttons, inputs, cards, badges) rather than composing app-level features
34
- - The component is **imported by multiple consumers** or is designed to be reusable
35
- - The component does **not fetch data or contain business logic** — it receives everything via props
36
-
37
- Do NOT add `data-uidex-primitive` to:
38
- - Page components, layouts, or route-level files
39
- - Feature orchestrators (dialogs that compose multiple primitives with business logic)
40
- - Components that call hooks for data fetching (`useQuery`, `useMutation`, etc.)
41
- - One-off components only used in a single place (use `data-uidex` or `data-uidex-block` instead)
42
-
43
- Files with `data-uidex-primitive` are exempt from missing-annotation lint checks.
44
- The scanner detects primitives by the attribute alone — directory structure does not matter.
45
-
46
- After fixing all issues, run `npx uidex scan` to regenerate the component registry.
package/claude/rules.md DELETED
@@ -1,167 +0,0 @@
1
- # uidex conventions
2
-
3
- This project uses [uidex](https://github.com/soel/uidex) for UI element tracking and documentation.
4
-
5
- ## Annotations
6
-
7
- uidex uses three annotation types:
8
-
9
- ### `data-uidex` — interactive elements
10
-
11
- Every user-facing interactive element MUST have a `data-uidex="kebab-id"` attribute.
12
-
13
- - IDs should be descriptive and kebab-cased: `data-uidex="submit-btn"`, `data-uidex="user-avatar"`.
14
- - When creating new components with interactive elements, always add `data-uidex` attributes.
15
- - When modifying components, preserve existing `data-uidex` attributes. If you rename or remove an element, update or remove its attribute accordingly.
16
-
17
- ### `data-uidex-block` — structural regions
18
-
19
- Structural UI regions (pages, sections, panels, toolbars) use `data-uidex-block="kebab-id"`.
20
-
21
- - Add `data-uidex-block` to the root wrapper of each page/component.
22
- - Add `data-uidex-block` to meaningful sections within a page (forms, tables, sidebars, etc.).
23
- - Blocks do NOT need JSDoc comments — they are spatial context, not documented behavior.
24
-
25
- ```tsx
26
- <div data-uidex-block="settings-page">
27
- <form data-uidex-block="settings-form">
28
- /** @uidex save-btn - Saves current settings */
29
- <button data-uidex="save-btn">Save</button>
30
- </form>
31
- </div>
32
- ```
33
-
34
- ### `data-uidex-primitive` — reusable UI component definitions
35
-
36
- Reusable, presentational components (buttons, inputs, cards, etc.) use `data-uidex-primitive="kebab-name"` on the root element inside the component definition.
37
-
38
- - Place the attribute on the root element of the component's **definition**, not at call sites.
39
- - IDs should be descriptive and kebab-cased: `data-uidex-primitive="button"`, `data-uidex-primitive="sign-in-card"`.
40
- - A single element can carry both `data-uidex-primitive` and `data-uidex` without conflict.
41
- - Files containing `data-uidex-primitive` are exempt from missing-annotation lint checks.
42
- - Primitives do NOT need JSDoc comments — they are tracked by the scanner automatically.
43
-
44
- ```tsx
45
- // Button.tsx — component definition
46
- export function Button({ children, ...props }) {
47
- return <button data-uidex-primitive="button" {...props}>{children}</button>;
48
- }
49
- ```
50
-
51
- ### When to use which annotation
52
-
53
- | Annotation | Use for | Placed on |
54
- |---|---|---|
55
- | `data-uidex` | Interactive elements (buttons, inputs, links) | Usage sites in consumer components |
56
- | `data-uidex-block` | Structural regions (pages, forms, sections) | Wrapper elements in consumer components |
57
- | `data-uidex-primitive` | Reusable UI component definitions | Root element inside the component definition file |
58
-
59
- ## @uidex JSDoc comments
60
-
61
- Add JSDoc comments above `data-uidex` (interactive) elements to enrich documentation. Do NOT add JSDoc to `data-uidex-block` elements.
62
-
63
- ```tsx
64
- /** @uidex submit-btn - Primary action button for form submission */
65
- <button data-uidex="submit-btn">Submit</button>
66
- ```
67
-
68
- Multi-line format for longer descriptions:
69
-
70
- ```tsx
71
- /**
72
- * @uidex user-profile-card
73
- * Displays user avatar, name, and role.
74
- * Shown in the sidebar and team directory.
75
- */
76
- <div data-uidex="user-profile-card">...</div>
77
- ```
78
-
79
- ## UIDEX_PAGE.md files
80
-
81
- Page docs describe a **route** (a URL the user can visit). Components in the directory are automatically associated.
82
-
83
- - Place `UIDEX_PAGE.md` only in **route directories** (e.g., `app/settings/`, `app/dashboard/`, `app/(auth)/login/`).
84
- - Do NOT place `UIDEX_PAGE.md` in non-route directories like `contexts/`, `hooks/`, `lib/`, `utils/`, or shared `components/`.
85
- - Each route directory should have a `UIDEX_PAGE.md` with acceptance criteria.
86
- - The description MUST be a `>` blockquote immediately after the `# Title` heading.
87
- - Acceptance criteria MUST use `- [ ]` checkbox syntax.
88
- - When adding components to a route directory that lacks a `UIDEX_PAGE.md`, create one.
89
- - When removing all components from a route directory, remove its `UIDEX_PAGE.md`.
90
-
91
- ```markdown
92
- # Page Name
93
-
94
- > Brief description of what this page does and its purpose.
95
-
96
- ## Acceptance
97
- - [ ] User can do X
98
- - [ ] Y is displayed when Z
99
- ```
100
-
101
- ## UIDEX_FEATURE.md files
102
-
103
- Feature docs describe a cross-cutting feature that spans multiple pages. Components are explicitly listed via frontmatter.
104
-
105
- - Place feature docs in a `features/` directory inside the scanner root (e.g., `app/features/auth/UIDEX_FEATURE.md`).
106
- - Use one subdirectory per feature: `features/auth/`, `features/billing/`, `features/notifications/`, etc.
107
- - List associated component IDs in the `components:` frontmatter.
108
- - The description MUST be a `>` blockquote immediately after the `# Title` heading.
109
- - When a component participates in a cross-cutting concern (auth, billing, search, etc.) and isn't fully described by its page alone, add it to a feature.
110
-
111
- ```
112
- features/
113
- auth/
114
- UIDEX_FEATURE.md
115
- billing/
116
- UIDEX_FEATURE.md
117
- ```
118
-
119
- ```markdown
120
- ---
121
- components:
122
- - login-form
123
- - signup-btn
124
- ---
125
- # Feature Name
126
-
127
- > Brief description of the feature and its scope.
128
- ```
129
-
130
- ## Component registry
131
-
132
- 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.
133
-
134
- ## Scanner
135
-
136
- After adding, removing, or renaming `data-uidex`, `data-uidex-block`, or `data-uidex-primitive` attributes, run:
137
-
138
- ```bash
139
- npx uidex scan
140
- ```
141
-
142
- To validate coverage and check for missing annotations:
143
-
144
- ```bash
145
- npx uidex scan --audit
146
- ```
147
-
148
- ## Setup & Administration
149
-
150
- Manage authentication, project linking, and integrations via the CLI. These commands run in the user's terminal — use `! uidex <command>` to execute them from Claude Code.
151
-
152
- ```bash
153
- # Auth & context
154
- ! uidex login # Authenticate with uidex server
155
- ! uidex status # Show auth and link state
156
- ! uidex link # Link current directory to org/project
157
-
158
- # Integrations
159
- ! uidex integrations list # List configured integrations
160
- ! uidex integrations add jira # Add Jira (interactive prompts)
161
- ! uidex integrations add github # Add GitHub (browser flow)
162
- ! uidex integrations test <id> # Test integration connection
163
- ! uidex integrations remove <id> # Remove integration (with confirmation)
164
- ! uidex integrations targets <id> # List available targets (repos, projects)
165
- ```
166
-
167
- The prerequisite flow is: `login` → `link` → `integrations`. Adding/removing integrations requires org owner role.