@proveanything/smartlinks-utils-ui 0.2.13 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1178 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { LucideIcon } from 'lucide-react';
4
+ import * as _tanstack_query_core from '@tanstack/query-core';
5
+ import { RecordScope, AppRecord, RecordTarget, MatchResult } from '@proveanything/smartlinks/dist/types/appObjects';
6
+
7
+ type ScopeKind = 'product' | 'facet' | 'variant' | 'batch';
8
+ /** Parsed `ref` string — see `data/refs.ts`. Format: `kind:id` or chain. */
9
+ interface ParsedRef {
10
+ /** Most-specific scope this ref points at. */
11
+ kind: ScopeKind | 'collection';
12
+ /** Raw ref string, e.g. `product:abc123` or `facet:gluten-free/variant:500ml`. */
13
+ raw: string;
14
+ collectionId?: string;
15
+ productId?: string;
16
+ facetId?: string;
17
+ facetValue?: string;
18
+ variantId?: string;
19
+ batchId?: string;
20
+ proofId?: string;
21
+ }
22
+
23
+ type RecordSource = 'self' | 'inherited' | 'empty';
24
+ type RecordStatus = 'configured' | 'partial' | 'empty';
25
+ /** Optional badge displayed on a record card or row. */
26
+ interface RecordBadge {
27
+ label: string;
28
+ /** Semantic tone — apps may map to their own colour vocabulary. */
29
+ tone?: 'neutral' | 'info' | 'success' | 'warning' | 'danger';
30
+ }
31
+ interface RecordSummary<TData = unknown> {
32
+ id: string | null;
33
+ ref: string;
34
+ scope: ParsedRef;
35
+ data: TData | null;
36
+ status: RecordStatus;
37
+ /** Display label for the list row. */
38
+ label: string;
39
+ /** Optional subtitle (e.g. SKU, GTIN). */
40
+ subtitle?: string;
41
+ updatedAt?: string;
42
+ /**
43
+ * Optional thumbnail URL used by card / gallery presentations. Apps that
44
+ * don't supply one fall back to a coloured tile with the label initials.
45
+ */
46
+ thumbnail?: string;
47
+ /** Optional small badges (e.g. "Draft", "12 items"). */
48
+ badges?: RecordBadge[];
49
+ /**
50
+ * For `collection` cardinality record types: the per-item identifier within
51
+ * a scope. The full ref is `{scopeRef}/item:{itemId}`. Singleton records
52
+ * leave this undefined.
53
+ */
54
+ itemId?: string;
55
+ }
56
+ interface ResolvedRecord<TData = unknown> {
57
+ data: TData | null;
58
+ source: RecordSource;
59
+ /** Where the resolved value came from (own scope or parent ref). */
60
+ sourceRef?: string;
61
+ /** The parent value that would be inherited if this scope cleared its override. */
62
+ parentValue?: TData | null;
63
+ }
64
+
65
+ interface EditorContext<TData = unknown> {
66
+ value: TData;
67
+ onChange: (next: TData) => void;
68
+ source: RecordSource;
69
+ parentValue?: TData | null;
70
+ scope: ParsedRef;
71
+ isDirty: boolean;
72
+ save: () => Promise<void>;
73
+ /** Discard local changes (reverts to last saved/inherited value). */
74
+ reset: () => void;
75
+ /** Permanently delete this record's own override (when `source === 'self'`). */
76
+ remove: () => Promise<void>;
77
+ /** True when a saved-own record exists that can be removed. */
78
+ canRemove: boolean;
79
+ /** True while an optimistic save is in flight (request not yet acknowledged). */
80
+ isSaving?: boolean;
81
+ /** Last save error, if any. Cleared when the next save attempt starts. */
82
+ saveError?: unknown | null;
83
+ }
84
+
85
+ interface CsvSchemaColumn<TData> {
86
+ key: string;
87
+ header: string;
88
+ toCell: (v: TData) => string;
89
+ fromCell: (s: string) => unknown;
90
+ }
91
+ interface CsvSchema<TData> {
92
+ columns: Array<CsvSchemaColumn<TData>>;
93
+ validate?: (row: Record<string, unknown>) => string | null;
94
+ }
95
+
96
+ /**
97
+ * How records are laid out in the browse rail / panel.
98
+ * - `list` → dense rows with status dot + label + subtitle (default).
99
+ * - `grid` → square cards with thumbnail + label.
100
+ * - `gallery` → larger 16:9 cards with thumbnail + label + subtitle.
101
+ * - `compact` → minimal one-line rows (no subtitle, no status dot).
102
+ */
103
+ type RecordPresentation = 'list' | 'grid' | 'gallery' | 'compact';
104
+ declare const ALL_PRESENTATIONS: RecordPresentation[];
105
+ /**
106
+ * Whether each scope holds at most one record of this type, or many.
107
+ * - `singleton` → one record per scope (e.g. washing instructions). Default.
108
+ * - `collection` → many records per scope (e.g. FAQs, recipes, SOPs).
109
+ */
110
+ type RecordCardinality = 'singleton' | 'collection';
111
+ /**
112
+ * How `useCollectedRecords` orders the aggregated chain.
113
+ * - `specificity` → most-specific scope first (default).
114
+ * - `field` → use a numeric/string `data.<field>` to sort.
115
+ */
116
+ type CollectedSort = {
117
+ kind: 'specificity';
118
+ direction?: 'asc' | 'desc';
119
+ } | {
120
+ kind: 'field';
121
+ field: string;
122
+ direction?: 'asc' | 'desc';
123
+ };
124
+
125
+ type TelemetryEvent = {
126
+ type: 'record.save';
127
+ recordType: string;
128
+ ref: string;
129
+ isCreate: boolean;
130
+ } | {
131
+ type: 'record.delete';
132
+ recordType: string;
133
+ ref: string;
134
+ } | {
135
+ type: 'scope.change';
136
+ recordType: string;
137
+ from: ScopeKind;
138
+ to: ScopeKind;
139
+ } | {
140
+ type: 'intro.dismiss';
141
+ recordType: string;
142
+ } | {
143
+ type: 'csv.import';
144
+ recordType: string;
145
+ rows: number;
146
+ errors: number;
147
+ } | {
148
+ type: 'csv.export';
149
+ recordType: string;
150
+ rows: number;
151
+ } | {
152
+ type: 'bulk.apply';
153
+ recordType: string;
154
+ targetCount: number;
155
+ } | {
156
+ type: 'presentation.change';
157
+ recordType: string;
158
+ from: RecordPresentation;
159
+ to: RecordPresentation;
160
+ } | {
161
+ type: 'item.create';
162
+ recordType: string;
163
+ scopeRef: string;
164
+ };
165
+
166
+ interface RecordsAdminI18n {
167
+ emptyTitle: string;
168
+ emptyBody: string;
169
+ noResults: string;
170
+ searchPlaceholder: string;
171
+ filterAll: string;
172
+ filterConfigured: string;
173
+ filterPartial: string;
174
+ filterEmpty: string;
175
+ save: string;
176
+ discard: string;
177
+ delete: string;
178
+ /** Save button label while a save is in flight. */
179
+ saving: string;
180
+ /** Inline error label shown next to the Save button when a save fails. */
181
+ saveError: string;
182
+ inherited: string;
183
+ override: string;
184
+ reset: string;
185
+ bulkActions: string;
186
+ applyToMany: string;
187
+ copyFrom: string;
188
+ clear: string;
189
+ importCsv: string;
190
+ exportCsv: string;
191
+ preview: string;
192
+ editor: string;
193
+ closePreview: string;
194
+ openPreview: string;
195
+ previewAs: string;
196
+ previewAsDefault: string;
197
+ confirmDelete: string;
198
+ unsavedBadge: string;
199
+ unsavedPromptTitle: string;
200
+ unsavedPromptBody: string;
201
+ unsavedPromptDiscard: string;
202
+ unsavedPromptCancel: string;
203
+ unsavedPromptSave: string;
204
+ /** Presentation mode switcher labels. */
205
+ presentationList: string;
206
+ presentationGrid: string;
207
+ presentationGallery: string;
208
+ presentationCompact: string;
209
+ /** Collection cardinality affordances. */
210
+ newItem: string;
211
+ noItemsTitle: string;
212
+ noItemsBody: string;
213
+ }
214
+ declare const DEFAULT_I18N: RecordsAdminI18n;
215
+
216
+ interface RecordsAdminIcons {
217
+ scope: Record<ScopeKind | 'universal', LucideIcon>;
218
+ status: {
219
+ own: LucideIcon;
220
+ inherited: LucideIcon;
221
+ missing: LucideIcon;
222
+ };
223
+ action: {
224
+ add: LucideIcon;
225
+ edit: LucideIcon;
226
+ duplicate: LucideIcon;
227
+ delete: LucideIcon;
228
+ import: LucideIcon;
229
+ export: LucideIcon;
230
+ more: LucideIcon;
231
+ };
232
+ preview: {
233
+ eye: LucideIcon;
234
+ switch: LucideIcon;
235
+ };
236
+ empty: {
237
+ default: LucideIcon;
238
+ search: LucideIcon;
239
+ };
240
+ intro: {
241
+ info: LucideIcon;
242
+ };
243
+ /** Header — used when no `headerIcon` prop is passed. Apps can override
244
+ * `byRecordType[recordType]` for a contextual icon, or `default` as the
245
+ * catch-all. */
246
+ header: {
247
+ default: LucideIcon;
248
+ byRecordType: Record<string, LucideIcon>;
249
+ };
250
+ group: {
251
+ chevron: LucideIcon;
252
+ };
253
+ }
254
+ /** Library defaults — every consumer gets these unless `icons` is overridden. */
255
+ declare const DEFAULT_ICONS: RecordsAdminIcons;
256
+ /** Deep-merge an `icons` prop onto the defaults. */
257
+ declare function mergeIcons(override?: Partial<RecordsAdminIcons>): RecordsAdminIcons;
258
+ /** Look up the right header icon based on prop precedence:
259
+ * explicit `headerIcon` > `icons.header.byRecordType[type]` > `icons.header.default`. */
260
+ declare function pickHeaderIcon(icons: RecordsAdminIcons, recordType: string): LucideIcon;
261
+
262
+ /** Minimal SDK shape consumed by the shell. */
263
+ type SmartLinksSDK = any;
264
+ /** Slot context passed to renderCard / renderListRow. */
265
+ interface RecordSlotContext {
266
+ selected: boolean;
267
+ onSelect: () => void;
268
+ isDirty?: boolean;
269
+ }
270
+ interface RecordsAdminShellProps<TData = unknown> {
271
+ SL: SmartLinksSDK;
272
+ appId: string;
273
+ collectionId: string;
274
+ recordType: string;
275
+ label: string;
276
+ scopes: ScopeKind[];
277
+ defaultScope?: ScopeKind;
278
+ /**
279
+ * Context from the host URL (productId/variantId/batchId).
280
+ * When provided, the browser is constrained to that subtree:
281
+ * — `batchId` set → editing one batch (no list / variants pinned)
282
+ * — `variantId` set → list of batches under that variant
283
+ * — `productId` set → list of variants/batches/the product itself
284
+ * — none set → full collection-level browser (current behaviour)
285
+ */
286
+ contextScope?: {
287
+ productId?: string;
288
+ variantId?: string;
289
+ batchId?: string;
290
+ };
291
+ renderEditor: (ctx: EditorContext<TData>) => ReactNode;
292
+ /**
293
+ * Render the preview surface. Receives the editor's current value so the
294
+ * preview tracks unsaved edits. When `previewScope` differs from the
295
+ * editing scope (because the user picked something in `<PreviewScopePicker>`),
296
+ * `previewScope` reflects the chosen target — apps can use it to load
297
+ * resolved/collected/merged data for that scope instead of `resolved`.
298
+ */
299
+ renderPreview?: (ctx: {
300
+ resolved: TData | null;
301
+ previewScope: ParsedRef;
302
+ }) => ReactNode;
303
+ /**
304
+ * How the preview surface attaches to the editor pane.
305
+ * - `inline` (default): rendered below the form (current behaviour).
306
+ * - `side`: resizable right pane next to the editor.
307
+ * - `tab`: Editor / Preview siblings inside the right pane.
308
+ * - `drawer`: slide-out from the right, toggled by a button.
309
+ */
310
+ previewMode?: 'inline' | 'side' | 'tab' | 'drawer';
311
+ /**
312
+ * When true, render a `<PreviewScopePicker>` in the preview chrome so admins
313
+ * can preview as a different product/variant/batch than they're editing.
314
+ * Default false.
315
+ */
316
+ previewScopePicker?: boolean;
317
+ /**
318
+ * How to handle unsaved edits when the user navigates to a different
319
+ * record/scope/tab.
320
+ * - `prompt` (default): show a confirm dialog (Discard / Cancel / Save).
321
+ * - `autosave`: silently save before navigating away.
322
+ * - `keep`: hold edits in memory keyed by ref; coming back restores them.
323
+ */
324
+ dirtyStrategy?: 'prompt' | 'autosave' | 'keep';
325
+ /**
326
+ * Pre-delete hook. Return `false` to abort. Lets apps run app-specific
327
+ * confirms (e.g. "this batch has 12k linked proofs"). When omitted the
328
+ * inline two-step confirm is the only safeguard.
329
+ */
330
+ onBeforeDelete?: (scope: ParsedRef) => boolean | Promise<boolean>;
331
+ /**
332
+ * Disable the browser-level `beforeunload` prompt. Default false.
333
+ * Set true when the host platform handles unload guarding itself.
334
+ */
335
+ disableBeforeUnload?: boolean;
336
+ intro?: {
337
+ title: string;
338
+ body: ReactNode;
339
+ };
340
+ csvSchema?: CsvSchema<TData>;
341
+ classify?: (record: RecordSummary<TData>) => RecordStatus;
342
+ defaultData?: () => TData;
343
+ /**
344
+ * Which layouts the rail offers. Default `['list']`. When more than one is
345
+ * supplied, a switcher appears above the list and the choice persists
346
+ * per-app/recordType.
347
+ */
348
+ presentations?: RecordPresentation[];
349
+ /** Initial presentation when nothing is persisted. Default first of `presentations`. */
350
+ defaultPresentation?: RecordPresentation;
351
+ /**
352
+ * Optional custom card renderer used by `grid` / `gallery` presentations.
353
+ * If omitted the shell uses `<DefaultRecordCard>`.
354
+ */
355
+ renderCard?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
356
+ /**
357
+ * Optional custom list-row renderer used by `list` / `compact` presentations.
358
+ * If omitted the shell uses `<DefaultRecordRow>`.
359
+ */
360
+ renderListRow?: (record: RecordSummary<TData>, ctx: RecordSlotContext) => ReactNode;
361
+ /**
362
+ * Optional custom empty-state renderer for the rail. If omitted the shell
363
+ * uses the default `<EmptyState>` with `i18n.emptyTitle` / `i18n.emptyBody`.
364
+ */
365
+ renderEmpty?: (ctx: {
366
+ scope: ParsedRef | null;
367
+ }) => ReactNode;
368
+ /**
369
+ * Whether each scope holds at most one record (`singleton`, default) or
370
+ * many (`collection`, e.g. FAQs / recipes / SOPs). In collection mode the
371
+ * shell treats scopes as folders containing items, exposes a "+ New" action,
372
+ * and the `RecordEditor` works on individual items via `itemId`-suffixed refs.
373
+ */
374
+ cardinality?: RecordCardinality;
375
+ /** Display name for an item in collection mode (defaults to `'item'`). */
376
+ itemNoun?: string;
377
+ /**
378
+ * Generate a per-item id when "+ New" is clicked. Defaults to a ULID-like
379
+ * timestamp+random string.
380
+ */
381
+ generateItemId?: () => string;
382
+ /** Display title shown in the header card. Falls back to `label`, then `recordType`. */
383
+ title?: string;
384
+ /** Single-line muted subtitle under the header title. */
385
+ subtitle?: string;
386
+ /** Icon shown in the header card. Falls back to `icons.header.byRecordType[recordType]`
387
+ * or `icons.header.default`. Pass a Lucide icon component or any ReactNode. */
388
+ headerIcon?: ReactNode;
389
+ /** Right-aligned header content — typically a primary "+ New" button + secondary controls. */
390
+ headerActions?: ReactNode;
391
+ /** Show the per-scope counts strip in the header (Products · Facets · …). Default true. */
392
+ showStats?: boolean;
393
+ /** Override the default Lucide icons used by the shell (scope tabs, statuses,
394
+ * empty states, action menus, etc.). Deep-merged onto `DEFAULT_ICONS`. */
395
+ icons?: Partial<RecordsAdminIcons>;
396
+ /** When supplied, the records list is rendered as accordion-style groups.
397
+ * Return `null` to put a record in the default "Other" bucket. */
398
+ groupBy?: (record: RecordSummary) => {
399
+ key: string;
400
+ label: string;
401
+ icon?: ReactNode;
402
+ } | null;
403
+ /** Custom empty-state renderer for the records rail. Falls back to the styled
404
+ * built-in empty state (icon + title + body + optional CTAs). */
405
+ renderEmptyState?: (ctx: {
406
+ scope: ScopeKind;
407
+ }) => ReactNode;
408
+ /** Visual density. Default `comfortable`. */
409
+ density?: 'comfortable' | 'compact';
410
+ i18n?: Partial<RecordsAdminI18n>;
411
+ onTelemetry?: (event: TelemetryEvent) => void;
412
+ className?: string;
413
+ }
414
+
415
+ declare function RecordsAdminShell<TData = unknown>(props: RecordsAdminShellProps<TData>): react_jsx_runtime.JSX.Element;
416
+
417
+ interface RecordBrowserProps {
418
+ scopes: ScopeKind[];
419
+ activeScope: ScopeKind;
420
+ onActiveScopeChange: (s: ScopeKind) => void;
421
+ selectedRef?: string;
422
+ onSelectRef: (item: RecordSummary) => void;
423
+ items: RecordSummary[];
424
+ counts: {
425
+ all: number;
426
+ configured: number;
427
+ partial: number;
428
+ empty: number;
429
+ };
430
+ isLoading: boolean;
431
+ error: Error | null;
432
+ filter: 'all' | RecordStatus;
433
+ onFilterChange: (v: 'all' | RecordStatus) => void;
434
+ search: string;
435
+ onSearchChange: (v: string) => void;
436
+ hasNextPage?: boolean;
437
+ isFetchingNextPage?: boolean;
438
+ onLoadMore?: () => void;
439
+ scopesLoading?: boolean;
440
+ i18n: RecordsAdminI18n;
441
+ }
442
+ 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;
443
+
444
+ interface Props$9 {
445
+ scopes: ScopeKind[];
446
+ active: ScopeKind;
447
+ onChange: (s: ScopeKind) => void;
448
+ /** When true, tabs render disabled (used while collection flags are loading). */
449
+ loading?: boolean;
450
+ /** Optional per-scope counts displayed as tiny badges. */
451
+ counts?: Partial<Record<ScopeKind, number>>;
452
+ /** Override icons used per scope. Falls back to DEFAULT_ICONS.scope. */
453
+ icons?: RecordsAdminIcons['scope'];
454
+ }
455
+ declare const ScopeTabs: ({ scopes, active, onChange, loading, counts, icons, }: Props$9) => react_jsx_runtime.JSX.Element;
456
+
457
+ interface Props$8 {
458
+ source?: RecordSource;
459
+ status?: RecordStatus;
460
+ className?: string;
461
+ }
462
+ /** Emerald = own data, amber = inherited, muted = empty */
463
+ declare const StatusDot: ({ source, status, className }: Props$8) => react_jsx_runtime.JSX.Element;
464
+
465
+ interface Props$7 {
466
+ value: 'all' | RecordStatus;
467
+ onChange: (v: 'all' | RecordStatus) => void;
468
+ counts: {
469
+ all: number;
470
+ configured: number;
471
+ partial: number;
472
+ empty: number;
473
+ };
474
+ i18n: RecordsAdminI18n;
475
+ }
476
+ declare const StatusFilterPills: ({ value, onChange, counts, i18n }: Props$7) => react_jsx_runtime.JSX.Element;
477
+
478
+ interface Props$6 {
479
+ items: RecordSummary[];
480
+ selectedRef?: string;
481
+ onSelect: (item: RecordSummary) => void;
482
+ /** When set, the matching row gets a small "unsaved" indicator. */
483
+ dirtyRef?: string;
484
+ /** Layout. Defaults to `list` for full back-compat. */
485
+ presentation?: RecordPresentation;
486
+ /** Optional override for `list` / `compact` rows. */
487
+ renderListRow?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
488
+ /** Optional override for `grid` / `gallery` cards. */
489
+ renderCard?: (record: RecordSummary, ctx: RecordSlotContext) => ReactNode;
490
+ /** Optional grouping function. Returning null buckets the row under "Other". */
491
+ groupBy?: (record: RecordSummary) => {
492
+ key: string;
493
+ label: string;
494
+ icon?: ReactNode;
495
+ } | null;
496
+ }
497
+ declare const RecordList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
498
+ declare const ProductList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
499
+ declare const FacetList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
500
+ declare const VariantList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
501
+ declare const BatchList: ({ items, selectedRef, onSelect, dirtyRef, presentation, renderListRow, renderCard, groupBy, }: Props$6) => react_jsx_runtime.JSX.Element;
502
+
503
+ interface DefaultRecordRowProps {
504
+ record: RecordSummary;
505
+ ctx: RecordSlotContext;
506
+ /** Drop the icon + subtitle (used by the `compact` preset). */
507
+ compact?: boolean;
508
+ }
509
+ declare const DefaultRecordRow: ({ record, ctx, compact }: DefaultRecordRowProps) => react_jsx_runtime.JSX.Element;
510
+
511
+ interface DefaultRecordCardProps {
512
+ record: RecordSummary;
513
+ ctx: RecordSlotContext;
514
+ /** `grid` → 1:1, `gallery` → 16:9 with denser meta. Default `grid`. */
515
+ variant?: 'grid' | 'gallery';
516
+ }
517
+ declare const DefaultRecordCard: ({ record, ctx, variant }: DefaultRecordCardProps) => react_jsx_runtime.JSX.Element;
518
+
519
+ interface PresentationSwitcherProps {
520
+ options: RecordPresentation[];
521
+ value: RecordPresentation;
522
+ onChange: (next: RecordPresentation) => void;
523
+ i18n: RecordsAdminI18n;
524
+ }
525
+ declare const PresentationSwitcher: ({ options, value, onChange, i18n }: PresentationSwitcherProps) => react_jsx_runtime.JSX.Element | null;
526
+
527
+ interface EmptyStateProps {
528
+ title: string;
529
+ body?: ReactNode;
530
+ /** Primary call-to-action — typically "+ Add your first …". */
531
+ action?: ReactNode;
532
+ /** Optional secondary action — typically "Import from CSV". */
533
+ secondaryAction?: ReactNode;
534
+ /** Lucide icon component. Defaults to `Inbox`. */
535
+ icon?: LucideIcon;
536
+ }
537
+ declare const EmptyState: ({ title, body, action, secondaryAction, icon: Icon, }: EmptyStateProps) => react_jsx_runtime.JSX.Element;
538
+ declare const LoadingState: () => react_jsx_runtime.JSX.Element;
539
+ declare const ErrorState: ({ error }: {
540
+ error: Error;
541
+ }) => react_jsx_runtime.JSX.Element;
542
+
543
+ interface Props$5 {
544
+ i18n: RecordsAdminI18n;
545
+ onApplyToMany?: () => void;
546
+ onCopyFrom?: () => void;
547
+ onClearMany?: () => void;
548
+ onImportCsv?: () => void;
549
+ onExportCsv?: () => void;
550
+ }
551
+ declare const BulkActionsMenu: ({ i18n, onApplyToMany, onCopyFrom, onClearMany, onImportCsv, onExportCsv, }: Props$5) => react_jsx_runtime.JSX.Element | null;
552
+
553
+ interface Props$4<T> {
554
+ ctx: EditorContext<T>;
555
+ i18n: RecordsAdminI18n;
556
+ children: ReactNode;
557
+ preview?: ReactNode;
558
+ bulkActions?: React.ComponentProps<typeof BulkActionsMenu>;
559
+ /** Extra slot rendered in the footer between the danger actions and Save. */
560
+ footerExtra?: ReactNode;
561
+ /** Optional pre-delete hook (see DeleteButton). */
562
+ onBeforeDelete?: () => boolean | Promise<boolean>;
563
+ }
564
+ declare function RecordEditor<T>({ ctx, i18n, children, preview, bulkActions, footerExtra, onBeforeDelete, }: Props$4<T>): react_jsx_runtime.JSX.Element;
565
+
566
+ interface InheritanceCtx {
567
+ parentValue?: Record<string, unknown> | null;
568
+ ownValue?: Record<string, unknown> | null;
569
+ onResetField?: (field: string) => void;
570
+ }
571
+ declare const InheritanceProvider: ({ parentValue, ownValue, onResetField, children, }: InheritanceCtx & {
572
+ children: ReactNode;
573
+ }) => react_jsx_runtime.JSX.Element;
574
+ interface MarkerProps {
575
+ field: string;
576
+ inheritedValue?: unknown;
577
+ value?: unknown;
578
+ children: ReactNode;
579
+ }
580
+ declare const InheritanceMarker: ({ field, inheritedValue, value, children }: MarkerProps) => react_jsx_runtime.JSX.Element;
581
+
582
+ interface Props$3 {
583
+ children: ReactNode;
584
+ }
585
+ declare const ResolvedPreview: ({ children }: Props$3) => react_jsx_runtime.JSX.Element;
586
+
587
+ interface ProductChildItem {
588
+ /** Variant or batch id (the `<id>` part of `variant:<id>` / `batch:<id>`). */
589
+ id: string;
590
+ /** Display name. */
591
+ name: string;
592
+ /** Optional subtitle (sku, expiry, etc.). */
593
+ subtitle?: string;
594
+ }
595
+ interface UseProductChildrenArgs {
596
+ SL: SmartLinksSDK;
597
+ collectionId: string;
598
+ productId: string | undefined;
599
+ /** Which child kind to fetch. Hooks only fire when both productId and kind are set. */
600
+ kind: 'variant' | 'batch' | null;
601
+ enabled?: boolean;
602
+ }
603
+ /**
604
+ * Lazy children loader. When `kind` is `null` or `productId` is missing the
605
+ * underlying query is disabled, so opening the shell at collection level never
606
+ * touches `variant.list` / `batch.list`.
607
+ */
608
+ declare const useProductChildren: (args: UseProductChildrenArgs) => {
609
+ items: ProductChildItem[];
610
+ isLoading: boolean;
611
+ error: Error | null;
612
+ refetch: () => Promise<void>;
613
+ };
614
+
615
+ type DrillTab = 'product' | 'variant' | 'batch';
616
+ interface Props$2 {
617
+ productLabel: string;
618
+ /** Which child types are available on the collection. */
619
+ showVariants: boolean;
620
+ showBatches: boolean;
621
+ active: DrillTab;
622
+ onChange: (tab: DrillTab) => void;
623
+ /** Currently selected child id (variant or batch), if any. */
624
+ selectedChildId?: string;
625
+ onSelectChild: (id: string) => void;
626
+ variants: ProductChildItem[];
627
+ batches: ProductChildItem[];
628
+ variantsLoading: boolean;
629
+ batchesLoading: boolean;
630
+ /** Editor body rendered to the right of the picker. */
631
+ children: ReactNode;
632
+ }
633
+ declare const ProductDrillDown: ({ productLabel, showVariants, showBatches, active, onChange, selectedChildId, onSelectChild, variants, batches, variantsLoading, batchesLoading, children, }: Props$2) => react_jsx_runtime.JSX.Element;
634
+
635
+ type PreviewMode = 'inline' | 'side' | 'tab' | 'drawer';
636
+ interface CommonProps {
637
+ /** App-rendered preview body. */
638
+ children: ReactNode;
639
+ /** Optional scope picker rendered in the chrome header. */
640
+ scopePicker?: ReactNode;
641
+ /** Header label (defaults to "Preview"). */
642
+ label?: string;
643
+ }
644
+ declare const InlinePreview: ({ children, scopePicker, label }: CommonProps) => react_jsx_runtime.JSX.Element;
645
+ declare const SidePreview: ({ children, scopePicker, label }: CommonProps) => react_jsx_runtime.JSX.Element;
646
+ interface TabbedPreviewProps {
647
+ editor: ReactNode;
648
+ preview: ReactNode;
649
+ scopePicker?: ReactNode;
650
+ /** Initial active tab (default `editor`). */
651
+ defaultTab?: 'editor' | 'preview';
652
+ i18n?: {
653
+ editor?: string;
654
+ preview?: string;
655
+ };
656
+ }
657
+ declare const TabbedPreview: ({ editor, preview, scopePicker, defaultTab, i18n, }: TabbedPreviewProps) => react_jsx_runtime.JSX.Element;
658
+ interface DrawerPreviewProps extends CommonProps {
659
+ open: boolean;
660
+ onClose: () => void;
661
+ /** Drawer width (default 420). */
662
+ width?: number;
663
+ }
664
+ declare const DrawerPreview: ({ open, onClose, children, scopePicker, label, width, }: DrawerPreviewProps) => react_jsx_runtime.JSX.Element | null;
665
+ declare const PreviewToggleButton: ({ onClick, label, }: {
666
+ onClick: () => void;
667
+ label?: string;
668
+ }) => react_jsx_runtime.JSX.Element;
669
+
670
+ interface PreviewScopePickerProps {
671
+ SL: SmartLinksSDK;
672
+ collectionId: string;
673
+ /** The scope currently being edited — used as the "Same as edited" default. */
674
+ editingScope: ParsedRef;
675
+ /** The current preview scope. */
676
+ value: ParsedRef;
677
+ onChange: (next: ParsedRef) => void;
678
+ /** Are variants/batches available for drill-down. */
679
+ showVariants: boolean;
680
+ showBatches: boolean;
681
+ /** i18n strings (uses defaults if omitted). */
682
+ i18n?: {
683
+ previewAs?: string;
684
+ previewAsDefault?: string;
685
+ };
686
+ }
687
+ declare const PreviewScopePicker: ({ SL, collectionId, editingScope, value, onChange, showVariants, showBatches, i18n, }: PreviewScopePickerProps) => react_jsx_runtime.JSX.Element;
688
+
689
+ declare const ScopeBreadcrumb: ({ scope }: {
690
+ scope: ParsedRef;
691
+ }) => react_jsx_runtime.JSX.Element | null;
692
+
693
+ type IntroTone = 'info' | 'success' | 'warning';
694
+ interface Props$1 {
695
+ title: string;
696
+ body: ReactNode;
697
+ onDismiss: () => void;
698
+ /** Visual tone — controls icon and surface tint. */
699
+ tone?: IntroTone;
700
+ /** Optional "Learn more" link or button rendered inline after the body. */
701
+ action?: ReactNode;
702
+ }
703
+ declare const IntroCard: ({ title, body, onDismiss, tone, action }: Props$1) => react_jsx_runtime.JSX.Element;
704
+
705
+ interface Props {
706
+ label: string;
707
+ introHidden: boolean;
708
+ onShowIntro?: () => void;
709
+ }
710
+ declare const UtilityRow: ({ label, introHidden, onShowIntro }: Props) => react_jsx_runtime.JSX.Element | null;
711
+
712
+ interface RecordsCtx {
713
+ SL: SmartLinksSDK;
714
+ collectionId: string;
715
+ appId: string;
716
+ recordType: string;
717
+ }
718
+ /** Shape of a single record write — uses the SDK's structured `scope`. */
719
+ interface RecordWrite<T = unknown> {
720
+ /** Logical key. The server canonicalises this from `scope` if omitted. */
721
+ ref?: string;
722
+ /** Structured scope; required for specificity / `match()` to work. */
723
+ scope: RecordScope;
724
+ data: T;
725
+ visibility?: 'public' | 'owner' | 'admin';
726
+ productId?: string;
727
+ status?: string;
728
+ startsAt?: string | null;
729
+ expiresAt?: string | null;
730
+ customId?: string;
731
+ sourceSystem?: string;
732
+ }
733
+ declare const listRecords: (ctx: RecordsCtx, params?: {
734
+ ref?: string;
735
+ refPrefix?: string;
736
+ q?: string;
737
+ limit?: number;
738
+ offset?: number;
739
+ sort?: string;
740
+ }) => Promise<{
741
+ data: AppRecord[];
742
+ total: number;
743
+ hasMore: boolean;
744
+ }>;
745
+ /** Look up a single record by its canonical ref. */
746
+ declare const getRecordByRef: (ctx: RecordsCtx, ref: string) => Promise<AppRecord | null>;
747
+ /**
748
+ * Atomic upsert via the SDK. Always sends `scope` so the server can compute
749
+ * `specificity` and derive `ref` deterministically.
750
+ */
751
+ declare const upsertRecord: <T>(ctx: RecordsCtx, write: RecordWrite<T>) => Promise<{
752
+ record: AppRecord;
753
+ isCreate: boolean;
754
+ }>;
755
+ declare const deleteRecord: (ctx: RecordsCtx, ref: string) => Promise<boolean>;
756
+ /** Restore a soft-deleted record by ID (admin only). */
757
+ declare const restoreRecord: (ctx: RecordsCtx, recordId: string) => Promise<AppRecord>;
758
+ /**
759
+ * Server-side match — single round trip that returns every record whose
760
+ * `scope` is satisfied by `target`, ordered by descending `specificity`.
761
+ * Replaces the per-scope N+1 chain walk we used pre-1.9.
762
+ */
763
+ declare const matchRecords: (ctx: RecordsCtx, target: RecordTarget, opts?: {
764
+ strategy?: "all" | "best";
765
+ at?: string;
766
+ includeScheduled?: boolean;
767
+ includeExpired?: boolean;
768
+ limit?: number;
769
+ }) => Promise<MatchResult>;
770
+ /**
771
+ * Server-side bulk upsert. Up to 500 rows per call; any leftover is chunked.
772
+ * Each row is error-isolated by the server.
773
+ */
774
+ declare const bulkUpsert: <T>(ctx: RecordsCtx, entries: Array<{
775
+ ref?: string;
776
+ scope: RecordScope;
777
+ data: T;
778
+ status?: string;
779
+ }>) => Promise<{
780
+ saved: number;
781
+ failed: number;
782
+ }>;
783
+ /**
784
+ * Server-side bulk delete. Accepts an explicit ref list (max 1000 per call,
785
+ * chunked here) or a scope anchor (single call).
786
+ */
787
+ declare const bulkDelete: (ctx: RecordsCtx, input: {
788
+ refs: string[];
789
+ } | {
790
+ scope: Omit<RecordScope, "facets">;
791
+ }) => Promise<{
792
+ removed: number;
793
+ }>;
794
+
795
+ interface UseRecordListArgs {
796
+ ctx: RecordsCtx;
797
+ scopeKind: ScopeKind;
798
+ search?: string;
799
+ filter?: 'all' | RecordStatus;
800
+ classify?: (record: RecordSummary) => RecordStatus;
801
+ enabled?: boolean;
802
+ scaffolder?: (existing: RecordSummary[]) => Promise<RecordSummary[]> | RecordSummary[];
803
+ /**
804
+ * Constrain the list to a subtree (e.g. a specific product when the host URL
805
+ * pins one). Records whose ParsedRef does not match every set field are dropped.
806
+ */
807
+ contextScope?: {
808
+ productId?: string;
809
+ variantId?: string;
810
+ batchId?: string;
811
+ };
812
+ /** Page size requested from the SDK (default 100). */
813
+ pageSize?: number;
814
+ }
815
+ declare const useRecordList: (args: UseRecordListArgs) => {
816
+ items: RecordSummary<unknown>[];
817
+ total: number;
818
+ counts: {
819
+ all: number;
820
+ configured: number;
821
+ partial: number;
822
+ empty: number;
823
+ };
824
+ isLoading: boolean;
825
+ error: Error | null;
826
+ refetch: () => void;
827
+ hasNextPage: boolean;
828
+ isFetchingNextPage: boolean;
829
+ fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
830
+ data: AppRecord[];
831
+ total: number;
832
+ hasMore: boolean;
833
+ nextOffset: number;
834
+ }, unknown>, Error>>;
835
+ };
836
+
837
+ interface UseRecordEditorArgs<T> {
838
+ ctx: RecordsCtx;
839
+ scope: ParsedRef;
840
+ resolved: ResolvedRecord<T>;
841
+ defaultData?: () => T;
842
+ onSaved?: () => void;
843
+ onDeleted?: () => void;
844
+ /** Fired after a save fails and local state has been rolled back. */
845
+ onSaveError?: (err: unknown) => void;
846
+ /**
847
+ * Controls how the editor reseeds its local value when the resolved
848
+ * record changes. Default `'always'` matches legacy behaviour.
849
+ * `'preserve-dirty'` keeps unsaved local edits across resolve refreshes
850
+ * (used by the shell when `dirtyStrategy === 'keep'`).
851
+ */
852
+ reseed?: 'always' | 'preserve-dirty';
853
+ }
854
+ declare function useRecordEditor<T>(args: UseRecordEditorArgs<T>): EditorContext<T>;
855
+
856
+ interface UseResolvedRecordArgs {
857
+ SL: SmartLinksSDK;
858
+ appId: string;
859
+ recordType: string;
860
+ collectionId: string;
861
+ productId?: string;
862
+ variantId?: string;
863
+ batchId?: string;
864
+ facetId?: string;
865
+ facetValue?: string;
866
+ proofId?: string;
867
+ supportedScopes?: ScopeKind[];
868
+ enabled?: boolean;
869
+ withParent?: boolean;
870
+ }
871
+ declare function useResolvedRecord<T = unknown>(args: UseResolvedRecordArgs): {
872
+ isLoading: boolean;
873
+ error: Error | null;
874
+ data: T | null;
875
+ source: RecordSource;
876
+ sourceRef?: string;
877
+ parentValue?: T | null | undefined;
878
+ };
879
+
880
+ interface UseScopeProbeArgs {
881
+ SL: SmartLinksSDK;
882
+ collectionId: string;
883
+ /** Force admin endpoint when fetching the collection (default true). */
884
+ admin?: boolean;
885
+ enabled?: boolean;
886
+ }
887
+ /**
888
+ * Reports whether a collection has variants/batches enabled.
889
+ *
890
+ * Uses the canonical `Collection.variants` / `Collection.batches` boolean flags
891
+ * from `SL.collection.get(collectionId)` (SDK ≥ 1.9). No more guessing by
892
+ * probing list endpoints.
893
+ */
894
+ declare const useScopeProbe: ({ SL, collectionId, admin, enabled }: UseScopeProbeArgs) => {
895
+ hasVariants: boolean;
896
+ hasBatches: boolean;
897
+ isLoading: boolean;
898
+ error: Error | null;
899
+ };
900
+
901
+ declare const useIntroDismissed: (SL: SmartLinksSDK, collectionId: string, appId: string, recordType: string) => {
902
+ dismissed: boolean;
903
+ dismiss: () => Promise<void>;
904
+ undismiss: () => void;
905
+ };
906
+
907
+ interface ProductBrowseItem {
908
+ /** Product id (used in refs as `product:<id>`). */
909
+ id: string;
910
+ /** Display name. */
911
+ name: string;
912
+ /** Optional subtitle. */
913
+ sku?: string | null;
914
+ /** Optional ordering hint. */
915
+ sortOrder?: number | null;
916
+ }
917
+ interface UseProductBrowseArgs {
918
+ SL: SmartLinksSDK;
919
+ collectionId: string;
920
+ search?: string;
921
+ /** Soft cap per page (default 50). */
922
+ pageSize?: number;
923
+ enabled?: boolean;
924
+ /** Override admin flag if needed (default true). */
925
+ admin?: boolean;
926
+ }
927
+ /**
928
+ * Cursor-paginated product list backed by `SL.product.query`. When the SDK
929
+ * returns a `nextCursor`, we use it; otherwise we fall back to offset-based
930
+ * paging so the hook works against older SDKs or in-memory adapters too.
931
+ */
932
+ declare const useProductBrowse: (args: UseProductBrowseArgs) => {
933
+ items: any[];
934
+ total: any;
935
+ isLoading: boolean;
936
+ error: Error | null;
937
+ hasNextPage: boolean;
938
+ isFetchingNextPage: boolean;
939
+ fetchNextPage: (options?: _tanstack_query_core.FetchNextPageOptions) => Promise<_tanstack_query_core.InfiniteQueryObserverResult<_tanstack_query_core.InfiniteData<{
940
+ items: any;
941
+ nextOffset: any;
942
+ nextCursor: any;
943
+ hasMore: any;
944
+ total: any;
945
+ }, unknown>, Error>>;
946
+ refetch: () => Promise<void>;
947
+ };
948
+
949
+ interface CollectedRecord<T = unknown> {
950
+ /** The ref this record was loaded from. */
951
+ ref: string;
952
+ /** Parsed scope of the ref (gives you `productId`, `facetId`, etc.). */
953
+ scope: ParsedRef;
954
+ /** Domain payload. */
955
+ data: T;
956
+ /** Index in the resolution chain (0 = most-specific). */
957
+ depth: number;
958
+ }
959
+ interface UseCollectedRecordsArgs {
960
+ SL: SmartLinksSDK;
961
+ appId: string;
962
+ recordType: string;
963
+ collectionId: string;
964
+ productId?: string;
965
+ variantId?: string;
966
+ batchId?: string;
967
+ facetId?: string;
968
+ facetValue?: string;
969
+ /** Scopes to walk. Defaults to all four. */
970
+ supportedScopes?: ScopeKind[];
971
+ /** Drop scopes that don't have a record (default true). */
972
+ filterEmpty?: boolean;
973
+ /**
974
+ * Ordering applied to the result. Defaults to specificity-descending
975
+ * (most-specific scope first), matching the previous behaviour and aligning
976
+ * with the future `match()` API.
977
+ */
978
+ sort?: CollectedSort;
979
+ enabled?: boolean;
980
+ }
981
+ declare function useCollectedRecords<T = unknown>(args: UseCollectedRecordsArgs): {
982
+ items: CollectedRecord<T>[];
983
+ isLoading: boolean;
984
+ error: Error | null;
985
+ refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<CollectedRecord<T>[], Error>>;
986
+ };
987
+
988
+ type MergeStrategy = 'deep' | 'shallow';
989
+ interface MergedRecord<T = unknown> {
990
+ /** The merged result. */
991
+ data: T | null;
992
+ /**
993
+ * For each top-level key, the ref of the record that contributed its
994
+ * winning value. Useful for "Inherited from facet:gluten-free" hints.
995
+ */
996
+ provenance: Record<string, string>;
997
+ /** Refs that contributed something to the merge, deepest-first. */
998
+ layers: string[];
999
+ }
1000
+ interface UseMergedRecordArgs<T> {
1001
+ SL: SmartLinksSDK;
1002
+ appId: string;
1003
+ recordType: string;
1004
+ collectionId: string;
1005
+ productId?: string;
1006
+ variantId?: string;
1007
+ batchId?: string;
1008
+ facetId?: string;
1009
+ facetValue?: string;
1010
+ supportedScopes?: ScopeKind[];
1011
+ /**
1012
+ * Merge strategy. `deep` recursively merges plain objects; arrays are
1013
+ * replaced wholesale. `shallow` does Object.assign at the top level.
1014
+ * Default `deep`.
1015
+ */
1016
+ strategy?: MergeStrategy;
1017
+ /** Custom merge fn. Receives `(parent, child)` and returns the winner. */
1018
+ merge?: (parent: T | null, child: T) => T;
1019
+ enabled?: boolean;
1020
+ }
1021
+ declare function useMergedRecord<T = unknown>(args: UseMergedRecordArgs<T>): {
1022
+ isLoading: boolean;
1023
+ error: Error | null;
1024
+ refetch: (options?: _tanstack_query_core.RefetchOptions) => Promise<_tanstack_query_core.QueryObserverResult<MergedRecord<T>, Error>>;
1025
+ /** The merged result. */
1026
+ data: T | null;
1027
+ /**
1028
+ * For each top-level key, the ref of the record that contributed its
1029
+ * winning value. Useful for "Inherited from facet:gluten-free" hints.
1030
+ */
1031
+ provenance: Record<string, string>;
1032
+ /** Refs that contributed something to the merge, deepest-first. */
1033
+ layers: string[];
1034
+ };
1035
+
1036
+ interface UseUnsavedGuardArgs {
1037
+ /** Whether there are unsaved edits right now. */
1038
+ isDirty: boolean;
1039
+ /**
1040
+ * Optional human-friendly label (e.g. recordType). Posted to the parent
1041
+ * frame so the host can describe what's unsaved.
1042
+ */
1043
+ label?: string;
1044
+ /**
1045
+ * Optional async confirm impl. Defaults to `window.confirm`. Apps can pass
1046
+ * a custom dialog (e.g. a shadcn AlertDialog promise wrapper).
1047
+ */
1048
+ confirm?: (message: string) => boolean | Promise<boolean>;
1049
+ /**
1050
+ * Disable the browser-level `beforeunload` listener (e.g. when running
1051
+ * inside a host that handles it already). Default false.
1052
+ */
1053
+ disableBeforeUnload?: boolean;
1054
+ /**
1055
+ * Disable the parent-frame `postMessage` notification. Default false.
1056
+ */
1057
+ disableParentMessage?: boolean;
1058
+ }
1059
+ /**
1060
+ * Tracks unsaved state and wires up the browser/iframe guards. Returns a
1061
+ * `confirmDiscard` helper apps can `await` from their own navigation code.
1062
+ */
1063
+ declare function useUnsavedGuard({ isDirty, label, confirm, disableBeforeUnload, disableParentMessage, }: UseUnsavedGuardArgs): {
1064
+ confirmDiscard: (message?: string) => Promise<boolean>;
1065
+ };
1066
+
1067
+ type DirtyStrategy = 'prompt' | 'autosave' | 'keep';
1068
+ interface UseDirtyNavigationArgs {
1069
+ strategy: DirtyStrategy;
1070
+ isDirty: boolean;
1071
+ /** Persist current edits. Resolves when the SDK call completes. */
1072
+ save: () => void | Promise<void>;
1073
+ /** Discard local edits to the saved snapshot. */
1074
+ reset: () => void;
1075
+ /**
1076
+ * Confirm impl: returns `'save' | 'discard' | 'cancel'`. Defaults to a
1077
+ * `window.confirm` fallback (Save = OK, Discard = Cancel, no Cancel option).
1078
+ */
1079
+ confirm?: (i18n?: NavConfirmI18n) => Promise<'save' | 'discard' | 'cancel'>;
1080
+ i18n?: NavConfirmI18n;
1081
+ }
1082
+ interface NavConfirmI18n {
1083
+ title?: string;
1084
+ body?: string;
1085
+ save?: string;
1086
+ discard?: string;
1087
+ cancel?: string;
1088
+ }
1089
+ /**
1090
+ * Returns `runWithGuard(action)` — call it instead of executing the
1091
+ * navigation directly. The guard handles dirty-state policy and only calls
1092
+ * `action()` when it's safe to proceed.
1093
+ */
1094
+ declare const useDirtyNavigation: ({ strategy, isDirty, save, reset, confirm, i18n, }: UseDirtyNavigationArgs) => {
1095
+ runWithGuard: (action: () => void) => Promise<boolean>;
1096
+ };
1097
+
1098
+ declare function usePresentationPref(args: {
1099
+ appId: string;
1100
+ recordType: string;
1101
+ options: RecordPresentation[];
1102
+ defaultValue: RecordPresentation;
1103
+ }): [RecordPresentation, (next: RecordPresentation) => void];
1104
+
1105
+ interface DeleteButtonProps {
1106
+ /** Performs the delete. Should resolve when the SDK call completes. */
1107
+ onConfirm: () => void | Promise<void>;
1108
+ /**
1109
+ * Optional async pre-check — return false to abort BEFORE the inline
1110
+ * confirm flips. Lets apps run their own modal for high-stakes deletes.
1111
+ */
1112
+ onBeforeDelete?: () => boolean | Promise<boolean>;
1113
+ /** Default label (idle state). */
1114
+ label?: string;
1115
+ /** Confirm-state label. */
1116
+ confirmLabel?: string;
1117
+ /** Auto-revert timeout in ms (default 3000). */
1118
+ revertMs?: number;
1119
+ disabled?: boolean;
1120
+ }
1121
+ declare const DeleteButton: ({ onConfirm, onBeforeDelete, label, confirmLabel, revertMs, disabled, }: DeleteButtonProps) => react_jsx_runtime.JSX.Element;
1122
+
1123
+ declare const parseRef: (raw: string) => ParsedRef;
1124
+ interface BuildRefArgs {
1125
+ collectionId?: string;
1126
+ productId?: string;
1127
+ facetId?: string;
1128
+ facetValue?: string;
1129
+ variantId?: string;
1130
+ batchId?: string;
1131
+ proofId?: string;
1132
+ }
1133
+ declare const buildRef: (a: BuildRefArgs) => string;
1134
+ /**
1135
+ * Build the resolution chain for a target ref, from most specific → least specific.
1136
+ * `supportedScopes` prunes scopes the app doesn't care about.
1137
+ */
1138
+ declare const resolutionChain: (target: ParsedRef, supportedScopes: ScopeKind[]) => string[];
1139
+
1140
+ interface ResolveArgs {
1141
+ ctx: RecordsCtx;
1142
+ target: ParsedRef;
1143
+ /**
1144
+ * Ignored — kept for API compatibility. The server determines the
1145
+ * resolution chain from the structured scope.
1146
+ */
1147
+ supportedScopes?: ScopeKind[];
1148
+ /** When true, also returns the parent value (next-best after self). */
1149
+ withParent?: boolean;
1150
+ admin?: boolean;
1151
+ }
1152
+ declare const resolveRecord: <T>(args: ResolveArgs) => Promise<ResolvedRecord<T>>;
1153
+
1154
+ declare const parsedRefToScope: (ref: ParsedRef) => RecordScope;
1155
+ declare const parsedRefToTarget: (ref: ParsedRef) => RecordTarget;
1156
+ /**
1157
+ * Compare a winning record's scope against the editing scope.
1158
+ * Returns true when they refer to the same node — used to decide
1159
+ * "self" vs "inherited" when consuming `match()` results.
1160
+ */
1161
+ declare const scopesEqual: (a: RecordScope, b: RecordScope) => boolean;
1162
+
1163
+ interface ImportReport {
1164
+ total: number;
1165
+ saved: number;
1166
+ failed: number;
1167
+ errorRows: Array<{
1168
+ row: number;
1169
+ error: string;
1170
+ }>;
1171
+ /** Annotated CSV ready for download (input + `error` column). */
1172
+ annotatedCsv: string;
1173
+ }
1174
+ declare const exportCsv: <T>(records: RecordSummary<T>[], schema: CsvSchema<T>) => Blob;
1175
+ declare const importCsv: <T>(file: File, schema: CsvSchema<T>, ctx: RecordsCtx) => Promise<ImportReport>;
1176
+ declare const downloadBlob: (blob: Blob, filename: string) => void;
1177
+
1178
+ 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, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolvedRecord, useScopeProbe, useUnsavedGuard };