@ai-matrx/kit 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -0
  4. package/dist/autosave.cjs +92 -0
  5. package/dist/autosave.cjs.map +1 -0
  6. package/dist/autosave.d.cts +17 -0
  7. package/dist/autosave.d.ts +17 -0
  8. package/dist/autosave.js +72 -0
  9. package/dist/autosave.js.map +1 -0
  10. package/dist/clipboard.cjs +152 -0
  11. package/dist/clipboard.cjs.map +1 -0
  12. package/dist/clipboard.d.cts +17 -0
  13. package/dist/clipboard.d.ts +17 -0
  14. package/dist/clipboard.js +132 -0
  15. package/dist/clipboard.js.map +1 -0
  16. package/dist/color-util.cjs +790 -0
  17. package/dist/color-util.cjs.map +1 -0
  18. package/dist/color-util.d.cts +235 -0
  19. package/dist/color-util.d.ts +235 -0
  20. package/dist/color-util.js +767 -0
  21. package/dist/color-util.js.map +1 -0
  22. package/dist/concurrency.cjs +59 -0
  23. package/dist/concurrency.cjs.map +1 -0
  24. package/dist/concurrency.d.cts +19 -0
  25. package/dist/concurrency.d.ts +19 -0
  26. package/dist/concurrency.js +38 -0
  27. package/dist/concurrency.js.map +1 -0
  28. package/dist/confirm.cjs +344 -0
  29. package/dist/confirm.cjs.map +1 -0
  30. package/dist/confirm.d.cts +132 -0
  31. package/dist/confirm.d.ts +132 -0
  32. package/dist/confirm.js +312 -0
  33. package/dist/confirm.js.map +1 -0
  34. package/dist/delimiter-guard.cjs +172 -0
  35. package/dist/delimiter-guard.cjs.map +1 -0
  36. package/dist/delimiter-guard.d.cts +119 -0
  37. package/dist/delimiter-guard.d.ts +119 -0
  38. package/dist/delimiter-guard.js +151 -0
  39. package/dist/delimiter-guard.js.map +1 -0
  40. package/dist/drafts.cjs +223 -0
  41. package/dist/drafts.cjs.map +1 -0
  42. package/dist/drafts.d.cts +62 -0
  43. package/dist/drafts.d.ts +62 -0
  44. package/dist/drafts.js +201 -0
  45. package/dist/drafts.js.map +1 -0
  46. package/dist/idb-store.cjs +178 -0
  47. package/dist/idb-store.cjs.map +1 -0
  48. package/dist/idb-store.d.cts +111 -0
  49. package/dist/idb-store.d.ts +111 -0
  50. package/dist/idb-store.js +155 -0
  51. package/dist/idb-store.js.map +1 -0
  52. package/dist/idle-scheduler.cjs +268 -0
  53. package/dist/idle-scheduler.cjs.map +1 -0
  54. package/dist/idle-scheduler.d.cts +185 -0
  55. package/dist/idle-scheduler.d.ts +185 -0
  56. package/dist/idle-scheduler.js +246 -0
  57. package/dist/idle-scheduler.js.map +1 -0
  58. package/dist/index.cjs +3113 -0
  59. package/dist/index.cjs.map +1 -0
  60. package/dist/index.d.cts +897 -0
  61. package/dist/index.d.ts +897 -0
  62. package/dist/index.js +3088 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/invalidation.cjs +64 -0
  65. package/dist/invalidation.cjs.map +1 -0
  66. package/dist/invalidation.d.cts +52 -0
  67. package/dist/invalidation.d.ts +52 -0
  68. package/dist/invalidation.js +43 -0
  69. package/dist/invalidation.js.map +1 -0
  70. package/dist/json-format.cjs +319 -0
  71. package/dist/json-format.cjs.map +1 -0
  72. package/dist/json-format.d.cts +148 -0
  73. package/dist/json-format.d.ts +148 -0
  74. package/dist/json-format.js +286 -0
  75. package/dist/json-format.js.map +1 -0
  76. package/dist/latest-request.cjs +36 -0
  77. package/dist/latest-request.cjs.map +1 -0
  78. package/dist/latest-request.d.cts +29 -0
  79. package/dist/latest-request.d.ts +29 -0
  80. package/dist/latest-request.js +16 -0
  81. package/dist/latest-request.js.map +1 -0
  82. package/dist/qr.cjs +109 -0
  83. package/dist/qr.cjs.map +1 -0
  84. package/dist/qr.d.cts +47 -0
  85. package/dist/qr.d.ts +47 -0
  86. package/dist/qr.js +78 -0
  87. package/dist/qr.js.map +1 -0
  88. package/dist/react-tree.cjs +69 -0
  89. package/dist/react-tree.cjs.map +1 -0
  90. package/dist/react-tree.d.cts +38 -0
  91. package/dist/react-tree.d.ts +38 -0
  92. package/dist/react-tree.js +38 -0
  93. package/dist/react-tree.js.map +1 -0
  94. package/dist/search-scoring.cjs +116 -0
  95. package/dist/search-scoring.cjs.map +1 -0
  96. package/dist/search-scoring.d.cts +77 -0
  97. package/dist/search-scoring.d.ts +77 -0
  98. package/dist/search-scoring.js +95 -0
  99. package/dist/search-scoring.js.map +1 -0
  100. package/dist/text-case.cjs +313 -0
  101. package/dist/text-case.cjs.map +1 -0
  102. package/dist/text-case.d.cts +53 -0
  103. package/dist/text-case.d.ts +53 -0
  104. package/dist/text-case.js +292 -0
  105. package/dist/text-case.js.map +1 -0
  106. package/dist/toast.cjs +74 -0
  107. package/dist/toast.cjs.map +1 -0
  108. package/dist/toast.d.cts +92 -0
  109. package/dist/toast.d.ts +92 -0
  110. package/dist/toast.js +54 -0
  111. package/dist/toast.js.map +1 -0
  112. package/dist/url-state.cjs +212 -0
  113. package/dist/url-state.cjs.map +1 -0
  114. package/dist/url-state.d.cts +90 -0
  115. package/dist/url-state.d.ts +90 -0
  116. package/dist/url-state.js +199 -0
  117. package/dist/url-state.js.map +1 -0
  118. package/package.json +286 -0
@@ -0,0 +1,897 @@
1
+ export { AutosaveStatus, UseAutosaveResult, useAutosave } from './autosave.cjs';
2
+ export { BeginRequest, useLatestRequest } from './latest-request.cjs';
3
+ export { ClipboardNotifyKind, UseClipboardOptions, UseClipboardResult, useClipboard } from './clipboard.cjs';
4
+ export { DraftSource, LocalDraft, LocalDraftInput, captureDrafts, discardDraft, getDraft, getDraftsVersion, listDrafts, registerDraftSource, subscribeDrafts, useDurableDraft } from './drafts.cjs';
5
+ export { ConfirmDialog, ConfirmDialogHost, ConfirmDialogProps, ConfirmOptions, confirm } from './confirm.cjs';
6
+ export { CapturedToastInfo, CreateMatrxToastOptions, MatrxToast, SonnerLikeToast, ToastData, ToastMessage, createMatrxToast } from './toast.cjs';
7
+ export { FlushState, IdlePriority, IdleRegistration, UnregisterFn, getSchedulerState, onFlushComplete, registerIdleTask, resetScheduler, useIdleGate, useIdleReady, useIdleRegister, useIdleTask, whenPageIdle } from './idle-scheduler.cjs';
8
+ export { MirroredUrlStateOptions, SetUrlStateOptions, UrlHistoryMode, UrlStateCodec, UrlStateRouter, booleanUrlCodec, commitUrlParams, enumUrlCodec, historyModeForParamChange, jsonUrlCodec, positiveIntegerUrlCodec, setUrlStateRouter, stringUrlCodec, useMirroredUrlState, useUrlSearchParams, useUrlState } from './url-state.cjs';
9
+ import { IDBPDatabase } from 'idb';
10
+ import * as React from 'react';
11
+
12
+ /**
13
+ * Relevance-weighted search scoring.
14
+ *
15
+ * Use instead of the naive `name.includes(q) || description.includes(q)` pattern
16
+ * so that title/name matches rank above description matches, and exact/prefix
17
+ * matches rank above partial ones.
18
+ *
19
+ * ── Quick start ────────────────────────────────────────────────────────────────
20
+ * const filtered = filterAndSortBySearch(items, query, [
21
+ * { get: (t) => t.name, weight: "title" },
22
+ * { get: (t) => t.description, weight: "body" },
23
+ * { get: (t) => t.tags, weight: "tag" },
24
+ * ]);
25
+ *
26
+ * ── Weight tiers (higher = more important field) ──────────────────────────────
27
+ * title — the primary identifier (name, label, subject)
28
+ * subtitle — secondary identifier (vendor, author, category name)
29
+ * body — long-form descriptive text (description, summary)
30
+ * tag — tag/category labels
31
+ * meta — weak metadata (modelId, type)
32
+ * id — raw identifiers (uuid, slug) — only useful for pasted-id lookups
33
+ *
34
+ * Within each field, an EXACT match > STARTS-WITH match > INCLUDES match.
35
+ * Fields declared first are a slight tiebreaker (via field-index bonus).
36
+ *
37
+ * ── Automatic id matching ─────────────────────────────────────────────────────
38
+ * Every item with a string `id` is ALSO matched against the query at the `id`
39
+ * weight tier, automatically — you do NOT need to declare an id field. This
40
+ * means a user can paste a full or partial UUID into ANY search box wired to
41
+ * this helper and find the record. It kicks in from {@link MIN_AUTO_ID_QUERY_LEN}
42
+ * characters up (so short queries don't match random hex). Declare an explicit
43
+ * `{ weight: "id" }` field only if you want id matching at any length / on a
44
+ * non-`id` property; doing so opts that callsite out of the automatic pass.
45
+ */
46
+ type SearchFieldWeight = "title" | "subtitle" | "body" | "tag" | "meta" | "id" | "custom";
47
+ interface SearchFieldConfig<T> {
48
+ /**
49
+ * Extracts the value(s) from the item. Return a string, an array of strings,
50
+ * or null/undefined. Arrays score based on the best-matching element.
51
+ */
52
+ get: (item: T) => string | string[] | null | undefined;
53
+ /** Field importance tier. Defaults to "body". */
54
+ weight?: SearchFieldWeight;
55
+ /** Optional override for custom tiers. Ignored when `weight` is preset. */
56
+ exact?: number;
57
+ startsWith?: number;
58
+ includes?: number;
59
+ }
60
+ /**
61
+ * Compute a weighted relevance score for `item` against `query`.
62
+ * Returns 0 if there is no match — callers can treat `> 0` as a match predicate.
63
+ *
64
+ * Within each field, multiple values (e.g. tags) contribute the BEST match,
65
+ * not the sum, so an item with many tags doesn't unfairly outrank one with a
66
+ * single exact title match.
67
+ */
68
+ declare function computeSearchScore<T>(item: T, query: string, fields: SearchFieldConfig<T>[]): number;
69
+ declare function matchesSearch<T>(item: T, query: string, fields: SearchFieldConfig<T>[]): boolean;
70
+ /**
71
+ * Drop-in id-match for hand-rolled `.filter()` predicates that can't (yet) move
72
+ * onto {@link filterAndSortBySearch}. Returns true when `query` is a substring
73
+ * of the item's string `id`, applying the same {@link MIN_AUTO_ID_QUERY_LEN}
74
+ * guard as the automatic pass so short queries don't match random hex.
75
+ *
76
+ * list.filter((x) => x.name.toLowerCase().includes(q) || idMatchesQuery(x, q))
77
+ *
78
+ * Prefer migrating the callsite to `filterAndSortBySearch` (which does this for
79
+ * free); reach for this only when an existing custom sort must be preserved.
80
+ */
81
+ declare function idMatchesQuery(item: unknown, query: string): boolean;
82
+ /**
83
+ * Filter out non-matches and sort remaining items by descending relevance.
84
+ * Stable with respect to the original order when two items tie.
85
+ */
86
+ declare function filterAndSortBySearch<T>(items: readonly T[], query: string, fields: SearchFieldConfig<T>[]): T[];
87
+
88
+ interface ConcurrencyFailure<T> {
89
+ item: T;
90
+ index: number;
91
+ error: unknown;
92
+ }
93
+ interface ConcurrencyResult<T> {
94
+ started: number;
95
+ succeeded: number;
96
+ failed: number;
97
+ failures: ConcurrencyFailure<T>[];
98
+ }
99
+ /**
100
+ * Runs independent items through a bounded worker pool. One item failing does
101
+ * not stop the rest, and `shouldStart` can stop new work without interrupting
102
+ * items already in flight.
103
+ */
104
+ declare function runWithConcurrency<T>(items: readonly T[], limit: number, worker: (item: T, index: number) => Promise<void>, shouldStart?: () => boolean): Promise<ConcurrencyResult<T>>;
105
+
106
+ /**
107
+ * Text formatting options
108
+ */
109
+ type TextCaseOption = 'title' | 'sentence' | 'normal' | 'lower' | 'upper';
110
+ interface TextFormatterOptions {
111
+ /**
112
+ * Text case to apply after normalization
113
+ * - title: First Letter Of Each Word Capitalized
114
+ * - sentence: First letter of first word capitalized
115
+ * - normal: No case transformation after normalization
116
+ * - lower: all text lowercase
117
+ * - upper: ALL TEXT UPPERCASE
118
+ */
119
+ textCase?: TextCaseOption;
120
+ /**
121
+ * Map of words to replace with specific formatting
122
+ * Example: { 'api': 'API', 'ui': 'UI' }
123
+ */
124
+ wordReplacements?: Record<string, string>;
125
+ /**
126
+ * Whether to trim the result
127
+ */
128
+ trim?: boolean;
129
+ }
130
+ /**
131
+ * Default word replacements for common acronyms and terms
132
+ */
133
+ type ReplacementMap = Readonly<Record<string, string>>;
134
+ declare const DEFAULT_WORD_REPLACEMENTS: ReplacementMap;
135
+ /**
136
+ * Formats text by normalizing case styles, applying case transformations,
137
+ * and replacing specific words with custom formatting.
138
+ *
139
+ * @param text The input text to format
140
+ * @param options Formatting options
141
+ * @returns Formatted text
142
+ */
143
+ declare function formatText(text: string, options?: TextFormatterOptions): string;
144
+ /**
145
+ * Creates a pre-configured formatter function with specific options
146
+ *
147
+ * @param defaultOptions Default options for the formatter
148
+ * @returns A formatter function with the specified default options
149
+ */
150
+ declare function createFormatter(defaultOptions?: TextFormatterOptions): (text: string, overrideOptions?: TextFormatterOptions) => string;
151
+ declare const formatTitleCase: (text: string, overrideOptions?: TextFormatterOptions) => string;
152
+ declare const formatSentenceCase: (text: string, overrideOptions?: TextFormatterOptions) => string;
153
+ declare const formatNormalCase: (text: string, overrideOptions?: TextFormatterOptions) => string;
154
+ declare const formatUpperCase: (text: string, overrideOptions?: TextFormatterOptions) => string;
155
+ declare const formatLowerCase: (text: string, overrideOptions?: TextFormatterOptions) => string;
156
+ declare const formatWithoutReplacements: (text: string, overrideOptions?: TextFormatterOptions) => string;
157
+
158
+ /**
159
+ * @ai-matrx/kit/invalidation — a tiny name-keyed callback registry that lets a
160
+ * UBIQUITOUS module (a stream processor, an effect runner) trigger cache
161
+ * invalidation inside a HEAVY chunk cluster with ZERO import edge between them.
162
+ *
163
+ * WHY THIS EXISTS (the fragmentation-law incident): a module statically
164
+ * reachable from ~every route context reached a heavy registry cluster with an
165
+ * `await import()` — one line that added +14 GB peak build RSS / +50% compile
166
+ * time and OOM-killed 12 straight production builds. The sanctioned shape is
167
+ * the INVERSION implemented here: the heavy cluster registers a callback at
168
+ * its own module init (it is always initialized wherever its output can
169
+ * render — if the chunk never loaded, nothing stale is mounted), and the
170
+ * ubiquitous module fires the callback by NAME. The only shared code is this
171
+ * module, which imports nothing.
172
+ *
173
+ * RULES:
174
+ * - This module must NEVER grow an import. It is in every chunk that touches
175
+ * it; any dependency it gains is multiplied across all of them.
176
+ * - Firing an unregistered name is a NO-OP by design, not an error — the
177
+ * consumer chunk simply isn't loaded in this tab, so there is nothing
178
+ * stale to invalidate.
179
+ * - Callbacks never break the caller: each runs in its own try/catch and
180
+ * screams to the console on failure.
181
+ * - Key constants live in the HOST app (one shared constants module per app),
182
+ * so producer and consumer agree on the name without importing each other.
183
+ * The registry itself is generic over names — app-specific keys never move
184
+ * into this package.
185
+ *
186
+ * Ported from matrx-frontend `lib/invalidation/invalidation-registry.ts`, with
187
+ * ONE structural inversion: the callback map lives on `globalThis` under a
188
+ * `Symbol.for` slot instead of a module-level variable. With the package built
189
+ * `splitting: false` in dual ESM/CJS format, this module is duplicated into
190
+ * the root bundle and the `./invalidation` bundle, and CJS/ESM each
191
+ * instantiate their own module graph — a module-level Map would silently split
192
+ * the producers from the consumers (the same hazard the confirm opener
193
+ * documents). Behavior is unchanged; never "clean this up" into a module
194
+ * local.
195
+ */
196
+ type InvalidationCallback = (detail?: unknown) => void;
197
+ /**
198
+ * Register a callback for a name. Idempotent-friendly: returns the
199
+ * unsubscribe. Module-scope registration in the consumer chunk is the
200
+ * intended pattern (register once per chunk load, never unsubscribe).
201
+ */
202
+ declare function registerInvalidationCallback(name: string, callback: InvalidationCallback): () => void;
203
+ /**
204
+ * Fire every callback registered under `name`. Returns true when at least one
205
+ * callback ran. Never throws — a failing callback screams and the rest run.
206
+ */
207
+ declare function fireInvalidation(name: string, detail?: unknown): boolean;
208
+
209
+ /**
210
+ * @ai-matrx/kit/delimiter-guard — stops ONE stray markdown delimiter from
211
+ * swallowing a whole section of a streamed answer.
212
+ *
213
+ * THE FAILURE CLASS
214
+ * -----------------
215
+ * Markdown delimiters pair greedily and blindly. A single stray opener emitted
216
+ * by a model (the common shape is a mangled citation:
217
+ * `…/a-quicker-way-to-heal-prp-and-prf$$ .`) pairs with the next matching
218
+ * delimiter anywhere later in the message, and everything in between —
219
+ * headings, bold, links, whole sections — collapses into one node.
220
+ *
221
+ * Two delimiters cause this in a remark-math + CommonMark pipeline:
222
+ *
223
+ * 1. `$$` (remark-math). The swallowed prose becomes a math node, KaTeX fails
224
+ * to parse it, and `rehype-katex` falls back to its built-in error
225
+ * rendering: the raw source re-emitted inside `<span class="katex-error"
226
+ * style="color:#cc0000">`. The symptom is a huge block of BRIGHT RED
227
+ * unrendered markdown mid-answer. That red is KaTeX reporting a parse error
228
+ * on text that was never math — not a style of yours.
229
+ * 2. `[` (CommonMark link label). The swallowed prose becomes the label of one
230
+ * enormous hyperlink — the same bug wearing blue instead of red.
231
+ *
232
+ * WHAT THIS DOES
233
+ * --------------
234
+ * Before the markdown pipeline runs, each candidate span is checked for
235
+ * plausibility. A `$$…$$` span carrying markdown structure (links, URLs, bold,
236
+ * headings, list markers) or reading as prose is not math; a link label that is
237
+ * hundreds of characters long or contains block structure is not a label. The
238
+ * offending OPENER is neutralized (`&#36;&#36;`, `&#91;`) and scanning resumes at the next
239
+ * delimiter, so genuine math and genuine links later in the same message still
240
+ * render. Real content is never touched.
241
+ *
242
+ * LOUD RECOVERY: every firing is a real upstream defect (a model emitting
243
+ * malformed delimiters, or a producer mangling a citation). Callers report the
244
+ * returned violations — see `reportDelimiterViolations`.
245
+ *
246
+ * Ported verbatim from matrx-frontend `lib/markdown/delimiter-guard.ts`, with
247
+ * ONE coupling inversion: the app's `captureError` store import became the
248
+ * injected `capture` sink on `reportDelimiterViolations`' context (payload
249
+ * shape preserved exactly). No sink means console-only loud recovery.
250
+ */
251
+ type DelimiterViolationReason =
252
+ /** A `$$…$$` pair whose contents are prose/markdown, not math. */
253
+ "prose-span"
254
+ /** A `$$` with no closing partner in the content. */
255
+ | "unpaired"
256
+ /** A `[…](…)` link whose label swallowed prose/structure. */
257
+ | "runaway-link";
258
+ interface DelimiterViolation {
259
+ reason: DelimiterViolationReason;
260
+ /** Character offset of the offending delimiter in the input string. */
261
+ index: number;
262
+ /** Length of the span between the delimiters (0 for `unpaired`). */
263
+ spanLength: number;
264
+ /** Short excerpt of what would have been swallowed. */
265
+ preview: string;
266
+ }
267
+ interface DelimiterGuardResult {
268
+ /** Input with runaway openers escaped. */
269
+ text: string;
270
+ violations: DelimiterViolation[];
271
+ }
272
+ /**
273
+ * Escapes `$$` delimiters that would make remark-math swallow non-math text.
274
+ * Pure — safe to call on every render / stream chunk.
275
+ */
276
+ declare function guardMathDelimiters(text: string): DelimiterGuardResult;
277
+ /**
278
+ * Escapes the `[` of a markdown link whose label ran away — the link twin of
279
+ * the stray-`$$` bug. An unclosed citation bracket pairs with a `]` hundreds of
280
+ * characters later and turns an entire section into one hyperlink.
281
+ *
282
+ * Pure. Runs after the math guard so both share one escaping pass conceptually,
283
+ * but each is independently usable.
284
+ */
285
+ declare function guardRunawayLinks(text: string): DelimiterGuardResult;
286
+ /**
287
+ * The front door: run every delimiter guard in order. Offsets in the returned
288
+ * violations refer to each guard's own input, so they are for diagnostics only.
289
+ */
290
+ declare function guardMarkdownDelimiters(text: string): DelimiterGuardResult;
291
+ /**
292
+ * The payload handed to the injected capture sink — exactly the shape the
293
+ * original passed to the Matrx `captureError` store. A Matrx host passes
294
+ * `captureError` straight through; any host can log/report it its own way.
295
+ */
296
+ interface DelimiterCaptureInput {
297
+ source: "markdown-delimiters";
298
+ message: string;
299
+ relation: string;
300
+ details: string;
301
+ conversationId?: string | undefined;
302
+ callSite: "guardMarkdownDelimiters";
303
+ raw: {
304
+ messageId?: string | undefined;
305
+ violations: DelimiterViolation[];
306
+ };
307
+ }
308
+ interface DelimiterReportContext {
309
+ renderPath: string;
310
+ messageId?: string | undefined;
311
+ conversationId?: string | undefined;
312
+ /**
313
+ * Optional error-capture sink (a Matrx host passes its `captureError`).
314
+ * Absent, the loud recovery is console-only. Must never be relied on to
315
+ * throw — failures inside it are swallowed so capture can never break
316
+ * rendering.
317
+ */
318
+ capture?: ((input: DelimiterCaptureInput) => void) | undefined;
319
+ }
320
+ /**
321
+ * Loud recovery. A firing means malformed math delimiters reached the renderer
322
+ * — the guard kept the message readable, but the producer is still emitting
323
+ * broken content and must be found.
324
+ */
325
+ declare function reportDelimiterViolations(violations: DelimiterViolation[], context: DelimiterReportContext): void;
326
+
327
+ /**
328
+ * Canonical JSON value types + narrowing guards.
329
+ *
330
+ * Ported (the subset this subpath needs) from matrx-frontend `types/json.ts` —
331
+ * the honest names for "this is just JSON" that are NOT `any` and NOT a bare
332
+ * `unknown`. The package must stand alone, so the types live here; a Matrx
333
+ * host keeps using its own `@/types/json` for app code and the two are
334
+ * structurally identical.
335
+ */
336
+ type JsonPrimitive = string | number | boolean | null;
337
+ /**
338
+ * A JSON object. Values are `JsonValue | undefined` so optional keys read
339
+ * cleanly (mirrors Supabase's original generated `Json` object member).
340
+ */
341
+ interface JsonObject {
342
+ [key: string]: JsonValue | undefined;
343
+ }
344
+ type JsonArray = JsonValue[];
345
+ type JsonValue = JsonPrimitive | JsonObject | JsonArray;
346
+ /**
347
+ * Narrow an `unknown` (e.g. a bare JSONB column) to a `JsonObject`.
348
+ * Plain object only — arrays and `null` return false.
349
+ */
350
+ declare function isJsonObject(value: unknown): value is JsonObject;
351
+ /** Narrow an `unknown` to a `JsonArray`. */
352
+ declare function isJsonArray(value: unknown): value is JsonArray;
353
+ /** Narrow an `unknown` to a JSON primitive (string | number | boolean | null). */
354
+ declare function isJsonPrimitive(value: unknown): value is JsonPrimitive;
355
+
356
+ /**
357
+ * How the JSON is laid out.
358
+ *
359
+ * - `minify` — one line, zero optional whitespace. The smallest legal form.
360
+ * - `compact` — width-aware: any subtree that fits within `width` is inlined,
361
+ * and sibling entries that fit are packed onto shared lines.
362
+ * Readable AND small; this is the "scrunch it down" default.
363
+ * - `pretty` — classic one-entry-per-line indentation.
364
+ */
365
+ type JsonFormatStyle = "minify" | "compact" | "pretty";
366
+ /** What to do with the markdown code fence around the JSON. */
367
+ type JsonFenceMode =
368
+ /** Keep the fence exactly as it arrived (and add none if there was none). */
369
+ "preserve"
370
+ /** Remove the fence, leaving bare JSON. */
371
+ | "strip"
372
+ /** Ensure a ```json fence, adding one if the source was bare. */
373
+ | "add";
374
+ /** A markdown code fence wrapping the JSON payload. */
375
+ interface JsonFence {
376
+ /** The opening marker as written, e.g. "```" or "~~~~". */
377
+ marker: string;
378
+ /** Info string after the marker, e.g. "json" (empty when the fence was bare). */
379
+ lang: string;
380
+ /** Whitespace the opening fence line was indented by. */
381
+ indent: string;
382
+ /** False when the source ran out before a closing fence (unterminated). */
383
+ closed: boolean;
384
+ }
385
+ /** Which JSON container sits at the root of the payload. */
386
+ type JsonRootKind = "object" | "array" | "scalar";
387
+ /** How permissively the payload had to be parsed. */
388
+ type JsonParser =
389
+ /** Valid JSON — `JSON.parse` accepted it verbatim. */
390
+ "strict"
391
+ /** Only JSON5 accepted it: trailing commas, comments, unquoted keys, … */
392
+ | "tolerant";
393
+ interface JsonDetection {
394
+ /** The payload parsed (strictly or tolerantly) into a value. */
395
+ ok: boolean;
396
+ /**
397
+ * The text is JSON-SHAPED even if it did not parse — it opens with `{`/`[`
398
+ * and closes with the matching bracket, or it arrived in a ```json fence.
399
+ * This is the gate for OFFERING json actions; `ok` is the gate for running
400
+ * them.
401
+ */
402
+ looksLikeJson: boolean;
403
+ /** The parsed value. Present only when `ok`. */
404
+ value?: JsonValue | undefined;
405
+ parser?: JsonParser | undefined;
406
+ /** Human-readable parse failure. Present only when `!ok`. */
407
+ error?: string | undefined;
408
+ /** The fence that was stripped before parsing, when the source had one. */
409
+ fence: JsonFence | null;
410
+ /** Text before the payload (fence excluded) — preserved verbatim on format. */
411
+ leading: string;
412
+ /** Text after the payload (fence excluded) — preserved verbatim on format. */
413
+ trailing: string;
414
+ /** The JSON text itself: fence and surrounding whitespace removed. */
415
+ payload: string;
416
+ root?: JsonRootKind | undefined;
417
+ /** Lines the payload currently occupies. */
418
+ lineCount: number;
419
+ /** Characters the payload currently occupies. */
420
+ charCount: number;
421
+ }
422
+ interface JsonFormatOptions {
423
+ style: JsonFormatStyle;
424
+ /** Spaces per indent level for `compact` / `pretty`. Default 2. */
425
+ indent?: number | undefined;
426
+ /** Target line width for `compact`. Default 100. Ignored otherwise. */
427
+ width?: number | undefined;
428
+ /** Sort object keys alphabetically at every depth. Default false. */
429
+ sortKeys?: boolean | undefined;
430
+ /** Fence handling. Default "preserve". */
431
+ fence?: JsonFenceMode | undefined;
432
+ }
433
+ /** Size of a chunk of text, for before/after reporting. */
434
+ interface JsonTextSize {
435
+ lines: number;
436
+ chars: number;
437
+ }
438
+ interface JsonFormatResult {
439
+ /** False when the text could not be parsed; `text` is then the input, unchanged. */
440
+ ok: boolean;
441
+ /** The re-formatted text (full input: leading + fence + payload + trailing). */
442
+ text: string;
443
+ error?: string | undefined;
444
+ /** True when `text` differs from the input. */
445
+ changed: boolean;
446
+ detection: JsonDetection;
447
+ before: JsonTextSize;
448
+ after: JsonTextSize;
449
+ }
450
+
451
+ /**
452
+ * Detect JSON in `text`. Never throws; a non-JSON string comes back with
453
+ * `ok: false` and `looksLikeJson: false`, which is the signal to offer nothing.
454
+ */
455
+ declare function detectJson(text: string): JsonDetection;
456
+
457
+ declare const DEFAULT_JSON_INDENT = 2;
458
+ declare const DEFAULT_JSON_WIDTH = 100;
459
+ /**
460
+ * Serialize a JSON value in one of the three styles. This is the entry point
461
+ * for callers that already HAVE a value (a DB JSONB blob, an API frame) and
462
+ * just want it laid out; text callers want {@link formatJsonText}.
463
+ */
464
+ declare function stringifyJson(value: JsonValue, options: JsonFormatOptions): string;
465
+ /**
466
+ * Format the JSON found in `text`, preserving everything around it.
467
+ *
468
+ * Never throws. When the text does not parse, the result is `ok: false`,
469
+ * `changed: false`, and `text` is the input verbatim — a formatter that
470
+ * mangles text it did not understand is a data-loss bug.
471
+ */
472
+ declare function formatJsonText(text: string, options: JsonFormatOptions): JsonFormatResult;
473
+
474
+ /**
475
+ * @ai-matrx/kit/idb-store — base manager.
476
+ *
477
+ * Ported verbatim from matrx-frontend `lib/idb/store-manager.ts`, with ONE
478
+ * structural inversion: the original documented a `protected static _instance`
479
+ * convention (each subclass held its singleton on its own static field).
480
+ * A class-static is module state — with this package built `splitting: false`
481
+ * in dual ESM/CJS format the class is duplicated into the root bundle and the
482
+ * `./idb-store` bundle, and CJS/ESM each instantiate their own module graph,
483
+ * so a static field would silently split "the" singleton into up to four
484
+ * instances racing the same IndexedDB database. The singleton slot therefore
485
+ * lives on `globalThis` under `Symbol.for("ai-matrx.kit.idb-store-state")`
486
+ * instead — see `singleton.ts` (`getIdbStoreSingleton`). Never "clean this
487
+ * up" back into a static field.
488
+ */
489
+
490
+ type AsyncResult<T> = Promise<{
491
+ data: T | null;
492
+ error: Error | null;
493
+ }>;
494
+ declare abstract class DBStoreManager<T> {
495
+ protected db: IDBPDatabase | null;
496
+ protected dbName: string;
497
+ protected version: number;
498
+ protected constructor(dbName: string, version: number);
499
+ protected abstract setupStores(db: IDBPDatabase): void;
500
+ protected initDB(): Promise<void>;
501
+ protected add<TRecord = T>(storeName: string, data: TRecord): AsyncResult<string>;
502
+ protected get<TRecord = T>(storeName: string, id: string): AsyncResult<TRecord>;
503
+ protected getAll(storeName: string): AsyncResult<T[]>;
504
+ protected update<U extends object>(storeName: string, id: number, data: Partial<U>): AsyncResult<boolean>;
505
+ protected delete(storeName: string, id: number): AsyncResult<boolean>;
506
+ protected query<U>(storeName: string, indexName: string, query: IDBValidKey | IDBKeyRange): AsyncResult<U[]>;
507
+ }
508
+
509
+ /**
510
+ * Public CRUD surface over `DBStoreManager` — ported verbatim from
511
+ * matrx-frontend `lib/idb/store-interface.ts`. The base keeps its operations
512
+ * `protected` so feature stores expose intent-named methods; this subclass is
513
+ * the escape hatch for hosts that want the raw generic surface (e.g. a
514
+ * `useIDB(store)` hook driving arbitrary stores).
515
+ *
516
+ * NOTE (original API shape, kept verbatim): `getItem` takes a string id while
517
+ * `updateItem`/`deleteItem` take a number id. With an `autoIncrement` key path
518
+ * the generated keys are numbers, so reads use `String(id)` only against
519
+ * string-keyed stores — this asymmetry is the original public contract.
520
+ */
521
+
522
+ declare abstract class PublicStoreManager<T> extends DBStoreManager<T> {
523
+ constructor(dbName: string, version: number);
524
+ addItem(storeName: string, data: T): AsyncResult<string>;
525
+ getItem(storeName: string, id: string): AsyncResult<T>;
526
+ getAllItems(storeName: string): AsyncResult<T[]>;
527
+ updateItem<U extends object>(storeName: string, id: number, data: Partial<U>): AsyncResult<boolean>;
528
+ deleteItem(storeName: string, id: number): AsyncResult<boolean>;
529
+ queryItems<U>(storeName: string, indexName: string, query: IDBValidKey | IDBKeyRange): AsyncResult<U[]>;
530
+ }
531
+
532
+ /**
533
+ * Convenience base for a store class bound to one named object store —
534
+ * ported from matrx-frontend `lib/idb/feature-store.ts`.
535
+ *
536
+ * DEFECT FIX vs the original: the constructor kicks off `initDB()` without
537
+ * awaiting it (by design — construction stays synchronous and operations
538
+ * before init resolve `{ error: "Database not initialized" }`), but the
539
+ * original left that floating promise unhandled, so a failed `openDB` (e.g.
540
+ * private-mode storage denial) surfaced as an unhandled promise rejection on
541
+ * top of the `initDB` console.error. The rejection is now absorbed here —
542
+ * `initDB` has already screamed, and every operation still reports the
543
+ * uninitialized state through its `AsyncResult` error.
544
+ */
545
+
546
+ declare abstract class FeatureStore<T> extends PublicStoreManager<T> {
547
+ protected storeName: string;
548
+ protected constructor(dbName: string, version: number, storeName: string);
549
+ protected abstract setupStores(db: IDBPDatabase): void;
550
+ getStoreName(): string;
551
+ }
552
+
553
+ /**
554
+ * The one place a store singleton may live.
555
+ *
556
+ * The original host pattern held each store's singleton on a
557
+ * `protected static _instance` field of the store class. In this package that
558
+ * is a hazard, not a convenience: built `splitting: false` in dual ESM/CJS
559
+ * format, the class body is duplicated into the root bundle and the
560
+ * `./idb-store` bundle, and the ESM and CJS graphs each instantiate their own
561
+ * copy — a class-static would silently split "the" singleton into several
562
+ * instances, each opening its own connection (and racing upgrades) against
563
+ * the same IndexedDB database. So per-store instances live on `globalThis`
564
+ * under `Symbol.for("ai-matrx.kit.idb-store-state")`, keyed by a
565
+ * caller-chosen name. Never "clean this up" into module or class state.
566
+ *
567
+ * Host usage (replaces the old static `getInstance` body):
568
+ *
569
+ * class AudioStore extends DBStoreManager<Recording> { ... }
570
+ * export const audioStore = getIdbStoreSingleton("voiceNotesDB/audio", () => new AudioStore());
571
+ */
572
+ /**
573
+ * Returns the one instance registered under `key`, creating it via `create`
574
+ * on first call. The key should uniquely name the store (a good convention is
575
+ * `"<dbName>/<storeName>"`) — two different classes registering the same key
576
+ * is a caller bug and gets whichever registered first.
577
+ */
578
+ declare function getIdbStoreSingleton<T>(key: string, create: () => T): T;
579
+ /** @internal Test-only: drop every registered store instance. */
580
+ declare function _resetIdbStoreSingletons(): void;
581
+
582
+ /**
583
+ * The bidirectional Tailwind-token mapping — the heart of the S14 unit,
584
+ * ported from matrx-frontend `utils/color-utils/color-change-util.ts` /
585
+ * `tailwind-color-util.ts`.
586
+ *
587
+ * token → hex: `getColorFromTailwind("slate-500")` → `"#64748b"` (plus the
588
+ * fuzzy `formatTailwindColor` for messy user input like `"skyblue598"`).
589
+ * color → token: `findNearestTailwindColor` — the verbatim nearest-match
590
+ * scan. The original took a colord instance; the colord coupling is inverted
591
+ * structurally: pass any object with `.delta(hex) => number` (every colord
592
+ * instance with the lab plugin qualifies — no import, no peer), or pass a
593
+ * plain hex / `rgb()` string and the built-in colord-identical CIEDE2000
594
+ * engine (`lab-delta.ts`) computes the distances with zero dependencies.
595
+ */
596
+ /**
597
+ * Anything that can measure its perceptual distance to a hex color —
598
+ * structurally satisfied by a colord instance extended with the lab plugin.
599
+ */
600
+ interface ColorDelta {
601
+ delta(color: string): number;
602
+ }
603
+ /**
604
+ * Function to find the hex value for a given Tailwind color string.
605
+ * @param tailwindColorString - The Tailwind color string (e.g., 'slate-500').
606
+ * @returns The hex value of the corresponding color (e.g., '#64748b'), or an empty string if not found.
607
+ */
608
+ declare function getColorFromTailwind(tailwindColorString: string): string;
609
+ /**
610
+ * Function to find the nearest Tailwind color for a given input color.
611
+ * @param inputColor - The input color: a hex or `rgb()` string, or any
612
+ * `{ delta(hex) }` measurer (e.g. a colord instance with the lab plugin).
613
+ * @returns The nearest Tailwind color string (e.g., 'slate-500'), or an
614
+ * empty string when a string input cannot be parsed.
615
+ */
616
+ declare function findNearestTailwindColor(inputColor: string | ColorDelta): string;
617
+ /**
618
+ * Utility to format a Tailwind color string.
619
+ * Matches Tailwind color names and returns the closest Tailwind value.
620
+ * Handles cases like "skyblue600", "sky598", or "sky-600".
621
+ * @param tailwindColor - The user-provided Tailwind color string.
622
+ * @returns A properly formatted Tailwind color string.
623
+ */
624
+ declare function formatTailwindColor(tailwindColor: string): string;
625
+
626
+ /**
627
+ * The Tailwind CSS default palette lookup table — ported verbatim from
628
+ * matrx-frontend `constants/tailwind-colors.ts` (the data the S14
629
+ * "tailwind-color-util" unit does its lookups against). 22 color groups,
630
+ * shades 50–950.
631
+ */
632
+ interface TailwindColorGroup {
633
+ name: string;
634
+ shades: Record<string, string>;
635
+ }
636
+ declare const tailwindColors: readonly TailwindColorGroup[];
637
+
638
+ /**
639
+ * The pure input normalizers — ported verbatim from matrx-frontend
640
+ * `utils/color-utils/color-change-util.ts`. Each takes the messy shape a
641
+ * human pastes ("61, 135, 204", "r:61,g:135,b:204", "94% 29% 0% 9%",
642
+ * "0x3d87cc"…) and returns the canonical string for that format, or `''`
643
+ * when the input does not fit — string in, string out, zero dependencies.
644
+ *
645
+ * ONE deliberate divergence from the original: `formatLabString` /
646
+ * `formatLchString` logged every call (input, match, miss) with
647
+ * unconditional `console.log`s — debug spam on a parse path, removed here.
648
+ * Matching/return behavior is unchanged.
649
+ */
650
+ /**
651
+ * Utility to format a hex string.
652
+ * Adds the # prefix if missing.
653
+ * @param hex - The user-provided hex color.
654
+ * @returns A properly formatted hex color string.
655
+ */
656
+ declare function formatHex(hex: string): string;
657
+ /**
658
+ * Utility to format an rgb string.
659
+ * Accepts various formats like "61, 135, 204" or "(61, 135, 204)".
660
+ * @param rgb - The user-provided rgb color.
661
+ * @returns A properly formatted rgb color string.
662
+ */
663
+ declare function formatRgbString(rgb: string): string;
664
+ /**
665
+ * Utility to format an RGB object.
666
+ * Accepts various object-like formats such as '{"r":61,"g":135,"b":204,"a":1}', '"r":61,"g":135,"b":204,"a":1', or 'r:61,g:135,b:204,a:1'.
667
+ * @param rgbObject - The user-provided RGB object-like string.
668
+ * @returns A properly formatted RGB object.
669
+ */
670
+ declare function formatRgbObject(rgbObject: string): string;
671
+ /**
672
+ * Utility to format an HSL object.
673
+ * Accepts various object-like formats such as '{"h":199,"s":89,"l":48,"a":1}' or 'h:199,s:89,l:48,a:1'.
674
+ * @param hslObject - The user-provided HSL object-like string.
675
+ * @returns A properly formatted HSL object.
676
+ */
677
+ declare function formatHslObject(hslObject: string): string;
678
+ /**
679
+ * Utility to format an HSL string.
680
+ * Accepts formats like "hsl(199, 89%, 48%)" or "199, 89%, 48%".
681
+ * @param hslString - The user-provided HSL string.
682
+ * @returns A properly formatted HSL string.
683
+ */
684
+ declare function formatHslString(hslString: string): string;
685
+ /**
686
+ * Utility to format an HSV percentage string.
687
+ * Accepts formats like "94% 29% 0%" or "(94% 29% 0%)".
688
+ * @param hsvString - The user-provided HSV percentage string.
689
+ * @returns A properly formatted HSV string.
690
+ */
691
+ declare function formatHsvString(hsvString: string): string;
692
+ /**
693
+ * Utility to format a regular CMYK string.
694
+ * Accepts formats like "94, 29, 0, 9" or "(94, 29, 0, 9)".
695
+ * @param cmykString - The user-provided CMYK string.
696
+ * @returns A properly formatted CMYK string.
697
+ */
698
+ declare function formatRegularCmykString(cmykString: string): string;
699
+ /**
700
+ * Utility to format a CMYK object.
701
+ * Accepts various object-like formats such as '{"c":94,"m":29,"y":0,"k":9,"a":1}' or 'c:94,m:29,y:0,k:9,a:1'.
702
+ * @param cmykObject - The user-provided CMYK object-like string.
703
+ * @returns A properly formatted CMYK object.
704
+ */
705
+ declare function formatCmykObject(cmykObject: string): string;
706
+ /**
707
+ * Utility to format a CMYK percentage string.
708
+ * Accepts formats like "94% 29% 0% 9%" or "(94% 29% 0% 9%)".
709
+ * @param cmykString - The user-provided CMYK percentage string.
710
+ * @returns A properly formatted CMYK string.
711
+ */
712
+ declare function formatCmykString(cmykString: string): string;
713
+ /**
714
+ * Utility to detect device-cmyk color strings ("device-cmyk(94% 29% 0% 9%)").
715
+ * @param cmykString - The user-provided CMYK string.
716
+ * @returns True when the string is a well-formed device-cmyk expression.
717
+ */
718
+ declare function isDeviceCmyk(cmykString: string): boolean;
719
+ /**
720
+ * Utility to format an HWB string.
721
+ * Accepts formats like "hwb(199 24% 20%)" or "hwb(199deg 24% 20%)".
722
+ * @param hwbString - The user-provided HWB string.
723
+ * @returns A properly formatted HWB string.
724
+ */
725
+ declare function formatHwbString(hwbString: string): string;
726
+ /**
727
+ * Utility to format a Lab string.
728
+ * Accepts formats like "lab(55.715 -14.02 -32.329)" or "55.715 -14.02 -32.329".
729
+ * @param labString - The user-provided Lab string.
730
+ * @returns A properly formatted Lab string.
731
+ */
732
+ declare function formatLabString(labString: string): string;
733
+ /**
734
+ * Utility to format an LCH string.
735
+ * Accepts formats like "lch(55.715 35.17 246.6)" or "55.715 35.17 246.6".
736
+ * @param lchString - The user-provided LCH string.
737
+ * @returns A properly formatted LCH string.
738
+ */
739
+ declare function formatLchString(lchString: string): string;
740
+ /**
741
+ * Utility to format a hex string with 0x prefix.
742
+ * Converts "0x3d87cc" to "#3d87cc".
743
+ * @param hex - The user-provided hex color with 0x prefix.
744
+ * @returns A properly formatted hex color string.
745
+ */
746
+ declare function formatHexWith0x(hex: string): string;
747
+
748
+ /**
749
+ * The "accept anything a human pastes" waterfall — ported from matrx-frontend
750
+ * `color-change-util.ts` `normalizeColorInput`, with the colord coupling
751
+ * inverted: the original gated several tiers on `colord(input).isValid()`;
752
+ * here the host injects that validity check (`createColorNormalizer({
753
+ * isValid })` — pass `(c) => colord(c).isValid()` or any equivalent). The
754
+ * tier ORDER, the tiers that skip validation, and the returned
755
+ * `{ value, type }` shapes are verbatim.
756
+ *
757
+ * Deliberate divergence (same class as formats.ts): the original logged every
758
+ * tier hit/miss with unconditional `console.log`s — removed.
759
+ */
760
+ interface NormalizedColor {
761
+ value: string;
762
+ type: string;
763
+ }
764
+ interface ColorNormalizerOptions {
765
+ /**
766
+ * "Can this string be parsed as a color?" — the host's color engine
767
+ * (e.g. `(c) => colord(c).isValid()`).
768
+ */
769
+ isValid: (color: string) => boolean;
770
+ }
771
+ /**
772
+ * Builds `normalizeColorInput`: tries the format conversions in the original
773
+ * fixed order until one produces a valid (or structurally well-formed) color,
774
+ * returning `{ value, type }`, or `null` when nothing fits.
775
+ */
776
+ declare function createColorNormalizer({ isValid }: ColorNormalizerOptions): (colorInput: string) => NormalizedColor | null;
777
+
778
+ /**
779
+ * The zero-dependency perceptual-distance engine behind the string input path
780
+ * of `findNearestTailwindColor`.
781
+ *
782
+ * The matrx-frontend original computed distance through colord's lab plugin
783
+ * (`colordInstance.delta(hex)`). This module reproduces that plugin's exact
784
+ * pipeline — sRGB → XYZ(D65) → chromatic adaptation to D50 (colord's
785
+ * matrices, including its channel clamps) → CIE L*a*b* rounded to 2 decimals
786
+ * → CIEDE2000 (colord >= 2.10 formulation) ÷ 100, rounded to 3 decimals and
787
+ * clamped to [0, 1] — so the nearest-token answer is bit-identical to the
788
+ * colord path. The test suite cross-checks this against colord itself (a
789
+ * devDependency only).
790
+ */
791
+ interface Rgb {
792
+ r: number;
793
+ g: number;
794
+ b: number;
795
+ }
796
+ interface Lab {
797
+ l: number;
798
+ a: number;
799
+ b: number;
800
+ }
801
+ /** RGB → CIE L*a*b*, rounded to 2 decimals exactly like colord's `toLab`. */
802
+ declare function rgbToLab(rgb: Rgb): Lab;
803
+ /**
804
+ * Perceptual distance between two RGB colors, normalized exactly like
805
+ * colord's `.delta()`: CIEDE2000 / 100, rounded to 3 decimals, clamped [0,1].
806
+ */
807
+ declare function rgbDelta(rgb1: Rgb, rgb2: Rgb): number;
808
+ /**
809
+ * Parses the two input shapes this unit is chartered for — hex (`#rgb`,
810
+ * `#rrggbb`, with or without `#`, alpha digits tolerated and ignored) and
811
+ * `rgb()`/`rgba()` strings — into RGB channels. Returns `null` for anything
812
+ * else; this is deliberately NOT a general CSS color parser.
813
+ */
814
+ declare function parseHexOrRgb(input: string): Rgb | null;
815
+
816
+ /**
817
+ * @ai-matrx/kit/react-tree — safe React children-tree scanning.
818
+ *
819
+ * Ported from matrx-frontend `lib/react/treeContainsComponent.ts`; this
820
+ * subpath is the ONE home of the scanner inside the kit (the `/confirm`
821
+ * subpath's alert-dialog imports it from here — no duplicate bodies).
822
+ *
823
+ * Two deliberate divergences from the frontend original, both documented:
824
+ * - the dev-mode scream checks `typeof process` first, since this package may
825
+ * load in an unbundled browser context where `process` is undefined;
826
+ * - a React PORTAL child (`createPortal(...)` passed as a child) is a valid
827
+ * React child, but it is not an element, an iterable, or a primitive — the
828
+ * original fell through to the non-renderable branch, screamed a false
829
+ * positive in dev, and skipped the portal's content. Portals are now
830
+ * recognized and their children traversed.
831
+ */
832
+
833
+ /**
834
+ * Returns true if `Component` appears anywhere in the React element tree under
835
+ * `node`. Used to detect optional a11y children (e.g. DialogDescription)
836
+ * without rendering duplicates.
837
+ *
838
+ * DEFENSIVE + LOUD. A non-renderable node — a plain object or function passed
839
+ * as a React child — is a real bug: React throws "Objects are not valid as a
840
+ * React child" the instant it renders one. This a11y probe must NOT be the
841
+ * crash site. `React.Children.toArray` would throw HERE, producing a trace that
842
+ * points at the dialog primitive instead of the component that leaked the
843
+ * object (this misdirection has burned real debugging hours). So we walk the
844
+ * tree by hand, SKIP any non-renderable node, and scream in dev with its keys —
845
+ * then let React report the defect at the true render site with the offending
846
+ * component in the stack. For every VALID tree the result is identical to the
847
+ * old `React.Children.toArray(node).some(...)`.
848
+ */
849
+ declare function treeContainsComponent(node: React.ReactNode, Component: React.ElementType): boolean;
850
+
851
+ /**
852
+ * @ai-matrx/kit/qr — THE QR-code decoder (client-side, in memory). Ported
853
+ * verbatim from matrx-frontend `lib/qr/decode.ts`.
854
+ *
855
+ * One primitive, three inputs (a File/Blob, an `ImageData` frame, a
856
+ * `<video>`/`<canvas>` element), one answer: the text the QR encodes, or
857
+ * `null` when no code is present. Nothing here uploads, stores, or persists
858
+ * anything — the bytes live in a canvas for the length of one call.
859
+ *
860
+ * Engine order:
861
+ * 1. `BarcodeDetector` — native, fast, handles rotation and poor contrast.
862
+ * 2. `jsqr` — pure-JS fallback (lazily imported, so it only enters the
863
+ * bundle of a surface that actually decodes), for Safari/Firefox where
864
+ * the native detector does not exist.
865
+ *
866
+ * Runtime dependency of this subpath (and only when the fallback fires):
867
+ * `jsqr` — a browser without `BarcodeDetector` decoding a pasted screenshot
868
+ * IS the capability, not an optional extra; without the fallback the unit
869
+ * silently does nothing on Safari/Firefox.
870
+ *
871
+ * Browser capability: decoding needs a DOM (`document`, canvas 2D,
872
+ * `createImageBitmap`) at call time; importing is inert and SSR-safe, and
873
+ * `hasNativeQrDetector()` is safe to call anywhere (it only probes
874
+ * `globalThis`).
875
+ *
876
+ * 🚨 Reach for THIS, never a second decoder. If a surface needs a new input
877
+ * shape, add an adapter here.
878
+ */
879
+ /** True when the browser can decode without downloading the JS fallback. */
880
+ declare function hasNativeQrDetector(): boolean;
881
+ /** Decode a QR code out of an already-rasterised frame. */
882
+ declare function decodeQrFromImageData(frame: ImageData): Promise<string | null>;
883
+ /**
884
+ * Decode a QR code out of a live `<video>` (a camera preview) or a `<canvas>`.
885
+ * Returns `null` when the current frame holds no code — call it on a tick.
886
+ */
887
+ declare function decodeQrFromElement(element: HTMLVideoElement | HTMLCanvasElement): Promise<string | null>;
888
+ /**
889
+ * Decode a QR code out of an image File/Blob — a pasted screenshot, a dropped
890
+ * PNG, a photo from the OS camera sheet.
891
+ *
892
+ * Resolves `null` when the image holds no QR code. Throws only when the file
893
+ * is not decodable as an image at all.
894
+ */
895
+ declare function decodeQrFromImageFile(file: Blob): Promise<string | null>;
896
+
897
+ export { type AsyncResult, type ColorDelta, type ColorNormalizerOptions, type ConcurrencyFailure, type ConcurrencyResult, DBStoreManager, DEFAULT_JSON_INDENT, DEFAULT_JSON_WIDTH, DEFAULT_WORD_REPLACEMENTS, type DelimiterCaptureInput, type DelimiterGuardResult, type DelimiterReportContext, type DelimiterViolation, type DelimiterViolationReason, FeatureStore, type InvalidationCallback, type JsonArray, type JsonDetection, type JsonFence, type JsonFenceMode, type JsonFormatOptions, type JsonFormatResult, type JsonFormatStyle, type JsonObject, type JsonParser, type JsonPrimitive, type JsonRootKind, type JsonTextSize, type JsonValue, type NormalizedColor, PublicStoreManager, type ReplacementMap, type Rgb, type SearchFieldConfig, type SearchFieldWeight, type TailwindColorGroup, type TextCaseOption, type TextFormatterOptions, _resetIdbStoreSingletons, computeSearchScore, createColorNormalizer, createFormatter, decodeQrFromElement, decodeQrFromImageData, decodeQrFromImageFile, detectJson, filterAndSortBySearch, findNearestTailwindColor, fireInvalidation, formatCmykObject, formatCmykString, formatHex, formatHexWith0x, formatHslObject, formatHslString, formatHsvString, formatHwbString, formatJsonText, formatLabString, formatLchString, formatLowerCase, formatNormalCase, formatRegularCmykString, formatRgbObject, formatRgbString, formatSentenceCase, formatTailwindColor, formatText, formatTitleCase, formatUpperCase, formatWithoutReplacements, getColorFromTailwind, getIdbStoreSingleton, guardMarkdownDelimiters, guardMathDelimiters, guardRunawayLinks, hasNativeQrDetector, idMatchesQuery, isDeviceCmyk, isJsonArray, isJsonObject, isJsonPrimitive, matchesSearch, parseHexOrRgb, registerInvalidationCallback, reportDelimiterViolations, rgbDelta, rgbToLab, runWithConcurrency, stringifyJson, tailwindColors, treeContainsComponent };