@proveanything/smartlinks-utils-ui 0.3.9 → 0.3.12

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.
@@ -1,12 +1,26 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ComponentType, ReactNode } from 'react';
2
+ import { ReactNode, ComponentType } from 'react';
3
3
  import * as _proveanything_smartlinks_dist_types_appObjects from '@proveanything/smartlinks/dist/types/appObjects';
4
- import { MatchedAt, FacetRule, RecordScope, AppRecord, RecordTarget, MatchResult, MatchEntry } from '@proveanything/smartlinks/dist/types/appObjects';
4
+ import { FacetRule, MatchedAt, RecordScope, AppRecord, RecordTarget, MatchResult, MatchEntry } from '@proveanything/smartlinks/dist/types/appObjects';
5
5
  import { LucideIcon } from 'lucide-react';
6
6
  import * as _tanstack_query_core from '@tanstack/query-core';
7
7
  import { InfiniteData } from '@tanstack/react-query';
8
8
 
9
- type ScopeKind = 'product' | 'facet' | 'variant' | 'batch';
9
+ /**
10
+ * Where a record is anchored in the inheritance chain.
11
+ *
12
+ * - `collection` — terminal rung (collection-wide default). Inherited by every
13
+ * product/facet/variant/batch when nothing more specific exists. Most useful
14
+ * for collection-cardinality records (a global pool of FAQs / recipes / SOPs).
15
+ * - `facet` — anchored to a facet value (e.g. `bagel-type=white`).
16
+ * - `product` — anchored to a product.
17
+ * - `variant` — anchored to a product variant.
18
+ * - `batch` — anchored to a product batch.
19
+ * - `rule` — synthetic UI scope: records that target via a `facetRule`
20
+ * (AND-of-OR over facets) rather than being pinned to a
21
+ * specific node in the chain. Their refs start with `rule:`.
22
+ */
23
+ type ScopeKind = 'collection' | 'product' | 'facet' | 'variant' | 'batch' | 'rule';
10
24
  /** Parsed `ref` string — see `data/refs.ts`. Format: `kind:id` or chain. */
11
25
  interface ParsedRef {
12
26
  /** Most-specific scope this ref points at. */
@@ -20,6 +34,19 @@ interface ParsedRef {
20
34
  variantId?: string;
21
35
  batchId?: string;
22
36
  proofId?: string;
37
+ /**
38
+ * Opaque id of a rule-targeted record (ref starts with `rule:`). Set when
39
+ * `kind === 'rule'`. The actual rule lives on the record's `facetRule`
40
+ * field — this is just the addressing handle used by the URL/refs layer.
41
+ */
42
+ ruleId?: string;
43
+ /**
44
+ * For `collection` cardinality records: the per-item identifier within the
45
+ * scope. Singleton records leave this undefined. The full ref is
46
+ * `{scopeRef}/item:{itemId}`, or just `item:{itemId}` for collection-rooted
47
+ * items.
48
+ */
49
+ itemId?: string;
23
50
  }
24
51
 
25
52
  type RecordSource = 'self' | 'inherited' | 'empty';
@@ -54,6 +81,13 @@ interface RecordSummary<TData = unknown> {
54
81
  * leave this undefined.
55
82
  */
56
83
  itemId?: string;
84
+ /**
85
+ * For rule-targeted records (ref begins `rule:`). The AND-of-OR clauses
86
+ * the server uses to match this record against a product's facets.
87
+ * `null` for non-rule records. The browser uses this to render a friendly
88
+ * rule summary as the row subtitle.
89
+ */
90
+ facetRule?: FacetRule | null;
57
91
  }
58
92
  interface ResolvedRecord<TData = unknown> {
59
93
  data: TData | null;
@@ -104,14 +138,27 @@ interface CsvSchema<TData> {
104
138
  }
105
139
 
106
140
  /**
107
- * How records are laid out in the browse rail / panel.
108
- * - `list` → dense rows with status dot + label + subtitle (default).
109
- * - `grid` → square cards with thumbnail + label.
110
- * - `gallery` → larger 16:9 cards with thumbnail + label + subtitle.
111
- * - `compact` → minimal one-line rows (no subtitle, no status dot).
141
+ * How records are laid out in the **left rail** (the navigator).
142
+ *
143
+ * The rail is intentionally a dense tree/list — cards or galleries belong on
144
+ * the right pane, not in a 260px column. Only the row densities are offered:
145
+ *
146
+ * - `list` → dense rows with status dot + label + subtitle (default).
147
+ * - `compact` → minimal one-line rows (no subtitle, no status dot).
112
148
  */
113
- type RecordPresentation = 'list' | 'grid' | 'gallery' | 'compact';
149
+ type RecordPresentation = 'list' | 'compact';
114
150
  declare const ALL_PRESENTATIONS: RecordPresentation[];
151
+ /**
152
+ * How a multi-item collection is rendered in the **right pane** when a scope
153
+ * is selected and no item is open. Right-pane only — cards and galleries
154
+ * have the room to breathe here.
155
+ *
156
+ * - `table` → declarative columns (built-in default with `itemColumns`).
157
+ * - `cards` → square cards with thumbnail + label.
158
+ * - `gallery` → larger 16:9 cards with thumbnail + label + subtitle.
159
+ */
160
+ type ItemView = 'table' | 'cards' | 'gallery';
161
+ declare const ALL_ITEM_VIEWS: ItemView[];
115
162
  /**
116
163
  * Whether each scope holds at most one record of this type, or many.
117
164
  * - `singleton` → one record per scope (e.g. washing instructions). Default.
@@ -131,6 +178,17 @@ type CollectedSort = {
131
178
  field: string;
132
179
  direction?: 'asc' | 'desc';
133
180
  };
181
+ /**
182
+ * What the left rail shows once an item is open in the editor (collection
183
+ * cardinality only):
184
+ *
185
+ * - `siblings` *(default)* — rail flips to the list of items in the current
186
+ * scope. Click any sibling to switch instantly. A pinned `← All scopes`
187
+ * link returns to scope navigation.
188
+ * - `scopes` — rail keeps showing scopes. Switching items requires going
189
+ * back to the right-pane list first.
190
+ */
191
+ type CollectionRailMode = 'siblings' | 'scopes';
134
192
 
135
193
  type TelemetryEvent = {
136
194
  type: 'record.save';
@@ -171,6 +229,22 @@ type TelemetryEvent = {
171
229
  type: 'item.create';
172
230
  recordType?: string;
173
231
  scopeRef: string;
232
+ } | {
233
+ type: 'item.open';
234
+ recordType?: string;
235
+ scopeRef: string;
236
+ itemId: string;
237
+ } | {
238
+ type: 'item.delete';
239
+ recordType?: string;
240
+ scopeRef: string;
241
+ itemId: string;
242
+ } | {
243
+ type: 'item.view.change';
244
+ recordType?: string;
245
+ scopeRef: string;
246
+ from: string;
247
+ to: string;
174
248
  } | {
175
249
  type: 'clipboard.copy';
176
250
  recordType?: string;
@@ -228,13 +302,30 @@ interface RecordsAdminI18n {
228
302
  unsavedBannerBody: string;
229
303
  /** Presentation mode switcher labels. */
230
304
  presentationList: string;
231
- presentationGrid: string;
232
- presentationGallery: string;
233
305
  presentationCompact: string;
306
+ /** Right-pane item view (collection cardinality) switcher labels. */
307
+ itemViewTable: string;
308
+ itemViewCards: string;
309
+ itemViewGallery: string;
234
310
  /** Collection cardinality affordances. */
235
311
  newItem: string;
236
312
  noItemsTitle: string;
237
313
  noItemsBody: string;
314
+ /**
315
+ * Right-pane multi-item flow strings (collection cardinality).
316
+ * `{noun}` is replaced with `itemNoun` (e.g. "recipe", "FAQ").
317
+ */
318
+ backToList: string;
319
+ prevItem: string;
320
+ nextItem: string;
321
+ itemListTitle: string;
322
+ /** Friendly singular for table column header in built-in default. */
323
+ itemColumnLabel: string;
324
+ itemColumnUpdated: string;
325
+ itemActions: string;
326
+ /** Sibling rail (rail flips to items when one is open). */
327
+ backToScopes: string;
328
+ siblingsHeading: string;
238
329
  /**
239
330
  * Empty-state copy for the LEFT browse rail (the list itself is empty).
240
331
  * Defaults to a friendly "no records exist yet" message — distinct from
@@ -261,6 +352,88 @@ interface RecordsAdminI18n {
261
352
  }
262
353
  declare const DEFAULT_I18N: RecordsAdminI18n;
263
354
 
355
+ /**
356
+ * How URL changes are pushed onto the browser history.
357
+ *
358
+ * - `push` — every change adds a history entry (rich back stack).
359
+ * - `replace` — every change replaces the current entry (no history bloat).
360
+ * - `smart` — meaningful navigation (open item, change scope) pushes;
361
+ * cosmetic changes (toggle view, prev/next sibling) replace.
362
+ * Default.
363
+ */
364
+ type DeepLinkHistoryMode = 'push' | 'replace' | 'smart';
365
+ /**
366
+ * Customise the URL parameter names the shell reads / writes. Useful when
367
+ * a host already uses one of these keys for something else.
368
+ */
369
+ interface DeepLinkParamNames {
370
+ /** Default `'item'`. */
371
+ item?: string;
372
+ /** Default `'scope'`. */
373
+ scope?: string;
374
+ /** Default `'view'`. */
375
+ view?: string;
376
+ }
377
+ /**
378
+ * The shell-owned deep-link state. All fields are optional — `null` /
379
+ * `undefined` means "absent from the URL".
380
+ */
381
+ interface DeepLinkState {
382
+ /** Item id when an item is open in the editor. */
383
+ item?: string | null;
384
+ /** Scope ref when no item is open and the user is browsing a list. */
385
+ scope?: string | null;
386
+ /** Right-pane view choice (`table` / `cards` / `gallery`). */
387
+ view?: string | null;
388
+ }
389
+ /**
390
+ * Pluggable bridge between the shell and the host's URL. Hosts can supply
391
+ * their own adapter to integrate with React Router, hash routing, the
392
+ * SmartLinks `persistentQueryParams` helper, etc. When omitted the shell
393
+ * falls back to a plain `window.location` + `window.history` adapter.
394
+ */
395
+ interface DeepLinkAdapter {
396
+ /** Read the current values for the shell-owned params. */
397
+ read(): DeepLinkState;
398
+ /**
399
+ * Write a partial state update. `mode` is the resolved push/replace hint
400
+ * (the shell already mapped `'smart'` to one of the two before calling).
401
+ * Hosts should preserve every other URL parameter — only the keys
402
+ * supplied here may be touched.
403
+ */
404
+ write(partial: DeepLinkState, mode: 'push' | 'replace'): void;
405
+ /**
406
+ * Subscribe to external URL changes (back/forward, host-driven nav).
407
+ * Should fire whenever the params the shell cares about may have
408
+ * changed. Returns an unsubscribe function.
409
+ */
410
+ subscribe(listener: () => void): () => void;
411
+ }
412
+ interface DeepLinkOptions {
413
+ /** Master switch — default `false` (opt in per app). */
414
+ enabled?: boolean;
415
+ /** Push/replace strategy. Default `'smart'`. */
416
+ history?: DeepLinkHistoryMode;
417
+ /** Override the default param key names. */
418
+ paramNames?: DeepLinkParamNames;
419
+ /**
420
+ * Pluggable URL bridge. Defaults to `window.location` + `window.history`
421
+ * with a `popstate` subscription. Provide one when the host owns routing
422
+ * (React Router, hash router, iframe postMessage relay, etc.).
423
+ */
424
+ adapter?: DeepLinkAdapter;
425
+ }
426
+ /**
427
+ * Resolved param names — everywhere outside the public surface uses these
428
+ * to avoid the optional-key ceremony.
429
+ */
430
+ interface ResolvedDeepLinkParamNames {
431
+ item: string;
432
+ scope: string;
433
+ view: string;
434
+ }
435
+ declare const DEFAULT_DEEP_LINK_PARAM_NAMES: ResolvedDeepLinkParamNames;
436
+
264
437
  interface RecordsAdminIcons {
265
438
  scope: Record<ScopeKind | 'universal', LucideIcon>;
266
439
  status: {
@@ -348,6 +521,46 @@ interface RecordSlotContext {
348
521
  /** Friendly label for "Paste from {sourceLabel}" in row menus. */
349
522
  clipboardSourceLabel?: string;
350
523
  }
524
+ /**
525
+ * Declarative column definition for the built-in default item table.
526
+ * Supply `itemColumns` for the lazy path; supply `renderItemList` /
527
+ * `renderItemCard` for full control.
528
+ */
529
+ interface ItemColumn<TData = unknown> {
530
+ /** Stable key — used for React keys and per-column persistence. */
531
+ key: string;
532
+ /** Column header label. */
533
+ header: string;
534
+ /**
535
+ * Render the cell content. Receives the record summary plus the typed
536
+ * `data` for convenience.
537
+ */
538
+ render: (record: RecordSummary<TData>) => ReactNode;
539
+ /** CSS width hint applied to the `<th>` (and matching `<td>`). */
540
+ width?: string;
541
+ /** Right-align the cell (numeric columns). */
542
+ align?: 'left' | 'right' | 'center';
543
+ }
544
+ /**
545
+ * Context passed to right-pane item-view slot renderers.
546
+ */
547
+ interface ItemViewContext {
548
+ /** Open the item in the editor. */
549
+ onOpen: (itemId: string) => void;
550
+ /** Create a new item in the current scope. */
551
+ onCreate: () => void;
552
+ /** Delete an item by id. */
553
+ onDelete: (itemId: string) => void;
554
+ /** The scope the items belong to. */
555
+ scope: ParsedRef;
556
+ /** Item id currently open in the editor (when applicable). */
557
+ selectedId?: string;
558
+ /** True while the items are loading. */
559
+ isLoading: boolean;
560
+ }
561
+ interface ItemSlotContext extends ItemViewContext {
562
+ selected: boolean;
563
+ }
351
564
  interface RecordsAdminShellProps<TData = unknown> {
352
565
  SL: SmartLinksSDK;
353
566
  appId: string;
@@ -426,6 +639,24 @@ interface RecordsAdminShellProps<TData = unknown> {
426
639
  intro?: {
427
640
  title: string;
428
641
  body: ReactNode;
642
+ /**
643
+ * Where to surface the "show again" affordance after the user dismisses
644
+ * the intro banner.
645
+ *
646
+ * - `'header'` *(default)* — small ghost icon-button (`?`) inline inside
647
+ * the `ShellHeader`, right-aligned next to `headerActions`. Zero
648
+ * vertical footprint. Auto-falls back to `'footer'` when no header
649
+ * card is rendered (host hasn't enabled it).
650
+ * - `'footer'` — render the `?` button in the quiet utility row that
651
+ * sits above the rail/editor split.
652
+ * - `'inline'` — legacy behaviour: full-width strip with a "How it
653
+ * works" pill on the right.
654
+ * - `'hidden'` — once dismissed, do not offer a way to bring it back
655
+ * from this surface. The host can still re-enable via state reset.
656
+ */
657
+ reopenAffordance?: 'header' | 'footer' | 'inline' | 'hidden';
658
+ /** Override the default "How it works" label / tooltip. */
659
+ reopenLabel?: string;
429
660
  };
430
661
  csvSchema?: CsvSchema<TData>;
431
662
  classify?: (record: RecordSummary<TData>) => RecordStatus;
@@ -439,13 +670,9 @@ interface RecordsAdminShellProps<TData = unknown> {
439
670
  /** Initial presentation when nothing is persisted. Default first of `presentations`. */
440
671
  defaultPresentation?: RecordPresentation;
441
672
  /**
442
- * Optional custom card renderer used by `grid` / `gallery` presentations.
443
- * If omitted the shell uses `<DefaultRecordCard>`.
444
- */
445
- renderCard?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
446
- /**
447
- * Optional custom list-row renderer used by `list` / `compact` presentations.
448
- * If omitted the shell uses `<DefaultRecordRow>`.
673
+ * Optional custom row renderer for the rail. Applied to both `list` and
674
+ * `compact` densities. Cards/galleries are not supported in the rail
675
+ * they belong on the right pane (see `renderItemList` / `itemView`).
449
676
  */
450
677
  renderListRow?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
451
678
  /**
@@ -458,8 +685,11 @@ interface RecordsAdminShellProps<TData = unknown> {
458
685
  /**
459
686
  * Whether each scope holds at most one record (`singleton`, default) or
460
687
  * many (`collection`, e.g. FAQs / recipes / SOPs). In collection mode the
461
- * shell treats scopes as folders containing items, exposes a "+ New" action,
462
- * and the `RecordEditor` works on individual items via `itemId`-suffixed refs.
688
+ * shell treats scopes as folders containing items: when a scope is selected
689
+ * with no item open, the right pane shows the multi-item view (default
690
+ * table, or `renderItemList` / `itemView` overrides). Clicking an item
691
+ * opens it in the editor with Back / prev / next nav, and the rail flips
692
+ * to siblings (see `collectionRailMode`).
463
693
  */
464
694
  cardinality?: RecordCardinality;
465
695
  /** Display name for an item in collection mode (defaults to `'item'`). */
@@ -469,6 +699,47 @@ interface RecordsAdminShellProps<TData = unknown> {
469
699
  * timestamp+random string.
470
700
  */
471
701
  generateItemId?: () => string;
702
+ /**
703
+ * Which built-in item views the right pane offers when a scope is selected
704
+ * and no item is open. Default `['table']`. When more than one is supplied,
705
+ * a switcher appears above the item view and the choice persists per
706
+ * `appId` + `recordType`.
707
+ *
708
+ * Ignored when `renderItemList` is supplied (the host owns the entire view).
709
+ */
710
+ itemViews?: ItemView[];
711
+ /** Initial item view when nothing is persisted. Default first of `itemViews`. */
712
+ defaultItemView?: ItemView;
713
+ /**
714
+ * Declarative columns for the built-in default `table` view. The shell
715
+ * renders a styled table — most apps with a few well-defined fields
716
+ * (FAQ question + last-updated, recipe name + cuisine + servings) want
717
+ * this rather than a custom renderer.
718
+ */
719
+ itemColumns?: ItemColumn<TData>[];
720
+ /**
721
+ * Full custom item-view renderer. When supplied, replaces the built-in
722
+ * table / cards / gallery entirely — `itemViews`, `itemColumns`, and
723
+ * `renderItemCard` are ignored.
724
+ */
725
+ renderItemList?: (items: RecordSummary<TData>[], ctx: ItemViewContext) => ReactNode;
726
+ /**
727
+ * Custom card renderer for the `cards` and `gallery` item views. Falls
728
+ * back to a styled built-in card when omitted.
729
+ */
730
+ renderItemCard?: (record: RecordSummary<TData>, ctx: ItemSlotContext) => ReactNode;
731
+ /**
732
+ * Custom empty state when a scope has no items yet. Falls back to a
733
+ * styled built-in empty state with a "+ New {noun}" CTA.
734
+ */
735
+ renderItemEmpty?: (ctx: ItemViewContext) => ReactNode;
736
+ /**
737
+ * What the rail shows once an item is open (collection cardinality only).
738
+ * Default `'siblings'` — the rail flips to the items in the current scope
739
+ * with a pinned `← All scopes` link. Set to `'scopes'` to keep the rail
740
+ * on scope navigation (admin must use Back / prev / next instead).
741
+ */
742
+ collectionRailMode?: CollectionRailMode;
472
743
  /** Display title shown in the header card. Falls back to `label`, then `recordType`. */
473
744
  title?: string;
474
745
  /** Single-line muted subtitle under the header title. */
@@ -570,6 +841,21 @@ interface RecordsAdminShellProps<TData = unknown> {
570
841
  scope: ParsedRef;
571
842
  currentValue: TData | null;
572
843
  }) => TData | null;
844
+ /**
845
+ * Mirror the shell's runtime state (current scope, open item, right-pane
846
+ * view) into URL parameters so links open to the right place and the
847
+ * browser back/forward buttons feel native.
848
+ *
849
+ * Off by default. The shell only owns the params it knows about
850
+ * (`item`, `scope`, `view`); platform context like `appId` /
851
+ * `collectionId` lives in the host's URL and stays untouched.
852
+ *
853
+ * Pass an `adapter` to integrate with a host router (React Router,
854
+ * SmartLinks `persistentQueryParams`, etc.) — without one the shell
855
+ * uses a default `window.location` + `window.history` adapter that
856
+ * also handles hash routing.
857
+ */
858
+ deepLink?: DeepLinkOptions;
573
859
  }
574
860
  /**
575
861
  * Controls the small tab strip that appears above the editor body inside the
@@ -616,7 +902,7 @@ interface RecordBrowserProps {
616
902
  }
617
903
  declare const RecordBrowser: ({ scopes, activeScope, onActiveScopeChange, selectedRef, onSelectRef, items, counts, isLoading, error, filter, onFilterChange, search, onSearchChange, hasNextPage, isFetchingNextPage, onLoadMore, scopesLoading, i18n, }: RecordBrowserProps) => react_jsx_runtime.JSX.Element;
618
904
 
619
- interface Props$9 {
905
+ interface Props$g {
620
906
  scopes: ScopeKind[];
621
907
  active: ScopeKind;
622
908
  onChange: (s: ScopeKind) => void;
@@ -627,17 +913,31 @@ interface Props$9 {
627
913
  /** Override icons used per scope. Falls back to DEFAULT_ICONS.scope. */
628
914
  icons?: RecordsAdminIcons['scope'];
629
915
  }
630
- declare const ScopeTabs: ({ scopes, active, onChange, loading, counts, icons, }: Props$9) => react_jsx_runtime.JSX.Element;
916
+ declare const ScopeTabs: ({ scopes, active, onChange, loading, counts, icons, }: Props$g) => react_jsx_runtime.JSX.Element;
631
917
 
632
- interface Props$8 {
918
+ interface Props$f {
633
919
  source?: RecordSource;
634
920
  status?: RecordStatus;
635
921
  className?: string;
636
922
  }
637
923
  /** Emerald = own data, amber = inherited, muted = empty */
638
- declare const StatusDot: ({ source, status, className }: Props$8) => react_jsx_runtime.JSX.Element;
924
+ declare const StatusDot: ({ source, status, className }: Props$f) => react_jsx_runtime.JSX.Element;
639
925
 
640
- interface Props$7 {
926
+ type StatusTone = 'own' | 'shared' | 'missing';
927
+ interface Props$e {
928
+ source?: RecordSource;
929
+ status?: RecordStatus;
930
+ className?: string;
931
+ /** Override icon size (default 1.05rem). */
932
+ size?: string;
933
+ /** Optional accessible label — defaults to the tone name. */
934
+ label?: string;
935
+ }
936
+ declare const StatusIcon: ({ source, status, className, size, label }: Props$e) => react_jsx_runtime.JSX.Element;
937
+ /** Short label rendered next to / under the row title. */
938
+ declare const statusToneLabel: (tone: StatusTone) => string;
939
+
940
+ interface Props$d {
641
941
  value: 'all' | RecordStatus;
642
942
  onChange: (v: 'all' | RecordStatus) => void;
643
943
  counts: {
@@ -652,20 +952,18 @@ interface Props$7 {
652
952
  * active filter is always rendered so users never lose context. */
653
953
  hideZero?: Array<'all' | RecordStatus>;
654
954
  }
655
- declare const StatusFilterPills: ({ value, onChange, counts, i18n, hideZero }: Props$7) => react_jsx_runtime.JSX.Element;
955
+ declare const StatusFilterPills: ({ value, onChange, counts, i18n, hideZero }: Props$d) => react_jsx_runtime.JSX.Element;
656
956
 
657
- interface Props$6 {
957
+ interface Props$c {
658
958
  items: RecordSummary[];
659
959
  selectedRef?: string;
660
960
  onSelect: (item: RecordSummary) => void;
661
961
  /** When set, the matching row gets a small "unsaved" indicator. */
662
962
  dirtyRef?: string;
663
- /** Layout. Defaults to `list` for full back-compat. */
963
+ /** Rail row density. Defaults to `list`. */
664
964
  presentation?: RecordPresentation;
665
- /** Optional override for `list` / `compact` rows. */
965
+ /** Optional custom row renderer (still dense — applied to both densities). */
666
966
  renderListRow?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
667
- /** Optional override for `grid` / `gallery` cards. */
668
- renderCard?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
669
967
  /** Optional grouping function. Returning null buckets the row under "Other". */
670
968
  groupBy?: (record: RecordSummary) => {
671
969
  key: string;
@@ -685,11 +983,11 @@ interface Props$6 {
685
983
  clipboardSourceLabel?: string;
686
984
  } | null;
687
985
  }
688
- declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, rowClipboard, }: Props$6) => react_jsx_runtime.JSX.Element;
689
- declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, rowClipboard, }: Props$6) => react_jsx_runtime.JSX.Element;
690
- declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, rowClipboard, }: Props$6) => react_jsx_runtime.JSX.Element;
691
- declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, rowClipboard, }: Props$6) => react_jsx_runtime.JSX.Element;
692
- declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, rowClipboard, }: Props$6) => react_jsx_runtime.JSX.Element;
986
+ declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
987
+ declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
988
+ declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
989
+ declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
990
+ declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
693
991
 
694
992
  interface DefaultRecordRowProps {
695
993
  record: RecordSummary;
@@ -731,7 +1029,7 @@ declare const ErrorState: ({ error }: {
731
1029
  error: Error;
732
1030
  }) => react_jsx_runtime.JSX.Element;
733
1031
 
734
- interface Props$5 {
1032
+ interface Props$b {
735
1033
  i18n: RecordsAdminI18n;
736
1034
  onApplyToMany?: () => void;
737
1035
  onCopyFrom?: () => void;
@@ -739,9 +1037,9 @@ interface Props$5 {
739
1037
  onImportCsv?: () => void;
740
1038
  onExportCsv?: () => void;
741
1039
  }
742
- declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$5) => react_jsx_runtime.JSX.Element | null;
1040
+ declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$b) => react_jsx_runtime.JSX.Element | null;
743
1041
 
744
- interface Props$4<T> {
1042
+ interface Props$a<T> {
745
1043
  ctx: EditorContext<T>;
746
1044
  i18n: RecordsAdminI18n;
747
1045
  children: ReactNode;
@@ -784,7 +1082,7 @@ interface Props$4<T> {
784
1082
  /** Host-provided icons rendered before save / discard / delete labels. */
785
1083
  actionIcons?: Partial<Record<RecordsAdminActionKey, RecordsAdminActionIcon>>;
786
1084
  }
787
- declare function RecordEditor<T>({ ctx, i18n, children, preview, bulkActions, footerExtra, onBeforeDelete, headerLabel, headerSubtitle, headerMeta, clipboard, actionLabels, actionIcons, }: Props$4<T>): react_jsx_runtime.JSX.Element;
1085
+ declare function RecordEditor<T>({ ctx, i18n, children, preview, bulkActions, footerExtra, onBeforeDelete, headerLabel, headerSubtitle, headerMeta, clipboard, actionLabels, actionIcons, }: Props$a<T>): react_jsx_runtime.JSX.Element;
788
1086
 
789
1087
  interface InheritanceCtx {
790
1088
  parentValue?: Record<string, unknown> | null;
@@ -802,10 +1100,10 @@ interface MarkerProps {
802
1100
  }
803
1101
  declare const InheritanceMarker: ({ field, inheritedValue, value, children }: MarkerProps) => react_jsx_runtime.JSX.Element;
804
1102
 
805
- interface Props$3 {
1103
+ interface Props$9 {
806
1104
  children: ReactNode;
807
1105
  }
808
- declare const ResolvedPreview: ({ children }: Props$3) => react_jsx_runtime.JSX.Element;
1106
+ declare const ResolvedPreview: ({ children }: Props$9) => react_jsx_runtime.JSX.Element;
809
1107
 
810
1108
  interface ProductChildItem {
811
1109
  /** Variant or batch id (the `<id>` part of `variant:<id>` / `batch:<id>`). */
@@ -836,7 +1134,7 @@ declare const useProductChildren: (args: UseProductChildrenArgs) => {
836
1134
  };
837
1135
 
838
1136
  type DrillTab = 'product' | 'variant' | 'batch';
839
- interface Props$2 {
1137
+ interface Props$8 {
840
1138
  productLabel: string;
841
1139
  /** Which child types are available on the collection. */
842
1140
  showVariants: boolean;
@@ -861,7 +1159,7 @@ interface Props$2 {
861
1159
  */
862
1160
  hideSingleTab?: boolean;
863
1161
  }
864
- declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, hideSingleTab, }: Props$2) => react_jsx_runtime.JSX.Element;
1162
+ declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, hideSingleTab, }: Props$8) => react_jsx_runtime.JSX.Element;
865
1163
 
866
1164
  type PreviewMode = 'inline' | 'side' | 'tab' | 'drawer';
867
1165
  interface CommonProps {
@@ -928,7 +1226,7 @@ declare const ScopeBreadcrumb: ({ scope }: {
928
1226
  }) => react_jsx_runtime.JSX.Element | null;
929
1227
 
930
1228
  type IntroTone = 'info' | 'success' | 'warning';
931
- interface Props$1 {
1229
+ interface Props$7 {
932
1230
  title: string;
933
1231
  body: ReactNode;
934
1232
  onDismiss: () => void;
@@ -937,14 +1235,16 @@ interface Props$1 {
937
1235
  /** Optional "Learn more" link or button rendered inline after the body. */
938
1236
  action?: ReactNode;
939
1237
  }
940
- declare const IntroCard: ({ title, body, onDismiss, tone, action }: Props$1) => react_jsx_runtime.JSX.Element;
1238
+ declare const IntroCard: ({ title, body, onDismiss, tone, action }: Props$7) => react_jsx_runtime.JSX.Element;
941
1239
 
942
- interface Props {
1240
+ interface Props$6 {
943
1241
  label: string;
1242
+ /** Optional override for the button label. When set, takes precedence over `label`. */
1243
+ customLabel?: string;
944
1244
  introHidden: boolean;
945
1245
  onShowIntro?: () => void;
946
1246
  }
947
- declare const UtilityRow: ({ label, introHidden, onShowIntro }: Props) => react_jsx_runtime.JSX.Element | null;
1247
+ declare const UtilityRow: ({ label, customLabel, introHidden, onShowIntro }: Props$6) => react_jsx_runtime.JSX.Element | null;
948
1248
 
949
1249
  interface RecordsCtx {
950
1250
  SL: SmartLinksSDK;
@@ -1121,6 +1421,68 @@ declare function useResolvedRecord<T = unknown>(args: UseResolvedRecordArgs): {
1121
1421
  matchedRule?: _proveanything_smartlinks_dist_types_appObjects.FacetRule;
1122
1422
  };
1123
1423
 
1424
+ interface UseCollectionItemsArgs {
1425
+ ctx: RecordsCtx;
1426
+ /**
1427
+ * Scope the items live under. Items at the collection root pass an empty
1428
+ * `scope.raw` (the hook then uses `refPrefix: 'item:'`). Pass `null` to
1429
+ * keep the query disabled.
1430
+ */
1431
+ scope: ParsedRef | null;
1432
+ /** Per-page size requested from the SDK (default 100). */
1433
+ pageSize?: number;
1434
+ /**
1435
+ * Optional projector that derives the row label / subtitle / thumbnail
1436
+ * from a record's `data`. Falls back to the itemId.
1437
+ */
1438
+ toSummary?: (rec: AppRecord, base: RecordSummary) => RecordSummary;
1439
+ /** When false, the query is paused (used while the host is still booting). */
1440
+ enabled?: boolean;
1441
+ }
1442
+ /**
1443
+ * The shell's `useRecordList` filters by *scope kind*, which doesn't
1444
+ * differentiate items from singletons. This hook intentionally bypasses
1445
+ * that filter and asks for refs under a precise prefix.
1446
+ */
1447
+ declare function useCollectionItems<T = unknown>(args: UseCollectionItemsArgs): {
1448
+ items: RecordSummary<T>[];
1449
+ total: number;
1450
+ isLoading: boolean;
1451
+ error: Error | null;
1452
+ hasNextPage: boolean;
1453
+ isFetchingNextPage: boolean;
1454
+ fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
1455
+ data: AppRecord[];
1456
+ total: number;
1457
+ hasMore: boolean;
1458
+ nextOffset: number;
1459
+ }, unknown>, Error>>;
1460
+ refetch: () => void;
1461
+ };
1462
+
1463
+ /**
1464
+ * Which logical changes are "meaningful navigation" (push) vs "cosmetic /
1465
+ * incremental" (replace) under `'smart'` history mode. The shell tags each
1466
+ * emit with one of these.
1467
+ */
1468
+ type DeepLinkChangeKind = 'item.open' | 'item.close' | 'item.step' | 'scope' | 'view';
1469
+ interface UseDeepLinkStateResult {
1470
+ /** Latest snapshot read from the adapter. */
1471
+ urlState: DeepLinkState;
1472
+ /**
1473
+ * Push a partial update through the adapter. `kind` informs the
1474
+ * push-vs-replace decision under `'smart'` mode.
1475
+ */
1476
+ emit: (partial: DeepLinkState, kind: DeepLinkChangeKind) => void;
1477
+ /** Resolved param names for callers that need them. */
1478
+ paramNames: ResolvedDeepLinkParamNames;
1479
+ /** True when deep linking is active (host opted in + we have an adapter). */
1480
+ enabled: boolean;
1481
+ }
1482
+ declare function useDeepLinkState(options: DeepLinkOptions | undefined): UseDeepLinkStateResult;
1483
+
1484
+ declare const createDefaultDeepLinkAdapter: (paramNames: ResolvedDeepLinkParamNames) => DeepLinkAdapter;
1485
+
1124
1486
  interface UseResolveAllRecordsArgs {
1125
1487
  SL: SmartLinksSDK;
1126
1488
  collectionId: string;
@@ -1434,6 +1796,88 @@ declare function usePresentationPref(args: {
1434
1796
  options: RecordPresentation[];
1435
1797
  defaultValue: RecordPresentation;
1436
1798
  }): [RecordPresentation, (next: RecordPresentation) => void];
1799
+ /**
1800
+ * Twin of `usePresentationPref` for the right-pane item view (collection
1801
+ * cardinality only). Stored under a distinct key so toggling the rail
1802
+ * presentation never clobbers the table/cards/gallery preference.
1803
+ */
1804
+ declare function useItemViewPref(args: {
1805
+ appId: string;
1806
+ recordType?: string;
1807
+ options: ItemView[];
1808
+ defaultValue: ItemView;
1809
+ }): [ItemView, (next: ItemView) => void];
1810
+
1811
+ interface Props$5<T> {
1812
+ items: RecordSummary<T>[];
1813
+ isLoading: boolean;
1814
+ error: Error | null;
1815
+ ctx: ItemViewContext;
1816
+ itemNoun: string;
1817
+ view: ItemView;
1818
+ views: ItemView[];
1819
+ onViewChange: (view: ItemView) => void;
1820
+ renderItemList?: (items: RecordSummary<T>[], ctx: ItemViewContext) => ReactNode;
1821
+ renderItemCard?: (record: RecordSummary<T>, ctx: ItemSlotContext) => ReactNode;
1822
+ renderItemEmpty?: (ctx: ItemViewContext) => ReactNode;
1823
+ itemColumns?: ItemColumn<T>[];
1824
+ i18n: RecordsAdminI18n;
1825
+ }
1826
+ declare function ItemListView<T>({ items, isLoading, error, ctx, itemNoun, view, views, onViewChange, renderItemList, renderItemCard, renderItemEmpty, itemColumns, i18n, }: Props$5<T>): react_jsx_runtime.JSX.Element;
1827
+
1828
+ interface Props$4 {
1829
+ options: ItemView[];
1830
+ value: ItemView;
1831
+ onChange: (next: ItemView) => void;
1832
+ i18n: Pick<RecordsAdminI18n, 'itemViewTable' | 'itemViewCards' | 'itemViewGallery'>;
1833
+ }
1834
+ declare const ItemViewSwitcher: ({ options, value, onChange, i18n }: Props$4) => react_jsx_runtime.JSX.Element | null;
1835
+
1836
+ interface Props$3<T> {
1837
+ items: RecordSummary<T>[];
1838
+ columns?: ItemColumn<T>[];
1839
+ selectedId?: string;
1840
+ onOpen: (itemId: string) => void;
1841
+ onDelete: (itemId: string) => void;
1842
+ i18n: Pick<RecordsAdminI18n, 'itemColumnLabel' | 'itemColumnUpdated' | 'itemActions' | 'delete'>;
1843
+ }
1844
+ declare function DefaultItemTable<T>({ items, columns, selectedId, onOpen, onDelete, i18n, }: Props$3<T>): react_jsx_runtime.JSX.Element;
1845
+
1846
+ interface Props$2<T> {
1847
+ items: RecordSummary<T>[];
1848
+ variant: 'cards' | 'gallery';
1849
+ selectedId?: string;
1850
+ ctx: ItemViewContext;
1851
+ renderCard?: (record: RecordSummary<T>, slotCtx: ItemSlotContext) => ReactNode;
1852
+ i18n: Pick<RecordsAdminI18n, 'delete'>;
1853
+ }
1854
+ declare function DefaultItemCards<T>({ items, variant, selectedId, ctx, renderCard, i18n, }: Props$2<T>): react_jsx_runtime.JSX.Element;
1855
+
1856
+ interface Props$1 {
1857
+ /** Friendly name of the item being edited (mostly for screen readers). */
1858
+ label?: string;
1859
+ /** 1-based position within the current sibling list. */
1860
+ position?: number;
1861
+ total?: number;
1862
+ onBack: () => void;
1863
+ onPrev?: () => void;
1864
+ onNext?: () => void;
1865
+ canPrev: boolean;
1866
+ canNext: boolean;
1867
+ i18n: Pick<RecordsAdminI18n, 'backToList' | 'prevItem' | 'nextItem'>;
1868
+ }
1869
+ declare const EditorItemNav: ({ label, position, total, onBack, onPrev, onNext, canPrev, canNext, i18n, }: Props$1) => react_jsx_runtime.JSX.Element;
1870
+
1871
+ interface Props<T> {
1872
+ items: RecordSummary<T>[];
1873
+ selectedItemId?: string;
1874
+ isLoading: boolean;
1875
+ error: Error | null;
1876
+ onBack: () => void;
1877
+ onSelect: (itemId: string) => void;
1878
+ i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody'>;
1879
+ }
1880
+ declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
1437
1881
 
1438
1882
  interface ClipboardEntry<T = unknown> {
1439
1883
  value: T;
@@ -1504,6 +1948,7 @@ interface BuildRefArgs {
1504
1948
  variantId?: string;
1505
1949
  batchId?: string;
1506
1950
  proofId?: string;
1951
+ itemId?: string;
1507
1952
  }
1508
1953
  declare const buildRef: (a: BuildRefArgs) => string;
1509
1954
  /**
@@ -1511,6 +1956,17 @@ declare const buildRef: (a: BuildRefArgs) => string;
1511
1956
  * `supportedScopes` prunes scopes the app doesn't care about.
1512
1957
  */
1513
1958
  declare const resolutionChain: (target: ParsedRef, supportedScopes: ScopeKind[]) => string[];
1959
+ /**
1960
+ * Strip the trailing `/item:{id}` (or leading `item:{id}`) from a ref and
1961
+ * return both halves. For singleton refs `itemId` is `undefined` and
1962
+ * `scopeRef` equals the input.
1963
+ */
1964
+ declare const splitItemRef: (raw: string) => {
1965
+ scopeRef: string;
1966
+ itemId?: string;
1967
+ };
1968
+ /** Concatenate a scope ref (possibly empty for collection) and an itemId. */
1969
+ declare const buildItemRef: (scopeRef: string, itemId: string) => string;
1514
1970
 
1515
1971
  interface ResolveArgs {
1516
1972
  ctx: RecordsCtx;
@@ -1550,4 +2006,4 @@ declare const exportCsv: <T>(records: RecordSummary<T>[], schema: CsvSchema<T>)
1550
2006
  declare const importCsv: <T>(file: File, schema: CsvSchema<T>, ctx: RecordsCtx) => Promise<ImportReport>;
1551
2007
  declare const downloadBlob: (blob: Blob, filename: string) => void;
1552
2008
 
1553
- export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, type ClipboardEntry, type CollectedRecord, type CollectedSort, type CsvSchema, type CsvSchemaColumn, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, type DirtyStrategy, DrawerPreview, type EditorContext, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, type MergeStrategy, type MergedRecord, type NavConfirmI18n, type ParsedRef, type PasteCompatibility, type PasteCompatibilityResult, PresentationSwitcher, type PreviewMode, PreviewScopePicker, PreviewToggleButton, type ProductBrowseItem, type ProductChildItem, ProductDrillDown, ProductList, type RecordBadge, RecordBrowser, type RecordCardinality, RecordEditor, RecordList, type RecordPresentation, type RecordSlotContext, type RecordSource, type RecordStatus, type RecordSummary, type RecordsAdminI18n, type RecordsAdminIcons, RecordsAdminShell, type RecordsAdminShellProps, ResolvedPreview, type ResolvedRecord, ScopeBreadcrumb, type ScopeKind, ScopeTabs, SidePreview, type SmartLinksSDK, StatusDot, StatusFilterPills, TabbedPreview, type TelemetryEvent, type UseRecordClipboardArgs, type UseRecordClipboardReturn, type UseResolveAllRecordsArgs, type UseResolveAllResult, type UseRulePreviewArgs, type UseRulePreviewResult, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, checkPasteCompatibility, cloneValue, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
2009
+ export { ALL_ITEM_VIEWS, ALL_PRESENTATIONS, BatchList, BulkActionsMenu, type ClipboardEntry, type CollectedRecord, type CollectedSort, type CollectionRailMode, type CsvSchema, type CsvSchemaColumn, DEFAULT_DEEP_LINK_PARAM_NAMES, DEFAULT_I18N, DEFAULT_ICONS, type DeepLinkAdapter, type DeepLinkChangeKind, type DeepLinkHistoryMode, type DeepLinkOptions, type DeepLinkParamNames, type DeepLinkState, DefaultItemCards, DefaultItemTable, DefaultRecordCard, DefaultRecordRow, DeleteButton, type DirtyStrategy, DrawerPreview, type EditorContext, EditorItemNav, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, type ItemColumn, ItemListView, type ItemSlotContext, type ItemView, type ItemViewContext, ItemViewSwitcher, LoadingState, type MergeStrategy, type MergedRecord, type NavConfirmI18n, type ParsedRef, type PasteCompatibility, type PasteCompatibilityResult, PresentationSwitcher, type PreviewMode, PreviewScopePicker, PreviewToggleButton, type ProductBrowseItem, type ProductChildItem, ProductDrillDown, ProductList, type RecordBadge, RecordBrowser, type RecordCardinality, RecordEditor, RecordList, type RecordPresentation, type RecordSlotContext, type RecordSource, type RecordStatus, type RecordSummary, type RecordsAdminI18n, type RecordsAdminIcons, RecordsAdminShell, type RecordsAdminShellProps, type ResolvedDeepLinkParamNames, ResolvedPreview, type ResolvedRecord, ScopeBreadcrumb, type ScopeKind, ScopeTabs, SiblingRail, SidePreview, type SmartLinksSDK, StatusDot, StatusFilterPills, StatusIcon, type StatusTone, TabbedPreview, type TelemetryEvent, type UseCollectionItemsArgs, type UseRecordClipboardArgs, type UseRecordClipboardReturn, type UseResolveAllRecordsArgs, type UseResolveAllResult, type UseRulePreviewArgs, type UseRulePreviewResult, UtilityRow, VariantList, buildItemRef, buildRef, bulkDelete, bulkUpsert, checkPasteCompatibility, cloneValue, createDefaultDeepLinkAdapter, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, splitItemRef, statusToneLabel, upsertRecord, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };