@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.
- package/CHANGELOG.md +132 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/autosave.cjs +92 -0
- package/dist/autosave.cjs.map +1 -0
- package/dist/autosave.d.cts +17 -0
- package/dist/autosave.d.ts +17 -0
- package/dist/autosave.js +72 -0
- package/dist/autosave.js.map +1 -0
- package/dist/clipboard.cjs +152 -0
- package/dist/clipboard.cjs.map +1 -0
- package/dist/clipboard.d.cts +17 -0
- package/dist/clipboard.d.ts +17 -0
- package/dist/clipboard.js +132 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/color-util.cjs +790 -0
- package/dist/color-util.cjs.map +1 -0
- package/dist/color-util.d.cts +235 -0
- package/dist/color-util.d.ts +235 -0
- package/dist/color-util.js +767 -0
- package/dist/color-util.js.map +1 -0
- package/dist/concurrency.cjs +59 -0
- package/dist/concurrency.cjs.map +1 -0
- package/dist/concurrency.d.cts +19 -0
- package/dist/concurrency.d.ts +19 -0
- package/dist/concurrency.js +38 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/confirm.cjs +344 -0
- package/dist/confirm.cjs.map +1 -0
- package/dist/confirm.d.cts +132 -0
- package/dist/confirm.d.ts +132 -0
- package/dist/confirm.js +312 -0
- package/dist/confirm.js.map +1 -0
- package/dist/delimiter-guard.cjs +172 -0
- package/dist/delimiter-guard.cjs.map +1 -0
- package/dist/delimiter-guard.d.cts +119 -0
- package/dist/delimiter-guard.d.ts +119 -0
- package/dist/delimiter-guard.js +151 -0
- package/dist/delimiter-guard.js.map +1 -0
- package/dist/drafts.cjs +223 -0
- package/dist/drafts.cjs.map +1 -0
- package/dist/drafts.d.cts +62 -0
- package/dist/drafts.d.ts +62 -0
- package/dist/drafts.js +201 -0
- package/dist/drafts.js.map +1 -0
- package/dist/idb-store.cjs +178 -0
- package/dist/idb-store.cjs.map +1 -0
- package/dist/idb-store.d.cts +111 -0
- package/dist/idb-store.d.ts +111 -0
- package/dist/idb-store.js +155 -0
- package/dist/idb-store.js.map +1 -0
- package/dist/idle-scheduler.cjs +268 -0
- package/dist/idle-scheduler.cjs.map +1 -0
- package/dist/idle-scheduler.d.cts +185 -0
- package/dist/idle-scheduler.d.ts +185 -0
- package/dist/idle-scheduler.js +246 -0
- package/dist/idle-scheduler.js.map +1 -0
- package/dist/index.cjs +3113 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +897 -0
- package/dist/index.d.ts +897 -0
- package/dist/index.js +3088 -0
- package/dist/index.js.map +1 -0
- package/dist/invalidation.cjs +64 -0
- package/dist/invalidation.cjs.map +1 -0
- package/dist/invalidation.d.cts +52 -0
- package/dist/invalidation.d.ts +52 -0
- package/dist/invalidation.js +43 -0
- package/dist/invalidation.js.map +1 -0
- package/dist/json-format.cjs +319 -0
- package/dist/json-format.cjs.map +1 -0
- package/dist/json-format.d.cts +148 -0
- package/dist/json-format.d.ts +148 -0
- package/dist/json-format.js +286 -0
- package/dist/json-format.js.map +1 -0
- package/dist/latest-request.cjs +36 -0
- package/dist/latest-request.cjs.map +1 -0
- package/dist/latest-request.d.cts +29 -0
- package/dist/latest-request.d.ts +29 -0
- package/dist/latest-request.js +16 -0
- package/dist/latest-request.js.map +1 -0
- package/dist/qr.cjs +109 -0
- package/dist/qr.cjs.map +1 -0
- package/dist/qr.d.cts +47 -0
- package/dist/qr.d.ts +47 -0
- package/dist/qr.js +78 -0
- package/dist/qr.js.map +1 -0
- package/dist/react-tree.cjs +69 -0
- package/dist/react-tree.cjs.map +1 -0
- package/dist/react-tree.d.cts +38 -0
- package/dist/react-tree.d.ts +38 -0
- package/dist/react-tree.js +38 -0
- package/dist/react-tree.js.map +1 -0
- package/dist/search-scoring.cjs +116 -0
- package/dist/search-scoring.cjs.map +1 -0
- package/dist/search-scoring.d.cts +77 -0
- package/dist/search-scoring.d.ts +77 -0
- package/dist/search-scoring.js +95 -0
- package/dist/search-scoring.js.map +1 -0
- package/dist/text-case.cjs +313 -0
- package/dist/text-case.cjs.map +1 -0
- package/dist/text-case.d.cts +53 -0
- package/dist/text-case.d.ts +53 -0
- package/dist/text-case.js +292 -0
- package/dist/text-case.js.map +1 -0
- package/dist/toast.cjs +74 -0
- package/dist/toast.cjs.map +1 -0
- package/dist/toast.d.cts +92 -0
- package/dist/toast.d.ts +92 -0
- package/dist/toast.js +54 -0
- package/dist/toast.js.map +1 -0
- package/dist/url-state.cjs +212 -0
- package/dist/url-state.cjs.map +1 -0
- package/dist/url-state.d.cts +90 -0
- package/dist/url-state.d.ts +90 -0
- package/dist/url-state.js +199 -0
- package/dist/url-state.js.map +1 -0
- package/package.json +286 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
export { AutosaveStatus, UseAutosaveResult, useAutosave } from './autosave.js';
|
|
2
|
+
export { BeginRequest, useLatestRequest } from './latest-request.js';
|
|
3
|
+
export { ClipboardNotifyKind, UseClipboardOptions, UseClipboardResult, useClipboard } from './clipboard.js';
|
|
4
|
+
export { DraftSource, LocalDraft, LocalDraftInput, captureDrafts, discardDraft, getDraft, getDraftsVersion, listDrafts, registerDraftSource, subscribeDrafts, useDurableDraft } from './drafts.js';
|
|
5
|
+
export { ConfirmDialog, ConfirmDialogHost, ConfirmDialogProps, ConfirmOptions, confirm } from './confirm.js';
|
|
6
|
+
export { CapturedToastInfo, CreateMatrxToastOptions, MatrxToast, SonnerLikeToast, ToastData, ToastMessage, createMatrxToast } from './toast.js';
|
|
7
|
+
export { FlushState, IdlePriority, IdleRegistration, UnregisterFn, getSchedulerState, onFlushComplete, registerIdleTask, resetScheduler, useIdleGate, useIdleReady, useIdleRegister, useIdleTask, whenPageIdle } from './idle-scheduler.js';
|
|
8
|
+
export { MirroredUrlStateOptions, SetUrlStateOptions, UrlHistoryMode, UrlStateCodec, UrlStateRouter, booleanUrlCodec, commitUrlParams, enumUrlCodec, historyModeForParamChange, jsonUrlCodec, positiveIntegerUrlCodec, setUrlStateRouter, stringUrlCodec, useMirroredUrlState, useUrlSearchParams, useUrlState } from './url-state.js';
|
|
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 (`$$`, `[`) 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 };
|