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
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { StoreApi } from 'zustand/vanilla';
2
+ import { ReportPayload, ReportResult, IngestConfig, ReportListResponse, PinRecord, ArchiveReason } from '@uidex/api-client';
3
+ export { ArchiveReason, IngestConfig, PinRecord, ReportListRecord, ReportListResponse, ReportPayload, ReportResult } from '@uidex/api-client';
4
+ import { TemplateResult } from 'lit-html';
2
5
  import { IconNode } from 'lucide';
3
6
 
4
7
  declare const ENTITY_KINDS: readonly ["route", "page", "feature", "widget", "region", "element", "primitive", "flow"];
@@ -34,11 +37,16 @@ interface Route {
34
37
  path: string;
35
38
  page: string;
36
39
  }
40
+ interface FlowStep {
41
+ entityId: string;
42
+ action?: string;
43
+ }
37
44
  interface Flow {
38
45
  kind: "flow";
39
46
  id: string;
40
47
  loc: Location;
41
48
  touches: string[];
49
+ steps: FlowStep[];
42
50
  }
43
51
  interface Page extends EntityWithMetaBase {
44
52
  kind: "page";
@@ -71,6 +79,26 @@ declare class UnknownEntityKindError extends Error {
71
79
  }
72
80
  declare function assertEntityKind(kind: string): asserts kind is EntityKind;
73
81
 
82
+ interface ReportRecord {
83
+ id: string;
84
+ entity?: string;
85
+ reporter?: {
86
+ id?: string;
87
+ email?: string;
88
+ name?: string;
89
+ };
90
+ title?: string;
91
+ body: string;
92
+ type: string;
93
+ severity: string;
94
+ status: string;
95
+ labels?: string[];
96
+ url: string;
97
+ route?: string;
98
+ pageTitle?: string;
99
+ screenshot?: string;
100
+ createdAt: string;
101
+ }
74
102
  interface Registry {
75
103
  add(entity: Entity): void;
76
104
  get<K extends EntityKind>(kind: K, id: string): EntityByKind<K> | undefined;
@@ -78,6 +106,11 @@ interface Registry {
78
106
  query(predicate: (entity: Entity) => boolean): Entity[];
79
107
  byScope(scope: Scope): Entity[];
80
108
  touchedBy(flowId: string): Entity[];
109
+ setReports(kind: EntityKind, id: string, reports: readonly ReportRecord[]): void;
110
+ getReports(kind: EntityKind, id: string): readonly ReportRecord[];
111
+ listReportKeys(): readonly string[];
112
+ archiveReport?: (reportId: string, reason?: string) => void | Promise<void>;
113
+ onReportsChange(cb: () => void): () => void;
81
114
  }
82
115
  declare function createRegistry(): Registry;
83
116
 
@@ -93,11 +126,10 @@ declare const KIND_STYLE: Record<EntityKind, KindStyleEntry>;
93
126
  declare function prettify(id: string): string;
94
127
  declare function displayName(entity: Entity, node?: Element | null): string;
95
128
 
96
- interface HighlightContext {
97
- ref: EntityRef | null;
98
- element: HTMLElement | null;
99
- pinnedRef: EntityRef | null;
100
- color: string | null;
129
+ interface UserIdentity {
130
+ id: string;
131
+ name?: string;
132
+ avatar?: string;
101
133
  }
102
134
 
103
135
  type ThemePreference = "light" | "dark" | "auto";
@@ -116,114 +148,84 @@ interface SessionSnapshot {
116
148
  theme: ThemePreference;
117
149
  resolvedTheme: ResolvedTheme;
118
150
  ingestActive: boolean;
119
- }
120
- interface SessionActions {
121
- hover(ref: EntityRef | null): void;
122
- select(ref: EntityRef | null): void;
123
151
  /**
124
- * Append a view onto the view stack. Pure: never resolves defaults.
152
+ * Identity for the local user. Set once at session creation; gates realtime
153
+ * features (cursor labels, presence) and auto-populates report attribution.
125
154
  */
126
- pushStack(entry: ViewStackEntry): void;
127
- /**
128
- * Remove the top entry from the view stack. No-op when the stack is empty.
129
- */
130
- popStack(): void;
131
- /**
132
- * Empty the view stack.
133
- */
134
- clearStack(): void;
135
- /**
136
- * Open a view by id. If `ref` is omitted, the current `selection` is captured;
137
- * pass `null` explicitly to open the view with no ref context. Thin wrapper
138
- * around `pushStack`.
139
- */
140
- openView(id: string, ref?: EntityRef | null): void;
141
- /**
142
- * Clear the entire view stack. Thin wrapper around `clearStack`.
143
- */
144
- closeView(): void;
145
- setInspectorActive(active: boolean): void;
146
- toggleInspector(): void;
147
- /** Pin the overlay to `ref` until `clearPinnedHighlight` is called. */
148
- pinHighlight(ref: EntityRef): void;
149
- clearPinnedHighlight(): void;
150
- setTheme(theme: ThemePreference, resolved?: ResolvedTheme): void;
151
- setIngest(active: boolean): void;
152
- }
153
- interface SessionState extends SessionSnapshot {
154
- actions: SessionActions;
155
+ user: UserIdentity | null;
155
156
  }
157
+ type SessionState = SessionSnapshot;
156
158
 
157
- type SurfaceEvent = {
158
- type: "TOGGLE_INSPECTOR";
159
- } | {
160
- type: "OPEN_PALETTE";
161
- } | {
162
- type: "PUSH_VIEW";
163
- entry: ViewStackEntry;
164
- } | {
165
- type: "POP_VIEW";
166
- } | {
167
- type: "CLOSE";
168
- } | {
169
- type: "ESC";
170
- } | {
171
- type: "CMD_K";
172
- } | {
173
- type: "SELECT";
174
- ref: EntityRef | null;
175
- entry: ViewStackEntry;
176
- } | {
177
- type: "SET_SELECTION";
178
- ref: EntityRef | null;
179
- } | {
180
- type: "HOVER";
181
- ref: EntityRef;
182
- element: HTMLElement | null;
183
- color?: string | null;
184
- } | {
185
- type: "UNHOVER";
186
- } | {
187
- type: "PIN";
188
- ref?: EntityRef;
189
- } | {
190
- type: "UNPIN";
191
- } | {
192
- type: "SET_THEME";
193
- theme: ThemePreference;
194
- resolvedTheme: ResolvedTheme;
195
- } | {
196
- type: "SET_INGEST";
197
- active: boolean;
159
+ interface NavigationState {
160
+ stack: ViewStackEntry[];
161
+ }
162
+ interface NavigationActions {
163
+ push(entry: ViewStackEntry): void;
164
+ pop(): void;
165
+ replace(entry: ViewStackEntry): void;
166
+ clear(): void;
167
+ reset(stack: ViewStackEntry[]): void;
168
+ }
169
+ type NavigationStore = StoreApi<NavigationState> & {
170
+ nav: NavigationActions;
198
171
  };
172
+ declare function createNavigationStore(): NavigationStore;
199
173
 
200
- type SessionStore = StoreApi<SessionState> & {
201
- send(event: SurfaceEvent): void;
202
- };
203
- interface SurfaceBindings {
174
+ type SurfaceMode = "idle" | "inspecting" | "palette" | "viewing";
175
+ interface ModeSnapshot {
176
+ mode: SurfaceMode;
177
+ inspectorActive: boolean;
178
+ }
179
+ interface ModeBindings {
204
180
  mountInspector?: () => void;
205
181
  destroyInspector?: () => void;
206
- openActionsPopup?: () => void;
207
- closePopup?: () => void;
208
182
  }
209
- interface HighlightBindings {
210
- showOverlay?: (context: HighlightContext) => void;
211
- hideOverlay?: () => void;
212
- updateOverlay?: (context: HighlightContext) => void;
183
+ interface ModeTransitions {
184
+ openPalette(): void;
185
+ openInspector(): void;
186
+ closeInspector(): void;
187
+ toggleInspector(): void;
188
+ enterViewing(initialStack: ViewStackEntry[]): void;
189
+ dismiss(): void;
190
+ popOrTransition(): void;
191
+ pushView(entry: ViewStackEntry): void;
213
192
  }
214
- interface SessionBindings {
215
- surface?: SurfaceBindings;
216
- highlight?: HighlightBindings;
193
+ type ModeStore = StoreApi<ModeSnapshot> & {
194
+ transition: ModeTransitions;
195
+ };
196
+ interface CreateModeStoreOptions {
197
+ nav: NavigationStore;
198
+ bindings?: ModeBindings;
217
199
  }
200
+ declare function createModeStore(options: CreateModeStoreOptions): ModeStore;
201
+
202
+ interface HighlightContext {
203
+ ref: EntityRef | null;
204
+ element: HTMLElement | null;
205
+ pinnedRef: EntityRef | null;
206
+ color: string | null;
207
+ }
208
+ interface HighlightActions {
209
+ hover(ref: EntityRef, element?: HTMLElement | null, color?: string | null): void;
210
+ unhover(): void;
211
+ pin(ref?: EntityRef): void;
212
+ unpin(): void;
213
+ }
214
+ type SessionStore = StoreApi<SessionState> & {
215
+ readonly nav: NavigationStore;
216
+ readonly mode: ModeStore;
217
+ readonly highlight: HighlightActions;
218
+ select(ref: EntityRef | null): void;
219
+ setTheme(theme: ThemePreference, resolved?: ResolvedTheme): void;
220
+ setIngest(active: boolean): void;
221
+ };
218
222
  interface CreateSessionOptions extends Partial<SessionSnapshot> {
219
223
  detectTheme?: () => ResolvedTheme;
220
- /**
221
- * Action implementations injected into the surface machine and its
222
- * highlight child actor. Implementations may be closures that read from
223
- * mutable refs filled in after `createSession` returns (e.g., the SDK's
224
- * shell isn't available until `mount`).
225
- */
226
- bindings?: SessionBindings;
224
+ onMountInspector?: () => void;
225
+ onDestroyInspector?: () => void;
226
+ onShowOverlay?: (context: HighlightContext) => void;
227
+ onHideOverlay?: () => void;
228
+ onUpdateOverlay?: (context: HighlightContext) => void;
227
229
  }
228
230
  declare function resolveTheme(preference: ThemePreference, detect?: () => ResolvedTheme): ResolvedTheme;
229
231
  declare function createSession(options?: CreateSessionOptions): SessionStore;
@@ -274,92 +276,46 @@ declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkC
274
276
  declare const nativeFetch: typeof fetch | undefined;
275
277
  declare function getNativeFetch(): typeof fetch | undefined;
276
278
 
277
- interface ConsoleLogEntry {
278
- level: "log" | "warn" | "error" | "info";
279
- message: string;
280
- timestamp: string;
281
- }
282
- interface NetworkErrorEntry {
283
- url: string;
284
- method: string;
285
- status: number | null;
286
- statusText: string | null;
287
- timestamp: string;
288
- }
289
- interface Viewport {
290
- width: number;
291
- height: number;
292
- }
293
- interface SourceRef {
294
- filePath: string;
295
- line: number;
296
- }
297
- interface FeedbackSuggestedTarget {
298
- integrationId: string;
299
- targetConfig: Record<string, unknown>;
300
- }
301
- interface FeedbackPayload {
302
- type: "bug" | "feature" | "improvement" | "question";
303
- severity: "low" | "medium" | "high" | "critical";
304
- title?: string;
305
- description: string;
306
- componentId: string;
307
- element?: string | null;
308
- sources?: SourceRef[];
309
- url: string;
310
- path: string;
311
- route?: string | null;
312
- pageTitle?: string;
313
- sessionId?: string;
314
- reporterEmail?: string;
315
- reporterName?: string;
316
- timestamp: string;
317
- viewport: Viewport;
318
- screen: Viewport;
319
- userAgent: string;
320
- locale?: string;
321
- environment?: string;
322
- appVersion?: string;
323
- consoleLogs?: ConsoleLogEntry[];
324
- networkErrors?: NetworkErrorEntry[];
325
- metadata?: Record<string, string>;
326
- screenshot?: string;
327
- suggestedTarget?: FeedbackSuggestedTarget;
328
- }
329
- interface FeedbackExternalLink {
330
- ok: boolean;
331
- url?: string;
332
- key?: string;
333
- error?: string;
334
- }
335
- interface FeedbackResult {
336
- id: string;
337
- sequenceNumber: number;
338
- externalLink?: FeedbackExternalLink;
339
- }
340
- interface IngestConfigEpic {
341
- key: string;
342
- summary: string;
343
- status: string;
344
- }
345
- interface IngestConfig {
346
- hasJira: boolean;
347
- integrationId?: string;
348
- epics?: IngestConfigEpic[];
349
- }
350
- /**
351
- * Generic so third-party adapters (e.g. `uidex-cloud`) can plug in their own
352
- * payload/result/integration shapes. SDK-bundled `cloud()` returns the
353
- * defaulted form. `ViewContext.cloud` uses the fully-open variant; consumers
354
- * that need the SDK shape narrow with `as CloudAdapter`.
355
- */
356
- interface CloudAdapter<TPayload = FeedbackPayload, TResult = FeedbackResult, TIntegrations = {
279
+ type RealtimePresenceUser = {
280
+ userId: string;
281
+ name: string;
282
+ avatar: string | null;
283
+ };
284
+ type RealtimeChannelState = "connecting" | "connected" | "disconnected";
285
+ interface RealtimeConnectOpts {
286
+ user: UserIdentity;
287
+ route: string;
288
+ }
289
+ interface RealtimeChannel {
290
+ readonly state: RealtimeChannelState;
291
+ connect(): void;
292
+ disconnect(): void;
293
+ joinRoute(route: string): void;
294
+ onPresence(cb: (users: RealtimePresenceUser[]) => void): () => void;
295
+ onPin(cb: (pin: PinRecord) => void): () => void;
296
+ }
297
+ interface CloudAdapter<TPayload = ReportPayload, TResult = ReportResult, TIntegrations = {
357
298
  getConfig(): Promise<IngestConfig>;
299
+ getCachedConfig(): IngestConfig | null;
358
300
  }> {
359
- readonly feedback: {
301
+ readonly reports: {
360
302
  submit(payload: TPayload): Promise<TResult>;
303
+ list?(opts?: {
304
+ page?: number;
305
+ limit?: number;
306
+ }): Promise<ReportListResponse>;
361
307
  };
362
308
  readonly integrations: TIntegrations;
309
+ readonly realtime: {
310
+ connect(opts: RealtimeConnectOpts): RealtimeChannel;
311
+ };
312
+ readonly pins: {
313
+ list(params: {
314
+ route?: string;
315
+ entities?: string;
316
+ }): Promise<PinRecord[]>;
317
+ archive(reportId: string, reason?: ArchiveReason): Promise<void>;
318
+ };
363
319
  }
364
320
 
365
321
  interface IngestOptions {
@@ -410,6 +366,10 @@ interface CleanupStack {
410
366
  type CursorTooltipContent = {
411
367
  entity: Entity;
412
368
  node?: Element | null;
369
+ layer?: {
370
+ index: number;
371
+ total: number;
372
+ };
413
373
  } | string;
414
374
  interface CursorTooltip {
415
375
  destroy(): void;
@@ -432,15 +392,17 @@ interface OverlayShowOptions {
432
392
  borderStyle?: string;
433
393
  borderWidth?: number;
434
394
  fillOpacity?: number;
395
+ backdrop?: boolean;
435
396
  }
436
397
  interface Overlay {
437
398
  show(target: Element, options?: OverlayShowOptions): void;
438
399
  hide(): void;
439
400
  destroy(): void;
401
+ onDismiss: (() => void) | null;
440
402
  readonly isVisible: boolean;
441
403
  }
442
404
  interface OverlayDeps {
443
- container: Element;
405
+ container: Element | ShadowRoot;
444
406
  }
445
407
  declare function createOverlay(deps: OverlayDeps): Overlay;
446
408
 
@@ -451,11 +413,16 @@ interface InspectorMatch {
451
413
  /** Display name resolved via `displayName(entity, element)`. */
452
414
  label: string;
453
415
  }
416
+ interface InspectorMatchStack {
417
+ matches: InspectorMatch[];
418
+ index: number;
419
+ current: InspectorMatch;
420
+ }
454
421
  interface InspectorOptions {
455
422
  session: SessionStore;
456
423
  registry?: Registry;
457
- resolve?: (target: Element) => InspectorMatch | null;
458
- onHover?: (match: InspectorMatch | null, cursor: {
424
+ resolveAll?: (target: Element) => InspectorMatch[];
425
+ onHover?: (stack: InspectorMatchStack | null, cursor: {
459
426
  x: number;
460
427
  y: number;
461
428
  } | null) => void;
@@ -463,6 +430,10 @@ interface InspectorOptions {
463
430
  x: number;
464
431
  y: number;
465
432
  }) => void;
433
+ onCycle?: (stack: InspectorMatchStack, cursor: {
434
+ x: number;
435
+ y: number;
436
+ }) => void;
466
437
  }
467
438
  interface Inspector {
468
439
  mount(): void;
@@ -477,17 +448,67 @@ interface HighlightControllerLike {
477
448
  declare function createHighlightController(overlay: Overlay): HighlightControllerLike;
478
449
  declare function createInspector(options: InspectorOptions): Inspector;
479
450
 
451
+ type PinMatchMode = "route" | "pathname" | "component";
452
+ interface PinFilter {
453
+ branch: string | null;
454
+ commit: string | null;
455
+ }
456
+ interface PinFilterState extends PinFilter {
457
+ readonly commits: readonly string[];
458
+ readonly commitIndex: number;
459
+ }
460
+ interface PinLayer {
461
+ setPins(pins: readonly PinRecord[]): void;
462
+ addPin(pin: PinRecord): void;
463
+ removePin(reportId: string): void;
464
+ clear(): void;
465
+ getPinsForElement(componentId: string): readonly PinRecord[];
466
+ getAllPinsForElement(componentId: string): readonly PinRecord[];
467
+ getAllPins(): readonly PinRecord[];
468
+ attachChannel(channel: RealtimeChannel): () => void;
469
+ attachCloud(opts: AttachCloudOpts): () => void;
470
+ refresh(): Promise<void>;
471
+ readonly filterState: PinFilterState;
472
+ setFilter(filter: Partial<PinFilter>): void;
473
+ nextCommit(): void;
474
+ prevCommit(): void;
475
+ onFilterChange(cb: () => void): () => void;
476
+ readonly visible: boolean;
477
+ setVisible(visible: boolean): void;
478
+ destroy(): void;
479
+ }
480
+ interface AttachCloudOpts {
481
+ cloud: Pick<CloudAdapter, "pins">;
482
+ channel?: RealtimeChannel | null;
483
+ getRoute: () => string;
484
+ getPathname: () => string;
485
+ getVisibleEntities: () => string[];
486
+ getMatchMode: () => PinMatchMode;
487
+ onError?: (err: unknown) => void;
488
+ }
489
+ interface PinLayerOptions {
490
+ container: Element | ShadowRoot;
491
+ onOpenPinDetail?: (componentId: string, reportId: string) => void;
492
+ onHoverPin?: (anchor: HTMLElement | null, componentId: string | null) => void;
493
+ onPinsChanged?: () => void;
494
+ currentBranch?: string | null;
495
+ }
496
+ declare function createPinLayer(options: PinLayerOptions): PinLayer;
497
+
480
498
  type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
481
499
  interface MenuBarOptions {
482
500
  container: Element;
483
501
  session: SessionStore;
484
502
  initialCorner?: Corner;
485
503
  appTitle?: string;
504
+ channel?: RealtimeChannel | null;
505
+ pinLayer?: PinLayer | null;
486
506
  }
487
507
  interface MenuBar {
488
508
  destroy(): void;
489
509
  snapTo(corner: Corner): void;
490
510
  snapToNearest(x: number, y: number): void;
511
+ setPinLayer(layer: PinLayer): void;
491
512
  readonly corner: Corner;
492
513
  readonly root: HTMLElement;
493
514
  }
@@ -511,13 +532,18 @@ interface CreateSurfaceShellOptions {
511
532
  stylesheets?: string[];
512
533
  initialCorner?: Corner;
513
534
  appTitle?: string;
535
+ channel?: RealtimeChannel | null;
536
+ pinLayer?: PinLayer | null;
514
537
  /**
515
538
  * Additional inspector wiring. Hover is pre-wired to overlay + tooltip; pass
516
539
  * `onSelect` (and `onAfterHover`) to extend behaviour without rebuilding the
517
540
  * host.
518
541
  */
519
542
  inspector?: Pick<InspectorOptions, "onSelect"> & {
520
- onAfterHover?: InspectorOptions["onHover"];
543
+ onAfterHover?: (match: InspectorMatch | null, cursor: {
544
+ x: number;
545
+ y: number;
546
+ } | null) => void;
521
547
  };
522
548
  }
523
549
  interface SurfaceShell {
@@ -538,7 +564,29 @@ interface SurfaceShell {
538
564
  */
539
565
  declare function createSurfaceShell(options: CreateSurfaceShellOptions): SurfaceShell;
540
566
 
541
- type ViewSurface = ListSurface | DetailSurface | FormSurface | CustomSurface;
567
+ interface RouteMatch {
568
+ view: View;
569
+ priority: number;
570
+ }
571
+ interface Router {
572
+ resolve(ref: EntityRef): RouteMatch | null;
573
+ add(view: View, priority?: number): void;
574
+ remove(id: string): void;
575
+ get(id: string): View | undefined;
576
+ list(): readonly View[];
577
+ buildStack(entry: ViewStackEntry): ViewStackEntry[];
578
+ navigate(ref: EntityRef): void;
579
+ recents(): readonly EntityRef[];
580
+ favorites(): readonly EntityRef[];
581
+ toggleFavorite(ref: EntityRef): void;
582
+ isFavorite(ref: EntityRef): boolean;
583
+ }
584
+ interface CreateRouterOptions {
585
+ session: SessionStore;
586
+ }
587
+ declare function createRouter(options: CreateRouterOptions): Router;
588
+
589
+ type ViewSurface = ListSurface | DetailSurface | FormSurface;
542
590
  interface ListSurface {
543
591
  kind: "list";
544
592
  id: string;
@@ -547,6 +595,12 @@ interface ListSurface {
547
595
  emptyLabel?: string;
548
596
  defaultHighlight?: string;
549
597
  filter?(item: ListItem, query: string): boolean;
598
+ /**
599
+ * When false, the search input is hidden and the content element receives
600
+ * focus directly for keyboard navigation.
601
+ * @default true
602
+ */
603
+ searchable?: boolean;
550
604
  }
551
605
  interface ListItem {
552
606
  value: string;
@@ -566,12 +620,15 @@ interface ListItem {
566
620
  };
567
621
  /**
568
622
  * Optional leading-icon factory used for non-entity rows (e.g. a palette
569
- * "Feedback" command). Called per-render so the returned Node is fresh.
623
+ * "Report" command). Called per-render so the returned Node is fresh.
570
624
  * The shell wraps whatever is returned in a rounded-square tile.
571
625
  */
572
626
  leading?: () => Node;
573
627
  /** Raw data attribute tag appended to the row (for test/e2e hooks). */
574
628
  tag?: string;
629
+ trailing?: TemplateResult;
630
+ /** Contextual actions surfaced in the footer popup when this row is highlighted. */
631
+ actions?: readonly ShellAction[];
575
632
  }
576
633
  interface DetailSurface {
577
634
  kind: "detail";
@@ -595,6 +652,7 @@ interface DetailActionBase {
595
652
  ariaLabel?: string;
596
653
  hint?: string;
597
654
  intent?: ShellActionIntent;
655
+ group?: string;
598
656
  }
599
657
  type DetailAction = (DetailActionBase & {
600
658
  copy: string;
@@ -612,19 +670,21 @@ type DetailAction = (DetailActionBase & {
612
670
  }) | (DetailActionBase & {
613
671
  run: (ctx: DetailActionRunContext) => void | Promise<void>;
614
672
  });
615
- type DetailActionIcon = "copy" | "message-circle" | "chevron-down" | "target";
673
+ type DetailActionIcon = "archive-x" | "copy" | "camera" | "inbox" | "message-circle-warning" | "chevron-down" | "highlighter" | "sticky-note" | "ticket-plus" | "view";
616
674
  interface DetailActionRunContext {
617
675
  setLabel(next: string, durationMs?: number): void;
618
676
  }
677
+ interface ResolvedFlowStep {
678
+ ordinal: number;
679
+ entity: Entity;
680
+ action?: string;
681
+ }
619
682
  type DetailSection = {
620
683
  id: "description";
621
684
  text: string;
622
685
  } | {
623
686
  id: "acceptance";
624
687
  items: readonly string[];
625
- } | {
626
- id: "scopes";
627
- scopes: readonly string[];
628
688
  } | {
629
689
  id: "composes";
630
690
  label: string;
@@ -642,13 +702,26 @@ type DetailSection = {
642
702
  } | {
643
703
  id: "touches";
644
704
  entities: readonly Entity[];
645
- unresolved: readonly string[];
705
+ filterable?: boolean;
706
+ } | {
707
+ id: "steps";
708
+ steps: readonly ResolvedFlowStep[];
646
709
  filterable?: boolean;
647
710
  } | {
648
711
  id: "routes";
649
712
  paths: readonly string[];
650
713
  filterable?: boolean;
714
+ } | {
715
+ id: "screenshot";
716
+ url: string;
717
+ } | {
718
+ id: "metadata";
719
+ entries: readonly MetadataEntry[];
651
720
  };
721
+ interface MetadataEntry {
722
+ label: string;
723
+ value: string;
724
+ }
652
725
  interface FormSurface {
653
726
  kind: "form";
654
727
  id: string;
@@ -661,6 +734,11 @@ interface FormSurface {
661
734
  * routed to the matching `<FieldError>` and submission is skipped.
662
735
  */
663
736
  schema?: StandardSchemaV1<Record<string, unknown>>;
737
+ /**
738
+ * Promise resolving to a base64 screenshot data URL. When provided, the
739
+ * form renderer shows a thumbnail preview above the fields.
740
+ */
741
+ screenshotPreview?: Promise<string | null>;
664
742
  }
665
743
  /**
666
744
  * Minimal StandardSchemaV1 surface — kept inline so the SDK doesn't need a
@@ -740,10 +818,6 @@ type FormField = (FormFieldBase & {
740
818
  kind: "checkbox";
741
819
  value?: boolean;
742
820
  });
743
- interface CustomSurface {
744
- kind: "custom";
745
- render: (ctx: ViewContext, root: HTMLElement) => Cleanup | void;
746
- }
747
821
 
748
822
  interface ViewPalette {
749
823
  label: string;
@@ -754,9 +828,7 @@ interface ViewPalette {
754
828
  */
755
829
  icon?: () => Node;
756
830
  shortcut?: string;
757
- }
758
- interface ViewDirectory {
759
- list(): readonly View[];
831
+ subtitle?: string | (() => string);
760
832
  }
761
833
  interface ViewPushTarget {
762
834
  id: string;
@@ -770,16 +842,19 @@ interface ViewContext {
770
842
  ref: EntityRef | null;
771
843
  registry: Registry;
772
844
  cloud: CloudAdapter<unknown, unknown, unknown> | null;
773
- sdkCloud: CloudAdapter;
774
- views: ViewDirectory;
845
+ /**
846
+ * Local user identity from `createUidex`'s `user` option, or `null` when
847
+ * unconfigured. Views that auto-populate attribution (e.g. report
848
+ * `reporterName`) read this here instead of touching the session store.
849
+ */
850
+ user: UserIdentity | null;
851
+ views: Router;
775
852
  /** Push a view onto the stack. Unregistered ids are a no-op. */
776
853
  push: (target: ViewPushTarget) => void;
777
854
  /** Pop the top view off the stack. No-op when the stack is empty. */
778
855
  pop: () => void;
779
856
  /** Clear the entire view stack. */
780
857
  close: () => void;
781
- /** Resolve the first view matching `ref` and push it onto the stack. */
782
- navigate: (ref: EntityRef) => void;
783
858
  /** Pin the overlay to `ref` until the user presses Esc. */
784
859
  pinHighlight: (ref: EntityRef) => void;
785
860
  /** Shell-owned search input. The shell dispatches its value into the active surface. */
@@ -788,6 +863,11 @@ interface ViewContext {
788
863
  highlight: HighlightController;
789
864
  /** Snapshot of the current view stack (top-most last). */
790
865
  getStack: () => readonly ViewStackEntry[];
866
+ pushEscapeLayer(handler: EscapeHandler): Cleanup;
867
+ /** Called after a report submission succeeds, to refresh pins etc. */
868
+ onAfterSubmit?: () => void;
869
+ /** Return the current route pattern. Falls back to `location.pathname`. */
870
+ getRoute?: () => string;
791
871
  }
792
872
  interface ShellHint {
793
873
  key: string;
@@ -812,6 +892,7 @@ interface ShellAction {
812
892
  id: string;
813
893
  label: string;
814
894
  shortcut?: string;
895
+ icon?: () => Node;
815
896
  perform: () => void | Promise<void>;
816
897
  intent?: ShellActionIntent;
817
898
  }
@@ -832,51 +913,56 @@ interface View {
832
913
  */
833
914
  focusTarget?: (root: HTMLElement, ctx: ViewContext) => HTMLElement | null;
834
915
  surface: (ctx: ViewContext) => ViewSurface;
916
+ /** Direct render for integration adapters (React, etc.). Mutually exclusive with surface renderers. */
917
+ render?: (ctx: ViewContext, root: HTMLElement) => Cleanup | void;
918
+ parent?: (ref: EntityRef | null) => ViewStackEntry | null;
835
919
  }
836
920
  declare class ViewValidationError extends Error {
837
921
  constructor(message: string);
838
922
  }
839
923
 
840
924
  interface ActionsPopupHandle {
925
+ readonly trigger: HTMLButtonElement;
841
926
  readonly element: HTMLElement;
842
- open(actions: ShellAction[]): void;
927
+ setActions(actions: ShellAction[]): void;
928
+ open(): void;
843
929
  close(): void;
844
930
  isOpen(): boolean;
845
- handleKey(e: KeyboardEvent): boolean;
846
931
  destroy(): void;
847
932
  }
848
933
 
934
+ interface PaletteShortcut {
935
+ /** The key value (e.g. "k", "j", ";"). Matched case-insensitively. */
936
+ key: string;
937
+ /** Require Cmd (Mac) / Ctrl (Win/Linux). Default: true. */
938
+ mod?: boolean;
939
+ /** Require Shift. Default: false. */
940
+ shift?: boolean;
941
+ }
942
+ declare function formatShortcutLabel(shortcut: PaletteShortcut): string;
943
+ type EscapeHandler = () => boolean;
944
+
849
945
  declare const SURFACE_HOST_CLASS = "uidex-surface-host";
850
946
  declare const SURFACE_CONTAINER_CLASS = "uidex-container";
851
- declare const Z_BASE = 2147483644;
852
- declare const Z_OVERLAY = 2147483645;
853
- declare const Z_CHROME = 2147483646;
947
+ declare const Z_BASE = 2147483630;
948
+ declare const Z_OVERLAY = 2147483635;
949
+ declare const Z_PIN_LAYER = 2147483640;
950
+ declare const Z_CHROME = 2147483645;
854
951
  declare const SURFACE_IGNORE_SELECTOR = ".uidex-surface-host,.uidex-container";
855
952
 
856
- interface ViewRegistrar {
857
- register(view: View): void;
858
- unregister(id: string): void;
859
- list(): readonly View[];
860
- get(id: string): View | undefined;
861
- findMatch(ref: EntityRef): View | null;
862
- navigate(ref: EntityRef): void;
863
- }
864
- interface ViewRegistrarOptions {
865
- session: SessionStore;
866
- dev?: boolean;
867
- }
868
- declare function createViewRegistrar(options: ViewRegistrarOptions): ViewRegistrar;
869
-
870
953
  interface ViewStackOptions {
871
954
  container: HTMLElement;
872
- views: ViewRegistrar;
955
+ views: Router;
873
956
  session: SessionStore;
874
957
  registry: Registry;
875
958
  cloud?: CloudAdapter | null;
876
- sdkCloud: CloudAdapter;
877
959
  highlight: HighlightController;
878
960
  globalActions?: (ctx: ViewContext) => ShellAction[];
961
+ shortcut?: PaletteShortcut;
879
962
  dev?: boolean;
963
+ pushEscapeLayer?: ViewContext["pushEscapeLayer"];
964
+ onAfterSubmit?: () => void;
965
+ getRoute?: () => string;
880
966
  }
881
967
  interface ViewStack {
882
968
  /**
@@ -889,7 +975,7 @@ interface ViewStack {
889
975
  }
890
976
  declare function createViewStack(options: ViewStackOptions): ViewStack;
891
977
 
892
- declare const commandPaletteView: View;
978
+ declare function createCommandPaletteView(shortcut?: PaletteShortcut): View;
893
979
 
894
980
  declare const componentDetailView: View;
895
981
  declare const featureDetailView: View;
@@ -898,11 +984,13 @@ declare const widgetDetailView: View;
898
984
  declare const primitiveDetailView: View;
899
985
  declare const pageDetailView: View;
900
986
 
901
- declare const feedbackView: View;
987
+ declare const reportView: View;
902
988
 
903
989
  declare const flowDetailView: View;
904
990
 
905
- declare const DEFAULT_VIEWS: readonly [View, View, View, View, View, View, View, View, View, View, View, View];
991
+ declare const pinSettingsView: View;
992
+
993
+ declare function buildDefaultViews(shortcut?: PaletteShortcut): View[];
906
994
 
907
995
  interface CreateUidexOptions {
908
996
  theme?: ThemePreference;
@@ -923,17 +1011,37 @@ interface CreateUidexOptions {
923
1011
  * an options object to override either channel.
924
1012
  */
925
1013
  ingest?: IngestOptions | null;
1014
+ /** Keyboard shortcut for the command palette. Defaults to Cmd+K / Ctrl+K. */
1015
+ shortcut?: PaletteShortcut;
1016
+ /**
1017
+ * Identity for the local user. Required to opt into realtime collaborative
1018
+ * features (peer cursors, presence). When omitted, realtime stays
1019
+ * disconnected and report attribution falls back to whatever the user
1020
+ * types in the report form.
1021
+ */
1022
+ user?: UserIdentity;
1023
+ /** Git context for pin filtering. Typically mirrors `cloud({ git })`. */
1024
+ git?: {
1025
+ branch?: string;
1026
+ commit?: string;
1027
+ };
1028
+ /**
1029
+ * Return the current route pattern (e.g. `/users/:id`). Used for
1030
+ * route-based pin matching and report attribution. When omitted, the SDK
1031
+ * falls back to `location.pathname`.
1032
+ */
1033
+ getRoute?: () => string;
926
1034
  }
927
1035
  interface Uidex {
928
1036
  mount(target?: Element): void;
929
1037
  unmount(): void;
930
1038
  readonly registry: Registry;
931
1039
  readonly session: SessionStore;
932
- readonly views: ViewRegistrar;
1040
+ readonly views: Router;
933
1041
  readonly cloud: CloudAdapter | null;
934
1042
  readonly ingest: Ingest | null;
935
1043
  readonly shadowRoot: ShadowRoot | null;
936
1044
  }
937
1045
  declare function createUidex(options?: CreateUidexOptions): Uidex;
938
1046
 
939
- export { type Cleanup, type CloudAdapter, type ConsoleCapture, type ConsoleEntry, type ConsoleLevel, type ConsoleLogEntry, type Corner, type CreateSessionOptions, type CreateSurfaceShellOptions, type CreateUidexOptions, type CursorTooltip, type CursorTooltipDeps, type CustomSurface, DEFAULT_VIEWS, type DetailAction, type DetailActionIcon, type DetailActionRunContext, type DetailSection, type DetailSubtitle, type DetailSurface, ENTITY_KINDS, type Element$1 as Element, type Entity, type EntityByKind, type EntityKind, type EntityRef, type Feature, type FeedbackExternalLink, type FeedbackPayload, type FeedbackResult, type FeedbackSuggestedTarget, type Flow, type FormField, type FormSubmit, type FormSubmitResult, type FormSurface, type FormValue, type HighlightController, type Ingest, type IngestConfig, type IngestConfigEpic, type IngestOptions, type Inspector, type InspectorMatch, type InspectorOptions, KIND_STYLE, type KindStyleEntry, type ListItem, type ListSurface, type Location, type MenuBar, type MenuBarOptions, type MetaEntityKind, type Metadata, type NetworkCapture, type NetworkEntry, type NetworkErrorEntry, type Overlay, type OverlayShowOptions, type Page, type Primitive, type Region, type Registry, type ResolvedTheme, type Route, SURFACE_CONTAINER_CLASS, SURFACE_HOST_CLASS, SURFACE_IGNORE_SELECTOR, type Scope, type SessionActions, type SessionSnapshot, type SessionState, type SessionStore, type ShellAction, type ShellHint, type SourceRef, type SurfaceHost, type SurfaceHostOptions, type SurfaceShell, type ThemeDetector, type ThemeDetectorDeps, type ThemePreference, type Uidex, UnknownEntityKindError, type View, type ViewContext, type ViewDirectory, type ViewPalette, type ViewPushTarget, type ViewRegistrar, type ViewRegistrarOptions, type ViewStack, type ViewStackEntry, type ViewStackOptions, type ViewSurface, ViewValidationError, type Viewport, type Widget, Z_BASE, Z_CHROME, Z_OVERLAY, assertEntityKind, commandPaletteView, componentDetailView, createConsoleCapture, createCursorTooltip, createHighlightController, createIngest, createInspector, createMenuBar, createNetworkCapture, createOverlay, createRegistry, createSession, createSurfaceHost, createSurfaceShell, createThemeDetector, createUidex, createViewRegistrar, createViewStack, defaultResolveMatch, displayName, entityKey, featureDetailView, feedbackView, flowDetailView, getNativeFetch, isMetaKind, nativeFetch, pageDetailView, prettify, primitiveDetailView, regionDetailView, resolveEntityElement, resolveIngestOptions, resolveTheme, widgetDetailView };
1047
+ export { type AttachCloudOpts, type Cleanup, type CloudAdapter, type ConsoleCapture, type ConsoleEntry, type ConsoleLevel, type Corner, type CreateRouterOptions, type CreateSessionOptions, type CreateSurfaceShellOptions, type CreateUidexOptions, type CursorTooltip, type CursorTooltipDeps, type DetailAction, type DetailActionIcon, type DetailActionRunContext, type DetailSection, type DetailSubtitle, type DetailSurface, ENTITY_KINDS, type Element$1 as Element, type Entity, type EntityByKind, type EntityKind, type EntityRef, type Feature, type Flow, type FormField, type FormSubmit, type FormSubmitResult, type FormSurface, type FormValue, type HighlightActions, type HighlightController, type Ingest, type IngestOptions, type Inspector, type InspectorMatch, type InspectorMatchStack, type InspectorOptions, KIND_STYLE, type KindStyleEntry, type ListItem, type ListSurface, type Location, type MenuBar, type MenuBarOptions, type MetaEntityKind, type Metadata, type ModeBindings, type ModeSnapshot, type ModeStore, type ModeTransitions, type NavigationActions, type NavigationState, type NavigationStore, type NetworkCapture, type NetworkEntry, type Overlay, type OverlayShowOptions, type Page, type PaletteShortcut, type PinLayer, type PinLayerOptions, type PinMatchMode, type Primitive, type RealtimeChannel, type RealtimeChannelState, type RealtimePresenceUser, type Region, type Registry, type ReportRecord, type ResolvedTheme, type Route, type RouteMatch, type Router, SURFACE_CONTAINER_CLASS, SURFACE_HOST_CLASS, SURFACE_IGNORE_SELECTOR, type Scope, type SessionSnapshot, type SessionState, type SessionStore, type ShellAction, type ShellHint, type SurfaceHost, type SurfaceHostOptions, type SurfaceMode, type SurfaceShell, type ThemeDetector, type ThemeDetectorDeps, type ThemePreference, type Uidex, UnknownEntityKindError, type UserIdentity, type View, type ViewContext, type ViewPalette, type ViewPushTarget, type ViewStack, type ViewStackEntry, type ViewStackOptions, type ViewSurface, ViewValidationError, type Widget, Z_BASE, Z_CHROME, Z_OVERLAY, Z_PIN_LAYER, assertEntityKind, buildDefaultViews, componentDetailView, createCommandPaletteView, createConsoleCapture, createCursorTooltip, createHighlightController, createIngest, createInspector, createMenuBar, createModeStore, createNavigationStore, createNetworkCapture, createOverlay, createPinLayer, createRegistry, createRouter, createSession, createSurfaceHost, createSurfaceShell, createThemeDetector, createUidex, createViewStack, defaultResolveMatch, displayName, entityKey, featureDetailView, flowDetailView, formatShortcutLabel, getNativeFetch, isMetaKind, nativeFetch, pageDetailView, pinSettingsView, prettify, primitiveDetailView, regionDetailView, reportView, resolveEntityElement, resolveIngestOptions, resolveTheme, widgetDetailView };