uidex 0.3.0 → 0.5.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 (44) hide show
  1. package/dist/cli/cli.cjs +1116 -112
  2. package/dist/cli/cli.cjs.map +1 -1
  3. package/dist/cloud/index.cjs +395 -72
  4. package/dist/cloud/index.cjs.map +1 -1
  5. package/dist/cloud/index.d.cts +60 -86
  6. package/dist/cloud/index.d.ts +60 -86
  7. package/dist/cloud/index.js +396 -71
  8. package/dist/cloud/index.js.map +1 -1
  9. package/dist/headless/index.cjs +1505 -791
  10. package/dist/headless/index.cjs.map +1 -1
  11. package/dist/headless/index.d.cts +83 -75
  12. package/dist/headless/index.d.ts +83 -75
  13. package/dist/headless/index.js +1514 -791
  14. package/dist/headless/index.js.map +1 -1
  15. package/dist/index.cjs +6281 -3190
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +337 -229
  18. package/dist/index.d.ts +337 -229
  19. package/dist/index.js +6362 -3231
  20. package/dist/index.js.map +1 -1
  21. package/dist/playwright/index.cjs +4 -4
  22. package/dist/playwright/index.cjs.map +1 -1
  23. package/dist/playwright/index.js +3 -3
  24. package/dist/playwright/index.js.map +1 -1
  25. package/dist/playwright/reporter.cjs +3 -3
  26. package/dist/playwright/reporter.cjs.map +1 -1
  27. package/dist/playwright/reporter.js +3 -3
  28. package/dist/playwright/reporter.js.map +1 -1
  29. package/dist/react/index.cjs +6291 -3206
  30. package/dist/react/index.cjs.map +1 -1
  31. package/dist/react/index.d.cts +239 -186
  32. package/dist/react/index.d.ts +239 -186
  33. package/dist/react/index.js +6338 -3208
  34. package/dist/react/index.js.map +1 -1
  35. package/dist/scan/index.cjs +212 -82
  36. package/dist/scan/index.cjs.map +1 -1
  37. package/dist/scan/index.d.cts +31 -0
  38. package/dist/scan/index.d.ts +31 -0
  39. package/dist/scan/index.js +211 -81
  40. package/dist/scan/index.js.map +1 -1
  41. package/package.json +10 -8
  42. package/templates/claude/api.md +110 -0
  43. package/templates/claude/audit.md +8 -2
  44. package/templates/claude/rules.md +15 -0
@@ -1,6 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ComponentType, ReactElement } from 'react';
3
3
  import { StoreApi } from 'zustand/vanilla';
4
+ import { ReportPayload, ReportResult, IngestConfig, ReportListResponse, PinRecord, ArchiveReason } from '@uidex/api-client';
5
+ import { TemplateResult } from 'lit-html';
4
6
 
5
7
  declare const ENTITY_KINDS: readonly ["route", "page", "feature", "widget", "region", "element", "primitive", "flow"];
6
8
  type EntityKind = (typeof ENTITY_KINDS)[number];
@@ -35,11 +37,16 @@ interface Route {
35
37
  path: string;
36
38
  page: string;
37
39
  }
40
+ interface FlowStep {
41
+ entityId: string;
42
+ action?: string;
43
+ }
38
44
  interface Flow {
39
45
  kind: "flow";
40
46
  id: string;
41
47
  loc: Location;
42
48
  touches: string[];
49
+ steps: FlowStep[];
43
50
  }
44
51
  interface Page extends EntityWithMetaBase {
45
52
  kind: "page";
@@ -64,6 +71,26 @@ type EntityByKind<K extends EntityKind> = Extract<Entity, {
64
71
  kind: K;
65
72
  }>;
66
73
 
74
+ interface ReportRecord {
75
+ id: string;
76
+ entity?: string;
77
+ reporter?: {
78
+ id?: string;
79
+ email?: string;
80
+ name?: string;
81
+ };
82
+ title?: string;
83
+ body: string;
84
+ type: string;
85
+ severity: string;
86
+ status: string;
87
+ labels?: string[];
88
+ url: string;
89
+ route?: string;
90
+ pageTitle?: string;
91
+ screenshot?: string;
92
+ createdAt: string;
93
+ }
67
94
  interface Registry {
68
95
  add(entity: Entity): void;
69
96
  get<K extends EntityKind>(kind: K, id: string): EntityByKind<K> | undefined;
@@ -71,6 +98,17 @@ interface Registry {
71
98
  query(predicate: (entity: Entity) => boolean): Entity[];
72
99
  byScope(scope: Scope): Entity[];
73
100
  touchedBy(flowId: string): Entity[];
101
+ setReports(kind: EntityKind, id: string, reports: readonly ReportRecord[]): void;
102
+ getReports(kind: EntityKind, id: string): readonly ReportRecord[];
103
+ listReportKeys(): readonly string[];
104
+ archiveReport?: (reportId: string, reason?: string) => void | Promise<void>;
105
+ onReportsChange(cb: () => void): () => void;
106
+ }
107
+
108
+ interface UserIdentity {
109
+ id: string;
110
+ name?: string;
111
+ avatar?: string;
74
112
  }
75
113
 
76
114
  type ThemePreference = "light" | "dark" | "auto";
@@ -89,89 +127,60 @@ interface SessionSnapshot {
89
127
  theme: ThemePreference;
90
128
  resolvedTheme: ResolvedTheme;
91
129
  ingestActive: boolean;
92
- }
93
- interface SessionActions {
94
- hover(ref: EntityRef | null): void;
95
- select(ref: EntityRef | null): void;
96
- /**
97
- * Append a view onto the view stack. Pure: never resolves defaults.
98
- */
99
- pushStack(entry: ViewStackEntry): void;
100
- /**
101
- * Remove the top entry from the view stack. No-op when the stack is empty.
102
- */
103
- popStack(): void;
104
- /**
105
- * Empty the view stack.
106
- */
107
- clearStack(): void;
108
- /**
109
- * Open a view by id. If `ref` is omitted, the current `selection` is captured;
110
- * pass `null` explicitly to open the view with no ref context. Thin wrapper
111
- * around `pushStack`.
112
- */
113
- openView(id: string, ref?: EntityRef | null): void;
114
130
  /**
115
- * Clear the entire view stack. Thin wrapper around `clearStack`.
131
+ * Identity for the local user. Set once at session creation; gates realtime
132
+ * features (cursor labels, presence) and auto-populates report attribution.
116
133
  */
117
- closeView(): void;
118
- setInspectorActive(active: boolean): void;
119
- toggleInspector(): void;
120
- /** Pin the overlay to `ref` until `clearPinnedHighlight` is called. */
121
- pinHighlight(ref: EntityRef): void;
122
- clearPinnedHighlight(): void;
123
- setTheme(theme: ThemePreference, resolved?: ResolvedTheme): void;
124
- setIngest(active: boolean): void;
134
+ user: UserIdentity | null;
135
+ }
136
+ type SessionState = SessionSnapshot;
137
+
138
+ interface NavigationState {
139
+ stack: ViewStackEntry[];
125
140
  }
126
- interface SessionState extends SessionSnapshot {
127
- actions: SessionActions;
141
+ interface NavigationActions {
142
+ push(entry: ViewStackEntry): void;
143
+ pop(): void;
144
+ replace(entry: ViewStackEntry): void;
145
+ clear(): void;
146
+ reset(stack: ViewStackEntry[]): void;
128
147
  }
148
+ type NavigationStore = StoreApi<NavigationState> & {
149
+ nav: NavigationActions;
150
+ };
129
151
 
130
- type SurfaceEvent = {
131
- type: "TOGGLE_INSPECTOR";
132
- } | {
133
- type: "OPEN_PALETTE";
134
- } | {
135
- type: "PUSH_VIEW";
136
- entry: ViewStackEntry;
137
- } | {
138
- type: "POP_VIEW";
139
- } | {
140
- type: "CLOSE";
141
- } | {
142
- type: "ESC";
143
- } | {
144
- type: "CMD_K";
145
- } | {
146
- type: "SELECT";
147
- ref: EntityRef | null;
148
- entry: ViewStackEntry;
149
- } | {
150
- type: "SET_SELECTION";
151
- ref: EntityRef | null;
152
- } | {
153
- type: "HOVER";
154
- ref: EntityRef;
155
- element: HTMLElement | null;
156
- color?: string | null;
157
- } | {
158
- type: "UNHOVER";
159
- } | {
160
- type: "PIN";
161
- ref?: EntityRef;
162
- } | {
163
- type: "UNPIN";
164
- } | {
165
- type: "SET_THEME";
166
- theme: ThemePreference;
167
- resolvedTheme: ResolvedTheme;
168
- } | {
169
- type: "SET_INGEST";
170
- active: boolean;
152
+ type SurfaceMode = "idle" | "inspecting" | "palette" | "viewing";
153
+ interface ModeSnapshot {
154
+ mode: SurfaceMode;
155
+ inspectorActive: boolean;
156
+ }
157
+ interface ModeTransitions {
158
+ openPalette(): void;
159
+ openInspector(): void;
160
+ closeInspector(): void;
161
+ toggleInspector(): void;
162
+ enterViewing(initialStack: ViewStackEntry[]): void;
163
+ dismiss(): void;
164
+ popOrTransition(): void;
165
+ pushView(entry: ViewStackEntry): void;
166
+ }
167
+ type ModeStore = StoreApi<ModeSnapshot> & {
168
+ transition: ModeTransitions;
171
169
  };
172
170
 
171
+ interface HighlightActions {
172
+ hover(ref: EntityRef, element?: HTMLElement | null, color?: string | null): void;
173
+ unhover(): void;
174
+ pin(ref?: EntityRef): void;
175
+ unpin(): void;
176
+ }
173
177
  type SessionStore = StoreApi<SessionState> & {
174
- send(event: SurfaceEvent): void;
178
+ readonly nav: NavigationStore;
179
+ readonly mode: ModeStore;
180
+ readonly highlight: HighlightActions;
181
+ select(ref: EntityRef | null): void;
182
+ setTheme(theme: ThemePreference, resolved?: ResolvedTheme): void;
183
+ setIngest(active: boolean): void;
175
184
  };
176
185
 
177
186
  type ConsoleLevel = "warn" | "error";
@@ -215,92 +224,46 @@ interface NetworkCapture {
215
224
  clear(): void;
216
225
  }
217
226
 
218
- interface ConsoleLogEntry {
219
- level: "log" | "warn" | "error" | "info";
220
- message: string;
221
- timestamp: string;
222
- }
223
- interface NetworkErrorEntry {
224
- url: string;
225
- method: string;
226
- status: number | null;
227
- statusText: string | null;
228
- timestamp: string;
229
- }
230
- interface Viewport {
231
- width: number;
232
- height: number;
233
- }
234
- interface SourceRef {
235
- filePath: string;
236
- line: number;
237
- }
238
- interface FeedbackSuggestedTarget {
239
- integrationId: string;
240
- targetConfig: Record<string, unknown>;
241
- }
242
- interface FeedbackPayload {
243
- type: "bug" | "feature" | "improvement" | "question";
244
- severity: "low" | "medium" | "high" | "critical";
245
- title?: string;
246
- description: string;
247
- componentId: string;
248
- element?: string | null;
249
- sources?: SourceRef[];
250
- url: string;
251
- path: string;
252
- route?: string | null;
253
- pageTitle?: string;
254
- sessionId?: string;
255
- reporterEmail?: string;
256
- reporterName?: string;
257
- timestamp: string;
258
- viewport: Viewport;
259
- screen: Viewport;
260
- userAgent: string;
261
- locale?: string;
262
- environment?: string;
263
- appVersion?: string;
264
- consoleLogs?: ConsoleLogEntry[];
265
- networkErrors?: NetworkErrorEntry[];
266
- metadata?: Record<string, string>;
267
- screenshot?: string;
268
- suggestedTarget?: FeedbackSuggestedTarget;
269
- }
270
- interface FeedbackExternalLink {
271
- ok: boolean;
272
- url?: string;
273
- key?: string;
274
- error?: string;
275
- }
276
- interface FeedbackResult {
277
- id: string;
278
- sequenceNumber: number;
279
- externalLink?: FeedbackExternalLink;
280
- }
281
- interface IngestConfigEpic {
282
- key: string;
283
- summary: string;
284
- status: string;
285
- }
286
- interface IngestConfig {
287
- hasJira: boolean;
288
- integrationId?: string;
289
- epics?: IngestConfigEpic[];
290
- }
291
- /**
292
- * Generic so third-party adapters (e.g. `uidex-cloud`) can plug in their own
293
- * payload/result/integration shapes. SDK-bundled `cloud()` returns the
294
- * defaulted form. `ViewContext.cloud` uses the fully-open variant; consumers
295
- * that need the SDK shape narrow with `as CloudAdapter`.
296
- */
297
- interface CloudAdapter<TPayload = FeedbackPayload, TResult = FeedbackResult, TIntegrations = {
227
+ type RealtimePresenceUser = {
228
+ userId: string;
229
+ name: string;
230
+ avatar: string | null;
231
+ };
232
+ type RealtimeChannelState = "connecting" | "connected" | "disconnected";
233
+ interface RealtimeConnectOpts {
234
+ user: UserIdentity;
235
+ route: string;
236
+ }
237
+ interface RealtimeChannel {
238
+ readonly state: RealtimeChannelState;
239
+ connect(): void;
240
+ disconnect(): void;
241
+ joinRoute(route: string): void;
242
+ onPresence(cb: (users: RealtimePresenceUser[]) => void): () => void;
243
+ onPin(cb: (pin: PinRecord) => void): () => void;
244
+ }
245
+ interface CloudAdapter<TPayload = ReportPayload, TResult = ReportResult, TIntegrations = {
298
246
  getConfig(): Promise<IngestConfig>;
247
+ getCachedConfig(): IngestConfig | null;
299
248
  }> {
300
- readonly feedback: {
249
+ readonly reports: {
301
250
  submit(payload: TPayload): Promise<TResult>;
251
+ list?(opts?: {
252
+ page?: number;
253
+ limit?: number;
254
+ }): Promise<ReportListResponse>;
302
255
  };
303
256
  readonly integrations: TIntegrations;
257
+ readonly realtime: {
258
+ connect(opts: RealtimeConnectOpts): RealtimeChannel;
259
+ };
260
+ readonly pins: {
261
+ list(params: {
262
+ route?: string;
263
+ entities?: string;
264
+ }): Promise<PinRecord[]>;
265
+ archive(reportId: string, reason?: ArchiveReason): Promise<void>;
266
+ };
304
267
  }
305
268
 
306
269
  interface IngestOptions {
@@ -327,11 +290,30 @@ interface OverlayShowOptions {
327
290
  borderStyle?: string;
328
291
  borderWidth?: number;
329
292
  fillOpacity?: number;
293
+ backdrop?: boolean;
330
294
  }
331
295
 
332
296
  type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
333
297
 
334
- type ViewSurface = ListSurface | DetailSurface | FormSurface | CustomSurface;
298
+ interface RouteMatch {
299
+ view: View;
300
+ priority: number;
301
+ }
302
+ interface Router {
303
+ resolve(ref: EntityRef): RouteMatch | null;
304
+ add(view: View, priority?: number): void;
305
+ remove(id: string): void;
306
+ get(id: string): View | undefined;
307
+ list(): readonly View[];
308
+ buildStack(entry: ViewStackEntry): ViewStackEntry[];
309
+ navigate(ref: EntityRef): void;
310
+ recents(): readonly EntityRef[];
311
+ favorites(): readonly EntityRef[];
312
+ toggleFavorite(ref: EntityRef): void;
313
+ isFavorite(ref: EntityRef): boolean;
314
+ }
315
+
316
+ type ViewSurface = ListSurface | DetailSurface | FormSurface;
335
317
  interface ListSurface {
336
318
  kind: "list";
337
319
  id: string;
@@ -340,6 +322,12 @@ interface ListSurface {
340
322
  emptyLabel?: string;
341
323
  defaultHighlight?: string;
342
324
  filter?(item: ListItem, query: string): boolean;
325
+ /**
326
+ * When false, the search input is hidden and the content element receives
327
+ * focus directly for keyboard navigation.
328
+ * @default true
329
+ */
330
+ searchable?: boolean;
343
331
  }
344
332
  interface ListItem {
345
333
  value: string;
@@ -359,12 +347,15 @@ interface ListItem {
359
347
  };
360
348
  /**
361
349
  * Optional leading-icon factory used for non-entity rows (e.g. a palette
362
- * "Feedback" command). Called per-render so the returned Node is fresh.
350
+ * "Report" command). Called per-render so the returned Node is fresh.
363
351
  * The shell wraps whatever is returned in a rounded-square tile.
364
352
  */
365
353
  leading?: () => Node;
366
354
  /** Raw data attribute tag appended to the row (for test/e2e hooks). */
367
355
  tag?: string;
356
+ trailing?: TemplateResult;
357
+ /** Contextual actions surfaced in the footer popup when this row is highlighted. */
358
+ actions?: readonly ShellAction[];
368
359
  }
369
360
  interface DetailSurface {
370
361
  kind: "detail";
@@ -388,6 +379,7 @@ interface DetailActionBase {
388
379
  ariaLabel?: string;
389
380
  hint?: string;
390
381
  intent?: ShellActionIntent;
382
+ group?: string;
391
383
  }
392
384
  type DetailAction = (DetailActionBase & {
393
385
  copy: string;
@@ -405,19 +397,21 @@ type DetailAction = (DetailActionBase & {
405
397
  }) | (DetailActionBase & {
406
398
  run: (ctx: DetailActionRunContext) => void | Promise<void>;
407
399
  });
408
- type DetailActionIcon = "copy" | "message-circle" | "chevron-down" | "target";
400
+ type DetailActionIcon = "archive-x" | "copy" | "camera" | "inbox" | "message-circle-warning" | "chevron-down" | "highlighter" | "sticky-note" | "ticket-plus" | "view";
409
401
  interface DetailActionRunContext {
410
402
  setLabel(next: string, durationMs?: number): void;
411
403
  }
404
+ interface ResolvedFlowStep {
405
+ ordinal: number;
406
+ entity: Entity;
407
+ action?: string;
408
+ }
412
409
  type DetailSection = {
413
410
  id: "description";
414
411
  text: string;
415
412
  } | {
416
413
  id: "acceptance";
417
414
  items: readonly string[];
418
- } | {
419
- id: "scopes";
420
- scopes: readonly string[];
421
415
  } | {
422
416
  id: "composes";
423
417
  label: string;
@@ -435,13 +429,26 @@ type DetailSection = {
435
429
  } | {
436
430
  id: "touches";
437
431
  entities: readonly Entity[];
438
- unresolved: readonly string[];
432
+ filterable?: boolean;
433
+ } | {
434
+ id: "steps";
435
+ steps: readonly ResolvedFlowStep[];
439
436
  filterable?: boolean;
440
437
  } | {
441
438
  id: "routes";
442
439
  paths: readonly string[];
443
440
  filterable?: boolean;
441
+ } | {
442
+ id: "screenshot";
443
+ url: string;
444
+ } | {
445
+ id: "metadata";
446
+ entries: readonly MetadataEntry[];
444
447
  };
448
+ interface MetadataEntry {
449
+ label: string;
450
+ value: string;
451
+ }
445
452
  interface FormSurface {
446
453
  kind: "form";
447
454
  id: string;
@@ -454,6 +461,11 @@ interface FormSurface {
454
461
  * routed to the matching `<FieldError>` and submission is skipped.
455
462
  */
456
463
  schema?: StandardSchemaV1<Record<string, unknown>>;
464
+ /**
465
+ * Promise resolving to a base64 screenshot data URL. When provided, the
466
+ * form renderer shows a thumbnail preview above the fields.
467
+ */
468
+ screenshotPreview?: Promise<string | null>;
457
469
  }
458
470
  /**
459
471
  * Minimal StandardSchemaV1 surface — kept inline so the SDK doesn't need a
@@ -533,10 +545,6 @@ type FormField = (FormFieldBase & {
533
545
  kind: "checkbox";
534
546
  value?: boolean;
535
547
  });
536
- interface CustomSurface {
537
- kind: "custom";
538
- render: (ctx: ViewContext, root: HTMLElement) => Cleanup | void;
539
- }
540
548
 
541
549
  interface ViewPalette {
542
550
  label: string;
@@ -547,9 +555,7 @@ interface ViewPalette {
547
555
  */
548
556
  icon?: () => Node;
549
557
  shortcut?: string;
550
- }
551
- interface ViewDirectory {
552
- list(): readonly View[];
558
+ subtitle?: string | (() => string);
553
559
  }
554
560
  interface ViewPushTarget {
555
561
  id: string;
@@ -563,16 +569,19 @@ interface ViewContext {
563
569
  ref: EntityRef | null;
564
570
  registry: Registry;
565
571
  cloud: CloudAdapter<unknown, unknown, unknown> | null;
566
- sdkCloud: CloudAdapter;
567
- views: ViewDirectory;
572
+ /**
573
+ * Local user identity from `createUidex`'s `user` option, or `null` when
574
+ * unconfigured. Views that auto-populate attribution (e.g. report
575
+ * `reporterName`) read this here instead of touching the session store.
576
+ */
577
+ user: UserIdentity | null;
578
+ views: Router;
568
579
  /** Push a view onto the stack. Unregistered ids are a no-op. */
569
580
  push: (target: ViewPushTarget) => void;
570
581
  /** Pop the top view off the stack. No-op when the stack is empty. */
571
582
  pop: () => void;
572
583
  /** Clear the entire view stack. */
573
584
  close: () => void;
574
- /** Resolve the first view matching `ref` and push it onto the stack. */
575
- navigate: (ref: EntityRef) => void;
576
585
  /** Pin the overlay to `ref` until the user presses Esc. */
577
586
  pinHighlight: (ref: EntityRef) => void;
578
587
  /** Shell-owned search input. The shell dispatches its value into the active surface. */
@@ -581,6 +590,11 @@ interface ViewContext {
581
590
  highlight: HighlightController;
582
591
  /** Snapshot of the current view stack (top-most last). */
583
592
  getStack: () => readonly ViewStackEntry[];
593
+ pushEscapeLayer(handler: EscapeHandler): Cleanup;
594
+ /** Called after a report submission succeeds, to refresh pins etc. */
595
+ onAfterSubmit?: () => void;
596
+ /** Return the current route pattern. Falls back to `location.pathname`. */
597
+ getRoute?: () => string;
584
598
  }
585
599
  interface ShellHint {
586
600
  key: string;
@@ -605,6 +619,7 @@ interface ShellAction {
605
619
  id: string;
606
620
  label: string;
607
621
  shortcut?: string;
622
+ icon?: () => Node;
608
623
  perform: () => void | Promise<void>;
609
624
  intent?: ShellActionIntent;
610
625
  }
@@ -625,16 +640,20 @@ interface View {
625
640
  */
626
641
  focusTarget?: (root: HTMLElement, ctx: ViewContext) => HTMLElement | null;
627
642
  surface: (ctx: ViewContext) => ViewSurface;
643
+ /** Direct render for integration adapters (React, etc.). Mutually exclusive with surface renderers. */
644
+ render?: (ctx: ViewContext, root: HTMLElement) => Cleanup | void;
645
+ parent?: (ref: EntityRef | null) => ViewStackEntry | null;
628
646
  }
629
647
 
630
- interface ViewRegistrar {
631
- register(view: View): void;
632
- unregister(id: string): void;
633
- list(): readonly View[];
634
- get(id: string): View | undefined;
635
- findMatch(ref: EntityRef): View | null;
636
- navigate(ref: EntityRef): void;
648
+ interface PaletteShortcut {
649
+ /** The key value (e.g. "k", "j", ";"). Matched case-insensitively. */
650
+ key: string;
651
+ /** Require Cmd (Mac) / Ctrl (Win/Linux). Default: true. */
652
+ mod?: boolean;
653
+ /** Require Shift. Default: false. */
654
+ shift?: boolean;
637
655
  }
656
+ type EscapeHandler = () => boolean;
638
657
 
639
658
  interface CreateUidexOptions {
640
659
  theme?: ThemePreference;
@@ -655,13 +674,33 @@ interface CreateUidexOptions {
655
674
  * an options object to override either channel.
656
675
  */
657
676
  ingest?: IngestOptions | null;
677
+ /** Keyboard shortcut for the command palette. Defaults to Cmd+K / Ctrl+K. */
678
+ shortcut?: PaletteShortcut;
679
+ /**
680
+ * Identity for the local user. Required to opt into realtime collaborative
681
+ * features (peer cursors, presence). When omitted, realtime stays
682
+ * disconnected and report attribution falls back to whatever the user
683
+ * types in the report form.
684
+ */
685
+ user?: UserIdentity;
686
+ /** Git context for pin filtering. Typically mirrors `cloud({ git })`. */
687
+ git?: {
688
+ branch?: string;
689
+ commit?: string;
690
+ };
691
+ /**
692
+ * Return the current route pattern (e.g. `/users/:id`). Used for
693
+ * route-based pin matching and report attribution. When omitted, the SDK
694
+ * falls back to `location.pathname`.
695
+ */
696
+ getRoute?: () => string;
658
697
  }
659
698
  interface Uidex {
660
699
  mount(target?: Element): void;
661
700
  unmount(): void;
662
701
  readonly registry: Registry;
663
702
  readonly session: SessionStore;
664
- readonly views: ViewRegistrar;
703
+ readonly views: Router;
665
704
  readonly cloud: CloudAdapter | null;
666
705
  readonly ingest: Ingest | null;
667
706
  readonly shadowRoot: ShadowRoot | null;
@@ -670,15 +709,16 @@ interface Uidex {
670
709
  interface UidexProviderProps {
671
710
  /**
672
711
  * Pre-built uidex instance (e.g. the `uidex` export from `uidex.gen.ts`).
673
- * When provided, `projectKey`, `cloud`, and `config` are ignored.
712
+ * When provided, `projectKey`, `cloud`, `user`, and `config` are ignored.
674
713
  */
675
714
  instance?: Uidex;
676
715
  projectKey?: string;
677
716
  cloud?: CloudAdapter | null;
678
- config?: Omit<CreateUidexOptions, "cloud">;
717
+ user?: UserIdentity;
718
+ config?: Omit<CreateUidexOptions, "cloud" | "user">;
679
719
  children?: ReactNode;
680
720
  }
681
- declare function UidexProvider({ instance: externalInstance, projectKey, cloud, config, children, }: UidexProviderProps): react_jsx_runtime.JSX.Element;
721
+ declare function UidexProvider({ instance: externalInstance, projectKey, cloud, user, config, children, }: UidexProviderProps): react_jsx_runtime.JSX.Element;
682
722
 
683
723
  declare class UidexContextError extends Error {
684
724
  constructor(hookName: string);
@@ -708,4 +748,17 @@ interface KindChipProps {
708
748
  }
709
749
  declare function KindChip({ kind, label, withKindName, className, }: KindChipProps): ReactElement;
710
750
 
711
- export { KindChip, type KindChipProps, type ReactViewDef, UidexContextError, UidexMount, type UidexMountProps, UidexProvider, type UidexProviderProps, createReactView, useUidex };
751
+ interface UidexDevtoolsProps {
752
+ loadRegistry: (target: Registry) => Registry;
753
+ projectKey?: string;
754
+ endpoint?: string;
755
+ user?: UserIdentity;
756
+ git?: {
757
+ branch?: string;
758
+ commit?: string;
759
+ };
760
+ config?: Omit<CreateUidexOptions, "cloud" | "user" | "git">;
761
+ }
762
+ declare function UidexDevtools({ loadRegistry, projectKey, endpoint, user, git, config, }: UidexDevtoolsProps): react_jsx_runtime.JSX.Element | null;
763
+
764
+ export { KindChip, type KindChipProps, type ReactViewDef, UidexContextError, UidexDevtools, type UidexDevtoolsProps, UidexMount, type UidexMountProps, UidexProvider, type UidexProviderProps, createReactView, useUidex };