@proveanything/smartlinks-utils-ui 0.3.8 → 0.3.11

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,23 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { 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
4
  import { MatchedAt, FacetRule, 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
+ */
20
+ type ScopeKind = 'collection' | 'product' | 'facet' | 'variant' | 'batch';
10
21
  /** Parsed `ref` string — see `data/refs.ts`. Format: `kind:id` or chain. */
11
22
  interface ParsedRef {
12
23
  /** Most-specific scope this ref points at. */
@@ -20,6 +31,13 @@ interface ParsedRef {
20
31
  variantId?: string;
21
32
  batchId?: string;
22
33
  proofId?: string;
34
+ /**
35
+ * For `collection` cardinality records: the per-item identifier within the
36
+ * scope. Singleton records leave this undefined. The full ref is
37
+ * `{scopeRef}/item:{itemId}`, or just `item:{itemId}` for collection-rooted
38
+ * items.
39
+ */
40
+ itemId?: string;
23
41
  }
24
42
 
25
43
  type RecordSource = 'self' | 'inherited' | 'empty';
@@ -104,14 +122,27 @@ interface CsvSchema<TData> {
104
122
  }
105
123
 
106
124
  /**
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).
125
+ * How records are laid out in the **left rail** (the navigator).
126
+ *
127
+ * The rail is intentionally a dense tree/list — cards or galleries belong on
128
+ * the right pane, not in a 260px column. Only the row densities are offered:
129
+ *
130
+ * - `list` → dense rows with status dot + label + subtitle (default).
131
+ * - `compact` → minimal one-line rows (no subtitle, no status dot).
112
132
  */
113
- type RecordPresentation = 'list' | 'grid' | 'gallery' | 'compact';
133
+ type RecordPresentation = 'list' | 'compact';
114
134
  declare const ALL_PRESENTATIONS: RecordPresentation[];
135
+ /**
136
+ * How a multi-item collection is rendered in the **right pane** when a scope
137
+ * is selected and no item is open. Right-pane only — cards and galleries
138
+ * have the room to breathe here.
139
+ *
140
+ * - `table` → declarative columns (built-in default with `itemColumns`).
141
+ * - `cards` → square cards with thumbnail + label.
142
+ * - `gallery` → larger 16:9 cards with thumbnail + label + subtitle.
143
+ */
144
+ type ItemView = 'table' | 'cards' | 'gallery';
145
+ declare const ALL_ITEM_VIEWS: ItemView[];
115
146
  /**
116
147
  * Whether each scope holds at most one record of this type, or many.
117
148
  * - `singleton` → one record per scope (e.g. washing instructions). Default.
@@ -131,6 +162,17 @@ type CollectedSort = {
131
162
  field: string;
132
163
  direction?: 'asc' | 'desc';
133
164
  };
165
+ /**
166
+ * What the left rail shows once an item is open in the editor (collection
167
+ * cardinality only):
168
+ *
169
+ * - `siblings` *(default)* — rail flips to the list of items in the current
170
+ * scope. Click any sibling to switch instantly. A pinned `← All scopes`
171
+ * link returns to scope navigation.
172
+ * - `scopes` — rail keeps showing scopes. Switching items requires going
173
+ * back to the right-pane list first.
174
+ */
175
+ type CollectionRailMode = 'siblings' | 'scopes';
134
176
 
135
177
  type TelemetryEvent = {
136
178
  type: 'record.save';
@@ -171,6 +213,32 @@ type TelemetryEvent = {
171
213
  type: 'item.create';
172
214
  recordType?: string;
173
215
  scopeRef: string;
216
+ } | {
217
+ type: 'item.open';
218
+ recordType?: string;
219
+ scopeRef: string;
220
+ itemId: string;
221
+ } | {
222
+ type: 'item.delete';
223
+ recordType?: string;
224
+ scopeRef: string;
225
+ itemId: string;
226
+ } | {
227
+ type: 'item.view.change';
228
+ recordType?: string;
229
+ scopeRef: string;
230
+ from: string;
231
+ to: string;
232
+ } | {
233
+ type: 'clipboard.copy';
234
+ recordType?: string;
235
+ sourceRef: string;
236
+ } | {
237
+ type: 'clipboard.paste';
238
+ recordType?: string;
239
+ sourceRef: string;
240
+ destinationRef: string;
241
+ replaced: boolean;
174
242
  };
175
243
 
176
244
  interface RecordsAdminI18n {
@@ -218,13 +286,30 @@ interface RecordsAdminI18n {
218
286
  unsavedBannerBody: string;
219
287
  /** Presentation mode switcher labels. */
220
288
  presentationList: string;
221
- presentationGrid: string;
222
- presentationGallery: string;
223
289
  presentationCompact: string;
290
+ /** Right-pane item view (collection cardinality) switcher labels. */
291
+ itemViewTable: string;
292
+ itemViewCards: string;
293
+ itemViewGallery: string;
224
294
  /** Collection cardinality affordances. */
225
295
  newItem: string;
226
296
  noItemsTitle: string;
227
297
  noItemsBody: string;
298
+ /**
299
+ * Right-pane multi-item flow strings (collection cardinality).
300
+ * `{noun}` is replaced with `itemNoun` (e.g. "recipe", "FAQ").
301
+ */
302
+ backToList: string;
303
+ prevItem: string;
304
+ nextItem: string;
305
+ itemListTitle: string;
306
+ /** Friendly singular for table column header in built-in default. */
307
+ itemColumnLabel: string;
308
+ itemColumnUpdated: string;
309
+ itemActions: string;
310
+ /** Sibling rail (rail flips to items when one is open). */
311
+ backToScopes: string;
312
+ siblingsHeading: string;
228
313
  /**
229
314
  * Empty-state copy for the LEFT browse rail (the list itself is empty).
230
315
  * Defaults to a friendly "no records exist yet" message — distinct from
@@ -232,9 +317,107 @@ interface RecordsAdminI18n {
232
317
  */
233
318
  railEmptyTitle: string;
234
319
  railEmptyBody: string;
320
+ /** Clipboard / copy-paste affordance. */
321
+ copy: string;
322
+ paste: string;
323
+ pasteFrom: string;
324
+ pasteReplace: string;
325
+ clipboardEmpty: string;
326
+ copyToast: string;
327
+ pasteToast: string;
328
+ /** Confirm dialog when pasting onto a destination with an existing saved value. */
329
+ pasteConfirmTitle: string;
330
+ pasteConfirmBody: string;
331
+ pasteConfirmReplace: string;
332
+ pasteConfirmCancel: string;
333
+ /** Cross-scope warning copy (e.g. variant → product). */
334
+ pasteWarnTitle: string;
335
+ pasteWarnContinue: string;
235
336
  }
236
337
  declare const DEFAULT_I18N: RecordsAdminI18n;
237
338
 
339
+ /**
340
+ * How URL changes are pushed onto the browser history.
341
+ *
342
+ * - `push` — every change adds a history entry (rich back stack).
343
+ * - `replace` — every change replaces the current entry (no history bloat).
344
+ * - `smart` — meaningful navigation (open item, change scope) pushes;
345
+ * cosmetic changes (toggle view, prev/next sibling) replace.
346
+ * Default.
347
+ */
348
+ type DeepLinkHistoryMode = 'push' | 'replace' | 'smart';
349
+ /**
350
+ * Customise the URL parameter names the shell reads / writes. Useful when
351
+ * a host already uses one of these keys for something else.
352
+ */
353
+ interface DeepLinkParamNames {
354
+ /** Default `'item'`. */
355
+ item?: string;
356
+ /** Default `'scope'`. */
357
+ scope?: string;
358
+ /** Default `'view'`. */
359
+ view?: string;
360
+ }
361
+ /**
362
+ * The shell-owned deep-link state. All fields are optional — `null` /
363
+ * `undefined` means "absent from the URL".
364
+ */
365
+ interface DeepLinkState {
366
+ /** Item id when an item is open in the editor. */
367
+ item?: string | null;
368
+ /** Scope ref when no item is open and the user is browsing a list. */
369
+ scope?: string | null;
370
+ /** Right-pane view choice (`table` / `cards` / `gallery`). */
371
+ view?: string | null;
372
+ }
373
+ /**
374
+ * Pluggable bridge between the shell and the host's URL. Hosts can supply
375
+ * their own adapter to integrate with React Router, hash routing, the
376
+ * SmartLinks `persistentQueryParams` helper, etc. When omitted the shell
377
+ * falls back to a plain `window.location` + `window.history` adapter.
378
+ */
379
+ interface DeepLinkAdapter {
380
+ /** Read the current values for the shell-owned params. */
381
+ read(): DeepLinkState;
382
+ /**
383
+ * Write a partial state update. `mode` is the resolved push/replace hint
384
+ * (the shell already mapped `'smart'` to one of the two before calling).
385
+ * Hosts should preserve every other URL parameter — only the keys
386
+ * supplied here may be touched.
387
+ */
388
+ write(partial: DeepLinkState, mode: 'push' | 'replace'): void;
389
+ /**
390
+ * Subscribe to external URL changes (back/forward, host-driven nav).
391
+ * Should fire whenever the params the shell cares about may have
392
+ * changed. Returns an unsubscribe function.
393
+ */
394
+ subscribe(listener: () => void): () => void;
395
+ }
396
+ interface DeepLinkOptions {
397
+ /** Master switch — default `false` (opt in per app). */
398
+ enabled?: boolean;
399
+ /** Push/replace strategy. Default `'smart'`. */
400
+ history?: DeepLinkHistoryMode;
401
+ /** Override the default param key names. */
402
+ paramNames?: DeepLinkParamNames;
403
+ /**
404
+ * Pluggable URL bridge. Defaults to `window.location` + `window.history`
405
+ * with a `popstate` subscription. Provide one when the host owns routing
406
+ * (React Router, hash router, iframe postMessage relay, etc.).
407
+ */
408
+ adapter?: DeepLinkAdapter;
409
+ }
410
+ /**
411
+ * Resolved param names — everywhere outside the public surface uses these
412
+ * to avoid the optional-key ceremony.
413
+ */
414
+ interface ResolvedDeepLinkParamNames {
415
+ item: string;
416
+ scope: string;
417
+ view: string;
418
+ }
419
+ declare const DEFAULT_DEEP_LINK_PARAM_NAMES: ResolvedDeepLinkParamNames;
420
+
238
421
  interface RecordsAdminIcons {
239
422
  scope: Record<ScopeKind | 'universal', LucideIcon>;
240
423
  status: {
@@ -281,6 +464,17 @@ declare function mergeIcons(override?: Partial<RecordsAdminIcons>): RecordsAdmin
281
464
  * explicit `headerIcon` > `icons.header.byRecordType[type]` > `icons.header.default`. */
282
465
  declare function pickHeaderIcon(icons: RecordsAdminIcons, recordType?: string): LucideIcon;
283
466
 
467
+ /**
468
+ * Footer / banner action keys whose label and icon can be customised by
469
+ * the host. `save`, `discard`, `delete` are wired today. `saveAll` /
470
+ * `revertAll` are reserved for future "all dirty" affordances; passing
471
+ * them now is harmless and forward-compatible.
472
+ */
473
+ type RecordsAdminActionKey = 'save' | 'discard' | 'delete' | 'saveAll' | 'revertAll';
474
+ /** Lightweight icon contract — matches Lucide's component signature. */
475
+ type RecordsAdminActionIcon = ComponentType<{
476
+ className?: string;
477
+ }>;
284
478
  /** Minimal SDK shape consumed by the shell. */
285
479
  type SmartLinksSDK = any;
286
480
  /** Slot context passed to renderCard / renderListRow. */
@@ -288,6 +482,68 @@ interface RecordSlotContext {
288
482
  selected: boolean;
289
483
  onSelect: () => void;
290
484
  isDirty?: boolean;
485
+ /**
486
+ * When the host enables `enableClipboard`, the row context menu uses this
487
+ * callback to copy the row's resolved value into the shell-managed
488
+ * clipboard. Wired by the shell — apps don't need to implement it.
489
+ */
490
+ onCopy?: () => void;
491
+ /**
492
+ * Paste the current clipboard entry onto this row. Disabled when there is
493
+ * nothing to paste, when the source ref equals this row, or when the
494
+ * cross-scope rules deny it.
495
+ */
496
+ onPaste?: () => void;
497
+ /**
498
+ * Whether `onPaste` will succeed without a confirm. Lets the row menu
499
+ * label the action accurately (e.g. "Paste (replace)" when destination
500
+ * already has a saved value).
501
+ */
502
+ pasteWillReplace?: boolean;
503
+ /** True when the clipboard is non-empty — drives Paste menu visibility. */
504
+ canPaste?: boolean;
505
+ /** Friendly label for "Paste from {sourceLabel}" in row menus. */
506
+ clipboardSourceLabel?: string;
507
+ }
508
+ /**
509
+ * Declarative column definition for the built-in default item table.
510
+ * Supply `itemColumns` for the lazy path; supply `renderItemList` /
511
+ * `renderItemCard` for full control.
512
+ */
513
+ interface ItemColumn<TData = unknown> {
514
+ /** Stable key — used for React keys and per-column persistence. */
515
+ key: string;
516
+ /** Column header label. */
517
+ header: string;
518
+ /**
519
+ * Render the cell content. Receives the record summary plus the typed
520
+ * `data` for convenience.
521
+ */
522
+ render: (record: RecordSummary<TData>) => ReactNode;
523
+ /** CSS width hint applied to the `<th>` (and matching `<td>`). */
524
+ width?: string;
525
+ /** Right-align the cell (numeric columns). */
526
+ align?: 'left' | 'right' | 'center';
527
+ }
528
+ /**
529
+ * Context passed to right-pane item-view slot renderers.
530
+ */
531
+ interface ItemViewContext {
532
+ /** Open the item in the editor. */
533
+ onOpen: (itemId: string) => void;
534
+ /** Create a new item in the current scope. */
535
+ onCreate: () => void;
536
+ /** Delete an item by id. */
537
+ onDelete: (itemId: string) => void;
538
+ /** The scope the items belong to. */
539
+ scope: ParsedRef;
540
+ /** Item id currently open in the editor (when applicable). */
541
+ selectedId?: string;
542
+ /** True while the items are loading. */
543
+ isLoading: boolean;
544
+ }
545
+ interface ItemSlotContext extends ItemViewContext {
546
+ selected: boolean;
291
547
  }
292
548
  interface RecordsAdminShellProps<TData = unknown> {
293
549
  SL: SmartLinksSDK;
@@ -339,6 +595,12 @@ interface RecordsAdminShellProps<TData = unknown> {
339
595
  * Default false.
340
596
  */
341
597
  previewScopePicker?: boolean;
598
+ /**
599
+ * Editor tab strip behaviour. See {@link RecordsAdminEditorTabsMode}.
600
+ * Default `'off'` — the redundant single-tab strip is hidden when the
601
+ * product has no variants/batches.
602
+ */
603
+ editorTabs?: RecordsAdminEditorTabsMode;
342
604
  /**
343
605
  * How to handle unsaved edits when the user navigates to a different
344
606
  * record/scope/tab.
@@ -361,6 +623,24 @@ interface RecordsAdminShellProps<TData = unknown> {
361
623
  intro?: {
362
624
  title: string;
363
625
  body: ReactNode;
626
+ /**
627
+ * Where to surface the "show again" affordance after the user dismisses
628
+ * the intro banner.
629
+ *
630
+ * - `'header'` *(default)* — small ghost icon-button (`?`) inline inside
631
+ * the `ShellHeader`, right-aligned next to `headerActions`. Zero
632
+ * vertical footprint. Auto-falls back to `'footer'` when no header
633
+ * card is rendered (host hasn't enabled it).
634
+ * - `'footer'` — render the `?` button in the quiet utility row that
635
+ * sits above the rail/editor split.
636
+ * - `'inline'` — legacy behaviour: full-width strip with a "How it
637
+ * works" pill on the right.
638
+ * - `'hidden'` — once dismissed, do not offer a way to bring it back
639
+ * from this surface. The host can still re-enable via state reset.
640
+ */
641
+ reopenAffordance?: 'header' | 'footer' | 'inline' | 'hidden';
642
+ /** Override the default "How it works" label / tooltip. */
643
+ reopenLabel?: string;
364
644
  };
365
645
  csvSchema?: CsvSchema<TData>;
366
646
  classify?: (record: RecordSummary<TData>) => RecordStatus;
@@ -374,13 +654,9 @@ interface RecordsAdminShellProps<TData = unknown> {
374
654
  /** Initial presentation when nothing is persisted. Default first of `presentations`. */
375
655
  defaultPresentation?: RecordPresentation;
376
656
  /**
377
- * Optional custom card renderer used by `grid` / `gallery` presentations.
378
- * If omitted the shell uses `<DefaultRecordCard>`.
379
- */
380
- renderCard?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
381
- /**
382
- * Optional custom list-row renderer used by `list` / `compact` presentations.
383
- * If omitted the shell uses `<DefaultRecordRow>`.
657
+ * Optional custom row renderer for the rail. Applied to both `list` and
658
+ * `compact` densities. Cards/galleries are not supported in the rail
659
+ * they belong on the right pane (see `renderItemList` / `itemView`).
384
660
  */
385
661
  renderListRow?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
386
662
  /**
@@ -393,8 +669,11 @@ interface RecordsAdminShellProps<TData = unknown> {
393
669
  /**
394
670
  * Whether each scope holds at most one record (`singleton`, default) or
395
671
  * many (`collection`, e.g. FAQs / recipes / SOPs). In collection mode the
396
- * shell treats scopes as folders containing items, exposes a "+ New" action,
397
- * and the `RecordEditor` works on individual items via `itemId`-suffixed refs.
672
+ * shell treats scopes as folders containing items: when a scope is selected
673
+ * with no item open, the right pane shows the multi-item view (default
674
+ * table, or `renderItemList` / `itemView` overrides). Clicking an item
675
+ * opens it in the editor with Back / prev / next nav, and the rail flips
676
+ * to siblings (see `collectionRailMode`).
398
677
  */
399
678
  cardinality?: RecordCardinality;
400
679
  /** Display name for an item in collection mode (defaults to `'item'`). */
@@ -404,6 +683,47 @@ interface RecordsAdminShellProps<TData = unknown> {
404
683
  * timestamp+random string.
405
684
  */
406
685
  generateItemId?: () => string;
686
+ /**
687
+ * Which built-in item views the right pane offers when a scope is selected
688
+ * and no item is open. Default `['table']`. When more than one is supplied,
689
+ * a switcher appears above the item view and the choice persists per
690
+ * `appId` + `recordType`.
691
+ *
692
+ * Ignored when `renderItemList` is supplied (the host owns the entire view).
693
+ */
694
+ itemViews?: ItemView[];
695
+ /** Initial item view when nothing is persisted. Default first of `itemViews`. */
696
+ defaultItemView?: ItemView;
697
+ /**
698
+ * Declarative columns for the built-in default `table` view. The shell
699
+ * renders a styled table — most apps with a few well-defined fields
700
+ * (FAQ question + last-updated, recipe name + cuisine + servings) want
701
+ * this rather than a custom renderer.
702
+ */
703
+ itemColumns?: ItemColumn<TData>[];
704
+ /**
705
+ * Full custom item-view renderer. When supplied, replaces the built-in
706
+ * table / cards / gallery entirely — `itemViews`, `itemColumns`, and
707
+ * `renderItemCard` are ignored.
708
+ */
709
+ renderItemList?: (items: RecordSummary<TData>[], ctx: ItemViewContext) => ReactNode;
710
+ /**
711
+ * Custom card renderer for the `cards` and `gallery` item views. Falls
712
+ * back to a styled built-in card when omitted.
713
+ */
714
+ renderItemCard?: (record: RecordSummary<TData>, ctx: ItemSlotContext) => ReactNode;
715
+ /**
716
+ * Custom empty state when a scope has no items yet. Falls back to a
717
+ * styled built-in empty state with a "+ New {noun}" CTA.
718
+ */
719
+ renderItemEmpty?: (ctx: ItemViewContext) => ReactNode;
720
+ /**
721
+ * What the rail shows once an item is open (collection cardinality only).
722
+ * Default `'siblings'` — the rail flips to the items in the current scope
723
+ * with a pinned `← All scopes` link. Set to `'scopes'` to keep the rail
724
+ * on scope navigation (admin must use Back / prev / next instead).
725
+ */
726
+ collectionRailMode?: CollectionRailMode;
407
727
  /** Display title shown in the header card. Falls back to `label`, then `recordType`. */
408
728
  title?: string;
409
729
  /** Single-line muted subtitle under the header title. */
@@ -459,7 +779,83 @@ interface RecordsAdminShellProps<TData = unknown> {
459
779
  i18n?: Partial<RecordsAdminI18n>;
460
780
  onTelemetry?: (event: TelemetryEvent) => void;
461
781
  className?: string;
782
+ /**
783
+ * Override the resting label for footer / banner actions. When a key is
784
+ * omitted the shell falls back to the i18n default ("Save", "Discard",
785
+ * "Delete"). Loading / disabled states (e.g. "Saving…") remain owned by
786
+ * the shell — only the resting label is customisable here.
787
+ *
788
+ * Hosts that need translated labels should pass already-translated
789
+ * strings; the shell stays locale-agnostic.
790
+ */
791
+ actionLabels?: Partial<Record<RecordsAdminActionKey, string>>;
792
+ /**
793
+ * Optional icon component rendered before each action label (sized
794
+ * `h-4 w-4`). Omit a key to preserve current look — the editor footer
795
+ * renders Save/Discard without icons by default. Delete retains its
796
+ * built-in `Trash2` unless overridden here.
797
+ */
798
+ actionIcons?: Partial<Record<RecordsAdminActionKey, RecordsAdminActionIcon>>;
799
+ /**
800
+ * Enable in-shell Copy / Paste between scopes for the current `recordType`.
801
+ * Default `true`. The clipboard is scoped per `(appId, recordType)` and
802
+ * persists in `sessionStorage` so it survives host route changes.
803
+ */
804
+ enableClipboard?: boolean;
805
+ /**
806
+ * Optional transform on Copy. Receives the resolved value at the source
807
+ * scope and returns what should be stored in the clipboard. Use this to
808
+ * strip per-scope-only fields (e.g. country of origin) that shouldn't
809
+ * propagate. Defaults to a deep clone of the resolved value.
810
+ */
811
+ onCopy?: (source: {
812
+ value: TData;
813
+ scope: ParsedRef;
814
+ }) => TData;
815
+ /**
816
+ * Optional transform on Paste. Receives the clipboard payload and the
817
+ * destination scope/current value. Return the value to apply, or `null`
818
+ * to abort the paste. Defaults to replacing the destination's value with
819
+ * the clipboard's.
820
+ */
821
+ onPaste?: (clipboard: {
822
+ value: TData;
823
+ sourceScope: ParsedRef;
824
+ }, destination: {
825
+ scope: ParsedRef;
826
+ currentValue: TData | null;
827
+ }) => TData | null;
828
+ /**
829
+ * Mirror the shell's runtime state (current scope, open item, right-pane
830
+ * view) into URL parameters so links open to the right place and the
831
+ * browser back/forward buttons feel native.
832
+ *
833
+ * Off by default. The shell only owns the params it knows about
834
+ * (`item`, `scope`, `view`); platform context like `appId` /
835
+ * `collectionId` lives in the host's URL and stays untouched.
836
+ *
837
+ * Pass an `adapter` to integrate with a host router (React Router,
838
+ * SmartLinks `persistentQueryParams`, etc.) — without one the shell
839
+ * uses a default `window.location` + `window.history` adapter that
840
+ * also handles hash routing.
841
+ */
842
+ deepLink?: DeepLinkOptions;
462
843
  }
844
+ /**
845
+ * Controls the small tab strip that appears above the editor body inside the
846
+ * product drill-down.
847
+ *
848
+ * - `'off'` *(default)* — single-tab strip is hidden when the current product
849
+ * has no variants and no batches (the editor header below already names the
850
+ * record, so the strip is redundant). The strip still renders whenever
851
+ * variants or batches exist — at that point it's a real navigational
852
+ * choice between the product, its variants, and its batches.
853
+ * - `'multi'` — *reserved for a future release.* Will allow multiple records
854
+ * to be open at once with per-tab close buttons and a Save-all banner.
855
+ * Today this value behaves like `'off'` and emits a one-time console
856
+ * warning so consumers can opt in early without breaking when it ships.
857
+ */
858
+ type RecordsAdminEditorTabsMode = 'off' | 'multi';
463
859
 
464
860
  declare function RecordsAdminShell<TData = unknown>(props: RecordsAdminShellProps<TData>): react_jsx_runtime.JSX.Element;
465
861
 
@@ -490,7 +886,7 @@ interface RecordBrowserProps {
490
886
  }
491
887
  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;
492
888
 
493
- interface Props$9 {
889
+ interface Props$f {
494
890
  scopes: ScopeKind[];
495
891
  active: ScopeKind;
496
892
  onChange: (s: ScopeKind) => void;
@@ -501,17 +897,17 @@ interface Props$9 {
501
897
  /** Override icons used per scope. Falls back to DEFAULT_ICONS.scope. */
502
898
  icons?: RecordsAdminIcons['scope'];
503
899
  }
504
- declare const ScopeTabs: ({ scopes, active, onChange, loading, counts, icons, }: Props$9) => react_jsx_runtime.JSX.Element;
900
+ declare const ScopeTabs: ({ scopes, active, onChange, loading, counts, icons, }: Props$f) => react_jsx_runtime.JSX.Element;
505
901
 
506
- interface Props$8 {
902
+ interface Props$e {
507
903
  source?: RecordSource;
508
904
  status?: RecordStatus;
509
905
  className?: string;
510
906
  }
511
907
  /** Emerald = own data, amber = inherited, muted = empty */
512
- declare const StatusDot: ({ source, status, className }: Props$8) => react_jsx_runtime.JSX.Element;
908
+ declare const StatusDot: ({ source, status, className }: Props$e) => react_jsx_runtime.JSX.Element;
513
909
 
514
- interface Props$7 {
910
+ interface Props$d {
515
911
  value: 'all' | RecordStatus;
516
912
  onChange: (v: 'all' | RecordStatus) => void;
517
913
  counts: {
@@ -526,32 +922,42 @@ interface Props$7 {
526
922
  * active filter is always rendered so users never lose context. */
527
923
  hideZero?: Array<'all' | RecordStatus>;
528
924
  }
529
- declare const StatusFilterPills: ({ value, onChange, counts, i18n, hideZero }: Props$7) => react_jsx_runtime.JSX.Element;
925
+ declare const StatusFilterPills: ({ value, onChange, counts, i18n, hideZero }: Props$d) => react_jsx_runtime.JSX.Element;
530
926
 
531
- interface Props$6 {
927
+ interface Props$c {
532
928
  items: RecordSummary[];
533
929
  selectedRef?: string;
534
930
  onSelect: (item: RecordSummary) => void;
535
931
  /** When set, the matching row gets a small "unsaved" indicator. */
536
932
  dirtyRef?: string;
537
- /** Layout. Defaults to `list` for full back-compat. */
933
+ /** Rail row density. Defaults to `list`. */
538
934
  presentation?: RecordPresentation;
539
- /** Optional override for `list` / `compact` rows. */
935
+ /** Optional custom row renderer (still dense — applied to both densities). */
540
936
  renderListRow?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
541
- /** Optional override for `grid` / `gallery` cards. */
542
- renderCard?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
543
937
  /** Optional grouping function. Returning null buckets the row under "Other". */
544
938
  groupBy?: (record: RecordSummary) => {
545
939
  key: string;
546
940
  label: string;
547
941
  icon?: ReactNode;
548
942
  } | null;
943
+ /**
944
+ * Optional clipboard wiring per row. When provided the shell's Copy /
945
+ * Paste affordance appears in each row's context menu. Returning `null`
946
+ * for a row hides the menu for that row.
947
+ */
948
+ rowClipboard?: (record: RecordSummary) => {
949
+ onCopy?: () => void;
950
+ onPaste?: () => void;
951
+ canPaste?: boolean;
952
+ pasteWillReplace?: boolean;
953
+ clipboardSourceLabel?: string;
954
+ } | null;
549
955
  }
550
- declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
551
- declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
552
- declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
553
- declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
554
- declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
956
+ declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
957
+ declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
958
+ declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
959
+ declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
960
+ declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, groupBy, rowClipboard, }: Props$c) => react_jsx_runtime.JSX.Element;
555
961
 
556
962
  interface DefaultRecordRowProps {
557
963
  record: RecordSummary;
@@ -593,7 +999,7 @@ declare const ErrorState: ({ error }: {
593
999
  error: Error;
594
1000
  }) => react_jsx_runtime.JSX.Element;
595
1001
 
596
- interface Props$5 {
1002
+ interface Props$b {
597
1003
  i18n: RecordsAdminI18n;
598
1004
  onApplyToMany?: () => void;
599
1005
  onCopyFrom?: () => void;
@@ -601,9 +1007,9 @@ interface Props$5 {
601
1007
  onImportCsv?: () => void;
602
1008
  onExportCsv?: () => void;
603
1009
  }
604
- declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$5) => react_jsx_runtime.JSX.Element | null;
1010
+ declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$b) => react_jsx_runtime.JSX.Element | null;
605
1011
 
606
- interface Props$4<T> {
1012
+ interface Props$a<T> {
607
1013
  ctx: EditorContext<T>;
608
1014
  i18n: RecordsAdminI18n;
609
1015
  children: ReactNode;
@@ -628,8 +1034,25 @@ interface Props$4<T> {
628
1034
  * without it competing with the friendly name as a subtitle.
629
1035
  */
630
1036
  headerMeta?: string;
1037
+ /**
1038
+ * Clipboard footer actions. The shell wires these when `enableClipboard`
1039
+ * is on. Copy is always offered when there's an editing scope; Paste is
1040
+ * disabled when the clipboard is empty or the cross-scope rules deny it.
1041
+ */
1042
+ clipboard?: {
1043
+ onCopy: () => void;
1044
+ onPaste: () => void;
1045
+ canCopy: boolean;
1046
+ canPaste: boolean;
1047
+ pasteSourceLabel?: string;
1048
+ pasteWillReplace?: boolean;
1049
+ };
1050
+ /** Host-provided labels for save / discard / delete (resting state only). */
1051
+ actionLabels?: Partial<Record<RecordsAdminActionKey, string>>;
1052
+ /** Host-provided icons rendered before save / discard / delete labels. */
1053
+ actionIcons?: Partial<Record<RecordsAdminActionKey, RecordsAdminActionIcon>>;
631
1054
  }
632
- declare function RecordEditor<T>({ ctx, i18n, children, preview, bulkActions, footerExtra, onBeforeDelete, headerLabel, headerSubtitle, headerMeta, }: Props$4<T>): react_jsx_runtime.JSX.Element;
1055
+ 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;
633
1056
 
634
1057
  interface InheritanceCtx {
635
1058
  parentValue?: Record<string, unknown> | null;
@@ -647,10 +1070,10 @@ interface MarkerProps {
647
1070
  }
648
1071
  declare const InheritanceMarker: ({ field, inheritedValue, value, children }: MarkerProps) => react_jsx_runtime.JSX.Element;
649
1072
 
650
- interface Props$3 {
1073
+ interface Props$9 {
651
1074
  children: ReactNode;
652
1075
  }
653
- declare const ResolvedPreview: ({ children }: Props$3) => react_jsx_runtime.JSX.Element;
1076
+ declare const ResolvedPreview: ({ children }: Props$9) => react_jsx_runtime.JSX.Element;
654
1077
 
655
1078
  interface ProductChildItem {
656
1079
  /** Variant or batch id (the `<id>` part of `variant:<id>` / `batch:<id>`). */
@@ -681,7 +1104,7 @@ declare const useProductChildren: (args: UseProductChildrenArgs) => {
681
1104
  };
682
1105
 
683
1106
  type DrillTab = 'product' | 'variant' | 'batch';
684
- interface Props$2 {
1107
+ interface Props$8 {
685
1108
  productLabel: string;
686
1109
  /** Which child types are available on the collection. */
687
1110
  showVariants: boolean;
@@ -697,8 +1120,16 @@ interface Props$2 {
697
1120
  batchesLoading: boolean;
698
1121
  /** Editor body rendered to the right of the picker. */
699
1122
  children: ReactNode;
1123
+ /**
1124
+ * When true, the drill-down tab strip is hidden even when only the
1125
+ * `product` tab would render (i.e. no variants / batches). The editor
1126
+ * header below already names the record so the single-tab strip is
1127
+ * redundant. The strip still appears whenever variants or batches are
1128
+ * available — at that point it's a real navigational choice.
1129
+ */
1130
+ hideSingleTab?: boolean;
700
1131
  }
701
- declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, }: Props$2) => react_jsx_runtime.JSX.Element;
1132
+ declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, hideSingleTab, }: Props$8) => react_jsx_runtime.JSX.Element;
702
1133
 
703
1134
  type PreviewMode = 'inline' | 'side' | 'tab' | 'drawer';
704
1135
  interface CommonProps {
@@ -765,7 +1196,7 @@ declare const ScopeBreadcrumb: ({ scope }: {
765
1196
  }) => react_jsx_runtime.JSX.Element | null;
766
1197
 
767
1198
  type IntroTone = 'info' | 'success' | 'warning';
768
- interface Props$1 {
1199
+ interface Props$7 {
769
1200
  title: string;
770
1201
  body: ReactNode;
771
1202
  onDismiss: () => void;
@@ -774,14 +1205,16 @@ interface Props$1 {
774
1205
  /** Optional "Learn more" link or button rendered inline after the body. */
775
1206
  action?: ReactNode;
776
1207
  }
777
- declare const IntroCard: ({ title, body, onDismiss, tone, action }: Props$1) => react_jsx_runtime.JSX.Element;
1208
+ declare const IntroCard: ({ title, body, onDismiss, tone, action }: Props$7) => react_jsx_runtime.JSX.Element;
778
1209
 
779
- interface Props {
1210
+ interface Props$6 {
780
1211
  label: string;
1212
+ /** Optional override for the button label. When set, takes precedence over `label`. */
1213
+ customLabel?: string;
781
1214
  introHidden: boolean;
782
1215
  onShowIntro?: () => void;
783
1216
  }
784
- declare const UtilityRow: ({ label, introHidden, onShowIntro }: Props) => react_jsx_runtime.JSX.Element | null;
1217
+ declare const UtilityRow: ({ label, customLabel, introHidden, onShowIntro }: Props$6) => react_jsx_runtime.JSX.Element | null;
785
1218
 
786
1219
  interface RecordsCtx {
787
1220
  SL: SmartLinksSDK;
@@ -958,6 +1391,68 @@ declare function useResolvedRecord<T = unknown>(args: UseResolvedRecordArgs): {
958
1391
  matchedRule?: _proveanything_smartlinks_dist_types_appObjects.FacetRule;
959
1392
  };
960
1393
 
1394
+ interface UseCollectionItemsArgs {
1395
+ ctx: RecordsCtx;
1396
+ /**
1397
+ * Scope the items live under. Items at the collection root pass an empty
1398
+ * `scope.raw` (the hook then uses `refPrefix: 'item:'`). Pass `null` to
1399
+ * keep the query disabled.
1400
+ */
1401
+ scope: ParsedRef | null;
1402
+ /** Per-page size requested from the SDK (default 100). */
1403
+ pageSize?: number;
1404
+ /**
1405
+ * Optional projector that derives the row label / subtitle / thumbnail
1406
+ * from a record's `data`. Falls back to the itemId.
1407
+ */
1408
+ toSummary?: (rec: AppRecord, base: RecordSummary) => RecordSummary;
1409
+ /** When false, the query is paused (used while the host is still booting). */
1410
+ enabled?: boolean;
1411
+ }
1412
+ /**
1413
+ * The shell's `useRecordList` filters by *scope kind*, which doesn't
1414
+ * differentiate items from singletons. This hook intentionally bypasses
1415
+ * that filter and asks for refs under a precise prefix.
1416
+ */
1417
+ declare function useCollectionItems<T = unknown>(args: UseCollectionItemsArgs): {
1418
+ items: RecordSummary<T>[];
1419
+ total: number;
1420
+ isLoading: boolean;
1421
+ error: Error | null;
1422
+ hasNextPage: boolean;
1423
+ isFetchingNextPage: boolean;
1424
+ fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
1425
+ data: AppRecord[];
1426
+ total: number;
1427
+ hasMore: boolean;
1428
+ nextOffset: number;
1429
+ }, unknown>, Error>>;
1430
+ refetch: () => void;
1431
+ };
1432
+
1433
+ /**
1434
+ * Which logical changes are "meaningful navigation" (push) vs "cosmetic /
1435
+ * incremental" (replace) under `'smart'` history mode. The shell tags each
1436
+ * emit with one of these.
1437
+ */
1438
+ type DeepLinkChangeKind = 'item.open' | 'item.close' | 'item.step' | 'scope' | 'view';
1439
+ interface UseDeepLinkStateResult {
1440
+ /** Latest snapshot read from the adapter. */
1441
+ urlState: DeepLinkState;
1442
+ /**
1443
+ * Push a partial update through the adapter. `kind` informs the
1444
+ * push-vs-replace decision under `'smart'` mode.
1445
+ */
1446
+ emit: (partial: DeepLinkState, kind: DeepLinkChangeKind) => void;
1447
+ /** Resolved param names for callers that need them. */
1448
+ paramNames: ResolvedDeepLinkParamNames;
1449
+ /** True when deep linking is active (host opted in + we have an adapter). */
1450
+ enabled: boolean;
1451
+ }
1452
+ declare function useDeepLinkState(options: DeepLinkOptions | undefined): UseDeepLinkStateResult;
1453
+
1454
+ declare const createDefaultDeepLinkAdapter: (paramNames: ResolvedDeepLinkParamNames) => DeepLinkAdapter;
1455
+
961
1456
  interface UseResolveAllRecordsArgs {
962
1457
  SL: SmartLinksSDK;
963
1458
  collectionId: string;
@@ -1271,6 +1766,122 @@ declare function usePresentationPref(args: {
1271
1766
  options: RecordPresentation[];
1272
1767
  defaultValue: RecordPresentation;
1273
1768
  }): [RecordPresentation, (next: RecordPresentation) => void];
1769
+ /**
1770
+ * Twin of `usePresentationPref` for the right-pane item view (collection
1771
+ * cardinality only). Stored under a distinct key so toggling the rail
1772
+ * presentation never clobbers the table/cards/gallery preference.
1773
+ */
1774
+ declare function useItemViewPref(args: {
1775
+ appId: string;
1776
+ recordType?: string;
1777
+ options: ItemView[];
1778
+ defaultValue: ItemView;
1779
+ }): [ItemView, (next: ItemView) => void];
1780
+
1781
+ interface Props$5<T> {
1782
+ items: RecordSummary<T>[];
1783
+ isLoading: boolean;
1784
+ error: Error | null;
1785
+ ctx: ItemViewContext;
1786
+ itemNoun: string;
1787
+ view: ItemView;
1788
+ views: ItemView[];
1789
+ onViewChange: (view: ItemView) => void;
1790
+ renderItemList?: (items: RecordSummary<T>[], ctx: ItemViewContext) => ReactNode;
1791
+ renderItemCard?: (record: RecordSummary<T>, ctx: ItemSlotContext) => ReactNode;
1792
+ renderItemEmpty?: (ctx: ItemViewContext) => ReactNode;
1793
+ itemColumns?: ItemColumn<T>[];
1794
+ i18n: RecordsAdminI18n;
1795
+ }
1796
+ 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;
1797
+
1798
+ interface Props$4 {
1799
+ options: ItemView[];
1800
+ value: ItemView;
1801
+ onChange: (next: ItemView) => void;
1802
+ i18n: Pick<RecordsAdminI18n, 'itemViewTable' | 'itemViewCards' | 'itemViewGallery'>;
1803
+ }
1804
+ declare const ItemViewSwitcher: ({ options, value, onChange, i18n }: Props$4) => react_jsx_runtime.JSX.Element | null;
1805
+
1806
+ interface Props$3<T> {
1807
+ items: RecordSummary<T>[];
1808
+ columns?: ItemColumn<T>[];
1809
+ selectedId?: string;
1810
+ onOpen: (itemId: string) => void;
1811
+ onDelete: (itemId: string) => void;
1812
+ i18n: Pick<RecordsAdminI18n, 'itemColumnLabel' | 'itemColumnUpdated' | 'itemActions' | 'delete'>;
1813
+ }
1814
+ declare function DefaultItemTable<T>({ items, columns, selectedId, onOpen, onDelete, i18n, }: Props$3<T>): react_jsx_runtime.JSX.Element;
1815
+
1816
+ interface Props$2<T> {
1817
+ items: RecordSummary<T>[];
1818
+ variant: 'cards' | 'gallery';
1819
+ selectedId?: string;
1820
+ ctx: ItemViewContext;
1821
+ renderCard?: (record: RecordSummary<T>, slotCtx: ItemSlotContext) => ReactNode;
1822
+ i18n: Pick<RecordsAdminI18n, 'delete'>;
1823
+ }
1824
+ declare function DefaultItemCards<T>({ items, variant, selectedId, ctx, renderCard, i18n, }: Props$2<T>): react_jsx_runtime.JSX.Element;
1825
+
1826
+ interface Props$1 {
1827
+ /** Friendly name of the item being edited (mostly for screen readers). */
1828
+ label?: string;
1829
+ /** 1-based position within the current sibling list. */
1830
+ position?: number;
1831
+ total?: number;
1832
+ onBack: () => void;
1833
+ onPrev?: () => void;
1834
+ onNext?: () => void;
1835
+ canPrev: boolean;
1836
+ canNext: boolean;
1837
+ i18n: Pick<RecordsAdminI18n, 'backToList' | 'prevItem' | 'nextItem'>;
1838
+ }
1839
+ declare const EditorItemNav: ({ label, position, total, onBack, onPrev, onNext, canPrev, canNext, i18n, }: Props$1) => react_jsx_runtime.JSX.Element;
1840
+
1841
+ interface Props<T> {
1842
+ items: RecordSummary<T>[];
1843
+ selectedItemId?: string;
1844
+ isLoading: boolean;
1845
+ error: Error | null;
1846
+ onBack: () => void;
1847
+ onSelect: (itemId: string) => void;
1848
+ i18n: Pick<RecordsAdminI18n, 'backToScopes' | 'siblingsHeading' | 'noItemsTitle' | 'noItemsBody'>;
1849
+ }
1850
+ declare function SiblingRail<T>({ items, selectedItemId, isLoading, error, onBack, onSelect, i18n, }: Props<T>): react_jsx_runtime.JSX.Element;
1851
+
1852
+ interface ClipboardEntry<T = unknown> {
1853
+ value: T;
1854
+ sourceScope: ScopeKind | 'collection';
1855
+ sourceRef: string;
1856
+ /** Friendly label captured at copy time so paste targets can show
1857
+ * "Paste from {sourceLabel}" without re-fetching the source. */
1858
+ sourceLabel?: string;
1859
+ /** ISO timestamp — debug/telemetry only. */
1860
+ copiedAt: string;
1861
+ }
1862
+ interface UseRecordClipboardArgs {
1863
+ /** Required — clipboard is scoped per app to prevent cross-app paste. */
1864
+ appId: string;
1865
+ /** Required — also scoped per record type within the app. */
1866
+ recordType: string;
1867
+ }
1868
+ interface UseRecordClipboardReturn<T = unknown> {
1869
+ entry: ClipboardEntry<T> | null;
1870
+ hasEntry: boolean;
1871
+ set: (entry: Omit<ClipboardEntry<T>, 'copiedAt'>) => void;
1872
+ clear: () => void;
1873
+ }
1874
+ declare function useRecordClipboard<T = unknown>(args: UseRecordClipboardArgs): UseRecordClipboardReturn<T>;
1875
+ type PasteCompatibility = 'allowed' | 'warn' | 'denied';
1876
+ interface PasteCompatibilityResult {
1877
+ status: PasteCompatibility;
1878
+ /** Optional short reason — surfaced as a tooltip on the Paste button. */
1879
+ reason?: string;
1880
+ }
1881
+ /** Sane defaults — host can override entirely via `onPaste` returning null. */
1882
+ declare function checkPasteCompatibility(source: ParsedRef, destination: ParsedRef): PasteCompatibilityResult;
1883
+ /** Deep clone — uses `structuredClone` when available, JSON fallback otherwise. */
1884
+ declare function cloneValue<T>(value: T): T;
1274
1885
 
1275
1886
  interface DeleteButtonProps {
1276
1887
  /** Performs the delete. Should resolve when the SDK call completes. */
@@ -1287,8 +1898,16 @@ interface DeleteButtonProps {
1287
1898
  /** Auto-revert timeout in ms (default 3000). */
1288
1899
  revertMs?: number;
1289
1900
  disabled?: boolean;
1901
+ /**
1902
+ * Optional icon override for the resting state. Defaults to Lucide
1903
+ * `Trash2`. The confirm-state icon stays `Trash2` so the destructive
1904
+ * intent reads consistently regardless of the host's branding.
1905
+ */
1906
+ icon?: ComponentType<{
1907
+ className?: string;
1908
+ }>;
1290
1909
  }
1291
- declare const DeleteButton: ({ onConfirm, onBeforeDelete, label, confirmLabel, revertMs, disabled, }: DeleteButtonProps) => react_jsx_runtime.JSX.Element;
1910
+ declare const DeleteButton: ({ onConfirm, onBeforeDelete, label, confirmLabel, revertMs, disabled, icon, }: DeleteButtonProps) => react_jsx_runtime.JSX.Element;
1292
1911
 
1293
1912
  declare const parseRef: (raw: string) => ParsedRef;
1294
1913
  interface BuildRefArgs {
@@ -1299,6 +1918,7 @@ interface BuildRefArgs {
1299
1918
  variantId?: string;
1300
1919
  batchId?: string;
1301
1920
  proofId?: string;
1921
+ itemId?: string;
1302
1922
  }
1303
1923
  declare const buildRef: (a: BuildRefArgs) => string;
1304
1924
  /**
@@ -1306,6 +1926,17 @@ declare const buildRef: (a: BuildRefArgs) => string;
1306
1926
  * `supportedScopes` prunes scopes the app doesn't care about.
1307
1927
  */
1308
1928
  declare const resolutionChain: (target: ParsedRef, supportedScopes: ScopeKind[]) => string[];
1929
+ /**
1930
+ * Strip the trailing `/item:{id}` (or leading `item:{id}`) from a ref and
1931
+ * return both halves. For singleton refs `itemId` is `undefined` and
1932
+ * `scopeRef` equals the input.
1933
+ */
1934
+ declare const splitItemRef: (raw: string) => {
1935
+ scopeRef: string;
1936
+ itemId?: string;
1937
+ };
1938
+ /** Concatenate a scope ref (possibly empty for collection) and an itemId. */
1939
+ declare const buildItemRef: (scopeRef: string, itemId: string) => string;
1309
1940
 
1310
1941
  interface ResolveArgs {
1311
1942
  ctx: RecordsCtx;
@@ -1345,4 +1976,4 @@ declare const exportCsv: <T>(records: RecordSummary<T>[], schema: CsvSchema<T>)
1345
1976
  declare const importCsv: <T>(file: File, schema: CsvSchema<T>, ctx: RecordsCtx) => Promise<ImportReport>;
1346
1977
  declare const downloadBlob: (blob: Blob, filename: string) => void;
1347
1978
 
1348
- export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, 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, 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 UseResolveAllRecordsArgs, type UseResolveAllResult, type UseRulePreviewArgs, type UseRulePreviewResult, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, 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, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
1979
+ 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, 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, upsertRecord, useCollectedRecords, useCollectionItems, useDeepLinkState, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useItemViewPref, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };