@nivaro/react 0.1.116 → 0.1.118

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.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import { ImportParseResponse } from '@nivaro/sdk';
6
6
  import { ImportTemplateSummary } from '@nivaro/sdk';
7
7
  import { JSX } from 'react';
8
8
  import { JSXElementConstructor } from 'react';
9
+ import type { LucideIcon } from 'lucide-react';
9
10
  import { NivaroClient } from '@nivaro/sdk';
10
11
  import { default as React_2 } from 'react';
11
12
  import { ReactElement } from 'react';
@@ -246,6 +247,10 @@ declare type AutoAllocateConfig = {
246
247
  };
247
248
  };
248
249
 
250
+ export declare function AutolinkedText({ text }: {
251
+ text: string;
252
+ }): JSX.Element;
253
+
249
254
  export declare function BaseMap({ pins, bubbles, statusLine, onPinClick, onBubbleClick, minHeight, focus }: {
250
255
  pins: BaseMapPin[];
251
256
  bubbles?: BaseMapBubble[];
@@ -264,7 +269,7 @@ export declare function BaseMap({ pins, bubbles, statusLine, onPinClick, onBubbl
264
269
  } | null;
265
270
  }): JSX.Element;
266
271
 
267
- declare interface BaseMapBubble {
272
+ export declare interface BaseMapBubble {
268
273
  id: string;
269
274
  lat: number;
270
275
  lng: number;
@@ -273,7 +278,7 @@ declare interface BaseMapBubble {
273
278
  color?: string;
274
279
  }
275
280
 
276
- declare interface BaseMapPin {
281
+ export declare interface BaseMapPin {
277
282
  id: string;
278
283
  lat: number;
279
284
  lng: number;
@@ -764,6 +769,18 @@ export declare function ConformanceView({ className }: {
764
769
 
765
770
  export declare function createLeaderSocket(key: string, cbs: LeaderCallbacks): LeaderSocketHandle;
766
771
 
772
+ export declare function CronBuilder({ value, onChange, disabled }: {
773
+ value: string;
774
+ onChange: (cron: string) => void;
775
+ disabled?: boolean;
776
+ }): JSX.Element;
777
+
778
+ export declare function CustomActionButtons({ collection, itemId, draft }: {
779
+ collection: string;
780
+ itemId: string;
781
+ draft: Record<string, unknown>;
782
+ }): JSX.Element | null;
783
+
767
784
  export declare function DataTable<T = Record<string, unknown>>({ columns, rows, rowKey, total, page, limit, isLoading, isError, errorMessage, sort, onSortChange, onPageChange, onRowClick, onRowContextMenu, searchValue, onSearchChange, searchPlaceholder, filterDefs, filterValues, onFilterChange, toolbarRight, emptyMessage, minBodyHeight, fillHeight, hideFilterRow, nowrapCells, density, pinFirstColumn, columnPins, onColumnPinChange, columnFilterRow, hScrollProxy, selectedIds, onSelectionChange, rowClassName, rowGroups, collapsedGroups, onToggleGroup }: DataTableProps<T>): React_2.JSX.Element;
768
785
 
769
786
  export declare interface DataTableProps<T = Record<string, unknown>> {
@@ -845,8 +862,22 @@ export declare function DateField({ field, value, onChange, error, disabled, rea
845
862
  /** The admin's route shape — the default when the host supplies no itemUrl. */
846
863
  export declare function defaultItemUrl(t: ItemLinkTarget): string;
847
864
 
865
+ /**
866
+ * Definition registry (admin). Master-detail rather than a dialog: an operator
867
+ * comparing two imports' targets needs both the list and the editor on screen.
868
+ */
869
+ export declare function DefinitionsPanel({ definitions, runCounts, isLoading }: {
870
+ definitions: ImportDefinition[];
871
+ /** Runs per import key — a definition's history is the main thing that makes
872
+ * it recognisable, and the reason deactivating beats deleting. */
873
+ runCounts: Record<string, number>;
874
+ isLoading?: boolean;
875
+ }): JSX.Element;
876
+
848
877
  export declare function deriveGroupKey<T extends QueueGroupingRow>(row: T, attribute: string): string;
849
878
 
879
+ export declare function describeCron(cron: string): string;
880
+
850
881
  declare interface DirectoryChannel {
851
882
  id: number;
852
883
  key: string;
@@ -900,6 +931,17 @@ export declare function effectiveScopeSeedIds(scopes: {
900
931
  restricted: Record<string, Array<string | number>>;
901
932
  }, dimName: string): Array<string | number>;
902
933
 
934
+ /**
935
+ * Designed empty state (#322): what this surface is, why it's empty, and the
936
+ * one right action. Replaces bare "No results" strings on the major surfaces.
937
+ */
938
+ export declare function EmptyState({ icon: Icon, title, detail, action }: {
939
+ icon?: LucideIcon;
940
+ title: string;
941
+ detail?: string;
942
+ action?: ReactNode;
943
+ }): JSX.Element;
944
+
903
945
  export declare function ErpFailureBanner({ collection, itemId }: {
904
946
  collection: string;
905
947
  itemId: string;
@@ -912,6 +954,18 @@ export declare interface ErrorReplayLink {
912
954
  offset_ms: number | null;
913
955
  }
914
956
 
957
+ /**
958
+ * Error surface family (#324): 403/404/500 as one designed system — a name
959
+ * for what happened, an honest cause line, and the one right action. The
960
+ * smart-404 (#219) and AccessDeniedPanel remain the record-scoped variants;
961
+ * this is the page-level chrome.
962
+ */
963
+ export declare function ErrorSurface({ variant, detail, action }: {
964
+ variant: '403' | '404' | '500';
965
+ detail?: string;
966
+ action?: ReactNode;
967
+ }): JSX.Element;
968
+
915
969
  /** Boolean convenience — for guards and conditions. */
916
970
  export declare function evaluateBoolean(src: string, row: TokenResolver | Record<string, unknown>, opts?: EvaluateOptions): boolean;
917
971
 
@@ -1006,6 +1060,9 @@ export declare function ExternalRequestsChip({ collection, itemId }: {
1006
1060
  /** Field paths an expression reads, without evaluating it. */
1007
1061
  export declare function extractExpressionTokens(src: string): string[];
1008
1062
 
1063
+ /** Extract the slot key from an assignment field name, or null. */
1064
+ export declare function extSlotKey(field: string): string | null;
1065
+
1009
1066
  declare type Fetcher = () => Promise<ApiVersionInfo | null>;
1010
1067
 
1011
1068
  export declare function fetchSchema(client: NivaroClient, collection: string, includeHidden: boolean, layoutId?: number, layoutSlug?: string): Promise<FormSchema>;
@@ -1049,6 +1106,47 @@ export declare function fieldDrilldownConfig(field: {
1049
1106
  _overrides?: Record<string, unknown> | null;
1050
1107
  }): FieldDrilldownConfig | null;
1051
1108
 
1109
+ export declare interface FieldInterfaceHandle {
1110
+ update?: (props: FieldInterfaceProps) => void;
1111
+ destroy?: () => void;
1112
+ }
1113
+
1114
+ export declare interface FieldInterfacePlugin {
1115
+ interface: string;
1116
+ mount: (el: HTMLElement, props: FieldInterfaceProps) => FieldInterfaceHandle | void;
1117
+ }
1118
+
1119
+ /**
1120
+ * Pluggable field interfaces (#17): extensions register custom field
1121
+ * renderers (map picker, signature pad…) usable in any layout. The contract
1122
+ * is framework-free — a DOM mount function — so extension UI bundles need
1123
+ * nothing beyond the browser:
1124
+ *
1125
+ * window.__nvrRegisterFieldInterface({
1126
+ * interface: 'map-picker',
1127
+ * mount(el, props) {
1128
+ * // build DOM inside el; call props.onChange(next) on edits
1129
+ * return { update(next) { … }, destroy() { … } }
1130
+ * }
1131
+ * })
1132
+ *
1133
+ * FieldRenderer consults the registry before its built-in dispatch, so a
1134
+ * field whose `interface` matches a registered name renders through the
1135
+ * plugin; unknown interfaces keep falling through to the default renderer.
1136
+ */
1137
+ export declare interface FieldInterfaceProps {
1138
+ value: unknown;
1139
+ onChange: (next: unknown) => void;
1140
+ field: {
1141
+ field: string;
1142
+ label?: string | null;
1143
+ options?: unknown;
1144
+ };
1145
+ readOnly: boolean;
1146
+ collection: string;
1147
+ itemId: string | null;
1148
+ }
1149
+
1052
1150
  export declare function FieldRenderer({ field, value, onChange, relations, collection, itemId, cascadeFilter, requiredParentLabel, onCountChange, displayOnly, prefillParentId }: {
1053
1151
  field: CMSField;
1054
1152
  value: unknown;
@@ -1136,6 +1234,13 @@ export declare function FirstLoginChecklist({ profileUrl }: {
1136
1234
  profileUrl?: string;
1137
1235
  }): JSX.Element | null;
1138
1236
 
1237
+ /** "FY2026-Q1" — the FiscalPeriodField storage format. */
1238
+ export declare function fiscalPeriodOf(d: Date, startMonth?: number): string;
1239
+
1240
+ export declare function fiscalQuarterOf(d: Date, startMonth?: number): number;
1241
+
1242
+ export declare function fiscalYearOf(d: Date, startMonth?: number): number;
1243
+
1139
1244
  export declare function formatDate(date: string | Date, opts?: Intl.DateTimeFormatOptions): string;
1140
1245
 
1141
1246
  export declare function formatDateTime(date: string | Date): string;
@@ -1254,6 +1359,8 @@ export declare type FormStatus = {
1254
1359
  canSubmit: boolean;
1255
1360
  };
1256
1361
 
1362
+ export declare function formulaConstant(name: string): number | null;
1363
+
1257
1364
  /**
1258
1365
  * Authoring surface for `{{token}}` formulas.
1259
1366
  *
@@ -1338,6 +1445,12 @@ export declare type FormVisibilityRule = {
1338
1445
  /** The newer API build being served, or null while this tab is current. */
1339
1446
  export declare function getApiUpdate(): ApiVersionInfo | null;
1340
1447
 
1448
+ export declare function getFieldInterface(name: string | null | undefined): FieldInterfacePlugin | null;
1449
+
1450
+ export declare function getFiscalStartMonth(): number;
1451
+
1452
+ export declare function getLayoutSlot(key: string | null | undefined): LayoutSlotPlugin | null;
1453
+
1341
1454
  export declare function getMentionQuery(text: string, cursorPos: number): string | null;
1342
1455
 
1343
1456
  export declare const GridFlushContext: React_2.Context<GridFlushContextValue | null>;
@@ -1851,6 +1964,39 @@ declare interface LayoutGroup {
1851
1964
 
1852
1965
  export declare type LayoutItem = FormGroupDescriptor | '__ungrouped__';
1853
1966
 
1967
+ declare interface LayoutSlotHandle {
1968
+ update?: (props: LayoutSlotProps) => void;
1969
+ destroy?: () => void;
1970
+ }
1971
+
1972
+ declare interface LayoutSlotPlugin {
1973
+ key: string;
1974
+ mount: (el: HTMLElement, props: LayoutSlotProps) => LayoutSlotHandle | void;
1975
+ }
1976
+
1977
+ /**
1978
+ * Extension layout slots (#425): extensions render whole custom SECTIONS
1979
+ * inside record layouts — the section-level sibling of the field-interface
1980
+ * registry (#17). Same framework-free DOM contract:
1981
+ *
1982
+ * window.__nvrRegisterLayoutSlot({
1983
+ * key: 'shipment-tracker',
1984
+ * mount(el, props) {
1985
+ * // build DOM inside el; props carries {collection, itemId, draft}
1986
+ * return { update(props) { … }, destroy() { … } }
1987
+ * }
1988
+ * })
1989
+ *
1990
+ * Authoring: a layout assignment whose field is `__ext_<key>__` renders the
1991
+ * registered slot; an unregistered key renders an honest placeholder naming
1992
+ * it, so a missing bundle is visible instead of a silent blank.
1993
+ */
1994
+ declare interface LayoutSlotProps {
1995
+ collection: string;
1996
+ itemId: string | null;
1997
+ draft: Record<string, unknown>;
1998
+ }
1999
+
1854
2000
  declare interface LeaderCallbacks {
1855
2001
  /** Called when this tab becomes leader — create the real socket, wire onAny → deliver. */
1856
2002
  becomeLeader(deliver: (event: string, payload: any) => void, emit: {
@@ -1880,6 +2026,8 @@ export declare interface LeaderSocketHandle {
1880
2026
  destroy(): void;
1881
2027
  }
1882
2028
 
2029
+ export declare function listFieldInterfaces(): string[];
2030
+
1883
2031
  declare interface LockHolder {
1884
2032
  locked_by: string;
1885
2033
  locked_by_name: string | null;
@@ -2072,6 +2220,17 @@ export declare type NavigationContextValue = {
2072
2220
  userUrl?: (userId: string) => string | null;
2073
2221
  };
2074
2222
 
2223
+ export declare function networkdaysBetween(a: Date, b: Date): number;
2224
+
2225
+ export declare function NewImportDialog({ open, onOpenChange, definitions, runCounts, onQueued }: {
2226
+ open: boolean;
2227
+ onOpenChange: (open: boolean) => void;
2228
+ definitions: ImportDefinition[];
2229
+ /** All-time run count per import key — drives the "Frequently used" section. */
2230
+ runCounts?: Record<string, number>;
2231
+ onQueued: (id: number) => void;
2232
+ }): JSX.Element;
2233
+
2075
2234
  /**
2076
2235
  * Selects and renders the correct field component for a single field based on
2077
2236
  * its normalized fieldType. Override any type via `components`; falls back to
@@ -2179,6 +2338,18 @@ declare interface O2MFieldInfo {
2179
2338
  parentId: string;
2180
2339
  }
2181
2340
 
2341
+ /**
2342
+ * Offline banner (#348): connectivity loss shows an honest "offline — changes
2343
+ * paused" strip and clears itself when the connection returns. Uses the
2344
+ * browser online/offline events plus a confirm probe (the events lie on some
2345
+ * networks) against a HEAD to the app's own origin.
2346
+ */
2347
+ export declare function OfflineBanner(): JSX.Element | null;
2348
+
2349
+ /** Re-render hook support: components subscribe so a late-loading extension
2350
+ * bundle upgrades already-mounted fields. */
2351
+ export declare function onFieldInterfacesChange(cb: () => void): () => void;
2352
+
2182
2353
  /**
2183
2354
  * Called when idleness CHANGES, not on a timer — a host can push the flip
2184
2355
  * straight down its socket so coming back is seen in about a second instead of
@@ -2350,6 +2521,43 @@ export declare function QualityRulesView({ className }: {
2350
2521
  className?: string;
2351
2522
  }): JSX.Element;
2352
2523
 
2524
+ declare type QueryRowClick = {
2525
+ picker?: {
2526
+ collection: string;
2527
+ filter?: Record<string, unknown>;
2528
+ title?: string;
2529
+ };
2530
+ drill?: {
2531
+ collection?: string;
2532
+ layout_id?: number;
2533
+ width?: number | string;
2534
+ };
2535
+ matrix?: {
2536
+ config: MatrixEditorConfig;
2537
+ scope_field: string;
2538
+ width?: number | string;
2539
+ title?: string;
2540
+ };
2541
+ /** Open a nested query sheet for the clicked row. */
2542
+ sheet?: QuerySheetDef;
2543
+ };
2544
+
2545
+ /** A right-hand sheet hosting one (or tabbed) nested query views. Nested
2546
+ * config `params` values may use '$row.<field>' (clicked row), '$rows.<field>'
2547
+ * (comma-join over all visible rows — footer/'View All' drills),
2548
+ * '$param.<name>' (parent widget's effective params) and '$filters.<param>'
2549
+ * (parent filter selections, comma-joined). Title supports {{field}} tokens
2550
+ * from the clicked row. */
2551
+ declare interface QuerySheetDef {
2552
+ title?: string;
2553
+ width?: number | string;
2554
+ config?: QueryWidgetConfig;
2555
+ tabs?: Array<{
2556
+ label: string;
2557
+ config: QueryWidgetConfig;
2558
+ }>;
2559
+ }
2560
+
2353
2561
  export declare function QueryStatStrip({ stats, rows, effectiveParams, loading }: {
2354
2562
  stats: QueryWidgetStat[];
2355
2563
  rows: Array<Record<string, unknown>>;
@@ -2501,6 +2709,45 @@ export declare function QueryWidgetBody({ cfg, dateRange, entityFilters, refetch
2501
2709
  }) => void;
2502
2710
  }): JSX.Element;
2503
2711
 
2712
+ declare interface QueryWidgetConfig {
2713
+ query_slug?: string;
2714
+ params?: Record<string, unknown>;
2715
+ table?: QueryTableConfig;
2716
+ row_click?: QueryRowClick;
2717
+ filters?: QueryWidgetFilter[];
2718
+ stats?: QueryWidgetStat[];
2719
+ /** Trailing action buttons per row (and totals row) opening nested sheets. */
2720
+ row_actions?: Array<{
2721
+ label: string;
2722
+ sheet: QuerySheetDef;
2723
+ }>;
2724
+ /** Toolbar action buttons running a (write) custom query — params support
2725
+ * the same tokens as sheets ('$filters.<param>' / '$param.<name>' /
2726
+ * '$rows.<field>'); an unresolved param blocks the run with a message.
2727
+ * EFP 'Run Reforecasting'. */
2728
+ actions?: Array<{
2729
+ label: string;
2730
+ query_slug: string;
2731
+ params?: Record<string, unknown>;
2732
+ confirm?: string;
2733
+ success_message?: string;
2734
+ }>;
2735
+ }
2736
+
2737
+ declare type QueryWidgetFilter = {
2738
+ param: string;
2739
+ label?: string;
2740
+ collection: string;
2741
+ value_field?: string;
2742
+ label_field?: string;
2743
+ sort?: string;
2744
+ /** Option fetch cap (default 500) — bump for big pick lists (projects). */
2745
+ limit?: number;
2746
+ /** Pre-selected values on load. '$current_year' resolves to the current
2747
+ * calendar year (EFP default-funding-year behavior). */
2748
+ default_values?: Array<string | number>;
2749
+ };
2750
+
2504
2751
  declare interface QueryWidgetStat {
2505
2752
  label: string;
2506
2753
  /** Sum this field over the main query's rows. */
@@ -2537,6 +2784,13 @@ declare interface QueryWidgetStat {
2537
2784
  accent_negative?: string;
2538
2785
  }
2539
2786
 
2787
+ /** The full query-widget surface (filters, stats, table, row actions, drill
2788
+ * sheets) — shared by page renderers and nested drill sheets. A config with
2789
+ * display:'drawer' renders as a lone trigger button instead. */
2790
+ export declare function QueryWidgetView({ config }: {
2791
+ config: QueryWidgetConfig;
2792
+ }): JSX.Element;
2793
+
2540
2794
  export declare function QueueBulkBar({ count, states, busy, claimsEnabled, onClaim, onRelease, onTransition, onClear }: {
2541
2795
  count: number;
2542
2796
  states: Array<{
@@ -2781,6 +3035,12 @@ declare interface RecordGridEditorConfig {
2781
3035
  }>;
2782
3036
  }
2783
3037
 
3038
+ /** Editable record grid widget — 'drawer' display renders a lone button
3039
+ * opening the editor in a right sheet (Manage Production Numbers pattern). */
3040
+ export declare function RecordGridWidgetBody({ widget }: {
3041
+ widget: PageRendererWidget;
3042
+ }): JSX.Element;
3043
+
2784
3044
  export declare function RecordReadView({ collection, itemId, layoutData }: {
2785
3045
  collection: string;
2786
3046
  itemId: string;
@@ -2824,6 +3084,10 @@ export declare function RecordSubscribeButton({ collection, itemId, recordLabel
2824
3084
  /** Register the host's DM opener. Returns an unregister function. */
2825
3085
  export declare function registerDmOpener(fn: DmOpener): () => void;
2826
3086
 
3087
+ export declare function registerFieldInterface(plugin: FieldInterfacePlugin): void;
3088
+
3089
+ export declare function registerLayoutSlot(plugin: LayoutSlotPlugin): void;
3090
+
2827
3091
  export declare function registerRoomOpener(fn: RoomOpener): () => void;
2828
3092
 
2829
3093
  export declare function RelatedRecordsPanel({ collection, itemId, defaultExpanded }: {
@@ -2928,6 +3192,99 @@ export declare function resolveCollectionIcon(iconName: string | null | undefine
2928
3192
 
2929
3193
  export declare function resolveNotificationTarget(collection: string | null | undefined, item: string | null | undefined, routes: NotificationRouteMap): NotificationTarget;
2930
3194
 
3195
+ declare type ReviewListColumnFormat = 'currency' | 'number' | 'date' | 'datetime' | 'flag';
3196
+
3197
+ declare interface ReviewListColumnSpec {
3198
+ field: string;
3199
+ label?: string;
3200
+ format?: ReviewListColumnFormat;
3201
+ color?: string;
3202
+ }
3203
+
3204
+ declare interface ReviewListConfig {
3205
+ host_collection: string;
3206
+ collection: string;
3207
+ path: Array<{
3208
+ kind: 'm2o' | 'm2m';
3209
+ field: string;
3210
+ }>;
3211
+ static_filter?: Array<{
3212
+ field: string;
3213
+ op: 'eq' | 'neq' | 'nnull';
3214
+ value?: unknown;
3215
+ }>;
3216
+ group_by: string;
3217
+ aggregate_sum?: string | null;
3218
+ aggregate_sum_format?: ReviewListColumnFormat | null;
3219
+ group_meta?: Array<string | ReviewListColumnSpec>;
3220
+ line_columns?: Array<string | ReviewListColumnSpec>;
3221
+ status: {
3222
+ field: string;
3223
+ options: ReviewListStatusOption[];
3224
+ empty_label?: string | null;
3225
+ empty_color?: string | null;
3226
+ stamp_user_field?: string | null;
3227
+ stamp_date_field?: string | null;
3228
+ /** POST this endpoint with {ids, status, note, workflow_id} instead of
3229
+ * per-row PATCHes — lets a deployment route decisions through an
3230
+ * endpoint that owns stamping, authorization, notes and notifications
3231
+ * (EFP: /efp/invoice-approvals/decide). */
3232
+ action_endpoint?: string | null;
3233
+ };
3234
+ }
3235
+
3236
+ declare interface ReviewListResult {
3237
+ rows: ReviewListRow[];
3238
+ columns: {
3239
+ group_meta: Array<{
3240
+ field: string;
3241
+ label: string;
3242
+ format?: ReviewListColumnFormat | null;
3243
+ color?: string | null;
3244
+ }>;
3245
+ line_columns: Array<{
3246
+ field: string;
3247
+ label: string;
3248
+ format?: ReviewListColumnFormat | null;
3249
+ color?: string | null;
3250
+ }>;
3251
+ };
3252
+ truncated: boolean;
3253
+ }
3254
+
3255
+ declare interface ReviewListRow {
3256
+ id: string | number;
3257
+ group: unknown;
3258
+ values: Record<string, unknown>;
3259
+ status: unknown;
3260
+ stamp_user: {
3261
+ id: string;
3262
+ label: string;
3263
+ } | null;
3264
+ stamp_date: string | null;
3265
+ }
3266
+
3267
+ declare interface ReviewListStatusOption {
3268
+ value: string;
3269
+ label: string;
3270
+ color: string;
3271
+ /** Picking this option first asks the reviewer for a note (required). */
3272
+ require_note?: boolean;
3273
+ }
3274
+
3275
+ export declare function ReviewListWidget({ data, config, loading, error, onRefetch, hostRecordId }: ReviewListWidgetProps): JSX.Element;
3276
+
3277
+ declare interface ReviewListWidgetProps {
3278
+ data: ReviewListResult | null;
3279
+ config: ReviewListConfig;
3280
+ loading?: boolean;
3281
+ error?: string | null;
3282
+ onRefetch: () => void;
3283
+ /** Host record id (the workflow the widget is slotted on) — forwarded to
3284
+ * action_endpoint as workflow_id so a rejection note can anchor to it. */
3285
+ hostRecordId?: string | number | null;
3286
+ }
3287
+
2931
3288
  export declare function RevisionsPanel({ collection, item, onRollback, triggerClassName, inlineTableFields, open, onOpenChange }: {
2932
3289
  collection: string;
2933
3290
  item: string;
@@ -3077,6 +3434,26 @@ export declare interface SessionRecorderOptions {
3077
3434
  disabled?: boolean;
3078
3435
  }
3079
3436
 
3437
+ export declare function setDisplayTimezone(tz: string | null): void;
3438
+
3439
+ /**
3440
+ * Fiscal calendar helpers (#343). The instance's fiscal-year start month
3441
+ * lives on nivaro_settings.fiscal_year_start_month; hosts hydrate it via
3442
+ * setFiscalStartMonth (ItemEditForm does this from /settings). Convention:
3443
+ * a fiscal year is NAMED for the calendar year it ends in when the start
3444
+ * month is not January (Oct 2025 with start month 10 → FY2026).
3445
+ */
3446
+ export declare function setFiscalStartMonth(m: number | null | undefined): void;
3447
+
3448
+ export declare function setFormulaConstants(map: Record<string, number> | null | undefined): void;
3449
+
3450
+ export declare function setNumberFormat(opts: {
3451
+ locale?: string;
3452
+ compact?: boolean;
3453
+ } | null | undefined): void;
3454
+
3455
+ export declare function setTimeDisplay(mode: string | null | undefined): void;
3456
+
3080
3457
  declare interface SheetItem {
3081
3458
  collection: string;
3082
3459
  item_id: string;
@@ -3136,6 +3513,18 @@ export declare function TextareaField({ field, value, onChange, error, disabled,
3136
3513
 
3137
3514
  export declare function TextField({ field, value, onChange, error, disabled, readOnly, inputId, errorId }: FieldComponentProps): JSX.Element;
3138
3515
 
3516
+ /**
3517
+ * Number ticker (#320): rolls to a new value with an eased count instead of
3518
+ * snapping. Cosmetic — the true value is always the final frame. Respects
3519
+ * prefers-reduced-motion (snaps), and the FIRST render never animates (a page
3520
+ * load is not a change).
3521
+ */
3522
+ export declare function TickerNumber({ value, format, durationMs }: {
3523
+ value: number;
3524
+ format?: (n: number) => string;
3525
+ durationMs?: number;
3526
+ }): JSX.Element;
3527
+
3139
3528
  /** Timezone preference (#31): applied to every datetime the shared
3140
3529
  * formatters render. Defaults to the browser's zone. */
3141
3530
  export declare function TimezoneCard(): JSX.Element;
@@ -3249,6 +3638,13 @@ user_ids: string[];
3249
3638
  name?: string;
3250
3639
  }, unknown>;
3251
3640
 
3641
+ /**
3642
+ * Returns a value that trails `value` by `delayMs`. Useful for keeping text
3643
+ * inputs bound to live state while queries key off the debounced copy so a
3644
+ * request doesn't fire per keystroke.
3645
+ */
3646
+ export declare function useDebounced<T>(value: T, delayMs?: number): T;
3647
+
3252
3648
  export declare function useDeleteMessage(room: string): UseMutationResult<unknown, Error, number, unknown>;
3253
3649
 
3254
3650
  export declare function useDrilldown(): DrilldownContextValue | null;
@@ -3258,6 +3654,13 @@ messageId: number;
3258
3654
  text: string;
3259
3655
  }, unknown>;
3260
3656
 
3657
+ /**
3658
+ * Long-request honesty (#370): after `thresholdMs` of continuous loading,
3659
+ * returns the elapsed seconds so surfaces can say "Still working — 8s" instead
3660
+ * of an eternal spinner. Returns null while under the threshold or idle.
3661
+ */
3662
+ export declare function useElapsedLoading(loading: boolean, thresholdMs?: number): number | null;
3663
+
3261
3664
  /**
3262
3665
  * Resolves an entity room ('wf:CR26-76773') to the record's URL, host-routed
3263
3666
  * via cfg.recordUrl. The room-type registry maps the prefix to a collection +
@@ -3272,6 +3675,22 @@ export declare function useFieldArray(form: UseNivaroFormReturn, field: string):
3272
3675
 
3273
3676
  export declare function useFieldState(form: UseNivaroFormReturn, field: string): FieldState;
3274
3677
 
3678
+ /**
3679
+ * Dead-file-link awareness for anything rendering file chips/links.
3680
+ *
3681
+ * Batch-verifies the given file ids against the storage provider (POST
3682
+ * /files/verify — persists the verdict on the rows too) and returns a lookup:
3683
+ * `health.isMissing(id)` → true when the physical bytes are gone. Cached 5
3684
+ * minutes per id-set; degrades to "nothing missing" on any failure — a
3685
+ * broken check must never paint healthy files as dead.
3686
+ */
3687
+ export declare function useFileHealth(ids: Array<string | null | undefined>): {
3688
+ isMissing: (id: string | null | undefined) => boolean;
3689
+ /** Fresh verdict when the check has run for this id, undefined otherwise —
3690
+ * lets callers prefer the live answer over a stale stored missing_at. */
3691
+ verdict: (id: string | null | undefined) => boolean | undefined;
3692
+ };
3693
+
3275
3694
  export declare function useFormDirty(form: UseNivaroFormReturn, initialValuesOverride?: Record<string, unknown>): FormDirtyState;
3276
3695
 
3277
3696
  /**