uidex 0.2.4 → 0.4.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.
- package/README.md +253 -353
- package/dist/cli/cli.cjs +3324 -0
- package/dist/cli/cli.cjs.map +1 -0
- package/dist/cloud/index.cjs +169 -0
- package/dist/cloud/index.cjs.map +1 -0
- package/dist/cloud/index.js +140 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/headless/index.cjs +4143 -0
- package/dist/headless/index.cjs.map +1 -0
- package/dist/headless/index.d.cts +220 -0
- package/dist/headless/index.d.ts +220 -0
- package/dist/headless/index.js +4130 -0
- package/dist/headless/index.js.map +1 -0
- package/dist/index.cjs +8704 -9883
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +968 -146
- package/dist/index.d.ts +968 -146
- package/dist/index.js +8327 -9492
- package/dist/index.js.map +1 -1
- package/dist/playwright/index.cjs +164 -24
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.cts +30 -53
- package/dist/playwright/index.d.ts +30 -53
- package/dist/playwright/index.js +148 -21
- package/dist/playwright/index.js.map +1 -1
- package/dist/playwright/reporter.cjs +62 -28
- package/dist/playwright/reporter.cjs.map +1 -1
- package/dist/playwright/reporter.d.cts +24 -12
- package/dist/playwright/reporter.d.ts +24 -12
- package/dist/playwright/reporter.js +62 -28
- package/dist/playwright/reporter.js.map +1 -1
- package/dist/react/index.cjs +8706 -9883
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +720 -146
- package/dist/react/index.d.ts +720 -146
- package/dist/react/index.js +8518 -9629
- package/dist/react/index.js.map +1 -1
- package/dist/scan/index.cjs +3360 -0
- package/dist/scan/index.cjs.map +1 -0
- package/dist/scan/index.d.cts +378 -0
- package/dist/scan/index.d.ts +378 -0
- package/dist/scan/index.js +3303 -0
- package/dist/scan/index.js.map +1 -0
- package/package.json +67 -60
- package/templates/claude/audit.md +43 -0
- package/templates/claude/rules.md +227 -0
- package/claude/audit-command.md +0 -46
- package/claude/rules.md +0 -167
- package/dist/api/index.cjs +0 -254
- package/dist/api/index.cjs.map +0 -1
- package/dist/api/index.d.cts +0 -236
- package/dist/api/index.d.ts +0 -236
- package/dist/api/index.js +0 -226
- package/dist/api/index.js.map +0 -1
- package/dist/core/index.cjs +0 -11045
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -424
- package/dist/core/index.d.ts +0 -424
- package/dist/core/index.global.js +0 -66516
- package/dist/core/index.global.js.map +0 -1
- package/dist/core/index.js +0 -10995
- package/dist/core/index.js.map +0 -1
- package/dist/core/style.css +0 -1529
- package/dist/scripts/cli.cjs +0 -3904
- package/uidex.schema.json +0 -93
package/README.md
CHANGED
|
@@ -1,465 +1,365 @@
|
|
|
1
1
|
# uidex
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
uidex surfaces structured context about a running UI — pages, features, widgets, regions, elements, primitives, flows — to humans (devtools overlay, command palette, detail panels), agents (typed `uidex.gen.ts` registry), and test runners (Playwright fixture + reporter).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This is **uidex 0.1** — a greenfield rewrite. The published surface is a single npm package with framework-neutral core, an optional React adapter, and a built-in cloud client, each reachable through its own subpath export.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
10
|
+
pnpm add uidex
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
## Quick
|
|
13
|
+
## Quick start (React)
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
#
|
|
15
|
-
npx uidex
|
|
16
|
-
|
|
17
|
-
# 2. Add data-uidex attributes to your elements
|
|
18
|
-
# <button data-uidex="submit-btn">Submit</button>
|
|
19
|
-
|
|
20
|
-
# 3. Run the scanner
|
|
21
|
-
npx uidex scan
|
|
16
|
+
npx uidex init # write .uidex.json and .gitignore entry
|
|
17
|
+
npx uidex scan # generate src/uidex.gen.ts
|
|
22
18
|
```
|
|
23
19
|
|
|
24
|
-
### React
|
|
25
|
-
|
|
26
20
|
```tsx
|
|
27
|
-
|
|
28
|
-
import {
|
|
21
|
+
// any React root
|
|
22
|
+
import { UidexProvider, UidexMount } from "uidex/react"
|
|
29
23
|
|
|
30
|
-
function
|
|
24
|
+
export default function Root({ children }: { children: React.ReactNode }) {
|
|
31
25
|
return (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
)
|
|
26
|
+
<UidexProvider projectKey="pk_your_project">
|
|
27
|
+
{children}
|
|
28
|
+
{process.env.NODE_ENV !== "production" && <UidexMount />}
|
|
29
|
+
</UidexProvider>
|
|
30
|
+
)
|
|
37
31
|
}
|
|
38
32
|
```
|
|
39
33
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```js
|
|
43
|
-
import './uidex.gen';
|
|
44
|
-
import { createUidexUI } from 'uidex/core';
|
|
45
|
-
|
|
46
|
-
const ui = createUidexUI();
|
|
47
|
-
ui.mount();
|
|
48
|
-
|
|
49
|
-
// Cleanup
|
|
50
|
-
ui.destroy();
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Script Tag
|
|
54
|
-
|
|
55
|
-
```html
|
|
56
|
-
<script src="https://unpkg.com/uidex/dist/core/index.global.js"></script>
|
|
57
|
-
<script>
|
|
58
|
-
const ui = UidexCore.createUidexUI({ components: { ... } });
|
|
59
|
-
ui.mount();
|
|
60
|
-
</script>
|
|
61
|
-
```
|
|
34
|
+
That is the full integration. Shadow DOM mounts under the `<UidexMount>` node, the Inspector is always on while the surface is mounted (hover highlights any uidex-annotated element; plain click opens the element menu anchored to its bounds), and Cmd+K opens the palette. Passing `projectKey` auto-wires the built-in cloud client; pass `cloud={null}` to opt out, or `cloud={customAdapter}` to supply your own.
|
|
62
35
|
|
|
63
|
-
|
|
36
|
+
### Dev-only mount
|
|
64
37
|
|
|
65
|
-
|
|
38
|
+
Because the Inspector intercepts clicks on every uidex-annotated element while mounted, uidex is a **dev-only surface**. Consumers MUST gate the mount on an environment check — the SDK does not enforce this itself.
|
|
66
39
|
|
|
67
|
-
```
|
|
40
|
+
```tsx
|
|
41
|
+
// React — gate <UidexMount> on NODE_ENV
|
|
68
42
|
{
|
|
69
|
-
"
|
|
70
|
-
"defaults": {
|
|
71
|
-
"color": "#3b82f6",
|
|
72
|
-
"borderStyle": "solid",
|
|
73
|
-
"borderWidth": 2,
|
|
74
|
-
"showLabel": true,
|
|
75
|
-
"labelPosition": "top-left"
|
|
76
|
-
},
|
|
77
|
-
"colors": {
|
|
78
|
-
"primary": "#3b82f6",
|
|
79
|
-
"success": "#10b981",
|
|
80
|
-
"warning": "#f59e0b",
|
|
81
|
-
"error": "#ef4444",
|
|
82
|
-
"info": "#6366f1"
|
|
83
|
-
},
|
|
84
|
-
"scanner": {
|
|
85
|
-
"rootDir": "src",
|
|
86
|
-
"include": ["**/*.tsx", "**/*.jsx"],
|
|
87
|
-
"exclude": ["**/*.test.*", "**/*.spec.*"],
|
|
88
|
-
"outputPath": "src/uidex.gen.ts"
|
|
89
|
-
}
|
|
43
|
+
process.env.NODE_ENV !== "production" && <UidexMount />
|
|
90
44
|
}
|
|
91
45
|
```
|
|
92
46
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
npx uidex scan # Generate registry
|
|
99
|
-
npx uidex scan --audit # Validate coverage and annotations
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
Add it to your build:
|
|
103
|
-
|
|
104
|
-
```json
|
|
105
|
-
{
|
|
106
|
-
"scripts": {
|
|
107
|
-
"prebuild": "npx uidex scan",
|
|
108
|
-
"build": "next build"
|
|
109
|
-
}
|
|
47
|
+
```ts
|
|
48
|
+
// Vanilla — gate createUidex().mount() on NODE_ENV
|
|
49
|
+
if (process.env.NODE_ENV !== "production") {
|
|
50
|
+
createUidex({ cloud: cloud({ projectKey: "pk_..." }) }).mount()
|
|
110
51
|
}
|
|
111
52
|
```
|
|
112
53
|
|
|
113
|
-
|
|
54
|
+
## Quick start (vanilla)
|
|
114
55
|
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
</nav>
|
|
56
|
+
```ts
|
|
57
|
+
import { createUidex } from "uidex"
|
|
58
|
+
import { cloud } from "uidex/cloud"
|
|
119
59
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
60
|
+
const uidex = createUidex({
|
|
61
|
+
cloud: cloud({ projectKey: "pk_your_project" }),
|
|
62
|
+
})
|
|
63
|
+
uidex.mount() // defaults to document.body
|
|
123
64
|
```
|
|
124
65
|
|
|
125
|
-
|
|
66
|
+
## Package layout
|
|
126
67
|
|
|
127
|
-
|
|
68
|
+
| Subpath | Purpose |
|
|
69
|
+
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `uidex` | Vanilla core. `createUidex`, session store (XState v5 surface + highlight + form machines), entity registry, injected surface, panel registry, Zag machines. No React, no network. |
|
|
71
|
+
| `uidex/react` | React boot wrapper. `UidexProvider`, `useUidex`, `UidexMount`, `createReactPanel`. No network. |
|
|
72
|
+
| `uidex/cloud` | Fetch client for the cloud ingest API. `cloud({ projectKey, endpoint })`, `FeedbackPayload`, etc. No React. |
|
|
73
|
+
| `uidex/headless` | Narrower vanilla entry — Registry + Session + Shadow DOM + Overlay + Inspector + MenuBar. No panels. |
|
|
74
|
+
| `uidex/scan` | Scanner pipeline (`discover → walk → extract → resolve → audit → emit`) and CLI. |
|
|
75
|
+
| `uidex/playwright` | Test fixture (`uidex(id)`). Optional peer: `@playwright/test`. |
|
|
76
|
+
| `uidex/playwright/reporter` | Coverage reporter. |
|
|
128
77
|
|
|
129
|
-
|
|
130
|
-
- `componentIds` — array of all ids
|
|
131
|
-
- `ComponentId` — union type of all ids
|
|
132
|
-
- `pages` — from `UIDEX_PAGE.md` files (directory-based component association)
|
|
133
|
-
- `features` — from `UIDEX_FEATURE.md` files (explicit component association)
|
|
134
|
-
- `uiComponents` — presentational primitives detected under `ui/` directories (see [UI Primitives](#ui-primitives))
|
|
135
|
-
- Auto-registration calls
|
|
78
|
+
The build enforces subpath isolation: `uidex` contains no React or network code, `uidex/react` contains no network code, `uidex/cloud` contains no React.
|
|
136
79
|
|
|
137
|
-
|
|
80
|
+
## Entity model
|
|
138
81
|
|
|
139
|
-
|
|
82
|
+
Eight kinds, one registry, one ref shape:
|
|
140
83
|
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
84
|
+
```ts
|
|
85
|
+
type EntityKind =
|
|
86
|
+
| "route"
|
|
87
|
+
| "page"
|
|
88
|
+
| "feature"
|
|
89
|
+
| "widget"
|
|
90
|
+
| "region"
|
|
91
|
+
| "element"
|
|
92
|
+
| "primitive"
|
|
93
|
+
| "flow"
|
|
94
|
+
|
|
95
|
+
interface EntityRef {
|
|
96
|
+
kind: EntityKind
|
|
97
|
+
id: string
|
|
151
98
|
}
|
|
152
99
|
```
|
|
153
100
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
101
|
+
- **route** — a URL pattern. Auto-detected from the framework router.
|
|
102
|
+
- **page** — the module that renders a route. `export const uidex = { page: "id", ... } as const satisfies Uidex.Page` overrides the derived id and declares acceptance.
|
|
103
|
+
- **feature** — cross-cutting behaviour under `src/features/*`. `export const uidex = { feature: "id", ... }` overrides.
|
|
104
|
+
- **widget** — composite interactive unit (video player, date picker). Declared with `data-uidex-widget="id"` on the DOM root AND `export const uidex = { widget: "id", acceptance: [...] }` on the component module. The scanner cross-validates the two.
|
|
105
|
+
- **region** — HTML5 landmark or `role="region"`. `data-uidex-region` overrides id.
|
|
106
|
+
- **element** — explicit interactive element. Always annotated via `data-uidex`.
|
|
107
|
+
- **primitive** — reusable presentational component (button, input) under `src/ui/**`.
|
|
108
|
+
- **flow** — top-level `test.describe` in `e2e/**`; `@uidex:flow` tag adds richer metadata. Opt out with `export const uidex = { notFlow: true }`.
|
|
157
109
|
|
|
158
|
-
|
|
110
|
+
## Runtime API
|
|
159
111
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
| `UIDEX_FEATURE.md` | `feature:<dir-name>` |
|
|
163
|
-
| `UIDEX_PAGE.md` | `page:<dir-name>` |
|
|
164
|
-
| neither | `global` |
|
|
165
|
-
|
|
166
|
-
`npx uidex scan --audit` flags imports of feature- or page-scoped primitives from outside their scope tree. Global primitives are exempt.
|
|
112
|
+
```ts
|
|
113
|
+
import { createUidex } from "uidex"
|
|
167
114
|
|
|
168
|
-
|
|
115
|
+
const uidex = createUidex({
|
|
116
|
+
cloud: null, // or a CloudAdapter from uidex/cloud
|
|
117
|
+
theme: "auto", // "light" | "dark" | "auto"
|
|
118
|
+
defaultPanels: true, // register bundled detail + palette panels
|
|
119
|
+
panels: [myCustomPanel], // additional panels (last registered wins on conflict)
|
|
120
|
+
})
|
|
169
121
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// interface PrimitiveEntry {
|
|
174
|
-
// name: string; // exported component name, e.g. "Button"
|
|
175
|
-
// filePath: string; // source-root-relative path, e.g. "ui/button.tsx"
|
|
176
|
-
// scope: string; // "global" | "feature:<name>" | "page:<name>"
|
|
177
|
-
// composes: string[]; // names of other primitives this one imports
|
|
178
|
-
// usedBy: string[]; // file paths of consumers (non-primitive sources)
|
|
179
|
-
// kind: 'ui';
|
|
180
|
-
// }
|
|
122
|
+
uidex.mount() // defaults to document.body
|
|
123
|
+
uidex.unmount()
|
|
181
124
|
```
|
|
182
125
|
|
|
183
|
-
`
|
|
126
|
+
`createUidex` returns:
|
|
184
127
|
|
|
185
|
-
|
|
128
|
+
| Field | Shape |
|
|
129
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
130
|
+
| `registry` | `Registry` — `.add`, `.get(kind, id)`, `.list(kind)`, `.query(fn)`. |
|
|
131
|
+
| `session` | Zustand store projecting the XState surface machine. `hover`, `selection`, `stack`, `inspectorActive`, `theme`, `ingestActive`. `session.send(event)` for direct machine events. |
|
|
132
|
+
| `panels` | `{ register, unregister, list }` — panel registrar. |
|
|
133
|
+
| `cloud` | The configured `CloudAdapter` or `null`. |
|
|
134
|
+
| `ingest` | Console + network capture (auto-enabled when `cloud` is configured). |
|
|
135
|
+
| `shadowRoot` | The Shadow DOM root after `mount()`, else `null`. |
|
|
186
136
|
|
|
187
|
-
|
|
137
|
+
### React wiring
|
|
188
138
|
|
|
189
139
|
```tsx
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
import { Button } from '@/components/ui/button';
|
|
140
|
+
import { UidexProvider, UidexMount, useUidex } from "uidex/react"
|
|
141
|
+
```
|
|
193
142
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
};
|
|
143
|
+
- `<UidexProvider>` accepts `projectKey`, `cloud`, and `config` props. It creates the core instance on mount and disposes on unmount. If `projectKey` is provided and `cloud` is not explicitly set, it auto-wires `cloud({ projectKey })` from `uidex/cloud`.
|
|
144
|
+
- `useUidex()` returns the core instance and throws `UidexContextError` outside a provider.
|
|
145
|
+
- `<UidexMount>` renders the host DOM node the surface attaches to; the surface detaches on unmount.
|
|
198
146
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
))}
|
|
209
|
-
</ul>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
147
|
+
### Headless
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { createHeadless } from "uidex/headless"
|
|
151
|
+
|
|
152
|
+
const h = createHeadless({ theme: "auto" })
|
|
153
|
+
h.mount()
|
|
154
|
+
h.overlay.show(el, { label: "target" })
|
|
155
|
+
h.inspector.start()
|
|
212
156
|
```
|
|
213
157
|
|
|
214
|
-
|
|
158
|
+
No panels, no `ViewStack`. Same Shadow DOM, Overlay, MenuBar, CursorTooltip, CommandPalette (empty). Intended for agents, screencasts, and non-panel consumers.
|
|
215
159
|
|
|
216
|
-
|
|
160
|
+
## Panel system
|
|
217
161
|
|
|
218
|
-
|
|
162
|
+
Panels are the pluggable UI surface. Both detail views and palette commands are `Panel`s. Panels are framework-agnostic — `render` is imperative:
|
|
219
163
|
|
|
220
|
-
|
|
164
|
+
```ts
|
|
165
|
+
interface Panel {
|
|
166
|
+
id: string
|
|
167
|
+
matches?: (ref: EntityRef) => boolean // detail panel
|
|
168
|
+
command?: { label: string; group?: string; shortcut?: string } // palette entry
|
|
169
|
+
render: (ctx: PanelContext, root: HTMLElement) => () => void
|
|
170
|
+
}
|
|
171
|
+
```
|
|
221
172
|
|
|
222
|
-
|
|
173
|
+
- `render` receives a mount root and returns a cleanup function.
|
|
174
|
+
- Last registered wins on `matches()` overlap. `id` collision replaces the prior registration.
|
|
175
|
+
- `ctx.navigate(ref)` pushes the matching panel for `ref` onto the view stack; the current view stays mounted underneath and is revealed on pop.
|
|
176
|
+
- `ctx.push({ id, ref? })` and `ctx.pop()` drive the stack directly; `ctx.close()` clears every entry at once.
|
|
177
|
+
- `ctx.cloud` is the configured `CloudAdapter` or `null`; cloud-backed affordances MUST degrade gracefully when it is null.
|
|
223
178
|
|
|
224
|
-
|
|
179
|
+
Authoring a panel with React:
|
|
225
180
|
|
|
226
181
|
```tsx
|
|
227
|
-
import {
|
|
182
|
+
import { createReactPanel } from "uidex/react"
|
|
228
183
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
184
|
+
export const myPanel = createReactPanel({
|
|
185
|
+
id: "my-panel",
|
|
186
|
+
matches: (ref) => ref.kind === "widget",
|
|
187
|
+
component: ({ ctx }) => <div>Widget: {ctx.ref?.id}</div>,
|
|
188
|
+
})
|
|
234
189
|
```
|
|
235
190
|
|
|
236
|
-
|
|
237
|
-
|------|------|---------|-------------|
|
|
238
|
-
| `components` | `UidexMap` | registry | Components map |
|
|
239
|
-
| `config` | `UidexConfig` | - | Styling configuration |
|
|
240
|
-
| `buttonPosition` | `ButtonPosition` | `'bottom-right'` | Initial button position |
|
|
241
|
-
| `theme` | `UidexTheme` | `'auto'` | Theme: `'dark'`, `'light'`, or `'auto'` |
|
|
242
|
-
| `disabled` | `boolean` | `false` | Disable devtools |
|
|
243
|
-
| `onSelect` | `(id: string) => void` | - | Selection callback |
|
|
244
|
-
| `inspectShortcut` | `KeyboardShortcut \| false` | `Shift+Cmd+U` | Inspect mode shortcut |
|
|
245
|
-
| `ingest` | `IngestConfig` | - | Server feedback submission config |
|
|
246
|
-
| `onSubmit` | `(report, result) => void` | - | Callback after feedback submission |
|
|
191
|
+
`createReactPanel` wraps `createRoot` / `unmount` and produces a conforming `Panel`.
|
|
247
192
|
|
|
248
|
-
###
|
|
193
|
+
### Element menu
|
|
249
194
|
|
|
250
|
-
|
|
195
|
+
Clicking any uidex-annotated element opens a popover menu anchored to the element's bounds. The menu is chrome owned by the Surface (not a `Panel`), composed from the shared `menu` + `popover` Zag machines. All rows close the menu on activation; Escape and outside-click dismiss and return focus to the prior focus holder.
|
|
251
196
|
|
|
252
|
-
|
|
253
|
-
|
|
197
|
+
| Row | When shown | Action |
|
|
198
|
+
| ---------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
199
|
+
| **Copy path** | Always | `navigator.clipboard.writeText("<kind>:<id>")`. |
|
|
200
|
+
| **Open details** | A registered panel matches the entity | `session.actions.select(ref)` + `session.actions.openPanel(panel.id, ref)` where `panel = panels.findMatch(ref)`. |
|
|
201
|
+
| **Submit feedback** | A registered panel matches the entity | Routes to the matched detail panel (feedback currently lives as a sub-view). Suppressed if there is no detail panel for the entity. |
|
|
202
|
+
| **View acceptance criteria** | `ref.kind === "widget"` and a detail panel matches | Opens the widget detail panel. |
|
|
203
|
+
| **Find usages** | `ref.kind === "primitive"` | Queries the registry for entities composing the primitive and opens the primitive detail panel. |
|
|
254
204
|
|
|
255
|
-
|
|
256
|
-
target={element}
|
|
257
|
-
label="Button"
|
|
258
|
-
color="#3b82f6"
|
|
259
|
-
/>
|
|
260
|
-
```
|
|
205
|
+
The ⌘K palette provides the keyboard-driven counterpart: its "On this page" section enumerates every uidex entity present in the current DOM, and selecting a row calls `ctx.navigate(ref)`.
|
|
261
206
|
|
|
262
|
-
|
|
263
|
-
|------|------|---------|-------------|
|
|
264
|
-
| `target` | `HTMLElement \| null` | required | Element to highlight |
|
|
265
|
-
| `label` | `string` | - | Label text |
|
|
266
|
-
| `color` | `string` | - | Border color (hex or named) |
|
|
267
|
-
| `borderStyle` | `BorderStyle` | `'solid'` | `solid`, `dashed`, or `dotted` |
|
|
268
|
-
| `borderWidth` | `number` | `2` | Border width in px |
|
|
269
|
-
| `showLabel` | `boolean` | `true` | Show label |
|
|
270
|
-
| `labelPosition` | `LabelPosition` | `'top-left'` | Label position |
|
|
271
|
-
| `colors` | `Record<string, string>` | - | Named colors map |
|
|
272
|
-
|
|
273
|
-
### createUidexUI (Vanilla JS)
|
|
274
|
-
|
|
275
|
-
```js
|
|
276
|
-
import { createUidexUI } from 'uidex/core';
|
|
277
|
-
|
|
278
|
-
const ui = createUidexUI({
|
|
279
|
-
components: { ... },
|
|
280
|
-
pages: [ ... ],
|
|
281
|
-
features: [ ... ],
|
|
282
|
-
config: { defaults: { color: '#3b82f6' } },
|
|
283
|
-
buttonPosition: 'bottom-right',
|
|
284
|
-
theme: 'auto',
|
|
285
|
-
inspectShortcut: { key: 'u', shiftKey: true, metaKey: true },
|
|
286
|
-
onSelect: (id) => console.log(id),
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
ui.mount();
|
|
290
|
-
ui.destroy();
|
|
291
|
-
```
|
|
207
|
+
### Bundled panels
|
|
292
208
|
|
|
293
|
-
|
|
209
|
+
`createUidex({ defaultPanels: true })` (the default) registers:
|
|
294
210
|
|
|
295
|
-
|
|
211
|
+
`commandPalettePanel`, `componentDetailPanel` (element), `pageDetailPanel`, `featureDetailPanel`, `widgetDetailPanel`, `flowDetailPanel`, `primitiveDetailPanel`, `regionDetailPanel`.
|
|
296
212
|
|
|
297
|
-
|
|
298
|
-
// Auto-detect (default) — matches host page or OS preference
|
|
299
|
-
<UidexDevtools theme="auto" />
|
|
213
|
+
All eight are vanilla TypeScript built on Zag machines and Tailwind classes.
|
|
300
214
|
|
|
301
|
-
|
|
302
|
-
<UidexDevtools theme="dark" />
|
|
303
|
-
<UidexDevtools theme="light" />
|
|
304
|
-
```
|
|
215
|
+
### Report uidex issue
|
|
305
216
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
```css
|
|
309
|
-
:root {
|
|
310
|
-
--background: #ffffff;
|
|
311
|
-
--foreground: #262626;
|
|
312
|
-
--card: #ffffff;
|
|
313
|
-
--card-foreground: #262626;
|
|
314
|
-
--popover: #ffffff;
|
|
315
|
-
--popover-foreground: #262626;
|
|
316
|
-
--primary: #262626;
|
|
317
|
-
--primary-foreground: #fafafa;
|
|
318
|
-
--muted: rgba(0, 0, 0, 0.04);
|
|
319
|
-
--muted-foreground: #6b6b6b;
|
|
320
|
-
--border: rgba(0, 0, 0, 0.08);
|
|
321
|
-
}
|
|
322
|
-
```
|
|
217
|
+
Every view's footer Actions popup (⌘K from depth 1, or the footer affordance) includes a **Report uidex issue** entry. It opens the SDK's built-in feedback form and submits to a uidex-maintained project — separate from your host `cloud` adapter, so SDK bugs surface to the uidex team without contaminating your own ticketing. The report is stamped with the SDK version and the originating view id (e.g. `uidex-sdk:command-palette`); when the SDK cannot reach its endpoint the form falls back to copying a Markdown report to the clipboard.
|
|
323
218
|
|
|
324
|
-
##
|
|
219
|
+
## Scanner
|
|
325
220
|
|
|
326
|
-
|
|
221
|
+
One pipeline, six stages, each stage in its own file and independently callable:
|
|
327
222
|
|
|
328
|
-
|
|
223
|
+
```
|
|
224
|
+
discover(cwd) // locate .uidex.json files across the monorepo
|
|
225
|
+
-> walk(sources) // enumerate files per include/exclude
|
|
226
|
+
-> extract(files) // data-uidex* attrs + export const uidex AST literals
|
|
227
|
+
-> resolve(ann) // apply conventions, compute scopes, back-references
|
|
228
|
+
-> audit(registry) // diagnostics: missing, scope-leak, acceptance coverage
|
|
229
|
+
-> emit(registry) // deterministic uidex.gen.ts
|
|
230
|
+
```
|
|
329
231
|
|
|
330
|
-
|
|
331
|
-
// Disable
|
|
332
|
-
<UidexDevtools inspectShortcut={false} />
|
|
232
|
+
CLI:
|
|
333
233
|
|
|
334
|
-
|
|
335
|
-
|
|
234
|
+
```bash
|
|
235
|
+
npx uidex init # scaffold .uidex.json
|
|
236
|
+
npx uidex scan # run full pipeline
|
|
237
|
+
npx uidex scan --check # fail on registry drift (CI gate)
|
|
238
|
+
npx uidex scan --lint # annotation lint diagnostics (includes legacy-jsdoc)
|
|
239
|
+
npx uidex scan --audit # --check + --lint + coverage
|
|
240
|
+
npx uidex scan --json # machine-readable output
|
|
241
|
+
npx uidex scaffold widget <id> # emit Playwright spec from declared acceptance
|
|
242
|
+
npx uidex claude install # Claude Code rules + skill + hooks
|
|
336
243
|
```
|
|
337
244
|
|
|
338
|
-
|
|
245
|
+
See the [docs site](https://github.com/soel/uidex#docs) for the convention table, annotation surfaces, and the acceptance workflow.
|
|
339
246
|
|
|
340
|
-
|
|
247
|
+
### Bundler plugins
|
|
341
248
|
|
|
342
|
-
|
|
249
|
+
Optional opt-in watch integrations regenerate `uidex.gen.ts` on file save:
|
|
343
250
|
|
|
344
|
-
|
|
345
|
-
|
|
251
|
+
- `@uidex/vite-plugin` — `plugins: [uidex()]` in `vite.config.ts`.
|
|
252
|
+
- `@uidex/webpack-plugin` — `new UidexPlugin()` in `webpack.config.js`.
|
|
253
|
+
- `@uidex/next-plugin` — `withUidex(config)` wrapper for `next.config.*`. Webpack transport today; Turbopack support tracked as a follow-up.
|
|
346
254
|
|
|
347
|
-
|
|
348
|
-
await uidex('submit-btn').click();
|
|
349
|
-
await uidex('success-message').waitFor();
|
|
350
|
-
});
|
|
351
|
-
```
|
|
255
|
+
All three wrap `@uidex/plugin-core` so behaviour and diagnostic shapes are identical across bundlers.
|
|
352
256
|
|
|
353
|
-
|
|
257
|
+
## Config (`.uidex.json`)
|
|
354
258
|
|
|
355
|
-
|
|
259
|
+
Flat schema. Legacy nested `scanner.*`, `defaults`, `colors` are rejected with descriptive errors.
|
|
356
260
|
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"$schema": "./node_modules/uidex/uidex.schema.json",
|
|
264
|
+
"sources": [
|
|
265
|
+
{
|
|
266
|
+
"rootDir": "src",
|
|
267
|
+
"include": ["**/*.{ts,tsx}"],
|
|
268
|
+
"exclude": ["**/*.test.*"]
|
|
269
|
+
}
|
|
366
270
|
],
|
|
367
|
-
|
|
271
|
+
"output": "src/uidex.gen.ts",
|
|
272
|
+
"flows": ["e2e/**/*.spec.ts"],
|
|
273
|
+
"typeMode": "strict",
|
|
274
|
+
"audit": { "scopeLeak": true, "coverage": true, "acceptance": true },
|
|
275
|
+
"conventions": {
|
|
276
|
+
"primitives": ["src/ui/**", "src/components/ui/**"],
|
|
277
|
+
"features": "src/features/*",
|
|
278
|
+
"pages": "auto",
|
|
279
|
+
"flows": ["e2e/**/*.spec.ts"],
|
|
280
|
+
"regions": "landmarks"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
368
283
|
```
|
|
369
284
|
|
|
370
|
-
|
|
285
|
+
Set any `conventions.*` entry to `false` to disable that auto-discovery stage.
|
|
371
286
|
|
|
372
|
-
|
|
287
|
+
`typeMode` controls whether emitted id types are literal unions (`"strict"`, default) or `string` (`"loose"`, a temporary migration knob).
|
|
373
288
|
|
|
374
|
-
|
|
289
|
+
## Acceptance workflow
|
|
375
290
|
|
|
376
|
-
|
|
377
|
-
|
|
291
|
+
Declare criteria in the module-scoped `export const uidex`:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import type { Uidex } from "@/uidex.gen"
|
|
295
|
+
|
|
296
|
+
export const uidex = {
|
|
297
|
+
widget: "video-player",
|
|
298
|
+
acceptance: [
|
|
299
|
+
"Plays on space",
|
|
300
|
+
"Mutes with m",
|
|
301
|
+
"Scrubs with arrow keys",
|
|
302
|
+
],
|
|
303
|
+
} as const satisfies Uidex.Widget
|
|
304
|
+
|
|
305
|
+
export function VideoPlayer() {
|
|
306
|
+
return <div data-uidex-widget="video-player">...</div>
|
|
307
|
+
}
|
|
378
308
|
```
|
|
379
309
|
|
|
380
|
-
|
|
310
|
+
1. Scanner extracts `acceptance` → `entity.meta.acceptance` (in source order).
|
|
311
|
+
2. Flows touching the widget (directly or via descendants) are collected into `meta.flows`.
|
|
312
|
+
3. `scan --audit` flags any criterion not covered by a flow; the hint suggests `uidex scaffold widget <id>`.
|
|
313
|
+
4. `uidex scaffold widget video-player` emits a Playwright spec with one `test()` per criterion, tagged `@uidex:flow`. Re-running is idempotent unless `--force` is passed.
|
|
381
314
|
|
|
382
|
-
|
|
315
|
+
## Cloud + ingest
|
|
383
316
|
|
|
384
|
-
|
|
385
|
-
npx uidex claude install # Add rules + skill + hooks
|
|
386
|
-
npx uidex claude uninstall # Remove everything
|
|
387
|
-
```
|
|
317
|
+
Configure cloud by passing a `CloudAdapter` into `createUidex({ cloud })` or, from React, by passing `projectKey` / `cloud` to `<UidexProvider>`. The built-in client lives at `uidex/cloud`:
|
|
388
318
|
|
|
389
|
-
|
|
319
|
+
```ts
|
|
320
|
+
import { cloud } from "uidex/cloud"
|
|
390
321
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
npx uidex claude hooks add|remove # PostToolUse hook in .claude/settings.json
|
|
322
|
+
const adapter = cloud({ projectKey: "pk_..." })
|
|
323
|
+
await adapter.feedback.submit(payload)
|
|
324
|
+
await adapter.integrations.getConfig()
|
|
395
325
|
```
|
|
396
326
|
|
|
397
|
-
|
|
327
|
+
`cloud()` takes `{ projectKey, endpoint?, fetch? }`. `endpoint` defaults to the production cloud URL; `fetch` allows tests or self-hosted deployments to inject a custom transport. Errors surface as `CloudError` with `status`, `retryAfter`, and `details`.
|
|
398
328
|
|
|
399
|
-
|
|
400
|
-
|--------|-------------|
|
|
401
|
-
| `uidex` | React components (re-exports `uidex/react`) |
|
|
402
|
-
| `uidex/core` | Vanilla JS classes (`createUidexUI`, `Overlay`, `Inspector`) |
|
|
403
|
-
| `uidex/react` | React wrappers (`UidexDevtools`, `UidexOverlay`) |
|
|
404
|
-
| `uidex/playwright` | Test fixture and helpers |
|
|
405
|
-
| `uidex/playwright/reporter` | Coverage reporter |
|
|
406
|
-
| `uidex/api` | API client (Node.js) |
|
|
407
|
-
| `uidex/styles.css` | Standalone CSS (optional, auto-injected via Shadow DOM) |
|
|
329
|
+
When `cloud` is configured, `uidex/ingest` auto-enables:
|
|
408
330
|
|
|
409
|
-
|
|
331
|
+
- Console capture: `warn`/`error`, ring buffer ≤ 50. Originals always invoked.
|
|
332
|
+
- Network capture: failed fetches only, ring buffer ≤ 20. Native `fetch` reference is captured at module load, so `cloud.feedback.submit` always uses an un-intercepted fetch.
|
|
410
333
|
|
|
411
|
-
|
|
412
|
-
npx uidex Show help / list commands
|
|
413
|
-
npx uidex init Create .uidex.json and update .gitignore
|
|
414
|
-
npx uidex scan Run component scanner
|
|
415
|
-
npx uidex scan --audit Validate coverage and annotations
|
|
416
|
-
npx uidex scaffold [dir] Generate Playwright test stubs
|
|
417
|
-
npx uidex claude install Set up Claude Code integration
|
|
418
|
-
npx uidex claude uninstall Remove Claude Code integration
|
|
419
|
-
npx uidex login Authenticate with uidex server
|
|
420
|
-
npx uidex logout Remove stored credentials
|
|
421
|
-
npx uidex link Link current directory to org/project
|
|
422
|
-
npx uidex status Show auth and link state
|
|
423
|
-
npx uidex feedback Manage feedback (list, show, update, delete)
|
|
424
|
-
npx uidex triage Manage triage (run, list, show, approve, dismiss, submit)
|
|
425
|
-
npx uidex integrations Manage integrations (list, add, remove, test, targets)
|
|
426
|
-
```
|
|
334
|
+
Override per-channel via `createUidex({ ingest: { console: {...}, network: null } })`, or pass `ingest: null` to disable entirely.
|
|
427
335
|
|
|
428
|
-
##
|
|
429
|
-
|
|
430
|
-
Full type definitions included:
|
|
336
|
+
## Playwright
|
|
431
337
|
|
|
432
338
|
```ts
|
|
433
|
-
import
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
PrimitiveEntry,
|
|
443
|
-
BorderStyle,
|
|
444
|
-
LabelPosition,
|
|
445
|
-
ButtonPosition,
|
|
446
|
-
KeyboardShortcut,
|
|
447
|
-
OverlayOptions,
|
|
448
|
-
IngestConfig,
|
|
449
|
-
FeedbackReport,
|
|
450
|
-
FeedbackResult,
|
|
451
|
-
FeedbackType,
|
|
452
|
-
FeedbackSeverity,
|
|
453
|
-
} from 'uidex';
|
|
339
|
+
import { test, expect } from "uidex/playwright"
|
|
340
|
+
|
|
341
|
+
test.describe("Add and complete a todo", { tag: "@uidex:flow" }, () => {
|
|
342
|
+
test("adds a todo", async ({ uidex }) => {
|
|
343
|
+
await uidex("todo-text-field").fill("Buy milk")
|
|
344
|
+
await uidex("todo-add-button").click()
|
|
345
|
+
await expect(uidex("todo-item")).toHaveCount(1)
|
|
346
|
+
})
|
|
347
|
+
})
|
|
454
348
|
```
|
|
455
349
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
See `examples/` for working demos:
|
|
350
|
+
Resolves any `data-uidex`, `data-uidex-region`, `data-uidex-widget`, or `data-uidex-primitive`. Register the coverage reporter to emit flow → entity coverage data — it is additive, compatible with any other reporter.
|
|
459
351
|
|
|
460
|
-
|
|
461
|
-
|
|
352
|
+
```ts
|
|
353
|
+
// playwright.config.ts
|
|
354
|
+
reporter: [["list"], ["uidex/playwright/reporter"]]
|
|
355
|
+
```
|
|
462
356
|
|
|
463
|
-
##
|
|
357
|
+
## Scripts
|
|
464
358
|
|
|
465
|
-
|
|
359
|
+
```bash
|
|
360
|
+
pnpm build # build:css + tsup + check:bundles
|
|
361
|
+
pnpm test # vitest (watch)
|
|
362
|
+
pnpm test:run # vitest (run)
|
|
363
|
+
pnpm typecheck # tsc --noEmit
|
|
364
|
+
pnpm lint # eslint src/
|
|
365
|
+
```
|