uidex 0.4.0 → 0.5.1

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 (42) hide show
  1. package/dist/cli/cli.cjs +1111 -87
  2. package/dist/cli/cli.cjs.map +1 -1
  3. package/dist/cloud/index.cjs +375 -72
  4. package/dist/cloud/index.cjs.map +1 -1
  5. package/dist/cloud/index.d.cts +82 -0
  6. package/dist/cloud/index.d.ts +82 -0
  7. package/dist/cloud/index.js +376 -71
  8. package/dist/cloud/index.js.map +1 -1
  9. package/dist/headless/index.cjs +623 -469
  10. package/dist/headless/index.cjs.map +1 -1
  11. package/dist/headless/index.d.cts +77 -75
  12. package/dist/headless/index.d.ts +77 -75
  13. package/dist/headless/index.js +627 -469
  14. package/dist/headless/index.js.map +1 -1
  15. package/dist/index.cjs +4258 -2884
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +275 -234
  18. package/dist/index.d.ts +275 -234
  19. package/dist/index.js +4280 -2890
  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 +4299 -2906
  30. package/dist/react/index.cjs.map +1 -1
  31. package/dist/react/index.d.cts +206 -200
  32. package/dist/react/index.d.ts +206 -200
  33. package/dist/react/index.js +4339 -2926
  34. package/dist/react/index.js.map +1 -1
  35. package/dist/scan/index.cjs +201 -49
  36. package/dist/scan/index.cjs.map +1 -1
  37. package/dist/scan/index.d.cts +27 -1
  38. package/dist/scan/index.d.ts +27 -1
  39. package/dist/scan/index.js +200 -48
  40. package/dist/scan/index.js.map +1 -1
  41. package/package.json +8 -14
  42. package/templates/claude/api.md +110 -0
package/dist/index.d.cts 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"];
@@ -76,6 +79,26 @@ declare class UnknownEntityKindError extends Error {
76
79
  }
77
80
  declare function assertEntityKind(kind: string): asserts kind is EntityKind;
78
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
+ }
79
102
  interface Registry {
80
103
  add(entity: Entity): void;
81
104
  get<K extends EntityKind>(kind: K, id: string): EntityByKind<K> | undefined;
@@ -83,6 +106,11 @@ interface Registry {
83
106
  query(predicate: (entity: Entity) => boolean): Entity[];
84
107
  byScope(scope: Scope): Entity[];
85
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;
86
114
  }
87
115
  declare function createRegistry(): Registry;
88
116
 
@@ -98,11 +126,10 @@ declare const KIND_STYLE: Record<EntityKind, KindStyleEntry>;
98
126
  declare function prettify(id: string): string;
99
127
  declare function displayName(entity: Entity, node?: Element | null): string;
100
128
 
101
- interface HighlightContext {
102
- ref: EntityRef | null;
103
- element: HTMLElement | null;
104
- pinnedRef: EntityRef | null;
105
- color: string | null;
129
+ interface UserIdentity {
130
+ id: string;
131
+ name?: string;
132
+ avatar?: string;
106
133
  }
107
134
 
108
135
  type ThemePreference = "light" | "dark" | "auto";
@@ -121,114 +148,84 @@ interface SessionSnapshot {
121
148
  theme: ThemePreference;
122
149
  resolvedTheme: ResolvedTheme;
123
150
  ingestActive: boolean;
124
- }
125
- interface SessionActions {
126
- hover(ref: EntityRef | null): void;
127
- select(ref: EntityRef | null): void;
128
- /**
129
- * Append a view onto the view stack. Pure: never resolves defaults.
130
- */
131
- pushStack(entry: ViewStackEntry): void;
132
- /**
133
- * Remove the top entry from the view stack. No-op when the stack is empty.
134
- */
135
- popStack(): void;
136
151
  /**
137
- * Empty the view stack.
152
+ * Identity for the local user. Set once at session creation; gates realtime
153
+ * features (cursor labels, presence) and auto-populates report attribution.
138
154
  */
139
- clearStack(): void;
140
- /**
141
- * Open a view by id. If `ref` is omitted, the current `selection` is captured;
142
- * pass `null` explicitly to open the view with no ref context. Thin wrapper
143
- * around `pushStack`.
144
- */
145
- openView(id: string, ref?: EntityRef | null): void;
146
- /**
147
- * Clear the entire view stack. Thin wrapper around `clearStack`.
148
- */
149
- closeView(): void;
150
- setInspectorActive(active: boolean): void;
151
- toggleInspector(): void;
152
- /** Pin the overlay to `ref` until `clearPinnedHighlight` is called. */
153
- pinHighlight(ref: EntityRef): void;
154
- clearPinnedHighlight(): void;
155
- setTheme(theme: ThemePreference, resolved?: ResolvedTheme): void;
156
- setIngest(active: boolean): void;
157
- }
158
- interface SessionState extends SessionSnapshot {
159
- actions: SessionActions;
155
+ user: UserIdentity | null;
160
156
  }
157
+ type SessionState = SessionSnapshot;
161
158
 
162
- type SurfaceEvent = {
163
- type: "TOGGLE_INSPECTOR";
164
- } | {
165
- type: "OPEN_PALETTE";
166
- } | {
167
- type: "PUSH_VIEW";
168
- entry: ViewStackEntry;
169
- } | {
170
- type: "POP_VIEW";
171
- } | {
172
- type: "CLOSE";
173
- } | {
174
- type: "ESC";
175
- } | {
176
- type: "CMD_K";
177
- } | {
178
- type: "SELECT";
179
- ref: EntityRef | null;
180
- entry: ViewStackEntry;
181
- } | {
182
- type: "SET_SELECTION";
183
- ref: EntityRef | null;
184
- } | {
185
- type: "HOVER";
186
- ref: EntityRef;
187
- element: HTMLElement | null;
188
- color?: string | null;
189
- } | {
190
- type: "UNHOVER";
191
- } | {
192
- type: "PIN";
193
- ref?: EntityRef;
194
- } | {
195
- type: "UNPIN";
196
- } | {
197
- type: "SET_THEME";
198
- theme: ThemePreference;
199
- resolvedTheme: ResolvedTheme;
200
- } | {
201
- type: "SET_INGEST";
202
- 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;
203
171
  };
172
+ declare function createNavigationStore(): NavigationStore;
204
173
 
205
- type SessionStore = StoreApi<SessionState> & {
206
- send(event: SurfaceEvent): void;
207
- };
208
- interface SurfaceBindings {
174
+ type SurfaceMode = "idle" | "inspecting" | "palette" | "viewing";
175
+ interface ModeSnapshot {
176
+ mode: SurfaceMode;
177
+ inspectorActive: boolean;
178
+ }
179
+ interface ModeBindings {
209
180
  mountInspector?: () => void;
210
181
  destroyInspector?: () => void;
211
- openActionsPopup?: () => void;
212
- closePopup?: () => void;
213
182
  }
214
- interface HighlightBindings {
215
- showOverlay?: (context: HighlightContext) => void;
216
- hideOverlay?: () => void;
217
- 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;
218
192
  }
219
- interface SessionBindings {
220
- surface?: SurfaceBindings;
221
- highlight?: HighlightBindings;
193
+ type ModeStore = StoreApi<ModeSnapshot> & {
194
+ transition: ModeTransitions;
195
+ };
196
+ interface CreateModeStoreOptions {
197
+ nav: NavigationStore;
198
+ bindings?: ModeBindings;
222
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
+ };
223
222
  interface CreateSessionOptions extends Partial<SessionSnapshot> {
224
223
  detectTheme?: () => ResolvedTheme;
225
- /**
226
- * Action implementations injected into the surface machine and its
227
- * highlight child actor. Implementations may be closures that read from
228
- * mutable refs filled in after `createSession` returns (e.g., the SDK's
229
- * shell isn't available until `mount`).
230
- */
231
- bindings?: SessionBindings;
224
+ onMountInspector?: () => void;
225
+ onDestroyInspector?: () => void;
226
+ onShowOverlay?: (context: HighlightContext) => void;
227
+ onHideOverlay?: () => void;
228
+ onUpdateOverlay?: (context: HighlightContext) => void;
232
229
  }
233
230
  declare function resolveTheme(preference: ThemePreference, detect?: () => ResolvedTheme): ResolvedTheme;
234
231
  declare function createSession(options?: CreateSessionOptions): SessionStore;
@@ -279,99 +276,46 @@ declare function createNetworkCapture(options?: NetworkCaptureOptions): NetworkC
279
276
  declare const nativeFetch: typeof fetch | undefined;
280
277
  declare function getNativeFetch(): typeof fetch | undefined;
281
278
 
282
- interface ConsoleLogEntry {
283
- level: "log" | "warn" | "error" | "info";
284
- message: string;
285
- timestamp: string;
286
- }
287
- interface NetworkErrorEntry {
288
- url: string;
289
- method: string;
290
- status: number | null;
291
- statusText: string | null;
292
- timestamp: string;
293
- }
294
- interface Viewport {
295
- width: number;
296
- height: number;
297
- }
298
- interface SourceRef {
299
- filePath: string;
300
- line: number;
301
- }
302
- interface FeedbackSuggestedTarget {
303
- integrationId: string;
304
- targetConfig: Record<string, unknown>;
305
- }
306
- interface FeedbackPayload {
307
- type: "bug" | "feature" | "improvement" | "question";
308
- severity: "low" | "medium" | "high" | "critical";
309
- title?: string;
310
- description: string;
311
- componentId: string;
312
- element?: string | null;
313
- sources?: SourceRef[];
314
- url: string;
315
- path: string;
316
- route?: string | null;
317
- pageTitle?: string;
318
- sessionId?: string;
319
- reporterEmail?: string;
320
- reporterName?: string;
321
- timestamp: string;
322
- viewport: Viewport;
323
- screen: Viewport;
324
- userAgent: string;
325
- locale?: string;
326
- environment?: string;
327
- appVersion?: string;
328
- consoleLogs?: ConsoleLogEntry[];
329
- networkErrors?: NetworkErrorEntry[];
330
- metadata?: Record<string, string>;
331
- screenshot?: string;
332
- suggestedTarget?: FeedbackSuggestedTarget;
333
- }
334
- interface FeedbackExternalLink {
335
- ok: boolean;
336
- url?: string;
337
- key?: string;
338
- error?: string;
339
- }
340
- interface FeedbackResult {
341
- id: string;
342
- sequenceNumber: number;
343
- externalLink?: FeedbackExternalLink;
344
- }
345
- interface IngestConfigIssue {
346
- id: string;
347
- key: string;
348
- summary: string;
349
- issueType: string;
350
- }
351
- interface IngestConfig {
352
- hasJira: boolean;
353
- integrationId?: string;
354
- parentIssues?: IngestConfigIssue[];
355
- }
356
- /**
357
- * Generic so third-party adapters (e.g. `uidex-cloud`) can plug in their own
358
- * payload/result/integration shapes. SDK-bundled `cloud()` returns the
359
- * defaulted form. `ViewContext.cloud` uses the fully-open variant; consumers
360
- * that need the SDK shape narrow with `as CloudAdapter`.
361
- */
362
- 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 = {
363
298
  getConfig(): Promise<IngestConfig>;
364
- /**
365
- * Synchronous read of the eagerly-fetched config. Returns `null` until the
366
- * initial fetch resolves; views that need a sync answer (e.g. detail
367
- * actions deciding whether to surface a Jira button) read this.
368
- */
369
299
  getCachedConfig(): IngestConfig | null;
370
300
  }> {
371
- readonly feedback: {
301
+ readonly reports: {
372
302
  submit(payload: TPayload): Promise<TResult>;
303
+ list?(opts?: {
304
+ page?: number;
305
+ limit?: number;
306
+ }): Promise<ReportListResponse>;
373
307
  };
374
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
+ };
375
319
  }
376
320
 
377
321
  interface IngestOptions {
@@ -458,7 +402,7 @@ interface Overlay {
458
402
  readonly isVisible: boolean;
459
403
  }
460
404
  interface OverlayDeps {
461
- container: Element;
405
+ container: Element | ShadowRoot;
462
406
  }
463
407
  declare function createOverlay(deps: OverlayDeps): Overlay;
464
408
 
@@ -504,17 +448,67 @@ interface HighlightControllerLike {
504
448
  declare function createHighlightController(overlay: Overlay): HighlightControllerLike;
505
449
  declare function createInspector(options: InspectorOptions): Inspector;
506
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
+
507
498
  type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
508
499
  interface MenuBarOptions {
509
500
  container: Element;
510
501
  session: SessionStore;
511
502
  initialCorner?: Corner;
512
503
  appTitle?: string;
504
+ channel?: RealtimeChannel | null;
505
+ pinLayer?: PinLayer | null;
513
506
  }
514
507
  interface MenuBar {
515
508
  destroy(): void;
516
509
  snapTo(corner: Corner): void;
517
510
  snapToNearest(x: number, y: number): void;
511
+ setPinLayer(layer: PinLayer): void;
518
512
  readonly corner: Corner;
519
513
  readonly root: HTMLElement;
520
514
  }
@@ -538,6 +532,8 @@ interface CreateSurfaceShellOptions {
538
532
  stylesheets?: string[];
539
533
  initialCorner?: Corner;
540
534
  appTitle?: string;
535
+ channel?: RealtimeChannel | null;
536
+ pinLayer?: PinLayer | null;
541
537
  /**
542
538
  * Additional inspector wiring. Hover is pre-wired to overlay + tooltip; pass
543
539
  * `onSelect` (and `onAfterHover`) to extend behaviour without rebuilding the
@@ -568,7 +564,29 @@ interface SurfaceShell {
568
564
  */
569
565
  declare function createSurfaceShell(options: CreateSurfaceShellOptions): SurfaceShell;
570
566
 
571
- 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;
572
590
  interface ListSurface {
573
591
  kind: "list";
574
592
  id: string;
@@ -577,6 +595,12 @@ interface ListSurface {
577
595
  emptyLabel?: string;
578
596
  defaultHighlight?: string;
579
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;
580
604
  }
581
605
  interface ListItem {
582
606
  value: string;
@@ -596,12 +620,13 @@ interface ListItem {
596
620
  };
597
621
  /**
598
622
  * Optional leading-icon factory used for non-entity rows (e.g. a palette
599
- * "Feedback" command). Called per-render so the returned Node is fresh.
623
+ * "Report" command). Called per-render so the returned Node is fresh.
600
624
  * The shell wraps whatever is returned in a rounded-square tile.
601
625
  */
602
626
  leading?: () => Node;
603
627
  /** Raw data attribute tag appended to the row (for test/e2e hooks). */
604
628
  tag?: string;
629
+ trailing?: TemplateResult;
605
630
  /** Contextual actions surfaced in the footer popup when this row is highlighted. */
606
631
  actions?: readonly ShellAction[];
607
632
  }
@@ -627,6 +652,7 @@ interface DetailActionBase {
627
652
  ariaLabel?: string;
628
653
  hint?: string;
629
654
  intent?: ShellActionIntent;
655
+ group?: string;
630
656
  }
631
657
  type DetailAction = (DetailActionBase & {
632
658
  copy: string;
@@ -644,7 +670,7 @@ type DetailAction = (DetailActionBase & {
644
670
  }) | (DetailActionBase & {
645
671
  run: (ctx: DetailActionRunContext) => void | Promise<void>;
646
672
  });
647
- type DetailActionIcon = "copy" | "camera" | "message-circle-warning" | "chevron-down" | "highlighter" | "ticket-plus" | "view";
673
+ type DetailActionIcon = "archive-x" | "copy" | "camera" | "inbox" | "message-circle-warning" | "chevron-down" | "highlighter" | "sticky-note" | "ticket-plus" | "view";
648
674
  interface DetailActionRunContext {
649
675
  setLabel(next: string, durationMs?: number): void;
650
676
  }
@@ -685,7 +711,17 @@ type DetailSection = {
685
711
  id: "routes";
686
712
  paths: readonly string[];
687
713
  filterable?: boolean;
714
+ } | {
715
+ id: "screenshot";
716
+ url: string;
717
+ } | {
718
+ id: "metadata";
719
+ entries: readonly MetadataEntry[];
688
720
  };
721
+ interface MetadataEntry {
722
+ label: string;
723
+ value: string;
724
+ }
689
725
  interface FormSurface {
690
726
  kind: "form";
691
727
  id: string;
@@ -782,10 +818,6 @@ type FormField = (FormFieldBase & {
782
818
  kind: "checkbox";
783
819
  value?: boolean;
784
820
  });
785
- interface CustomSurface {
786
- kind: "custom";
787
- render: (ctx: ViewContext, root: HTMLElement) => Cleanup | void;
788
- }
789
821
 
790
822
  interface ViewPalette {
791
823
  label: string;
@@ -796,11 +828,7 @@ interface ViewPalette {
796
828
  */
797
829
  icon?: () => Node;
798
830
  shortcut?: string;
799
- }
800
- interface ViewDirectory {
801
- list(): readonly View[];
802
- recents(): readonly EntityRef[];
803
- favorites(): readonly EntityRef[];
831
+ subtitle?: string | (() => string);
804
832
  }
805
833
  interface ViewPushTarget {
806
834
  id: string;
@@ -814,28 +842,32 @@ interface ViewContext {
814
842
  ref: EntityRef | null;
815
843
  registry: Registry;
816
844
  cloud: CloudAdapter<unknown, unknown, unknown> | null;
817
- sdkCloud: CloudAdapter;
818
- 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;
819
852
  /** Push a view onto the stack. Unregistered ids are a no-op. */
820
853
  push: (target: ViewPushTarget) => void;
821
854
  /** Pop the top view off the stack. No-op when the stack is empty. */
822
855
  pop: () => void;
823
856
  /** Clear the entire view stack. */
824
857
  close: () => void;
825
- /** Resolve the first view matching `ref` and push it onto the stack. */
826
- navigate: (ref: EntityRef) => void;
827
858
  /** Pin the overlay to `ref` until the user presses Esc. */
828
859
  pinHighlight: (ref: EntityRef) => void;
829
- /** Toggle favorite status for an entity ref. */
830
- toggleFavorite: (ref: EntityRef) => void;
831
- /** Check if an entity ref is favorited. */
832
- isFavorite: (ref: EntityRef) => boolean;
833
860
  /** Shell-owned search input. The shell dispatches its value into the active surface. */
834
861
  searchInput: HTMLInputElement;
835
862
  /** Drives the Surface's singleton overlay from inside a custom surface. */
836
863
  highlight: HighlightController;
837
864
  /** Snapshot of the current view stack (top-most last). */
838
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;
839
871
  }
840
872
  interface ShellHint {
841
873
  key: string;
@@ -881,6 +913,9 @@ interface View {
881
913
  */
882
914
  focusTarget?: (root: HTMLElement, ctx: ViewContext) => HTMLElement | null;
883
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;
884
919
  }
885
920
  declare class ViewValidationError extends Error {
886
921
  constructor(message: string);
@@ -905,43 +940,29 @@ interface PaletteShortcut {
905
940
  shift?: boolean;
906
941
  }
907
942
  declare function formatShortcutLabel(shortcut: PaletteShortcut): string;
943
+ type EscapeHandler = () => boolean;
908
944
 
909
945
  declare const SURFACE_HOST_CLASS = "uidex-surface-host";
910
946
  declare const SURFACE_CONTAINER_CLASS = "uidex-container";
911
- declare const Z_BASE = 2147483644;
912
- declare const Z_OVERLAY = 2147483645;
913
- 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;
914
951
  declare const SURFACE_IGNORE_SELECTOR = ".uidex-surface-host,.uidex-container";
915
952
 
916
- interface ViewRegistrar {
917
- register(view: View): void;
918
- unregister(id: string): void;
919
- list(): readonly View[];
920
- get(id: string): View | undefined;
921
- findMatch(ref: EntityRef): View | null;
922
- navigate(ref: EntityRef): void;
923
- recents(): readonly EntityRef[];
924
- favorites(): readonly EntityRef[];
925
- toggleFavorite(ref: EntityRef): void;
926
- isFavorite(ref: EntityRef): boolean;
927
- }
928
- interface ViewRegistrarOptions {
929
- session: SessionStore;
930
- dev?: boolean;
931
- }
932
- declare function createViewRegistrar(options: ViewRegistrarOptions): ViewRegistrar;
933
-
934
953
  interface ViewStackOptions {
935
954
  container: HTMLElement;
936
- views: ViewRegistrar;
955
+ views: Router;
937
956
  session: SessionStore;
938
957
  registry: Registry;
939
958
  cloud?: CloudAdapter | null;
940
- sdkCloud: CloudAdapter;
941
959
  highlight: HighlightController;
942
960
  globalActions?: (ctx: ViewContext) => ShellAction[];
943
961
  shortcut?: PaletteShortcut;
944
962
  dev?: boolean;
963
+ pushEscapeLayer?: ViewContext["pushEscapeLayer"];
964
+ onAfterSubmit?: () => void;
965
+ getRoute?: () => string;
945
966
  }
946
967
  interface ViewStack {
947
968
  /**
@@ -963,10 +984,12 @@ declare const widgetDetailView: View;
963
984
  declare const primitiveDetailView: View;
964
985
  declare const pageDetailView: View;
965
986
 
966
- declare const feedbackView: View;
987
+ declare const reportView: View;
967
988
 
968
989
  declare const flowDetailView: View;
969
990
 
991
+ declare const pinSettingsView: View;
992
+
970
993
  declare function buildDefaultViews(shortcut?: PaletteShortcut): View[];
971
994
 
972
995
  interface CreateUidexOptions {
@@ -990,17 +1013,35 @@ interface CreateUidexOptions {
990
1013
  ingest?: IngestOptions | null;
991
1014
  /** Keyboard shortcut for the command palette. Defaults to Cmd+K / Ctrl+K. */
992
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;
993
1034
  }
994
1035
  interface Uidex {
995
1036
  mount(target?: Element): void;
996
1037
  unmount(): void;
997
1038
  readonly registry: Registry;
998
1039
  readonly session: SessionStore;
999
- readonly views: ViewRegistrar;
1040
+ readonly views: Router;
1000
1041
  readonly cloud: CloudAdapter | null;
1001
1042
  readonly ingest: Ingest | null;
1002
1043
  readonly shadowRoot: ShadowRoot | null;
1003
1044
  }
1004
1045
  declare function createUidex(options?: CreateUidexOptions): Uidex;
1005
1046
 
1006
- 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, 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 IngestConfigIssue, 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 NetworkCapture, type NetworkEntry, type NetworkErrorEntry, type Overlay, type OverlayShowOptions, type Page, type PaletteShortcut, 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, buildDefaultViews, componentDetailView, createCommandPaletteView, createConsoleCapture, createCursorTooltip, createHighlightController, createIngest, createInspector, createMenuBar, createNetworkCapture, createOverlay, createRegistry, createSession, createSurfaceHost, createSurfaceShell, createThemeDetector, createUidex, createViewRegistrar, createViewStack, defaultResolveMatch, displayName, entityKey, featureDetailView, feedbackView, flowDetailView, formatShortcutLabel, 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 };