@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.cjs
ADDED
|
@@ -0,0 +1,3113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var src_exports = {};
|
|
33
|
+
__export(src_exports, {
|
|
34
|
+
ConfirmDialog: () => ConfirmDialog,
|
|
35
|
+
ConfirmDialogHost: () => ConfirmDialogHost,
|
|
36
|
+
DBStoreManager: () => DBStoreManager,
|
|
37
|
+
DEFAULT_JSON_INDENT: () => DEFAULT_JSON_INDENT,
|
|
38
|
+
DEFAULT_JSON_WIDTH: () => DEFAULT_JSON_WIDTH,
|
|
39
|
+
DEFAULT_WORD_REPLACEMENTS: () => DEFAULT_WORD_REPLACEMENTS,
|
|
40
|
+
FeatureStore: () => FeatureStore,
|
|
41
|
+
PublicStoreManager: () => PublicStoreManager,
|
|
42
|
+
_resetIdbStoreSingletons: () => _resetIdbStoreSingletons,
|
|
43
|
+
booleanUrlCodec: () => booleanUrlCodec,
|
|
44
|
+
captureDrafts: () => captureDrafts,
|
|
45
|
+
commitUrlParams: () => commitUrlParams,
|
|
46
|
+
computeSearchScore: () => computeSearchScore,
|
|
47
|
+
confirm: () => confirm,
|
|
48
|
+
createColorNormalizer: () => createColorNormalizer,
|
|
49
|
+
createFormatter: () => createFormatter,
|
|
50
|
+
createMatrxToast: () => createMatrxToast,
|
|
51
|
+
decodeQrFromElement: () => decodeQrFromElement,
|
|
52
|
+
decodeQrFromImageData: () => decodeQrFromImageData,
|
|
53
|
+
decodeQrFromImageFile: () => decodeQrFromImageFile,
|
|
54
|
+
detectJson: () => detectJson,
|
|
55
|
+
discardDraft: () => discardDraft,
|
|
56
|
+
enumUrlCodec: () => enumUrlCodec,
|
|
57
|
+
filterAndSortBySearch: () => filterAndSortBySearch,
|
|
58
|
+
findNearestTailwindColor: () => findNearestTailwindColor,
|
|
59
|
+
fireInvalidation: () => fireInvalidation,
|
|
60
|
+
formatCmykObject: () => formatCmykObject,
|
|
61
|
+
formatCmykString: () => formatCmykString,
|
|
62
|
+
formatHex: () => formatHex,
|
|
63
|
+
formatHexWith0x: () => formatHexWith0x,
|
|
64
|
+
formatHslObject: () => formatHslObject,
|
|
65
|
+
formatHslString: () => formatHslString,
|
|
66
|
+
formatHsvString: () => formatHsvString,
|
|
67
|
+
formatHwbString: () => formatHwbString,
|
|
68
|
+
formatJsonText: () => formatJsonText,
|
|
69
|
+
formatLabString: () => formatLabString,
|
|
70
|
+
formatLchString: () => formatLchString,
|
|
71
|
+
formatLowerCase: () => formatLowerCase,
|
|
72
|
+
formatNormalCase: () => formatNormalCase,
|
|
73
|
+
formatRegularCmykString: () => formatRegularCmykString,
|
|
74
|
+
formatRgbObject: () => formatRgbObject,
|
|
75
|
+
formatRgbString: () => formatRgbString,
|
|
76
|
+
formatSentenceCase: () => formatSentenceCase,
|
|
77
|
+
formatTailwindColor: () => formatTailwindColor,
|
|
78
|
+
formatText: () => formatText,
|
|
79
|
+
formatTitleCase: () => formatTitleCase,
|
|
80
|
+
formatUpperCase: () => formatUpperCase,
|
|
81
|
+
formatWithoutReplacements: () => formatWithoutReplacements,
|
|
82
|
+
getColorFromTailwind: () => getColorFromTailwind,
|
|
83
|
+
getDraft: () => getDraft,
|
|
84
|
+
getDraftsVersion: () => getDraftsVersion,
|
|
85
|
+
getIdbStoreSingleton: () => getIdbStoreSingleton,
|
|
86
|
+
getSchedulerState: () => getSchedulerState,
|
|
87
|
+
guardMarkdownDelimiters: () => guardMarkdownDelimiters,
|
|
88
|
+
guardMathDelimiters: () => guardMathDelimiters,
|
|
89
|
+
guardRunawayLinks: () => guardRunawayLinks,
|
|
90
|
+
hasNativeQrDetector: () => hasNativeQrDetector,
|
|
91
|
+
historyModeForParamChange: () => historyModeForParamChange,
|
|
92
|
+
idMatchesQuery: () => idMatchesQuery,
|
|
93
|
+
isDeviceCmyk: () => isDeviceCmyk,
|
|
94
|
+
isJsonArray: () => isJsonArray,
|
|
95
|
+
isJsonObject: () => isJsonObject,
|
|
96
|
+
isJsonPrimitive: () => isJsonPrimitive,
|
|
97
|
+
jsonUrlCodec: () => jsonUrlCodec,
|
|
98
|
+
listDrafts: () => listDrafts,
|
|
99
|
+
matchesSearch: () => matchesSearch,
|
|
100
|
+
onFlushComplete: () => onFlushComplete,
|
|
101
|
+
parseHexOrRgb: () => parseHexOrRgb,
|
|
102
|
+
positiveIntegerUrlCodec: () => positiveIntegerUrlCodec,
|
|
103
|
+
registerDraftSource: () => registerDraftSource,
|
|
104
|
+
registerIdleTask: () => registerIdleTask,
|
|
105
|
+
registerInvalidationCallback: () => registerInvalidationCallback,
|
|
106
|
+
reportDelimiterViolations: () => reportDelimiterViolations,
|
|
107
|
+
resetScheduler: () => resetScheduler,
|
|
108
|
+
rgbDelta: () => rgbDelta,
|
|
109
|
+
rgbToLab: () => rgbToLab,
|
|
110
|
+
runWithConcurrency: () => runWithConcurrency,
|
|
111
|
+
setUrlStateRouter: () => setUrlStateRouter,
|
|
112
|
+
stringUrlCodec: () => stringUrlCodec,
|
|
113
|
+
stringifyJson: () => stringifyJson,
|
|
114
|
+
subscribeDrafts: () => subscribeDrafts,
|
|
115
|
+
tailwindColors: () => tailwindColors,
|
|
116
|
+
treeContainsComponent: () => treeContainsComponent,
|
|
117
|
+
useAutosave: () => useAutosave,
|
|
118
|
+
useClipboard: () => useClipboard,
|
|
119
|
+
useDurableDraft: () => useDurableDraft,
|
|
120
|
+
useIdleGate: () => useIdleGate,
|
|
121
|
+
useIdleReady: () => useIdleReady,
|
|
122
|
+
useIdleRegister: () => useIdleRegister,
|
|
123
|
+
useIdleTask: () => useIdleTask,
|
|
124
|
+
useLatestRequest: () => useLatestRequest,
|
|
125
|
+
useMirroredUrlState: () => useMirroredUrlState,
|
|
126
|
+
useUrlSearchParams: () => useUrlSearchParams,
|
|
127
|
+
useUrlState: () => useUrlState,
|
|
128
|
+
whenPageIdle: () => whenPageIdle
|
|
129
|
+
});
|
|
130
|
+
module.exports = __toCommonJS(src_exports);
|
|
131
|
+
|
|
132
|
+
// src/autosave.ts
|
|
133
|
+
var import_react = require("react");
|
|
134
|
+
function useAutosave(opts) {
|
|
135
|
+
const { save, debounceMs = 900 } = opts;
|
|
136
|
+
const [status, setStatus] = (0, import_react.useState)("idle");
|
|
137
|
+
const [lastSavedAt, setLastSavedAt] = (0, import_react.useState)(null);
|
|
138
|
+
const pendingRef = (0, import_react.useRef)(null);
|
|
139
|
+
const timerRef = (0, import_react.useRef)(null);
|
|
140
|
+
const savingRef = (0, import_react.useRef)(false);
|
|
141
|
+
const saveRef = (0, import_react.useRef)(save);
|
|
142
|
+
(0, import_react.useEffect)(() => {
|
|
143
|
+
saveRef.current = save;
|
|
144
|
+
});
|
|
145
|
+
async function saveNow() {
|
|
146
|
+
if (savingRef.current) return;
|
|
147
|
+
const pending = pendingRef.current;
|
|
148
|
+
if (!pending) return;
|
|
149
|
+
pendingRef.current = null;
|
|
150
|
+
savingRef.current = true;
|
|
151
|
+
setStatus("saving");
|
|
152
|
+
let failed = false;
|
|
153
|
+
try {
|
|
154
|
+
const res = await saveRef.current(pending.value);
|
|
155
|
+
if (res.error) {
|
|
156
|
+
failed = true;
|
|
157
|
+
pendingRef.current = pendingRef.current ?? pending;
|
|
158
|
+
setStatus("error");
|
|
159
|
+
} else {
|
|
160
|
+
setLastSavedAt(/* @__PURE__ */ new Date());
|
|
161
|
+
setStatus(pendingRef.current ? "unsaved" : "saved");
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
failed = true;
|
|
165
|
+
pendingRef.current = pendingRef.current ?? pending;
|
|
166
|
+
setStatus("error");
|
|
167
|
+
} finally {
|
|
168
|
+
savingRef.current = false;
|
|
169
|
+
if (pendingRef.current && !failed) void saveNow();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function schedule(value) {
|
|
173
|
+
pendingRef.current = { value };
|
|
174
|
+
setStatus("unsaved");
|
|
175
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
176
|
+
timerRef.current = setTimeout(() => void saveNow(), debounceMs);
|
|
177
|
+
}
|
|
178
|
+
function flush2() {
|
|
179
|
+
if (timerRef.current) {
|
|
180
|
+
clearTimeout(timerRef.current);
|
|
181
|
+
timerRef.current = null;
|
|
182
|
+
}
|
|
183
|
+
void saveNow();
|
|
184
|
+
}
|
|
185
|
+
(0, import_react.useEffect)(() => {
|
|
186
|
+
return () => {
|
|
187
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
188
|
+
const pending = pendingRef.current;
|
|
189
|
+
if (pending && !savingRef.current) {
|
|
190
|
+
pendingRef.current = null;
|
|
191
|
+
void saveRef.current(pending.value);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}, []);
|
|
195
|
+
return { status, lastSavedAt, schedule, flush: flush2 };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/latest-request.ts
|
|
199
|
+
var import_react2 = require("react");
|
|
200
|
+
function useLatestRequest() {
|
|
201
|
+
const seqRef = (0, import_react2.useRef)(0);
|
|
202
|
+
return (0, import_react2.useCallback)(() => {
|
|
203
|
+
const mySeq = ++seqRef.current;
|
|
204
|
+
return () => mySeq === seqRef.current;
|
|
205
|
+
}, []);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/clipboard.ts
|
|
209
|
+
var import_react3 = require("react");
|
|
210
|
+
function toError(err, fallback) {
|
|
211
|
+
if (err instanceof Error) return err;
|
|
212
|
+
if (typeof err === "string" && err) return new Error(err);
|
|
213
|
+
return new Error(fallback);
|
|
214
|
+
}
|
|
215
|
+
function useClipboard(options = {}) {
|
|
216
|
+
const { notify } = options;
|
|
217
|
+
const [lastCopied, setLastCopied] = (0, import_react3.useState)(null);
|
|
218
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
219
|
+
const handleSuccess = (type, successMessage) => {
|
|
220
|
+
setLastCopied(type);
|
|
221
|
+
setError(null);
|
|
222
|
+
notify?.(
|
|
223
|
+
successMessage ?? `${type.charAt(0).toUpperCase() + type.slice(1)} copied to clipboard!`,
|
|
224
|
+
"success"
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
const handleError = (err, type) => {
|
|
228
|
+
const message = `Failed to copy ${type}`;
|
|
229
|
+
setError(toError(err, message));
|
|
230
|
+
console.error(message, err);
|
|
231
|
+
notify?.(message, "error");
|
|
232
|
+
};
|
|
233
|
+
const copyText = (0, import_react3.useCallback)(
|
|
234
|
+
async (text, successMessage) => {
|
|
235
|
+
try {
|
|
236
|
+
await navigator.clipboard.writeText(text);
|
|
237
|
+
handleSuccess("text", successMessage);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
handleError(err, "text");
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
[notify]
|
|
243
|
+
);
|
|
244
|
+
const copyImage = (0, import_react3.useCallback)(
|
|
245
|
+
async (imageSrc, successMessage) => {
|
|
246
|
+
try {
|
|
247
|
+
const response = await fetch(imageSrc);
|
|
248
|
+
if (!response.ok)
|
|
249
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
250
|
+
const blob = await response.blob();
|
|
251
|
+
const canvas = document.createElement("canvas");
|
|
252
|
+
const ctx = canvas.getContext("2d");
|
|
253
|
+
if (!ctx) throw new Error("Failed to get canvas context");
|
|
254
|
+
return new Promise((resolve, reject) => {
|
|
255
|
+
const img = new Image();
|
|
256
|
+
img.onload = () => {
|
|
257
|
+
canvas.width = img.width;
|
|
258
|
+
canvas.height = img.height;
|
|
259
|
+
ctx.drawImage(img, 0, 0);
|
|
260
|
+
canvas.toBlob((pngBlob) => {
|
|
261
|
+
if (pngBlob) {
|
|
262
|
+
const item = new ClipboardItem({ "image/png": pngBlob });
|
|
263
|
+
navigator.clipboard.write([item]).then(
|
|
264
|
+
() => {
|
|
265
|
+
handleSuccess("image", successMessage);
|
|
266
|
+
resolve();
|
|
267
|
+
},
|
|
268
|
+
(err) => reject(
|
|
269
|
+
toError(err, "Failed to write image to clipboard")
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
} else {
|
|
273
|
+
reject(new Error("Failed to convert image to PNG"));
|
|
274
|
+
}
|
|
275
|
+
}, "image/png");
|
|
276
|
+
};
|
|
277
|
+
img.onerror = () => reject(new Error("Failed to load image"));
|
|
278
|
+
img.src = URL.createObjectURL(blob);
|
|
279
|
+
});
|
|
280
|
+
} catch (err) {
|
|
281
|
+
handleError(err, "image");
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
[notify]
|
|
285
|
+
);
|
|
286
|
+
const copyLink = (0, import_react3.useCallback)(
|
|
287
|
+
async (link, stripParams = false, successMessage) => {
|
|
288
|
+
try {
|
|
289
|
+
let value = link;
|
|
290
|
+
if (stripParams) {
|
|
291
|
+
const url = new URL(link);
|
|
292
|
+
value = `${url.origin}${url.pathname}`;
|
|
293
|
+
}
|
|
294
|
+
await navigator.clipboard.writeText(value);
|
|
295
|
+
handleSuccess("link", successMessage);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
handleError(err, "link");
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
[notify]
|
|
301
|
+
);
|
|
302
|
+
const pasteText = (0, import_react3.useCallback)(async () => {
|
|
303
|
+
try {
|
|
304
|
+
const text = await navigator.clipboard.readText();
|
|
305
|
+
setError(null);
|
|
306
|
+
return text;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
setError(toError(err, "Failed to paste text"));
|
|
309
|
+
console.error("Failed to paste text: ", err);
|
|
310
|
+
return "";
|
|
311
|
+
}
|
|
312
|
+
}, []);
|
|
313
|
+
const pasteImage = (0, import_react3.useCallback)(async () => {
|
|
314
|
+
try {
|
|
315
|
+
if (!navigator.clipboard?.read) return null;
|
|
316
|
+
const items = await navigator.clipboard.read();
|
|
317
|
+
for (const item of items) {
|
|
318
|
+
const imageType = item.types.find((type) => type.startsWith("image/"));
|
|
319
|
+
if (imageType) {
|
|
320
|
+
const blob = await item.getType(imageType);
|
|
321
|
+
return new File([blob], "pasted-image.png", { type: imageType });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
} catch (err) {
|
|
326
|
+
setError(toError(err, "Failed to paste image"));
|
|
327
|
+
console.error("Failed to paste image: ", err);
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}, []);
|
|
331
|
+
return { copyText, copyImage, copyLink, pasteText, pasteImage, lastCopied, error };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/search-scoring.ts
|
|
335
|
+
var WEIGHT_TABLE = {
|
|
336
|
+
title: { exact: 1e4, startsWith: 5e3, includes: 2e3 },
|
|
337
|
+
subtitle: { exact: 2e3, startsWith: 1e3, includes: 500 },
|
|
338
|
+
body: { exact: 1e3, startsWith: 600, includes: 400 },
|
|
339
|
+
tag: { exact: 500, startsWith: 400, includes: 300 },
|
|
340
|
+
meta: { exact: 200, startsWith: 150, includes: 100 },
|
|
341
|
+
id: { exact: 100, startsWith: 75, includes: 50 }
|
|
342
|
+
};
|
|
343
|
+
var MIN_AUTO_ID_QUERY_LEN = 3;
|
|
344
|
+
function getStringId(item) {
|
|
345
|
+
if (item && typeof item === "object" && "id" in item) {
|
|
346
|
+
const id = item.id;
|
|
347
|
+
if (typeof id === "string" && id.length > 0) return id;
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
function resolveTiers(field) {
|
|
352
|
+
if (field.weight === "custom" || field.exact != null) {
|
|
353
|
+
return {
|
|
354
|
+
exact: field.exact ?? 0,
|
|
355
|
+
startsWith: field.startsWith ?? 0,
|
|
356
|
+
includes: field.includes ?? 0
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return WEIGHT_TABLE[field.weight ?? "body"];
|
|
360
|
+
}
|
|
361
|
+
function scoreValue(value, q, tiers) {
|
|
362
|
+
if (!value) return 0;
|
|
363
|
+
const v = value.toLowerCase();
|
|
364
|
+
if (v === q) return tiers.exact;
|
|
365
|
+
if (v.startsWith(q)) return tiers.startsWith;
|
|
366
|
+
if (v.includes(q)) return tiers.includes;
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
function computeSearchScore(item, query, fields) {
|
|
370
|
+
const trimmed = query.trim();
|
|
371
|
+
if (!trimmed) return 0;
|
|
372
|
+
const q = trimmed.toLowerCase();
|
|
373
|
+
let total = 0;
|
|
374
|
+
fields.forEach((field, idx) => {
|
|
375
|
+
const raw = field.get(item);
|
|
376
|
+
if (raw == null) return;
|
|
377
|
+
const tiers = resolveTiers(field);
|
|
378
|
+
let best = 0;
|
|
379
|
+
if (Array.isArray(raw)) {
|
|
380
|
+
for (const v of raw) {
|
|
381
|
+
if (typeof v !== "string") continue;
|
|
382
|
+
const s = scoreValue(v, q, tiers);
|
|
383
|
+
if (s > best) best = s;
|
|
384
|
+
}
|
|
385
|
+
} else if (typeof raw === "string") {
|
|
386
|
+
best = scoreValue(raw, q, tiers);
|
|
387
|
+
}
|
|
388
|
+
if (best > 0) {
|
|
389
|
+
total += best + (fields.length - idx);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
const hasExplicitId = fields.some((f) => f.weight === "id");
|
|
393
|
+
if (!hasExplicitId && q.length >= MIN_AUTO_ID_QUERY_LEN) {
|
|
394
|
+
const id = getStringId(item);
|
|
395
|
+
if (id) {
|
|
396
|
+
total += scoreValue(id, q, WEIGHT_TABLE.id);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return total;
|
|
400
|
+
}
|
|
401
|
+
function matchesSearch(item, query, fields) {
|
|
402
|
+
return computeSearchScore(item, query, fields) > 0;
|
|
403
|
+
}
|
|
404
|
+
function idMatchesQuery(item, query) {
|
|
405
|
+
const q = query.trim().toLowerCase();
|
|
406
|
+
if (q.length < MIN_AUTO_ID_QUERY_LEN) return false;
|
|
407
|
+
const id = getStringId(item);
|
|
408
|
+
return id != null && id.toLowerCase().includes(q);
|
|
409
|
+
}
|
|
410
|
+
function filterAndSortBySearch(items, query, fields) {
|
|
411
|
+
const trimmed = query.trim();
|
|
412
|
+
if (!trimmed) return items.slice();
|
|
413
|
+
const scored = [];
|
|
414
|
+
for (let i = 0; i < items.length; i++) {
|
|
415
|
+
const item = items[i];
|
|
416
|
+
const score = computeSearchScore(item, trimmed, fields);
|
|
417
|
+
if (score > 0) scored.push({ item, score, idx: i });
|
|
418
|
+
}
|
|
419
|
+
scored.sort((a, b) => b.score - a.score || a.idx - b.idx);
|
|
420
|
+
return scored.map((s) => s.item);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// src/concurrency.ts
|
|
424
|
+
async function runWithConcurrency(items, limit, worker, shouldStart = () => true) {
|
|
425
|
+
if (items.length === 0) {
|
|
426
|
+
return { started: 0, succeeded: 0, failed: 0, failures: [] };
|
|
427
|
+
}
|
|
428
|
+
let cursor = 0;
|
|
429
|
+
let started = 0;
|
|
430
|
+
let succeeded = 0;
|
|
431
|
+
const failures = [];
|
|
432
|
+
const requestedLimit = Number.isFinite(limit) ? Math.floor(limit) : 1;
|
|
433
|
+
const workerCount = Math.max(1, Math.min(requestedLimit, items.length));
|
|
434
|
+
const runners = Array.from({ length: workerCount }, async () => {
|
|
435
|
+
while (shouldStart()) {
|
|
436
|
+
const index = cursor++;
|
|
437
|
+
if (index >= items.length) return;
|
|
438
|
+
started += 1;
|
|
439
|
+
const item = items[index];
|
|
440
|
+
try {
|
|
441
|
+
await worker(item, index);
|
|
442
|
+
succeeded += 1;
|
|
443
|
+
} catch (error) {
|
|
444
|
+
failures.push({ item, index, error });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
await Promise.all(runners);
|
|
449
|
+
failures.sort((left, right) => left.index - right.index);
|
|
450
|
+
return {
|
|
451
|
+
started,
|
|
452
|
+
succeeded,
|
|
453
|
+
failed: failures.length,
|
|
454
|
+
failures
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/text-case.ts
|
|
459
|
+
var DEFAULT_WORD_REPLACEMENTS = {
|
|
460
|
+
// Acronyms & initialisms
|
|
461
|
+
"api": "API",
|
|
462
|
+
"apis": "APIs",
|
|
463
|
+
"ui": "UI",
|
|
464
|
+
"ux": "UX",
|
|
465
|
+
"id": "ID",
|
|
466
|
+
"ids": "IDs",
|
|
467
|
+
"qr": "QR",
|
|
468
|
+
"ssr": "SSR",
|
|
469
|
+
"csr": "CSR",
|
|
470
|
+
"ssg": "SSG",
|
|
471
|
+
"isr": "ISR",
|
|
472
|
+
"spa": "SPA",
|
|
473
|
+
"pwa": "PWA",
|
|
474
|
+
"sdk": "SDK",
|
|
475
|
+
"sdks": "SDKs",
|
|
476
|
+
"cli": "CLI",
|
|
477
|
+
"tty": "TTY",
|
|
478
|
+
"repl": "REPL",
|
|
479
|
+
"ci": "CI",
|
|
480
|
+
"cd": "CD",
|
|
481
|
+
"cpu": "CPU",
|
|
482
|
+
"cpus": "CPUs",
|
|
483
|
+
"gpu": "GPU",
|
|
484
|
+
"gpus": "GPUs",
|
|
485
|
+
"ram": "RAM",
|
|
486
|
+
"rom": "ROM",
|
|
487
|
+
"ssd": "SSD",
|
|
488
|
+
"ssds": "SSDs",
|
|
489
|
+
"hdd": "HDD",
|
|
490
|
+
"hdds": "HDDs",
|
|
491
|
+
"kpi": "KPI",
|
|
492
|
+
"kpis": "KPIs",
|
|
493
|
+
"sla": "SLA",
|
|
494
|
+
"slas": "SLAs",
|
|
495
|
+
"slo": "SLO",
|
|
496
|
+
"slos": "SLOs",
|
|
497
|
+
"sli": "SLI",
|
|
498
|
+
"slis": "SLIs",
|
|
499
|
+
"dom": "DOM",
|
|
500
|
+
// Web, formats, protocols
|
|
501
|
+
"url": "URL",
|
|
502
|
+
"urls": "URLs",
|
|
503
|
+
"uri": "URI",
|
|
504
|
+
"uris": "URIs",
|
|
505
|
+
"http": "HTTP",
|
|
506
|
+
"https": "HTTPS",
|
|
507
|
+
"html": "HTML",
|
|
508
|
+
"css": "CSS",
|
|
509
|
+
"json": "JSON",
|
|
510
|
+
"yaml": "YAML",
|
|
511
|
+
"yml": "YML",
|
|
512
|
+
"toml": "TOML",
|
|
513
|
+
"csv": "CSV",
|
|
514
|
+
"pdf": "PDF",
|
|
515
|
+
"tsv": "TSV",
|
|
516
|
+
"jpg": "JPG",
|
|
517
|
+
"jpeg": "JPEG",
|
|
518
|
+
"png": "PNG",
|
|
519
|
+
"gif": "GIF",
|
|
520
|
+
"webp": "WebP",
|
|
521
|
+
"heic": "HEIC",
|
|
522
|
+
"heif": "HEIF",
|
|
523
|
+
"bmp": "BMP",
|
|
524
|
+
"tiff": "TIFF",
|
|
525
|
+
"ico": "ICO",
|
|
526
|
+
"xml": "XML",
|
|
527
|
+
"sql": "SQL",
|
|
528
|
+
"db": "DB",
|
|
529
|
+
"dbs": "DBs",
|
|
530
|
+
"nosql": "NoSQL",
|
|
531
|
+
"graphql": "GraphQL",
|
|
532
|
+
"grpc": "gRPC",
|
|
533
|
+
"rest": "REST",
|
|
534
|
+
"restful": "RESTful",
|
|
535
|
+
"websocket": "WebSocket",
|
|
536
|
+
"websockets": "WebSockets",
|
|
537
|
+
"webrtc": "WebRTC",
|
|
538
|
+
// Networking
|
|
539
|
+
"ip": "IP",
|
|
540
|
+
"ipv4": "IPv4",
|
|
541
|
+
"ipv6": "IPv6",
|
|
542
|
+
"dns": "DNS",
|
|
543
|
+
"dhcp": "DHCP",
|
|
544
|
+
"nat": "NAT",
|
|
545
|
+
"tcp": "TCP",
|
|
546
|
+
"udp": "UDP",
|
|
547
|
+
"icmp": "ICMP",
|
|
548
|
+
"ttl": "TTL",
|
|
549
|
+
"lan": "LAN",
|
|
550
|
+
"wan": "WAN",
|
|
551
|
+
"vlan": "VLAN",
|
|
552
|
+
"cdn": "CDN",
|
|
553
|
+
"ftp": "FTP",
|
|
554
|
+
"ssh": "SSH",
|
|
555
|
+
"tls": "TLS",
|
|
556
|
+
"ssl": "SSL",
|
|
557
|
+
// Security & crypto
|
|
558
|
+
"jwt": "JWT",
|
|
559
|
+
"jws": "JWS",
|
|
560
|
+
"jwe": "JWE",
|
|
561
|
+
"hmac": "HMAC",
|
|
562
|
+
"rsa": "RSA",
|
|
563
|
+
"ecdsa": "ECDSA",
|
|
564
|
+
"aes": "AES",
|
|
565
|
+
"pbkdf2": "PBKDF2",
|
|
566
|
+
"argon2": "Argon2",
|
|
567
|
+
"scrypt": "scrypt",
|
|
568
|
+
"totp": "TOTP",
|
|
569
|
+
"hotp": "HOTP",
|
|
570
|
+
"mfa": "MFA",
|
|
571
|
+
"2fa": "2FA",
|
|
572
|
+
"csrf": "CSRF",
|
|
573
|
+
"xss": "XSS",
|
|
574
|
+
"ssrf": "SSRF",
|
|
575
|
+
"rce": "RCE",
|
|
576
|
+
"dos": "DoS",
|
|
577
|
+
"ddos": "DDoS",
|
|
578
|
+
"mitm": "MITM",
|
|
579
|
+
"csp": "CSP",
|
|
580
|
+
"cors": "CORS",
|
|
581
|
+
"pii": "PII",
|
|
582
|
+
"phi": "PHI",
|
|
583
|
+
"gdpr": "GDPR",
|
|
584
|
+
"ccpa": "CCPA",
|
|
585
|
+
"hipaa": "HIPAA",
|
|
586
|
+
"rfc": "RFC",
|
|
587
|
+
// Platforms, langs, tools (single-token)
|
|
588
|
+
"javascript": "JavaScript",
|
|
589
|
+
"typescript": "TypeScript",
|
|
590
|
+
"jsx": "JSX",
|
|
591
|
+
"tsx": "TSX",
|
|
592
|
+
"node": "Node",
|
|
593
|
+
// (used when tokenized alone)
|
|
594
|
+
"deno": "Deno",
|
|
595
|
+
"bun": "Bun",
|
|
596
|
+
"react": "React",
|
|
597
|
+
"nextjs": "Next.js",
|
|
598
|
+
// if your tokenizer drops dots, keep this
|
|
599
|
+
"nodejs": "Node.js",
|
|
600
|
+
"postgresql": "PostgreSQL",
|
|
601
|
+
"postgres": "Postgres",
|
|
602
|
+
"mysql": "MySQL",
|
|
603
|
+
"sqlite": "SQLite",
|
|
604
|
+
"redis": "Redis",
|
|
605
|
+
"supabase": "Supabase",
|
|
606
|
+
"docker": "Docker",
|
|
607
|
+
"kubernetes": "Kubernetes",
|
|
608
|
+
"k8s": "Kubernetes",
|
|
609
|
+
"helm": "Helm",
|
|
610
|
+
"npm": "npm",
|
|
611
|
+
"pnpm": "pnpm",
|
|
612
|
+
"yarn": "Yarn",
|
|
613
|
+
"eslint": "ESLint",
|
|
614
|
+
"prettier": "Prettier",
|
|
615
|
+
"vite": "Vite",
|
|
616
|
+
"webpack": "Webpack",
|
|
617
|
+
"babel": "Babel",
|
|
618
|
+
// OS & vendors
|
|
619
|
+
"macos": "macOS",
|
|
620
|
+
"ios": "iOS",
|
|
621
|
+
"ipados": "iPadOS",
|
|
622
|
+
"watchos": "watchOS",
|
|
623
|
+
"tvos": "tvOS",
|
|
624
|
+
"windows": "Windows",
|
|
625
|
+
"linux": "Linux",
|
|
626
|
+
"ubuntu": "Ubuntu",
|
|
627
|
+
"github": "GitHub",
|
|
628
|
+
"gitlab": "GitLab",
|
|
629
|
+
"bitbucket": "Bitbucket",
|
|
630
|
+
// Data & analytics
|
|
631
|
+
"etl": "ETL",
|
|
632
|
+
"elt": "ELT",
|
|
633
|
+
"olap": "OLAP",
|
|
634
|
+
"oltp": "OLTP",
|
|
635
|
+
"bi": "BI",
|
|
636
|
+
// Time & locales
|
|
637
|
+
"utc": "UTC",
|
|
638
|
+
"gmt": "GMT",
|
|
639
|
+
"pst": "PST",
|
|
640
|
+
"pdt": "PDT",
|
|
641
|
+
"pt": "PT",
|
|
642
|
+
// Common “small words” to keep lowercase (unless first/last word)
|
|
643
|
+
"or": "or",
|
|
644
|
+
"and": "and",
|
|
645
|
+
"the": "the",
|
|
646
|
+
"of": "of",
|
|
647
|
+
"in": "in",
|
|
648
|
+
"to": "to",
|
|
649
|
+
"with": "with",
|
|
650
|
+
"as": "as",
|
|
651
|
+
"by": "by",
|
|
652
|
+
"for": "for",
|
|
653
|
+
"on": "on",
|
|
654
|
+
"at": "at",
|
|
655
|
+
"up": "up",
|
|
656
|
+
"a": "a",
|
|
657
|
+
"an": "an",
|
|
658
|
+
"is": "is",
|
|
659
|
+
"are": "are",
|
|
660
|
+
"was": "was",
|
|
661
|
+
"were": "were",
|
|
662
|
+
"be": "be",
|
|
663
|
+
"but": "but",
|
|
664
|
+
"nor": "nor",
|
|
665
|
+
"so": "so",
|
|
666
|
+
"yet": "yet",
|
|
667
|
+
"per": "per",
|
|
668
|
+
"via": "via",
|
|
669
|
+
// Latin abbreviations (tokenized as words in some pipelines)
|
|
670
|
+
"eg": "e.g.",
|
|
671
|
+
"ie": "i.e.",
|
|
672
|
+
"etc": "etc.",
|
|
673
|
+
"aka": "aka",
|
|
674
|
+
"vs": "vs.",
|
|
675
|
+
"v": "v.",
|
|
676
|
+
// Client abbreviations
|
|
677
|
+
"CIC": "CIC",
|
|
678
|
+
"AGR": "AGR",
|
|
679
|
+
"AGER": "AGER",
|
|
680
|
+
"DD": "DD",
|
|
681
|
+
"TS": "TS",
|
|
682
|
+
"TM": "TM",
|
|
683
|
+
"arman": "Arman"
|
|
684
|
+
};
|
|
685
|
+
var DEFAULT_OPTIONS = {
|
|
686
|
+
textCase: "title",
|
|
687
|
+
wordReplacements: DEFAULT_WORD_REPLACEMENTS,
|
|
688
|
+
trim: true
|
|
689
|
+
};
|
|
690
|
+
function formatText(text, options = {}) {
|
|
691
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
692
|
+
if (!text) return "";
|
|
693
|
+
let normalized = text.replace(/_/g, " ").replace(/-/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\s+/g, " ");
|
|
694
|
+
if (opts.trim) {
|
|
695
|
+
normalized = normalized.trim();
|
|
696
|
+
}
|
|
697
|
+
let caseTransformed = normalized;
|
|
698
|
+
switch (opts.textCase) {
|
|
699
|
+
case "title":
|
|
700
|
+
caseTransformed = normalized.replace(
|
|
701
|
+
/\w\S*/g,
|
|
702
|
+
(word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
703
|
+
);
|
|
704
|
+
break;
|
|
705
|
+
case "sentence":
|
|
706
|
+
if (normalized.length > 0) {
|
|
707
|
+
caseTransformed = normalized.charAt(0).toUpperCase() + normalized.slice(1).toLowerCase();
|
|
708
|
+
}
|
|
709
|
+
break;
|
|
710
|
+
case "lower":
|
|
711
|
+
caseTransformed = normalized.toLowerCase();
|
|
712
|
+
break;
|
|
713
|
+
case "upper":
|
|
714
|
+
caseTransformed = normalized.toUpperCase();
|
|
715
|
+
break;
|
|
716
|
+
case "normal":
|
|
717
|
+
default:
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
let result = caseTransformed;
|
|
721
|
+
if (opts.wordReplacements) {
|
|
722
|
+
Object.entries(opts.wordReplacements).forEach(([key, value]) => {
|
|
723
|
+
const regex = new RegExp(`\\b${key}\\b`, "gi");
|
|
724
|
+
result = result.replace(regex, value);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
return result;
|
|
728
|
+
}
|
|
729
|
+
function createFormatter(defaultOptions = {}) {
|
|
730
|
+
return (text, overrideOptions = {}) => formatText(text, { ...defaultOptions, ...overrideOptions });
|
|
731
|
+
}
|
|
732
|
+
var formatTitleCase = createFormatter({ textCase: "title" });
|
|
733
|
+
var formatSentenceCase = createFormatter({ textCase: "sentence" });
|
|
734
|
+
var formatNormalCase = createFormatter({ textCase: "normal" });
|
|
735
|
+
var formatUpperCase = createFormatter({ textCase: "upper" });
|
|
736
|
+
var formatLowerCase = createFormatter({ textCase: "lower" });
|
|
737
|
+
var formatWithoutReplacements = createFormatter({ wordReplacements: {} });
|
|
738
|
+
|
|
739
|
+
// src/drafts/use-durable-draft.ts
|
|
740
|
+
var import_react4 = require("react");
|
|
741
|
+
var PREFIX = "matrx:durable-draft:";
|
|
742
|
+
var warnedStorageUnavailable = false;
|
|
743
|
+
function storageWrite(storageKey, value) {
|
|
744
|
+
try {
|
|
745
|
+
if (value) window.localStorage.setItem(storageKey, value);
|
|
746
|
+
else window.localStorage.removeItem(storageKey);
|
|
747
|
+
} catch (err) {
|
|
748
|
+
if (!warnedStorageUnavailable) {
|
|
749
|
+
warnedStorageUnavailable = true;
|
|
750
|
+
console.warn(
|
|
751
|
+
"[useDurableDraft] localStorage unavailable \u2014 drafts survive only in memory this session",
|
|
752
|
+
err
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function useDurableDraft(key) {
|
|
758
|
+
const storageKey = PREFIX + key;
|
|
759
|
+
const [draft, setDraftState] = (0, import_react4.useState)("");
|
|
760
|
+
const touchedKeyRef = (0, import_react4.useRef)(null);
|
|
761
|
+
(0, import_react4.useEffect)(() => {
|
|
762
|
+
let saved = null;
|
|
763
|
+
try {
|
|
764
|
+
saved = window.localStorage.getItem(storageKey);
|
|
765
|
+
} catch {
|
|
766
|
+
}
|
|
767
|
+
setDraftState(
|
|
768
|
+
(current) => touchedKeyRef.current === storageKey && current ? current : saved ?? ""
|
|
769
|
+
);
|
|
770
|
+
}, [storageKey]);
|
|
771
|
+
const setDraft = (value) => {
|
|
772
|
+
touchedKeyRef.current = storageKey;
|
|
773
|
+
setDraftState(value);
|
|
774
|
+
storageWrite(storageKey, value);
|
|
775
|
+
};
|
|
776
|
+
const clearDraft = () => {
|
|
777
|
+
touchedKeyRef.current = storageKey;
|
|
778
|
+
setDraftState("");
|
|
779
|
+
storageWrite(storageKey, "");
|
|
780
|
+
};
|
|
781
|
+
return { draft, setDraft, clearDraft };
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/drafts/local-drafts.ts
|
|
785
|
+
var STORAGE_KEY = "matrx.local-drafts.v1";
|
|
786
|
+
var DRAFT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
787
|
+
var MAX_DRAFT_CHARS = 4e5;
|
|
788
|
+
var MAX_TOTAL_CHARS = 15e5;
|
|
789
|
+
var TRUNCATION_MARKER = "\n\n[\u2026 truncated by the local draft store \u2014 the note was too large to snapshot in full]";
|
|
790
|
+
var sources = /* @__PURE__ */ new Map();
|
|
791
|
+
var unloadListenerAttached = false;
|
|
792
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
793
|
+
var version = 0;
|
|
794
|
+
function subscribeDrafts(listener) {
|
|
795
|
+
listeners.add(listener);
|
|
796
|
+
return () => {
|
|
797
|
+
listeners.delete(listener);
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
function getDraftsVersion() {
|
|
801
|
+
return version;
|
|
802
|
+
}
|
|
803
|
+
function emitDraftsChanged() {
|
|
804
|
+
version += 1;
|
|
805
|
+
for (const listener of listeners) {
|
|
806
|
+
try {
|
|
807
|
+
listener();
|
|
808
|
+
} catch (err) {
|
|
809
|
+
console.error("[LocalDrafts] subscriber threw:", err);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function registerDraftSource(id, collect) {
|
|
814
|
+
sources.set(id, collect);
|
|
815
|
+
attachUnloadListener();
|
|
816
|
+
return () => {
|
|
817
|
+
if (sources.get(id) === collect) sources.delete(id);
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
function attachUnloadListener() {
|
|
821
|
+
if (unloadListenerAttached || typeof window === "undefined") return;
|
|
822
|
+
unloadListenerAttached = true;
|
|
823
|
+
window.addEventListener("pagehide", () => {
|
|
824
|
+
captureDrafts("unload");
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
function captureDrafts(reason) {
|
|
828
|
+
if (typeof window === "undefined") return [];
|
|
829
|
+
const collected = [];
|
|
830
|
+
for (const [id, collect] of sources) {
|
|
831
|
+
try {
|
|
832
|
+
collected.push(...collect());
|
|
833
|
+
} catch (err) {
|
|
834
|
+
console.error("[LocalDrafts] draft source failed:", id, err);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (collected.length === 0) return [];
|
|
838
|
+
const now = Date.now();
|
|
839
|
+
const written = collected.map((input) => ({
|
|
840
|
+
...input,
|
|
841
|
+
content: input.content.length > MAX_DRAFT_CHARS ? input.content.slice(0, MAX_DRAFT_CHARS) + TRUNCATION_MARKER : input.content,
|
|
842
|
+
key: draftKey(input.namespace, input.entityId),
|
|
843
|
+
capturedAt: now,
|
|
844
|
+
reason
|
|
845
|
+
}));
|
|
846
|
+
const existing = readAll().filter(
|
|
847
|
+
(d) => !written.some((w) => w.key === d.key)
|
|
848
|
+
);
|
|
849
|
+
writeAll([...written, ...existing]);
|
|
850
|
+
console.warn(
|
|
851
|
+
`[LocalDrafts] snapshotted ${written.length} unsaved item(s) to this browser (reason: ${reason}).`,
|
|
852
|
+
written.map((d) => `${d.key} (${d.content.length} chars)`)
|
|
853
|
+
);
|
|
854
|
+
return written;
|
|
855
|
+
}
|
|
856
|
+
function listDrafts(namespace, ownerId) {
|
|
857
|
+
if (!ownerId) return [];
|
|
858
|
+
return readAll().filter((d) => d.namespace === namespace && d.ownerId === ownerId).sort((a, b) => b.capturedAt - a.capturedAt);
|
|
859
|
+
}
|
|
860
|
+
function getDraft(namespace, entityId, ownerId) {
|
|
861
|
+
if (!ownerId) return null;
|
|
862
|
+
const key = draftKey(namespace, entityId);
|
|
863
|
+
return readAll().find((d) => d.key === key && d.ownerId === ownerId) ?? null;
|
|
864
|
+
}
|
|
865
|
+
function discardDraft(namespace, entityId) {
|
|
866
|
+
const key = draftKey(namespace, entityId);
|
|
867
|
+
const all = readAll();
|
|
868
|
+
const next = all.filter((d) => d.key !== key);
|
|
869
|
+
if (next.length !== all.length) writeAll(next);
|
|
870
|
+
}
|
|
871
|
+
function draftKey(namespace, entityId) {
|
|
872
|
+
return `${namespace}:${entityId}`;
|
|
873
|
+
}
|
|
874
|
+
function readAll() {
|
|
875
|
+
if (typeof window === "undefined") return [];
|
|
876
|
+
let raw = null;
|
|
877
|
+
try {
|
|
878
|
+
raw = window.localStorage.getItem(STORAGE_KEY);
|
|
879
|
+
} catch {
|
|
880
|
+
return [];
|
|
881
|
+
}
|
|
882
|
+
if (!raw) return [];
|
|
883
|
+
try {
|
|
884
|
+
const parsed = JSON.parse(raw);
|
|
885
|
+
if (!Array.isArray(parsed)) return [];
|
|
886
|
+
const cutoff = Date.now() - DRAFT_TTL_MS;
|
|
887
|
+
return parsed.filter(isLocalDraft).filter((d) => d.capturedAt >= cutoff);
|
|
888
|
+
} catch {
|
|
889
|
+
return [];
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function writeAll(drafts) {
|
|
893
|
+
if (typeof window === "undefined") return;
|
|
894
|
+
const cutoff = Date.now() - DRAFT_TTL_MS;
|
|
895
|
+
const fresh = drafts.filter((d) => d.capturedAt >= cutoff).sort((a, b) => b.capturedAt - a.capturedAt);
|
|
896
|
+
const kept = [];
|
|
897
|
+
let total = 0;
|
|
898
|
+
for (const draft of fresh) {
|
|
899
|
+
if (total + draft.content.length > MAX_TOTAL_CHARS && kept.length > 0) {
|
|
900
|
+
console.warn(
|
|
901
|
+
"[LocalDrafts] draft budget exhausted \u2014 dropping older draft",
|
|
902
|
+
draft.key
|
|
903
|
+
);
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
kept.push(draft);
|
|
907
|
+
total += draft.content.length;
|
|
908
|
+
}
|
|
909
|
+
try {
|
|
910
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(kept));
|
|
911
|
+
} catch (err) {
|
|
912
|
+
console.error("[LocalDrafts] failed to persist drafts:", err);
|
|
913
|
+
if (kept.length > 1) {
|
|
914
|
+
try {
|
|
915
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify([kept[0]]));
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
emitDraftsChanged();
|
|
921
|
+
}
|
|
922
|
+
function isLocalDraft(value) {
|
|
923
|
+
if (typeof value !== "object" || value === null) return false;
|
|
924
|
+
const d = value;
|
|
925
|
+
return typeof d.key === "string" && typeof d.namespace === "string" && typeof d.entityId === "string" && typeof d.content === "string" && typeof d.capturedAt === "number";
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/confirm/opener.ts
|
|
929
|
+
var STATE_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.kit.confirm-opener-state");
|
|
930
|
+
function getState() {
|
|
931
|
+
const holder = globalThis;
|
|
932
|
+
let state = holder[STATE_SLOT];
|
|
933
|
+
if (!state) {
|
|
934
|
+
state = { host: null, queue: [] };
|
|
935
|
+
holder[STATE_SLOT] = state;
|
|
936
|
+
}
|
|
937
|
+
return state;
|
|
938
|
+
}
|
|
939
|
+
function _registerHost(controller) {
|
|
940
|
+
const state = getState();
|
|
941
|
+
state.host = controller;
|
|
942
|
+
while (state.queue.length > 0) {
|
|
943
|
+
const next = state.queue.shift();
|
|
944
|
+
controller.show(next.opts, next.resolve);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function _unregisterHost(controller) {
|
|
948
|
+
const state = getState();
|
|
949
|
+
if (state.host === controller) state.host = null;
|
|
950
|
+
}
|
|
951
|
+
function confirm(opts) {
|
|
952
|
+
return new Promise((resolve) => {
|
|
953
|
+
const state = getState();
|
|
954
|
+
if (state.host) {
|
|
955
|
+
state.host.show(opts, resolve);
|
|
956
|
+
} else {
|
|
957
|
+
state.queue.push({ opts, resolve });
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// src/confirm/host.tsx
|
|
963
|
+
var React3 = __toESM(require("react"), 1);
|
|
964
|
+
|
|
965
|
+
// src/confirm/cn.ts
|
|
966
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
967
|
+
function cn(...values) {
|
|
968
|
+
return (0, import_tailwind_merge.twMerge)(values.filter(Boolean).join(" "));
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/confirm/alert-dialog.tsx
|
|
972
|
+
var React2 = __toESM(require("react"), 1);
|
|
973
|
+
var AlertDialogPrimitive = __toESM(require("@radix-ui/react-alert-dialog"), 1);
|
|
974
|
+
|
|
975
|
+
// src/react-tree.ts
|
|
976
|
+
var React = __toESM(require("react"), 1);
|
|
977
|
+
var REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal");
|
|
978
|
+
function treeContainsComponent(node, Component) {
|
|
979
|
+
if (node == null || typeof node === "boolean") return false;
|
|
980
|
+
if (Array.isArray(node)) {
|
|
981
|
+
return node.some((child) => treeContainsComponent(child, Component));
|
|
982
|
+
}
|
|
983
|
+
if (React.isValidElement(node)) {
|
|
984
|
+
if (node.type === Component) return true;
|
|
985
|
+
const props = node.props;
|
|
986
|
+
return props.children != null ? treeContainsComponent(props.children, Component) : false;
|
|
987
|
+
}
|
|
988
|
+
if (typeof node === "string" || typeof node === "number") return false;
|
|
989
|
+
if (typeof node === "object" && node.$$typeof === REACT_PORTAL_TYPE) {
|
|
990
|
+
return treeContainsComponent(
|
|
991
|
+
node.children,
|
|
992
|
+
Component
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
if (typeof node === "object" && Symbol.iterator in node) {
|
|
996
|
+
return Array.from(node).some(
|
|
997
|
+
(child) => treeContainsComponent(child, Component)
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
if (typeof process !== "undefined" && true) {
|
|
1001
|
+
const keys = typeof node === "object" ? ` with keys {${Object.keys(node).join(", ")}}` : "";
|
|
1002
|
+
console.error(
|
|
1003
|
+
`[treeContainsComponent] A non-renderable value${keys} is being passed as a React child. React will throw 'Objects are not valid as a React child' at the real render site. Stringify it (e.g. JSON.stringify) before rendering.`,
|
|
1004
|
+
node
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/confirm/alert-dialog.tsx
|
|
1011
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1012
|
+
var buttonBase = "inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 h-9 px-4 py-2";
|
|
1013
|
+
var buttonDefault = "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90";
|
|
1014
|
+
var buttonOutline = "border border-border bg-card shadow-sm hover:bg-accent hover:text-accent-foreground";
|
|
1015
|
+
var AlertDialog = AlertDialogPrimitive.Root;
|
|
1016
|
+
var AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
1017
|
+
var AlertDialogOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1018
|
+
AlertDialogPrimitive.Overlay,
|
|
1019
|
+
{
|
|
1020
|
+
className: cn(
|
|
1021
|
+
"fixed inset-0 z-[10000] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
1022
|
+
className
|
|
1023
|
+
),
|
|
1024
|
+
...props,
|
|
1025
|
+
ref
|
|
1026
|
+
}
|
|
1027
|
+
));
|
|
1028
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
1029
|
+
var AlertDialogContentPrimitive = React2.forwardRef(({ ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogPrimitive.Content, { ...props, ref, "aria-modal": "true" }));
|
|
1030
|
+
AlertDialogContentPrimitive.displayName = "AlertDialogContentPrimitive";
|
|
1031
|
+
var AlertDialogDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1032
|
+
AlertDialogPrimitive.Description,
|
|
1033
|
+
{
|
|
1034
|
+
ref,
|
|
1035
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1036
|
+
...props
|
|
1037
|
+
}
|
|
1038
|
+
));
|
|
1039
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
1040
|
+
var AlertDialogContent = React2.forwardRef(({ className, children, ...props }, ref) => {
|
|
1041
|
+
const hasDescription = treeContainsComponent(children, AlertDialogDescription) || treeContainsComponent(children, AlertDialogPrimitive.Description);
|
|
1042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AlertDialogPortal, { children: [
|
|
1043
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogOverlay, {}),
|
|
1044
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1045
|
+
AlertDialogContentPrimitive,
|
|
1046
|
+
{
|
|
1047
|
+
ref,
|
|
1048
|
+
className: cn(
|
|
1049
|
+
"fixed left-[50%] top-[50%] z-[10000] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
1050
|
+
className
|
|
1051
|
+
),
|
|
1052
|
+
...props,
|
|
1053
|
+
children: [
|
|
1054
|
+
!hasDescription && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AlertDialogPrimitive.Description, { className: "sr-only", children: "Please confirm the action described in this dialog." }),
|
|
1055
|
+
children
|
|
1056
|
+
]
|
|
1057
|
+
}
|
|
1058
|
+
)
|
|
1059
|
+
] });
|
|
1060
|
+
});
|
|
1061
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
1062
|
+
var AlertDialogHeader = ({
|
|
1063
|
+
className,
|
|
1064
|
+
...props
|
|
1065
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1066
|
+
"div",
|
|
1067
|
+
{
|
|
1068
|
+
className: cn(
|
|
1069
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
1070
|
+
className
|
|
1071
|
+
),
|
|
1072
|
+
...props
|
|
1073
|
+
}
|
|
1074
|
+
);
|
|
1075
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
1076
|
+
var AlertDialogFooter = ({
|
|
1077
|
+
className,
|
|
1078
|
+
...props
|
|
1079
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1080
|
+
"div",
|
|
1081
|
+
{
|
|
1082
|
+
className: cn(
|
|
1083
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
1084
|
+
className
|
|
1085
|
+
),
|
|
1086
|
+
...props
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
1090
|
+
var AlertDialogTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1091
|
+
AlertDialogPrimitive.Title,
|
|
1092
|
+
{
|
|
1093
|
+
ref,
|
|
1094
|
+
className: cn("text-lg font-semibold", className),
|
|
1095
|
+
...props
|
|
1096
|
+
}
|
|
1097
|
+
));
|
|
1098
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
1099
|
+
var AlertDialogAction = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1100
|
+
AlertDialogPrimitive.Action,
|
|
1101
|
+
{
|
|
1102
|
+
ref,
|
|
1103
|
+
className: cn(buttonBase, buttonDefault, className),
|
|
1104
|
+
...props
|
|
1105
|
+
}
|
|
1106
|
+
));
|
|
1107
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
1108
|
+
var AlertDialogCancel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1109
|
+
AlertDialogPrimitive.Cancel,
|
|
1110
|
+
{
|
|
1111
|
+
ref,
|
|
1112
|
+
className: cn(buttonBase, buttonOutline, "mt-2 sm:mt-0", className),
|
|
1113
|
+
...props
|
|
1114
|
+
}
|
|
1115
|
+
));
|
|
1116
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
1117
|
+
|
|
1118
|
+
// src/confirm/confirm-dialog.tsx
|
|
1119
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1120
|
+
function SpinnerIcon({ className }) {
|
|
1121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1122
|
+
"svg",
|
|
1123
|
+
{
|
|
1124
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1125
|
+
width: 24,
|
|
1126
|
+
height: 24,
|
|
1127
|
+
viewBox: "0 0 24 24",
|
|
1128
|
+
fill: "none",
|
|
1129
|
+
stroke: "currentColor",
|
|
1130
|
+
strokeWidth: 2,
|
|
1131
|
+
strokeLinecap: "round",
|
|
1132
|
+
strokeLinejoin: "round",
|
|
1133
|
+
"aria-hidden": "true",
|
|
1134
|
+
className,
|
|
1135
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
|
|
1136
|
+
}
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
1139
|
+
function ConfirmDialog({
|
|
1140
|
+
open,
|
|
1141
|
+
onOpenChange,
|
|
1142
|
+
title,
|
|
1143
|
+
description,
|
|
1144
|
+
content,
|
|
1145
|
+
contentClassName,
|
|
1146
|
+
confirmLabel = "Confirm",
|
|
1147
|
+
cancelLabel = "Cancel",
|
|
1148
|
+
variant = "default",
|
|
1149
|
+
busy = false,
|
|
1150
|
+
confirmDisabled = false,
|
|
1151
|
+
onConfirm
|
|
1152
|
+
}) {
|
|
1153
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(AlertDialogContent, { className: contentClassName, children: [
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(AlertDialogHeader, { children: [
|
|
1155
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertDialogTitle, { children: title }),
|
|
1156
|
+
description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertDialogDescription, { children: description }) : null
|
|
1157
|
+
] }),
|
|
1158
|
+
content ?? null,
|
|
1159
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(AlertDialogFooter, { children: [
|
|
1160
|
+
cancelLabel === null ? null : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AlertDialogCancel, { className: "max-lg:min-h-11", disabled: busy, children: cancelLabel }),
|
|
1161
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1162
|
+
AlertDialogAction,
|
|
1163
|
+
{
|
|
1164
|
+
disabled: busy || confirmDisabled,
|
|
1165
|
+
onClick: (event) => {
|
|
1166
|
+
event.preventDefault();
|
|
1167
|
+
void onConfirm();
|
|
1168
|
+
},
|
|
1169
|
+
className: cn(
|
|
1170
|
+
"max-lg:min-h-11",
|
|
1171
|
+
variant === "destructive" && "bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
1172
|
+
),
|
|
1173
|
+
children: [
|
|
1174
|
+
busy ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SpinnerIcon, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
|
|
1175
|
+
confirmLabel
|
|
1176
|
+
]
|
|
1177
|
+
}
|
|
1178
|
+
)
|
|
1179
|
+
] })
|
|
1180
|
+
] }) });
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// src/confirm/host.tsx
|
|
1184
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1185
|
+
function ConfirmDialogHost() {
|
|
1186
|
+
const [active, setActive] = React3.useState(null);
|
|
1187
|
+
const [tick, setTick] = React3.useState(0);
|
|
1188
|
+
const queueRef = React3.useRef([]);
|
|
1189
|
+
React3.useEffect(() => {
|
|
1190
|
+
const controller = {
|
|
1191
|
+
show: (opts, resolve) => {
|
|
1192
|
+
queueRef.current.push({ opts, resolve });
|
|
1193
|
+
setTick((n) => n + 1);
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
_registerHost(controller);
|
|
1197
|
+
return () => _unregisterHost(controller);
|
|
1198
|
+
}, []);
|
|
1199
|
+
React3.useEffect(() => {
|
|
1200
|
+
if (active === null && queueRef.current.length > 0) {
|
|
1201
|
+
setActive(queueRef.current.shift());
|
|
1202
|
+
}
|
|
1203
|
+
}, [active, tick]);
|
|
1204
|
+
const handleConfirm = React3.useCallback(() => {
|
|
1205
|
+
if (!active) return;
|
|
1206
|
+
active.resolve(true);
|
|
1207
|
+
setActive(null);
|
|
1208
|
+
}, [active]);
|
|
1209
|
+
const handleOpenChange = React3.useCallback(
|
|
1210
|
+
(open) => {
|
|
1211
|
+
if (!open && active) {
|
|
1212
|
+
active.resolve(false);
|
|
1213
|
+
setActive(null);
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
[active]
|
|
1217
|
+
);
|
|
1218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1219
|
+
ConfirmDialog,
|
|
1220
|
+
{
|
|
1221
|
+
open: !!active,
|
|
1222
|
+
onOpenChange: handleOpenChange,
|
|
1223
|
+
title: active?.opts.title ?? "",
|
|
1224
|
+
description: active?.opts.description,
|
|
1225
|
+
confirmLabel: active?.opts.confirmLabel,
|
|
1226
|
+
cancelLabel: active?.opts.cancelLabel,
|
|
1227
|
+
variant: active?.opts.variant,
|
|
1228
|
+
onConfirm: handleConfirm
|
|
1229
|
+
}
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/toast.ts
|
|
1234
|
+
function messageText(message, data) {
|
|
1235
|
+
const description = data && typeof data.description === "string" ? data.description : "";
|
|
1236
|
+
const title = typeof message === "string" ? message : "";
|
|
1237
|
+
return [title, description].filter(Boolean).join(" \u2014 ") || "Error toast";
|
|
1238
|
+
}
|
|
1239
|
+
function createMatrxToast({
|
|
1240
|
+
toast: hostToast,
|
|
1241
|
+
capture
|
|
1242
|
+
}) {
|
|
1243
|
+
if (typeof hostToast !== "function") {
|
|
1244
|
+
throw new Error(
|
|
1245
|
+
"createMatrxToast: options.toast must be the sonner `toast` object (or a structural equivalent) \u2014 got " + typeof hostToast
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
function captureToast(kind, message, data) {
|
|
1249
|
+
if (!capture) return;
|
|
1250
|
+
try {
|
|
1251
|
+
capture({
|
|
1252
|
+
source: "user-toast",
|
|
1253
|
+
message: `${kind === "warning" ? "[warning] " : ""}${messageText(message, data)}`,
|
|
1254
|
+
userMessage: messageText(message, data),
|
|
1255
|
+
raw: {
|
|
1256
|
+
kind,
|
|
1257
|
+
message: typeof message === "string" ? message : void 0,
|
|
1258
|
+
data
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
} catch {
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
const error = (message, data) => {
|
|
1265
|
+
captureToast("error", message, data);
|
|
1266
|
+
return hostToast.error(message, data);
|
|
1267
|
+
};
|
|
1268
|
+
const warning = (message, data) => {
|
|
1269
|
+
captureToast("warning", message, data);
|
|
1270
|
+
return hostToast.warning(message, data);
|
|
1271
|
+
};
|
|
1272
|
+
const forward = hostToast;
|
|
1273
|
+
const toast = Object.assign(
|
|
1274
|
+
((...args) => forward(...args)),
|
|
1275
|
+
hostToast,
|
|
1276
|
+
{ error, warning }
|
|
1277
|
+
);
|
|
1278
|
+
const toastErrorAlreadyCaptured = ((message, data) => hostToast.error(message, data));
|
|
1279
|
+
return { toast, toastErrorAlreadyCaptured };
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/invalidation.ts
|
|
1283
|
+
var STATE_SLOT2 = /* @__PURE__ */ Symbol.for("ai-matrx.kit.invalidation-registry");
|
|
1284
|
+
function getCallbacks() {
|
|
1285
|
+
const holder = globalThis;
|
|
1286
|
+
let state = holder[STATE_SLOT2];
|
|
1287
|
+
if (!state) {
|
|
1288
|
+
state = /* @__PURE__ */ new Map();
|
|
1289
|
+
holder[STATE_SLOT2] = state;
|
|
1290
|
+
}
|
|
1291
|
+
return state;
|
|
1292
|
+
}
|
|
1293
|
+
function registerInvalidationCallback(name, callback) {
|
|
1294
|
+
const callbacks = getCallbacks();
|
|
1295
|
+
let set = callbacks.get(name);
|
|
1296
|
+
if (!set) {
|
|
1297
|
+
set = /* @__PURE__ */ new Set();
|
|
1298
|
+
callbacks.set(name, set);
|
|
1299
|
+
}
|
|
1300
|
+
set.add(callback);
|
|
1301
|
+
return () => {
|
|
1302
|
+
set.delete(callback);
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function fireInvalidation(name, detail) {
|
|
1306
|
+
const set = getCallbacks().get(name);
|
|
1307
|
+
if (!set || set.size === 0) return false;
|
|
1308
|
+
for (const callback of set) {
|
|
1309
|
+
try {
|
|
1310
|
+
callback(detail);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
console.error(
|
|
1313
|
+
`[invalidation-registry] callback for "${name}" threw`,
|
|
1314
|
+
error
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// src/delimiter-guard.ts
|
|
1322
|
+
var ZWSP = "\u200B";
|
|
1323
|
+
var ESCAPED_DOLLARS = `${ZWSP}$${ZWSP}$`;
|
|
1324
|
+
var ESCAPED_BRACKET = `${ZWSP}[`;
|
|
1325
|
+
var MAX_MATH_SPAN = 600;
|
|
1326
|
+
var STRUCTURAL_MARKDOWN = /\]\(|https?:\/\/|\*\*|(?:^|\n)[ \t]{0,3}#{1,6}[ \t]|(?:^|\n)[ \t]*[-*+][ \t]+|(?:^|\n)[ \t]*\d+[.)][ \t]/;
|
|
1327
|
+
var LATEX_COMMAND = /\\[a-zA-Z]/;
|
|
1328
|
+
var PROSE_WORD = /[A-Za-z]{3,}/g;
|
|
1329
|
+
var PROSE_WORD_LIMIT = 6;
|
|
1330
|
+
function looksLikeMath(inner) {
|
|
1331
|
+
const s = inner.trim();
|
|
1332
|
+
if (!s) return false;
|
|
1333
|
+
if (STRUCTURAL_MARKDOWN.test(s)) return false;
|
|
1334
|
+
if (s.length > MAX_MATH_SPAN) return false;
|
|
1335
|
+
if (LATEX_COMMAND.test(s)) {
|
|
1336
|
+
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT * 3;
|
|
1337
|
+
}
|
|
1338
|
+
if (/\n[ \t]*\n/.test(s)) return false;
|
|
1339
|
+
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
|
|
1340
|
+
}
|
|
1341
|
+
function protectedRanges(text) {
|
|
1342
|
+
const ranges = [];
|
|
1343
|
+
const patterns = [/```[\s\S]*?(?:```|$)/g, /~~~[\s\S]*?(?:~~~|$)/g, /`[^`\n]*`/g];
|
|
1344
|
+
for (const re of patterns) {
|
|
1345
|
+
let m;
|
|
1346
|
+
while ((m = re.exec(text)) !== null) {
|
|
1347
|
+
ranges.push([m.index, m.index + m[0].length]);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return ranges;
|
|
1351
|
+
}
|
|
1352
|
+
function isProtected(index, ranges) {
|
|
1353
|
+
return ranges.some(([start, end]) => index >= start && index < end);
|
|
1354
|
+
}
|
|
1355
|
+
function preview(text, max = 160) {
|
|
1356
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
1357
|
+
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
1358
|
+
}
|
|
1359
|
+
function guardMathDelimiters(text) {
|
|
1360
|
+
if (!text.includes("$$")) return { text, violations: [] };
|
|
1361
|
+
const ranges = protectedRanges(text);
|
|
1362
|
+
const tokens = [];
|
|
1363
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
1364
|
+
if (text[i] !== "$" || text[i + 1] !== "$") continue;
|
|
1365
|
+
if (!isProtected(i, ranges)) tokens.push(i);
|
|
1366
|
+
i++;
|
|
1367
|
+
}
|
|
1368
|
+
if (tokens.length === 0) return { text, violations: [] };
|
|
1369
|
+
const violations = [];
|
|
1370
|
+
const escapeAt = [];
|
|
1371
|
+
let j = 0;
|
|
1372
|
+
while (j < tokens.length) {
|
|
1373
|
+
const open = tokens[j];
|
|
1374
|
+
const close = tokens[j + 1];
|
|
1375
|
+
if (close === void 0) {
|
|
1376
|
+
violations.push({
|
|
1377
|
+
reason: "unpaired",
|
|
1378
|
+
index: open,
|
|
1379
|
+
spanLength: 0,
|
|
1380
|
+
// An unpaired `$$` is inert to remark-math (nothing closes it), so it
|
|
1381
|
+
// is reported but NOT escaped — it already renders as literal text.
|
|
1382
|
+
preview: preview(text.slice(open, open + 120))
|
|
1383
|
+
});
|
|
1384
|
+
break;
|
|
1385
|
+
}
|
|
1386
|
+
const inner = text.slice(open + 2, close);
|
|
1387
|
+
if (looksLikeMath(inner)) {
|
|
1388
|
+
j += 2;
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
escapeAt.push(open);
|
|
1392
|
+
violations.push({
|
|
1393
|
+
reason: "prose-span",
|
|
1394
|
+
index: open,
|
|
1395
|
+
spanLength: inner.length,
|
|
1396
|
+
preview: preview(inner)
|
|
1397
|
+
});
|
|
1398
|
+
j += 1;
|
|
1399
|
+
}
|
|
1400
|
+
let guarded = text;
|
|
1401
|
+
for (const index of [...escapeAt].sort((a, b) => b - a)) {
|
|
1402
|
+
guarded = `${guarded.slice(0, index)}${ESCAPED_DOLLARS}${guarded.slice(index + 2)}`;
|
|
1403
|
+
}
|
|
1404
|
+
return { text: guarded, violations };
|
|
1405
|
+
}
|
|
1406
|
+
var MAX_LINK_LABEL = 200;
|
|
1407
|
+
var LABEL_BLOCK_STRUCTURE = /\n[ \t]*\n|(?:^|\n)[ \t]*[-*+][ \t]+|#{2,6}[ \t]/;
|
|
1408
|
+
function guardRunawayLinks(text) {
|
|
1409
|
+
if (!text.includes("[")) return { text, violations: [] };
|
|
1410
|
+
const ranges = protectedRanges(text);
|
|
1411
|
+
const violations = [];
|
|
1412
|
+
const escapeAt = [];
|
|
1413
|
+
const linkRe = /\[((?:[^[\]]|\\.)*)\]\(([^\s)]*)/g;
|
|
1414
|
+
let m;
|
|
1415
|
+
while ((m = linkRe.exec(text)) !== null) {
|
|
1416
|
+
const open = m.index;
|
|
1417
|
+
if (isProtected(open, ranges)) continue;
|
|
1418
|
+
const label = m[1] ?? "";
|
|
1419
|
+
const runaway = label.length > MAX_LINK_LABEL || LABEL_BLOCK_STRUCTURE.test(label);
|
|
1420
|
+
if (!runaway) continue;
|
|
1421
|
+
escapeAt.push(open);
|
|
1422
|
+
violations.push({
|
|
1423
|
+
reason: "runaway-link",
|
|
1424
|
+
index: open,
|
|
1425
|
+
spanLength: label.length,
|
|
1426
|
+
preview: preview(label)
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
let guarded = text;
|
|
1430
|
+
for (const index of [...escapeAt].sort((a, b) => b - a)) {
|
|
1431
|
+
guarded = `${guarded.slice(0, index)}${ESCAPED_BRACKET}${guarded.slice(index + 1)}`;
|
|
1432
|
+
}
|
|
1433
|
+
return { text: guarded, violations };
|
|
1434
|
+
}
|
|
1435
|
+
function guardMarkdownDelimiters(text) {
|
|
1436
|
+
const math = guardMathDelimiters(text);
|
|
1437
|
+
const links = guardRunawayLinks(math.text);
|
|
1438
|
+
return {
|
|
1439
|
+
text: links.text,
|
|
1440
|
+
violations: [...math.violations, ...links.violations]
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function reportDelimiterViolations(violations, context) {
|
|
1444
|
+
if (violations.length === 0) return;
|
|
1445
|
+
try {
|
|
1446
|
+
const worst = violations.find((v) => v.reason !== "unpaired") ?? violations[0];
|
|
1447
|
+
if (!worst) return;
|
|
1448
|
+
const message = worst.reason === "prose-span" ? `Malformed math delimiters: a stray "$$" would have turned ${worst.spanLength} chars of prose into a math span (KaTeX would render it as red error text). Escaped it.` : worst.reason === "runaway-link" ? `Runaway markdown link: an unclosed "[" would have turned ${worst.spanLength} chars into one link label. Escaped it.` : `Malformed math delimiters: an unpaired "$$" reached the renderer.`;
|
|
1449
|
+
console.warn(`[markdown-delimiter-guard] ${message}`, {
|
|
1450
|
+
renderPath: context.renderPath,
|
|
1451
|
+
violations
|
|
1452
|
+
});
|
|
1453
|
+
context.capture?.({
|
|
1454
|
+
source: "markdown-delimiters",
|
|
1455
|
+
message,
|
|
1456
|
+
relation: `markdown:${context.renderPath}`,
|
|
1457
|
+
details: worst.preview,
|
|
1458
|
+
conversationId: context.conversationId,
|
|
1459
|
+
callSite: "guardMarkdownDelimiters",
|
|
1460
|
+
raw: { messageId: context.messageId, violations }
|
|
1461
|
+
});
|
|
1462
|
+
} catch {
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// src/json-format/detect.ts
|
|
1467
|
+
var import_json5 = __toESM(require("json5"), 1);
|
|
1468
|
+
var FENCE_OPEN = /^([ \t]*)(`{3,}|~{3,})[ \t]*([^\s`~]*)[ \t]*$/;
|
|
1469
|
+
var JSON_FENCE_LANGS = /* @__PURE__ */ new Set(["json", "jsonc", "json5", "geojson", "jsonl"]);
|
|
1470
|
+
function splitFence(text) {
|
|
1471
|
+
const lines = text.split("\n");
|
|
1472
|
+
const openIdx = lines.findIndex((l) => FENCE_OPEN.test(l));
|
|
1473
|
+
if (openIdx === -1) return null;
|
|
1474
|
+
const open = FENCE_OPEN.exec(lines[openIdx] ?? "");
|
|
1475
|
+
if (!open) return null;
|
|
1476
|
+
const [, indent = "", marker = "```", lang = ""] = open;
|
|
1477
|
+
const fenceChar = marker[0] ?? "`";
|
|
1478
|
+
const closeRe = new RegExp(`^[ \\t]*\\${fenceChar}{${marker.length},}[ \\t]*$`);
|
|
1479
|
+
let closeIdx = -1;
|
|
1480
|
+
for (let i = openIdx + 1; i < lines.length; i++) {
|
|
1481
|
+
if (closeRe.test(lines[i] ?? "")) {
|
|
1482
|
+
closeIdx = i;
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
const afterClose = closeIdx === -1 ? [] : lines.slice(closeIdx + 1);
|
|
1487
|
+
if (afterClose.some((l) => FENCE_OPEN.test(l))) return null;
|
|
1488
|
+
const bodyEnd = closeIdx === -1 ? lines.length : closeIdx;
|
|
1489
|
+
return {
|
|
1490
|
+
leading: openIdx > 0 ? `${lines.slice(0, openIdx).join("\n")}
|
|
1491
|
+
` : "",
|
|
1492
|
+
payload: lines.slice(openIdx + 1, bodyEnd).join("\n"),
|
|
1493
|
+
trailing: afterClose.length > 0 ? `
|
|
1494
|
+
${afterClose.join("\n")}` : "",
|
|
1495
|
+
fence: {
|
|
1496
|
+
marker,
|
|
1497
|
+
lang: lang.toLowerCase(),
|
|
1498
|
+
indent,
|
|
1499
|
+
closed: closeIdx !== -1
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
function splitBare(text) {
|
|
1504
|
+
const payload = text.trim();
|
|
1505
|
+
if (payload === "") {
|
|
1506
|
+
return { leading: text, payload: "", trailing: "", fence: null };
|
|
1507
|
+
}
|
|
1508
|
+
const start = text.indexOf(payload);
|
|
1509
|
+
return {
|
|
1510
|
+
leading: text.slice(0, start),
|
|
1511
|
+
payload,
|
|
1512
|
+
trailing: text.slice(start + payload.length),
|
|
1513
|
+
fence: null
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
function rootKindOf(value) {
|
|
1517
|
+
if (Array.isArray(value)) return "array";
|
|
1518
|
+
if (typeof value === "object" && value !== null) return "object";
|
|
1519
|
+
return "scalar";
|
|
1520
|
+
}
|
|
1521
|
+
function isBracketShaped(payload) {
|
|
1522
|
+
const first = payload[0];
|
|
1523
|
+
const last = payload[payload.length - 1];
|
|
1524
|
+
if (payload.length < 2) return false;
|
|
1525
|
+
return first === "{" && last === "}" || first === "[" && last === "]";
|
|
1526
|
+
}
|
|
1527
|
+
function errorMessage(err) {
|
|
1528
|
+
return err instanceof Error ? err.message : "Invalid JSON";
|
|
1529
|
+
}
|
|
1530
|
+
function countLines(s) {
|
|
1531
|
+
if (s === "") return 0;
|
|
1532
|
+
let n = 1;
|
|
1533
|
+
for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
|
|
1534
|
+
return n;
|
|
1535
|
+
}
|
|
1536
|
+
function detectJson(text) {
|
|
1537
|
+
const split = splitFence(text) ?? splitBare(text);
|
|
1538
|
+
const payload = split.fence ? split.payload.trim() : split.payload;
|
|
1539
|
+
const fenceSaysJson = split.fence !== null && (split.fence.lang === "" || JSON_FENCE_LANGS.has(split.fence.lang));
|
|
1540
|
+
const base = {
|
|
1541
|
+
fence: split.fence,
|
|
1542
|
+
leading: split.leading,
|
|
1543
|
+
trailing: split.trailing,
|
|
1544
|
+
payload,
|
|
1545
|
+
lineCount: countLines(payload),
|
|
1546
|
+
charCount: payload.length
|
|
1547
|
+
};
|
|
1548
|
+
if (payload === "") {
|
|
1549
|
+
return { ...base, ok: false, looksLikeJson: false };
|
|
1550
|
+
}
|
|
1551
|
+
if (split.fence !== null && !fenceSaysJson) {
|
|
1552
|
+
return { ...base, ok: false, looksLikeJson: false };
|
|
1553
|
+
}
|
|
1554
|
+
const shaped = isBracketShaped(payload);
|
|
1555
|
+
let value;
|
|
1556
|
+
let parser;
|
|
1557
|
+
let error;
|
|
1558
|
+
try {
|
|
1559
|
+
value = JSON.parse(payload);
|
|
1560
|
+
parser = "strict";
|
|
1561
|
+
} catch (strictErr) {
|
|
1562
|
+
try {
|
|
1563
|
+
value = import_json5.default.parse(payload);
|
|
1564
|
+
parser = "tolerant";
|
|
1565
|
+
} catch {
|
|
1566
|
+
error = errorMessage(strictErr);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (value === void 0 || parser === void 0) {
|
|
1570
|
+
return { ...base, ok: false, looksLikeJson: shaped, error };
|
|
1571
|
+
}
|
|
1572
|
+
const root = rootKindOf(value);
|
|
1573
|
+
const worthOffering = root !== "scalar" || fenceSaysJson;
|
|
1574
|
+
return { ...base, ok: true, looksLikeJson: worthOffering, value, parser, root };
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// src/json-format/json-value.ts
|
|
1578
|
+
function isJsonObject(value) {
|
|
1579
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1580
|
+
}
|
|
1581
|
+
function isJsonArray(value) {
|
|
1582
|
+
return Array.isArray(value);
|
|
1583
|
+
}
|
|
1584
|
+
function isJsonPrimitive(value) {
|
|
1585
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// src/json-format/format.ts
|
|
1589
|
+
var DEFAULT_JSON_INDENT = 2;
|
|
1590
|
+
var DEFAULT_JSON_WIDTH = 100;
|
|
1591
|
+
function orderedKeys(obj, sortKeys) {
|
|
1592
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0);
|
|
1593
|
+
return sortKeys ? [...keys].sort((a, b) => a.localeCompare(b)) : keys;
|
|
1594
|
+
}
|
|
1595
|
+
function writeScalar(value) {
|
|
1596
|
+
return JSON.stringify(value) ?? "null";
|
|
1597
|
+
}
|
|
1598
|
+
function flatten(value, cfg) {
|
|
1599
|
+
if (isJsonArray(value)) {
|
|
1600
|
+
if (value.length === 0) return "[]";
|
|
1601
|
+
const parts = value.map((v) => flatten(v ?? null, cfg));
|
|
1602
|
+
return cfg.spaced ? `[${parts.join(", ")}]` : `[${parts.join(",")}]`;
|
|
1603
|
+
}
|
|
1604
|
+
if (isJsonObject(value)) {
|
|
1605
|
+
const keys = orderedKeys(value, cfg.sortKeys);
|
|
1606
|
+
if (keys.length === 0) return "{}";
|
|
1607
|
+
const colon = cfg.spaced ? ": " : ":";
|
|
1608
|
+
const parts = keys.map(
|
|
1609
|
+
(k) => `${JSON.stringify(k)}${colon}${flatten(value[k] ?? null, cfg)}`
|
|
1610
|
+
);
|
|
1611
|
+
return cfg.spaced ? `{ ${parts.join(", ")} }` : `{${parts.join(",")}}`;
|
|
1612
|
+
}
|
|
1613
|
+
return writeScalar(value);
|
|
1614
|
+
}
|
|
1615
|
+
function layoutEntries(entries, pad, cfg) {
|
|
1616
|
+
if (!cfg.pack) return entries.map((e) => pad + e);
|
|
1617
|
+
const lines = [];
|
|
1618
|
+
let current = "";
|
|
1619
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1620
|
+
const entry = entries[i] ?? "";
|
|
1621
|
+
const isLast = i === entries.length - 1;
|
|
1622
|
+
const piece = isLast ? entry : `${entry},`;
|
|
1623
|
+
if (entry.includes("\n")) {
|
|
1624
|
+
if (current !== "") {
|
|
1625
|
+
lines.push(pad + current);
|
|
1626
|
+
current = "";
|
|
1627
|
+
}
|
|
1628
|
+
lines.push(pad + piece);
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (current === "") {
|
|
1632
|
+
current = piece;
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
const merged = `${current} ${piece}`;
|
|
1636
|
+
if (pad.length + merged.length <= cfg.width) {
|
|
1637
|
+
current = merged;
|
|
1638
|
+
} else {
|
|
1639
|
+
lines.push(pad + current);
|
|
1640
|
+
current = piece;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (current !== "") lines.push(pad + current);
|
|
1644
|
+
return lines;
|
|
1645
|
+
}
|
|
1646
|
+
function renderNode(value, level, used, cfg) {
|
|
1647
|
+
const isContainer = isJsonArray(value) || isJsonObject(value);
|
|
1648
|
+
if (!isContainer) return writeScalar(value);
|
|
1649
|
+
const flat = flatten(value, cfg);
|
|
1650
|
+
if (flat === "[]" || flat === "{}") return flat;
|
|
1651
|
+
if (cfg.width >= 0 && used + flat.length <= cfg.width) return flat;
|
|
1652
|
+
const pad = " ".repeat((level + 1) * cfg.indent);
|
|
1653
|
+
const closePad = " ".repeat(level * cfg.indent);
|
|
1654
|
+
if (isJsonArray(value)) {
|
|
1655
|
+
const entries2 = value.map(
|
|
1656
|
+
(v) => renderNode(v ?? null, level + 1, pad.length, cfg)
|
|
1657
|
+
);
|
|
1658
|
+
const body2 = layoutEntries(entries2, pad, cfg);
|
|
1659
|
+
const joined2 = cfg.pack ? body2.join("\n") : body2.join(",\n");
|
|
1660
|
+
return `[
|
|
1661
|
+
${joined2}
|
|
1662
|
+
${closePad}]`;
|
|
1663
|
+
}
|
|
1664
|
+
const keys = orderedKeys(value, cfg.sortKeys);
|
|
1665
|
+
const entries = keys.map((k) => {
|
|
1666
|
+
const prefix = `${JSON.stringify(k)}: `;
|
|
1667
|
+
const rendered = renderNode(
|
|
1668
|
+
value[k] ?? null,
|
|
1669
|
+
level + 1,
|
|
1670
|
+
pad.length + prefix.length,
|
|
1671
|
+
cfg
|
|
1672
|
+
);
|
|
1673
|
+
return prefix + rendered;
|
|
1674
|
+
});
|
|
1675
|
+
const body = layoutEntries(entries, pad, cfg);
|
|
1676
|
+
const joined = cfg.pack ? body.join("\n") : body.join(",\n");
|
|
1677
|
+
return `{
|
|
1678
|
+
${joined}
|
|
1679
|
+
${closePad}}`;
|
|
1680
|
+
}
|
|
1681
|
+
function stringifyJson(value, options) {
|
|
1682
|
+
const indent = options.indent ?? DEFAULT_JSON_INDENT;
|
|
1683
|
+
const sortKeys = options.sortKeys ?? false;
|
|
1684
|
+
if (options.style === "minify") {
|
|
1685
|
+
return flatten(value, { indent, width: -1, pack: false, spaced: false, sortKeys });
|
|
1686
|
+
}
|
|
1687
|
+
const cfg = {
|
|
1688
|
+
indent,
|
|
1689
|
+
width: options.style === "pretty" ? -1 : options.width ?? DEFAULT_JSON_WIDTH,
|
|
1690
|
+
pack: options.style === "compact",
|
|
1691
|
+
spaced: true,
|
|
1692
|
+
sortKeys
|
|
1693
|
+
};
|
|
1694
|
+
return renderNode(value, 0, 0, cfg);
|
|
1695
|
+
}
|
|
1696
|
+
function sizeOf(text) {
|
|
1697
|
+
let lines = text === "" ? 0 : 1;
|
|
1698
|
+
for (let i = 0; i < text.length; i++) if (text[i] === "\n") lines++;
|
|
1699
|
+
return { lines, chars: text.length };
|
|
1700
|
+
}
|
|
1701
|
+
function reassemble(detection, payload, mode) {
|
|
1702
|
+
const { fence, leading, trailing } = detection;
|
|
1703
|
+
const keepFence = mode === "add" || mode === "preserve" && fence !== null;
|
|
1704
|
+
if (!keepFence) {
|
|
1705
|
+
return leading + payload + trailing;
|
|
1706
|
+
}
|
|
1707
|
+
const marker = fence?.marker ?? "```";
|
|
1708
|
+
const lang = fence?.lang && fence.lang !== "" ? fence.lang : "json";
|
|
1709
|
+
const indent = fence?.indent ?? "";
|
|
1710
|
+
const body = indent ? payload.split("\n").map((l) => l === "" ? l : indent + l).join("\n") : payload;
|
|
1711
|
+
const close = fence !== null && !fence.closed ? "" : `
|
|
1712
|
+
${indent}${marker}`;
|
|
1713
|
+
return `${leading}${indent}${marker}${lang}
|
|
1714
|
+
${body}${close}${trailing}`;
|
|
1715
|
+
}
|
|
1716
|
+
function formatJsonText(text, options) {
|
|
1717
|
+
const detection = detectJson(text);
|
|
1718
|
+
const before = sizeOf(text);
|
|
1719
|
+
if (!detection.ok || detection.value === void 0) {
|
|
1720
|
+
return {
|
|
1721
|
+
ok: false,
|
|
1722
|
+
text,
|
|
1723
|
+
error: detection.error ?? "Selection is not JSON.",
|
|
1724
|
+
changed: false,
|
|
1725
|
+
detection,
|
|
1726
|
+
before,
|
|
1727
|
+
after: before
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
const payload = stringifyJson(detection.value, options);
|
|
1731
|
+
const next = reassemble(detection, payload, options.fence ?? "preserve");
|
|
1732
|
+
return {
|
|
1733
|
+
ok: true,
|
|
1734
|
+
text: next,
|
|
1735
|
+
changed: next !== text,
|
|
1736
|
+
detection,
|
|
1737
|
+
before,
|
|
1738
|
+
after: sizeOf(next)
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// src/idle-scheduler/scheduler.ts
|
|
1743
|
+
var STATE_SLOT3 = /* @__PURE__ */ Symbol.for("ai-matrx.kit.idle-scheduler-state");
|
|
1744
|
+
function getState2() {
|
|
1745
|
+
const holder = globalThis;
|
|
1746
|
+
let state = holder[STATE_SLOT3];
|
|
1747
|
+
if (!state) {
|
|
1748
|
+
state = {
|
|
1749
|
+
queue: /* @__PURE__ */ new Map(),
|
|
1750
|
+
flushState: "idle",
|
|
1751
|
+
cleanupFns: [],
|
|
1752
|
+
flushListeners: /* @__PURE__ */ new Set()
|
|
1753
|
+
};
|
|
1754
|
+
holder[STATE_SLOT3] = state;
|
|
1755
|
+
if (typeof window !== "undefined") {
|
|
1756
|
+
window.__idleSched = getSchedulerState;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
return state;
|
|
1760
|
+
}
|
|
1761
|
+
function registerIdleTask(key, priority, callback) {
|
|
1762
|
+
const state = getState2();
|
|
1763
|
+
if (state.flushState === "done") {
|
|
1764
|
+
scheduleImmediate(callback);
|
|
1765
|
+
return () => {
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
state.queue.set(key, { key, priority, callback });
|
|
1769
|
+
if (state.flushState === "idle") {
|
|
1770
|
+
startFlushPipeline(state);
|
|
1771
|
+
}
|
|
1772
|
+
return () => {
|
|
1773
|
+
state.queue.delete(key);
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
function onFlushComplete(listener) {
|
|
1777
|
+
const state = getState2();
|
|
1778
|
+
if (state.flushState === "done") {
|
|
1779
|
+
queueMicrotask(listener);
|
|
1780
|
+
return () => {
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
state.flushListeners.add(listener);
|
|
1784
|
+
if (state.flushState === "idle") {
|
|
1785
|
+
startFlushPipeline(state);
|
|
1786
|
+
}
|
|
1787
|
+
return () => {
|
|
1788
|
+
state.flushListeners.delete(listener);
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function whenPageIdle(signal) {
|
|
1792
|
+
if (signal?.aborted) return Promise.resolve(false);
|
|
1793
|
+
return new Promise((resolve) => {
|
|
1794
|
+
let settled = false;
|
|
1795
|
+
let unsubscribe = () => {
|
|
1796
|
+
};
|
|
1797
|
+
const finish = (ready) => {
|
|
1798
|
+
if (settled) return;
|
|
1799
|
+
settled = true;
|
|
1800
|
+
unsubscribe();
|
|
1801
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1802
|
+
resolve(ready);
|
|
1803
|
+
};
|
|
1804
|
+
const onAbort = () => finish(false);
|
|
1805
|
+
unsubscribe = onFlushComplete(() => finish(true));
|
|
1806
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
function getSchedulerState() {
|
|
1810
|
+
const state = getState2();
|
|
1811
|
+
return {
|
|
1812
|
+
flushState: state.flushState,
|
|
1813
|
+
pendingCount: state.queue.size,
|
|
1814
|
+
pendingKeys: Array.from(state.queue.keys())
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
function resetScheduler() {
|
|
1818
|
+
const state = getState2();
|
|
1819
|
+
state.cleanupFns.forEach((fn) => fn());
|
|
1820
|
+
state.cleanupFns = [];
|
|
1821
|
+
state.queue.clear();
|
|
1822
|
+
state.flushListeners.clear();
|
|
1823
|
+
state.flushState = "idle";
|
|
1824
|
+
}
|
|
1825
|
+
function startFlushPipeline(state) {
|
|
1826
|
+
if (typeof window === "undefined") return;
|
|
1827
|
+
state.flushState = "waiting";
|
|
1828
|
+
const waitForLoad = () => {
|
|
1829
|
+
if (document.readyState === "complete") {
|
|
1830
|
+
waitForPaint();
|
|
1831
|
+
} else {
|
|
1832
|
+
const onLoad = () => waitForPaint();
|
|
1833
|
+
window.addEventListener("load", onLoad, { once: true });
|
|
1834
|
+
state.cleanupFns.push(() => window.removeEventListener("load", onLoad));
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
const waitForPaint = () => {
|
|
1838
|
+
let advanced = false;
|
|
1839
|
+
const advance = () => {
|
|
1840
|
+
if (advanced) return;
|
|
1841
|
+
advanced = true;
|
|
1842
|
+
waitForIdle();
|
|
1843
|
+
};
|
|
1844
|
+
const rafId = requestAnimationFrame(advance);
|
|
1845
|
+
const timeoutId = setTimeout(
|
|
1846
|
+
advance,
|
|
1847
|
+
document.visibilityState === "hidden" ? 250 : 1500
|
|
1848
|
+
);
|
|
1849
|
+
state.cleanupFns.push(() => {
|
|
1850
|
+
cancelAnimationFrame(rafId);
|
|
1851
|
+
clearTimeout(timeoutId);
|
|
1852
|
+
});
|
|
1853
|
+
};
|
|
1854
|
+
const waitForIdle = () => {
|
|
1855
|
+
const schedulerGlobal = globalThis.scheduler;
|
|
1856
|
+
if (schedulerGlobal && "postTask" in schedulerGlobal) {
|
|
1857
|
+
schedulerGlobal.postTask(() => void flush(state), { priority: "background" }).catch(() => {
|
|
1858
|
+
});
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
if ("requestIdleCallback" in window) {
|
|
1862
|
+
const idleId = requestIdleCallback(() => void flush(state));
|
|
1863
|
+
state.cleanupFns.push(() => cancelIdleCallback(idleId));
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
const channel = new MessageChannel();
|
|
1867
|
+
channel.port1.onmessage = () => void flush(state);
|
|
1868
|
+
channel.port2.postMessage(void 0);
|
|
1869
|
+
};
|
|
1870
|
+
waitForLoad();
|
|
1871
|
+
}
|
|
1872
|
+
async function flush(state) {
|
|
1873
|
+
if (state.flushState === "done" || state.flushState === "flushing") return;
|
|
1874
|
+
state.flushState = "flushing";
|
|
1875
|
+
const sorted = Array.from(state.queue.values()).sort(
|
|
1876
|
+
(a, b) => a.priority - b.priority
|
|
1877
|
+
);
|
|
1878
|
+
state.queue.clear();
|
|
1879
|
+
for (const task of sorted) {
|
|
1880
|
+
try {
|
|
1881
|
+
await task.callback();
|
|
1882
|
+
} catch (err) {
|
|
1883
|
+
console.error(`[IdleScheduler] Task "${task.key}" failed:`, err);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
state.flushState = "done";
|
|
1887
|
+
if (state.queue.size > 0) {
|
|
1888
|
+
const late = Array.from(state.queue.values()).sort(
|
|
1889
|
+
(a, b) => a.priority - b.priority
|
|
1890
|
+
);
|
|
1891
|
+
state.queue.clear();
|
|
1892
|
+
for (const task of late) scheduleImmediate(task.callback);
|
|
1893
|
+
}
|
|
1894
|
+
state.flushListeners.forEach((listener) => {
|
|
1895
|
+
try {
|
|
1896
|
+
listener();
|
|
1897
|
+
} catch (err) {
|
|
1898
|
+
console.error("[IdleScheduler] Flush listener failed:", err);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
state.flushListeners.clear();
|
|
1902
|
+
}
|
|
1903
|
+
function scheduleImmediate(callback) {
|
|
1904
|
+
requestAnimationFrame(() => {
|
|
1905
|
+
const schedulerGlobal = globalThis.scheduler;
|
|
1906
|
+
if (schedulerGlobal && "postTask" in schedulerGlobal) {
|
|
1907
|
+
schedulerGlobal.postTask(() => void callback(), { priority: "background" }).catch(() => {
|
|
1908
|
+
});
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
if ("requestIdleCallback" in window) {
|
|
1912
|
+
requestIdleCallback(() => void callback());
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
const channel = new MessageChannel();
|
|
1916
|
+
channel.port1.onmessage = () => void callback();
|
|
1917
|
+
channel.port2.postMessage(void 0);
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// src/idle-scheduler/hooks.ts
|
|
1922
|
+
var import_react5 = require("react");
|
|
1923
|
+
function useIdleTask(key, priority, callback) {
|
|
1924
|
+
const callbackRef = (0, import_react5.useRef)(callback);
|
|
1925
|
+
callbackRef.current = callback;
|
|
1926
|
+
(0, import_react5.useEffect)(() => {
|
|
1927
|
+
const unregister = registerIdleTask(key, priority, () => {
|
|
1928
|
+
return callbackRef.current();
|
|
1929
|
+
});
|
|
1930
|
+
return unregister;
|
|
1931
|
+
}, [key, priority]);
|
|
1932
|
+
}
|
|
1933
|
+
function useIdleReady() {
|
|
1934
|
+
const [ready, setReady] = (0, import_react5.useState)(false);
|
|
1935
|
+
(0, import_react5.useEffect)(() => {
|
|
1936
|
+
const unsubscribe = onFlushComplete(() => {
|
|
1937
|
+
setReady(true);
|
|
1938
|
+
});
|
|
1939
|
+
return unsubscribe;
|
|
1940
|
+
}, []);
|
|
1941
|
+
return ready;
|
|
1942
|
+
}
|
|
1943
|
+
function useIdleGate(key, priority, callback) {
|
|
1944
|
+
const [ready, setReady] = (0, import_react5.useState)(false);
|
|
1945
|
+
const callbackRef = (0, import_react5.useRef)(callback);
|
|
1946
|
+
callbackRef.current = callback;
|
|
1947
|
+
(0, import_react5.useEffect)(() => {
|
|
1948
|
+
const unregister = registerIdleTask(key, priority, async () => {
|
|
1949
|
+
await callbackRef.current();
|
|
1950
|
+
setReady(true);
|
|
1951
|
+
});
|
|
1952
|
+
return unregister;
|
|
1953
|
+
}, [key, priority]);
|
|
1954
|
+
return { ready };
|
|
1955
|
+
}
|
|
1956
|
+
function useIdleRegister() {
|
|
1957
|
+
const unregisterRefs = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1958
|
+
(0, import_react5.useEffect)(() => {
|
|
1959
|
+
const refs = unregisterRefs.current;
|
|
1960
|
+
return () => {
|
|
1961
|
+
refs.forEach((unregister) => unregister());
|
|
1962
|
+
refs.clear();
|
|
1963
|
+
};
|
|
1964
|
+
}, []);
|
|
1965
|
+
return (0, import_react5.useCallback)(
|
|
1966
|
+
(key, priority, callback) => {
|
|
1967
|
+
unregisterRefs.current.get(key)?.();
|
|
1968
|
+
const unregister = registerIdleTask(key, priority, callback);
|
|
1969
|
+
unregisterRefs.current.set(key, unregister);
|
|
1970
|
+
},
|
|
1971
|
+
[]
|
|
1972
|
+
);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// src/url-state.ts
|
|
1976
|
+
var import_react6 = require("react");
|
|
1977
|
+
var URL_STATE_EVENT = "matrx:url-state";
|
|
1978
|
+
var ROUTER_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.kit.url-state-router");
|
|
1979
|
+
function getRouter() {
|
|
1980
|
+
return globalThis[ROUTER_SLOT] ?? null;
|
|
1981
|
+
}
|
|
1982
|
+
function setUrlStateRouter(router) {
|
|
1983
|
+
globalThis[ROUTER_SLOT] = router;
|
|
1984
|
+
}
|
|
1985
|
+
function currentLocation() {
|
|
1986
|
+
const custom = getRouter()?.getLocation;
|
|
1987
|
+
if (custom) return custom();
|
|
1988
|
+
return {
|
|
1989
|
+
pathname: window.location.pathname,
|
|
1990
|
+
search: window.location.search,
|
|
1991
|
+
hash: window.location.hash
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
function subscribeToUrl(listener) {
|
|
1995
|
+
window.addEventListener("popstate", listener);
|
|
1996
|
+
window.addEventListener(URL_STATE_EVENT, listener);
|
|
1997
|
+
return () => {
|
|
1998
|
+
window.removeEventListener("popstate", listener);
|
|
1999
|
+
window.removeEventListener(URL_STATE_EVENT, listener);
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
function getUrlSnapshot() {
|
|
2003
|
+
return currentLocation().search;
|
|
2004
|
+
}
|
|
2005
|
+
function getServerUrlSnapshot() {
|
|
2006
|
+
return "";
|
|
2007
|
+
}
|
|
2008
|
+
function useUrlSearchParams() {
|
|
2009
|
+
const search = (0, import_react6.useSyncExternalStore)(
|
|
2010
|
+
subscribeToUrl,
|
|
2011
|
+
getUrlSnapshot,
|
|
2012
|
+
getServerUrlSnapshot
|
|
2013
|
+
);
|
|
2014
|
+
return (0, import_react6.useMemo)(() => new URLSearchParams(search), [search]);
|
|
2015
|
+
}
|
|
2016
|
+
function commitUrlParams(patch, history) {
|
|
2017
|
+
const location = currentLocation();
|
|
2018
|
+
const params = new URLSearchParams(location.search);
|
|
2019
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
2020
|
+
if (value === null || value === "") params.delete(key);
|
|
2021
|
+
else params.set(key, value);
|
|
2022
|
+
}
|
|
2023
|
+
const query = params.toString();
|
|
2024
|
+
const next = `${location.pathname}${query ? `?${query}` : ""}${location.hash}`;
|
|
2025
|
+
const current = `${location.pathname}${location.search}${location.hash}`;
|
|
2026
|
+
if (next === current) return;
|
|
2027
|
+
const router = getRouter();
|
|
2028
|
+
if (router) {
|
|
2029
|
+
if (history === "replace") router.replace(next);
|
|
2030
|
+
else router.push(next);
|
|
2031
|
+
} else if (history === "replace") {
|
|
2032
|
+
window.history.replaceState(window.history.state, "", next);
|
|
2033
|
+
} else {
|
|
2034
|
+
window.history.pushState(window.history.state, "", next);
|
|
2035
|
+
}
|
|
2036
|
+
window.dispatchEvent(new Event(URL_STATE_EVENT));
|
|
2037
|
+
}
|
|
2038
|
+
function historyModeForParamChange(current, next, textKeys) {
|
|
2039
|
+
const keys = /* @__PURE__ */ new Set([...current.keys(), ...next.keys()]);
|
|
2040
|
+
const changed = [...keys].filter(
|
|
2041
|
+
(key) => current.get(key) !== next.get(key)
|
|
2042
|
+
);
|
|
2043
|
+
if (changed.length === 0) return "replace";
|
|
2044
|
+
return changed.every((key) => textKeys.includes(key)) ? "replace" : "push";
|
|
2045
|
+
}
|
|
2046
|
+
function useUrlState(key, codec) {
|
|
2047
|
+
const searchParams = useUrlSearchParams();
|
|
2048
|
+
const value = codec.parse(searchParams.get(key));
|
|
2049
|
+
const setValue = (next, options) => {
|
|
2050
|
+
commitUrlParams(
|
|
2051
|
+
{ [key]: codec.serialize(next) },
|
|
2052
|
+
options?.history ?? "push"
|
|
2053
|
+
);
|
|
2054
|
+
};
|
|
2055
|
+
return [value, setValue];
|
|
2056
|
+
}
|
|
2057
|
+
function stringUrlCodec(defaultValue = "") {
|
|
2058
|
+
return {
|
|
2059
|
+
defaultValue,
|
|
2060
|
+
parse: (raw) => raw ?? defaultValue,
|
|
2061
|
+
serialize: (value) => value === defaultValue ? null : value
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
function enumUrlCodec(values, defaultValue) {
|
|
2065
|
+
const allowed = new Set(values);
|
|
2066
|
+
return {
|
|
2067
|
+
defaultValue,
|
|
2068
|
+
parse: (raw) => raw && allowed.has(raw) ? raw : defaultValue,
|
|
2069
|
+
serialize: (value) => value === defaultValue ? null : value
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
function booleanUrlCodec(defaultValue = false) {
|
|
2073
|
+
return {
|
|
2074
|
+
defaultValue,
|
|
2075
|
+
parse: (raw) => raw === null ? defaultValue : raw === "1",
|
|
2076
|
+
serialize: (value) => value === defaultValue ? null : value ? "1" : "0"
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
function positiveIntegerUrlCodec(defaultValue) {
|
|
2080
|
+
return {
|
|
2081
|
+
defaultValue,
|
|
2082
|
+
parse: (raw) => {
|
|
2083
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
2084
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultValue;
|
|
2085
|
+
},
|
|
2086
|
+
serialize: (value) => value === defaultValue || !Number.isFinite(value) || value <= 0 ? null : String(Math.trunc(value))
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
function jsonUrlCodec(defaultValue, isValid) {
|
|
2090
|
+
return {
|
|
2091
|
+
defaultValue,
|
|
2092
|
+
parse: (raw) => {
|
|
2093
|
+
if (!raw) return defaultValue;
|
|
2094
|
+
try {
|
|
2095
|
+
const parsed = JSON.parse(raw);
|
|
2096
|
+
return isValid(parsed) ? parsed : defaultValue;
|
|
2097
|
+
} catch {
|
|
2098
|
+
return defaultValue;
|
|
2099
|
+
}
|
|
2100
|
+
},
|
|
2101
|
+
serialize: (value) => JSON.stringify(value) === JSON.stringify(defaultValue) ? null : JSON.stringify(value)
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
function useMirroredUrlState(options) {
|
|
2105
|
+
const { parse, toParams, isSame, textKeys = [], resetKey } = options;
|
|
2106
|
+
const params = useUrlSearchParams();
|
|
2107
|
+
const optionsRef = (0, import_react6.useRef)(options);
|
|
2108
|
+
optionsRef.current = options;
|
|
2109
|
+
const [value, setValue] = (0, import_react6.useState)(
|
|
2110
|
+
() => parse(
|
|
2111
|
+
typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(currentLocation().search)
|
|
2112
|
+
)
|
|
2113
|
+
);
|
|
2114
|
+
(0, import_react6.useEffect)(() => {
|
|
2115
|
+
if (typeof window === "undefined") return;
|
|
2116
|
+
const current = new URLSearchParams(currentLocation().search);
|
|
2117
|
+
const patch = optionsRef.current.toParams(value);
|
|
2118
|
+
const next = new URLSearchParams(current);
|
|
2119
|
+
for (const [key, v] of Object.entries(patch)) {
|
|
2120
|
+
if (v === null || v === "") next.delete(key);
|
|
2121
|
+
else next.set(key, v);
|
|
2122
|
+
}
|
|
2123
|
+
if (next.toString() === current.toString()) return;
|
|
2124
|
+
commitUrlParams(patch, historyModeForParamChange(current, next, textKeys));
|
|
2125
|
+
}, [value]);
|
|
2126
|
+
(0, import_react6.useEffect)(() => {
|
|
2127
|
+
const fromUrl = optionsRef.current.parse(
|
|
2128
|
+
new URLSearchParams(currentLocation().search)
|
|
2129
|
+
);
|
|
2130
|
+
setValue((prev) => optionsRef.current.isSame(prev, fromUrl) ? prev : fromUrl);
|
|
2131
|
+
}, [params]);
|
|
2132
|
+
const previousResetKey = (0, import_react6.useRef)(resetKey);
|
|
2133
|
+
(0, import_react6.useEffect)(() => {
|
|
2134
|
+
const previous = previousResetKey.current;
|
|
2135
|
+
previousResetKey.current = resetKey;
|
|
2136
|
+
if (previous === resetKey) return;
|
|
2137
|
+
if (previous === void 0 || previous === "") return;
|
|
2138
|
+
if (resetKey === void 0 || resetKey === "") return;
|
|
2139
|
+
const cleared = optionsRef.current.parse(new URLSearchParams());
|
|
2140
|
+
setValue(cleared);
|
|
2141
|
+
commitUrlParams(optionsRef.current.toParams(cleared), "replace");
|
|
2142
|
+
}, [resetKey]);
|
|
2143
|
+
const update = (0, import_react6.useCallback)((updater) => {
|
|
2144
|
+
setValue(
|
|
2145
|
+
(prev) => typeof updater === "function" ? updater(prev) : updater
|
|
2146
|
+
);
|
|
2147
|
+
}, []);
|
|
2148
|
+
return [value, update];
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// src/idb-store/store-manager.ts
|
|
2152
|
+
var import_idb = require("idb");
|
|
2153
|
+
var DBStoreManager = class {
|
|
2154
|
+
db = null;
|
|
2155
|
+
dbName;
|
|
2156
|
+
version;
|
|
2157
|
+
constructor(dbName, version2) {
|
|
2158
|
+
this.dbName = dbName;
|
|
2159
|
+
this.version = version2;
|
|
2160
|
+
}
|
|
2161
|
+
async initDB() {
|
|
2162
|
+
if (this.db) return;
|
|
2163
|
+
try {
|
|
2164
|
+
this.db = await (0, import_idb.openDB)(this.dbName, this.version, {
|
|
2165
|
+
upgrade: (db) => {
|
|
2166
|
+
this.setupStores(db);
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
} catch (error) {
|
|
2170
|
+
console.error("Failed to initialize database:", error);
|
|
2171
|
+
throw error;
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
// `add`/`get` carry their own generic (like `query` below) so a subclass
|
|
2175
|
+
// whose `T` covers one IDB object store (e.g. Recording) can still read/write
|
|
2176
|
+
// a different store's record shape (e.g. RecordingChunk) without a cast —
|
|
2177
|
+
// see the audio store in the original host, which manages both `recordings`
|
|
2178
|
+
// and `chunks` stores.
|
|
2179
|
+
async add(storeName, data) {
|
|
2180
|
+
try {
|
|
2181
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2182
|
+
const id = await this.db.add(storeName, data);
|
|
2183
|
+
return { data: id.toString(), error: null };
|
|
2184
|
+
} catch (error) {
|
|
2185
|
+
return { data: null, error };
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
async get(storeName, id) {
|
|
2189
|
+
try {
|
|
2190
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2191
|
+
const result = await this.db.get(storeName, id);
|
|
2192
|
+
return { data: result, error: null };
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
return { data: null, error };
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
async getAll(storeName) {
|
|
2198
|
+
try {
|
|
2199
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2200
|
+
const result = await this.db.getAll(storeName);
|
|
2201
|
+
return { data: result, error: null };
|
|
2202
|
+
} catch (error) {
|
|
2203
|
+
return { data: null, error };
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
async update(storeName, id, data) {
|
|
2207
|
+
try {
|
|
2208
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2209
|
+
const existing = await this.db.get(storeName, id);
|
|
2210
|
+
if (!existing) throw new Error("Record not found");
|
|
2211
|
+
const updated = { ...existing, ...data };
|
|
2212
|
+
await this.db.put(storeName, updated);
|
|
2213
|
+
return { data: true, error: null };
|
|
2214
|
+
} catch (error) {
|
|
2215
|
+
return { data: null, error };
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
async delete(storeName, id) {
|
|
2219
|
+
try {
|
|
2220
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2221
|
+
await this.db.delete(storeName, id);
|
|
2222
|
+
return { data: true, error: null };
|
|
2223
|
+
} catch (error) {
|
|
2224
|
+
return { data: null, error };
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
async query(storeName, indexName, query) {
|
|
2228
|
+
try {
|
|
2229
|
+
if (!this.db) throw new Error("Database not initialized");
|
|
2230
|
+
const result = await this.db.getAllFromIndex(storeName, indexName, query);
|
|
2231
|
+
return { data: result, error: null };
|
|
2232
|
+
} catch (error) {
|
|
2233
|
+
return { data: null, error };
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
// src/idb-store/store-interface.ts
|
|
2239
|
+
var PublicStoreManager = class extends DBStoreManager {
|
|
2240
|
+
constructor(dbName, version2) {
|
|
2241
|
+
super(dbName, version2);
|
|
2242
|
+
}
|
|
2243
|
+
addItem(storeName, data) {
|
|
2244
|
+
return this.add(storeName, data);
|
|
2245
|
+
}
|
|
2246
|
+
getItem(storeName, id) {
|
|
2247
|
+
return this.get(storeName, id);
|
|
2248
|
+
}
|
|
2249
|
+
getAllItems(storeName) {
|
|
2250
|
+
return this.getAll(storeName);
|
|
2251
|
+
}
|
|
2252
|
+
updateItem(storeName, id, data) {
|
|
2253
|
+
return this.update(storeName, id, data);
|
|
2254
|
+
}
|
|
2255
|
+
deleteItem(storeName, id) {
|
|
2256
|
+
return this.delete(storeName, id);
|
|
2257
|
+
}
|
|
2258
|
+
queryItems(storeName, indexName, query) {
|
|
2259
|
+
return this.query(storeName, indexName, query);
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// src/idb-store/feature-store.ts
|
|
2264
|
+
var FeatureStore = class extends PublicStoreManager {
|
|
2265
|
+
storeName;
|
|
2266
|
+
constructor(dbName, version2, storeName) {
|
|
2267
|
+
super(dbName, version2);
|
|
2268
|
+
this.storeName = storeName;
|
|
2269
|
+
this.initDB().catch(() => {
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
getStoreName() {
|
|
2273
|
+
return this.storeName;
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
// src/idb-store/singleton.ts
|
|
2278
|
+
var STATE_SLOT4 = /* @__PURE__ */ Symbol.for("ai-matrx.kit.idb-store-state");
|
|
2279
|
+
function getState3() {
|
|
2280
|
+
const holder = globalThis;
|
|
2281
|
+
let state = holder[STATE_SLOT4];
|
|
2282
|
+
if (!state) {
|
|
2283
|
+
state = { instances: /* @__PURE__ */ new Map() };
|
|
2284
|
+
holder[STATE_SLOT4] = state;
|
|
2285
|
+
}
|
|
2286
|
+
return state;
|
|
2287
|
+
}
|
|
2288
|
+
function getIdbStoreSingleton(key, create) {
|
|
2289
|
+
const state = getState3();
|
|
2290
|
+
if (!state.instances.has(key)) {
|
|
2291
|
+
state.instances.set(key, create());
|
|
2292
|
+
}
|
|
2293
|
+
return state.instances.get(key);
|
|
2294
|
+
}
|
|
2295
|
+
function _resetIdbStoreSingletons() {
|
|
2296
|
+
getState3().instances.clear();
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
// src/color-util/lab-delta.ts
|
|
2300
|
+
function round(value, digits = 0) {
|
|
2301
|
+
const factor = Math.pow(10, digits);
|
|
2302
|
+
return Math.round(factor * value) / factor + 0;
|
|
2303
|
+
}
|
|
2304
|
+
function clamp(value, min = 0, max = 1) {
|
|
2305
|
+
return value > max ? max : value > min ? value : min;
|
|
2306
|
+
}
|
|
2307
|
+
function linearize(channel) {
|
|
2308
|
+
const c = channel / 255;
|
|
2309
|
+
return c < 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
2310
|
+
}
|
|
2311
|
+
var WHITE_X = 96.422;
|
|
2312
|
+
var WHITE_Y = 100;
|
|
2313
|
+
var WHITE_Z = 82.521;
|
|
2314
|
+
var EPSILON = 216 / 24389;
|
|
2315
|
+
var KAPPA = 24389 / 27;
|
|
2316
|
+
function rgbToXyz(rgb) {
|
|
2317
|
+
const r = linearize(rgb.r);
|
|
2318
|
+
const g = linearize(rgb.g);
|
|
2319
|
+
const b = linearize(rgb.b);
|
|
2320
|
+
const d65 = {
|
|
2321
|
+
x: 100 * (0.4124564 * r + 0.3575761 * g + 0.1804375 * b),
|
|
2322
|
+
y: 100 * (0.2126729 * r + 0.7151522 * g + 0.072175 * b),
|
|
2323
|
+
z: 100 * (0.0193339 * r + 0.119192 * g + 0.9503041 * b)
|
|
2324
|
+
};
|
|
2325
|
+
const d50 = {
|
|
2326
|
+
x: 1.0478112 * d65.x + 0.0228866 * d65.y + -0.050127 * d65.z,
|
|
2327
|
+
y: 0.0295424 * d65.x + 0.9904844 * d65.y + -0.0170491 * d65.z,
|
|
2328
|
+
z: -92345e-7 * d65.x + 0.0150436 * d65.y + 0.7521316 * d65.z
|
|
2329
|
+
};
|
|
2330
|
+
return {
|
|
2331
|
+
x: clamp(d50.x, 0, WHITE_X),
|
|
2332
|
+
y: clamp(d50.y, 0, WHITE_Y),
|
|
2333
|
+
z: clamp(d50.z, 0, WHITE_Z)
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
function rgbToLab(rgb) {
|
|
2337
|
+
const xyz = rgbToXyz(rgb);
|
|
2338
|
+
let x = xyz.x / WHITE_X;
|
|
2339
|
+
let y = xyz.y / WHITE_Y;
|
|
2340
|
+
let z = xyz.z / WHITE_Z;
|
|
2341
|
+
x = x > EPSILON ? Math.cbrt(x) : (KAPPA * x + 16) / 116;
|
|
2342
|
+
y = y > EPSILON ? Math.cbrt(y) : (KAPPA * y + 16) / 116;
|
|
2343
|
+
z = z > EPSILON ? Math.cbrt(z) : (KAPPA * z + 16) / 116;
|
|
2344
|
+
return {
|
|
2345
|
+
l: round(116 * y - 16, 2),
|
|
2346
|
+
a: round(500 * (x - y), 2),
|
|
2347
|
+
b: round(200 * (y - z), 2)
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
function ciede2000(lab1, lab2) {
|
|
2351
|
+
const { l: l1, a: a1, b: b1 } = lab1;
|
|
2352
|
+
const { l: l2, a: a2, b: b2 } = lab2;
|
|
2353
|
+
const toDeg = 180 / Math.PI;
|
|
2354
|
+
const toRad = Math.PI / 180;
|
|
2355
|
+
const c1 = Math.pow(Math.pow(a1, 2) + Math.pow(b1, 2), 0.5);
|
|
2356
|
+
const c2 = Math.pow(Math.pow(a2, 2) + Math.pow(b2, 2), 0.5);
|
|
2357
|
+
const lBar = (l1 + l2) / 2;
|
|
2358
|
+
const cBar7 = Math.pow((c1 + c2) / 2, 7);
|
|
2359
|
+
const g = 0.5 * (1 - Math.pow(cBar7 / (cBar7 + Math.pow(25, 7)), 0.5));
|
|
2360
|
+
const a1p = a1 * (1 + g);
|
|
2361
|
+
const a2p = a2 * (1 + g);
|
|
2362
|
+
const c1p = Math.pow(Math.pow(a1p, 2) + Math.pow(b1, 2), 0.5);
|
|
2363
|
+
const c2p = Math.pow(Math.pow(a2p, 2) + Math.pow(b2, 2), 0.5);
|
|
2364
|
+
const cBarP = (c1p + c2p) / 2;
|
|
2365
|
+
let h1p = a1p === 0 && b1 === 0 ? 0 : Math.atan2(b1, a1p) * toDeg;
|
|
2366
|
+
let h2p = a2p === 0 && b2 === 0 ? 0 : Math.atan2(b2, a2p) * toDeg;
|
|
2367
|
+
if (h1p < 0) h1p += 360;
|
|
2368
|
+
if (h2p < 0) h2p += 360;
|
|
2369
|
+
let dhp = h2p - h1p;
|
|
2370
|
+
const hAbs = Math.abs(h2p - h1p);
|
|
2371
|
+
if (hAbs > 180 && h2p <= h1p) {
|
|
2372
|
+
dhp += 360;
|
|
2373
|
+
} else if (hAbs > 180 && h2p > h1p) {
|
|
2374
|
+
dhp -= 360;
|
|
2375
|
+
}
|
|
2376
|
+
let hBarP = h1p + h2p;
|
|
2377
|
+
if (hAbs <= 180) {
|
|
2378
|
+
hBarP /= 2;
|
|
2379
|
+
} else {
|
|
2380
|
+
hBarP = (h1p + h2p < 360 ? hBarP + 360 : hBarP - 360) / 2;
|
|
2381
|
+
}
|
|
2382
|
+
const t = 1 - 0.17 * Math.cos(toRad * (hBarP - 30)) + 0.24 * Math.cos(2 * toRad * hBarP) + 0.32 * Math.cos(toRad * (3 * hBarP + 6)) - 0.2 * Math.cos(toRad * (4 * hBarP - 63));
|
|
2383
|
+
const dL = l2 - l1;
|
|
2384
|
+
const dCp = c2p - c1p;
|
|
2385
|
+
const dHp = 2 * Math.sin(toRad * dhp / 2) * Math.pow(c1p * c2p, 0.5);
|
|
2386
|
+
const sl = 1 + 0.015 * Math.pow(lBar - 50, 2) / Math.pow(20 + Math.pow(lBar - 50, 2), 0.5);
|
|
2387
|
+
const sc = 1 + 0.045 * cBarP;
|
|
2388
|
+
const sh = 1 + 0.015 * cBarP * t;
|
|
2389
|
+
const dTheta = 30 * Math.exp(-1 * Math.pow((hBarP - 275) / 25, 2));
|
|
2390
|
+
const cBarP7 = Math.pow(cBarP, 7);
|
|
2391
|
+
const rt = -2 * Math.pow(cBarP7 / (cBarP7 + Math.pow(25, 7)), 0.5) * Math.sin(2 * toRad * dTheta);
|
|
2392
|
+
return Math.pow(
|
|
2393
|
+
Math.pow(dL / 1 / sl, 2) + Math.pow(dCp / 1 / sc, 2) + Math.pow(dHp / 1 / sh, 2) + rt * dCp * dHp / (1 * sc * 1 * sh),
|
|
2394
|
+
0.5
|
|
2395
|
+
);
|
|
2396
|
+
}
|
|
2397
|
+
function rgbDelta(rgb1, rgb2) {
|
|
2398
|
+
return clamp(round(ciede2000(rgbToLab(rgb1), rgbToLab(rgb2)) / 100, 3));
|
|
2399
|
+
}
|
|
2400
|
+
var HEX_RE = /^#?([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
2401
|
+
var RGB_FN_RE = /^rgba?\(\s*([+-]?[\d.]+)\s*[,\s]\s*([+-]?[\d.]+)\s*[,\s]\s*([+-]?[\d.]+)(?:\s*[,/]\s*[+-]?[\d.]+%?)?\s*\)$/i;
|
|
2402
|
+
function parseHexOrRgb(input) {
|
|
2403
|
+
const text = input.trim();
|
|
2404
|
+
const hexMatch = text.match(HEX_RE);
|
|
2405
|
+
if (hexMatch) {
|
|
2406
|
+
const hex = hexMatch[1];
|
|
2407
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
2408
|
+
return {
|
|
2409
|
+
r: parseInt(hex[0] + hex[0], 16),
|
|
2410
|
+
g: parseInt(hex[1] + hex[1], 16),
|
|
2411
|
+
b: parseInt(hex[2] + hex[2], 16)
|
|
2412
|
+
};
|
|
2413
|
+
}
|
|
2414
|
+
return {
|
|
2415
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
2416
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
2417
|
+
b: parseInt(hex.slice(4, 6), 16)
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
const rgbMatch = text.match(RGB_FN_RE);
|
|
2421
|
+
if (rgbMatch) {
|
|
2422
|
+
const r = Number(rgbMatch[1]);
|
|
2423
|
+
const g = Number(rgbMatch[2]);
|
|
2424
|
+
const b = Number(rgbMatch[3]);
|
|
2425
|
+
if ([r, g, b].some((v) => Number.isNaN(v))) return null;
|
|
2426
|
+
return { r: clamp(r, 0, 255), g: clamp(g, 0, 255), b: clamp(b, 0, 255) };
|
|
2427
|
+
}
|
|
2428
|
+
return null;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// src/color-util/tailwind-colors.ts
|
|
2432
|
+
var tailwindColors = [
|
|
2433
|
+
{
|
|
2434
|
+
name: "Slate",
|
|
2435
|
+
shades: {
|
|
2436
|
+
"50": "#f8fafc",
|
|
2437
|
+
"100": "#f1f5f9",
|
|
2438
|
+
"200": "#e2e8f0",
|
|
2439
|
+
"300": "#cbd5e1",
|
|
2440
|
+
"400": "#94a3b8",
|
|
2441
|
+
"500": "#64748b",
|
|
2442
|
+
"600": "#475569",
|
|
2443
|
+
"700": "#334155",
|
|
2444
|
+
"800": "#1e293b",
|
|
2445
|
+
"900": "#0f172a",
|
|
2446
|
+
"950": "#020617"
|
|
2447
|
+
}
|
|
2448
|
+
},
|
|
2449
|
+
{
|
|
2450
|
+
name: "Gray",
|
|
2451
|
+
shades: {
|
|
2452
|
+
"50": "#f9fafb",
|
|
2453
|
+
"100": "#f3f4f6",
|
|
2454
|
+
"200": "#e5e7eb",
|
|
2455
|
+
"300": "#d1d5db",
|
|
2456
|
+
"400": "#9ca3af",
|
|
2457
|
+
"500": "#6b7280",
|
|
2458
|
+
"600": "#4b5563",
|
|
2459
|
+
"700": "#374151",
|
|
2460
|
+
"800": "#1f2937",
|
|
2461
|
+
"900": "#111827",
|
|
2462
|
+
"950": "#030712"
|
|
2463
|
+
}
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
name: "Zinc",
|
|
2467
|
+
shades: {
|
|
2468
|
+
"50": "#fafafa",
|
|
2469
|
+
"100": "#f4f4f5",
|
|
2470
|
+
"200": "#e4e4e7",
|
|
2471
|
+
"300": "#d4d4d8",
|
|
2472
|
+
"400": "#a1a1aa",
|
|
2473
|
+
"500": "#71717a",
|
|
2474
|
+
"600": "#52525b",
|
|
2475
|
+
"700": "#3f3f46",
|
|
2476
|
+
"800": "#27272a",
|
|
2477
|
+
"900": "#18181b",
|
|
2478
|
+
"950": "#09090b"
|
|
2479
|
+
}
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
name: "Neutral",
|
|
2483
|
+
shades: {
|
|
2484
|
+
"50": "#fafafa",
|
|
2485
|
+
"100": "#f5f5f5",
|
|
2486
|
+
"200": "#e5e5e5",
|
|
2487
|
+
"300": "#d4d4d4",
|
|
2488
|
+
"400": "#a3a3a3",
|
|
2489
|
+
"500": "#737373",
|
|
2490
|
+
"600": "#525252",
|
|
2491
|
+
"700": "#404040",
|
|
2492
|
+
"800": "#262626",
|
|
2493
|
+
"900": "#171717",
|
|
2494
|
+
"950": "#0a0a0a"
|
|
2495
|
+
}
|
|
2496
|
+
},
|
|
2497
|
+
{
|
|
2498
|
+
name: "Stone",
|
|
2499
|
+
shades: {
|
|
2500
|
+
"50": "#fafaf9",
|
|
2501
|
+
"100": "#f5f5f4",
|
|
2502
|
+
"200": "#e7e5e4",
|
|
2503
|
+
"300": "#d6d3d1",
|
|
2504
|
+
"400": "#a8a29e",
|
|
2505
|
+
"500": "#78716c",
|
|
2506
|
+
"600": "#57534e",
|
|
2507
|
+
"700": "#44403c",
|
|
2508
|
+
"800": "#292524",
|
|
2509
|
+
"900": "#1c1917",
|
|
2510
|
+
"950": "#0c0a09"
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
name: "Red",
|
|
2515
|
+
shades: {
|
|
2516
|
+
"50": "#fef2f2",
|
|
2517
|
+
"100": "#fee2e2",
|
|
2518
|
+
"200": "#fecaca",
|
|
2519
|
+
"300": "#fca5a5",
|
|
2520
|
+
"400": "#f87171",
|
|
2521
|
+
"500": "#ef4444",
|
|
2522
|
+
"600": "#dc2626",
|
|
2523
|
+
"700": "#b91c1c",
|
|
2524
|
+
"800": "#991b1b",
|
|
2525
|
+
"900": "#7f1d1d",
|
|
2526
|
+
"950": "#450a0a"
|
|
2527
|
+
}
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
name: "Orange",
|
|
2531
|
+
shades: {
|
|
2532
|
+
"50": "#fff7ed",
|
|
2533
|
+
"100": "#ffedd5",
|
|
2534
|
+
"200": "#fed7aa",
|
|
2535
|
+
"300": "#fdba74",
|
|
2536
|
+
"400": "#fb923c",
|
|
2537
|
+
"500": "#f97316",
|
|
2538
|
+
"600": "#ea580c",
|
|
2539
|
+
"700": "#c2410c",
|
|
2540
|
+
"800": "#9a3412",
|
|
2541
|
+
"900": "#7c2d12",
|
|
2542
|
+
"950": "#431407"
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2545
|
+
{
|
|
2546
|
+
name: "Amber",
|
|
2547
|
+
shades: {
|
|
2548
|
+
"50": "#fffbeb",
|
|
2549
|
+
"100": "#fef3c7",
|
|
2550
|
+
"200": "#fde68a",
|
|
2551
|
+
"300": "#fcd34d",
|
|
2552
|
+
"400": "#fbbf24",
|
|
2553
|
+
"500": "#f59e0b",
|
|
2554
|
+
"600": "#d97706",
|
|
2555
|
+
"700": "#b45309",
|
|
2556
|
+
"800": "#92400e",
|
|
2557
|
+
"900": "#78350f",
|
|
2558
|
+
"950": "#451a03"
|
|
2559
|
+
}
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
name: "Yellow",
|
|
2563
|
+
shades: {
|
|
2564
|
+
"50": "#fefce8",
|
|
2565
|
+
"100": "#fef9c3",
|
|
2566
|
+
"200": "#fef08a",
|
|
2567
|
+
"300": "#fde047",
|
|
2568
|
+
"400": "#facc15",
|
|
2569
|
+
"500": "#eab308",
|
|
2570
|
+
"600": "#ca8a04",
|
|
2571
|
+
"700": "#a16207",
|
|
2572
|
+
"800": "#854d0e",
|
|
2573
|
+
"900": "#713f12",
|
|
2574
|
+
"950": "#422006"
|
|
2575
|
+
}
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
name: "Lime",
|
|
2579
|
+
shades: {
|
|
2580
|
+
"50": "#f7fee7",
|
|
2581
|
+
"100": "#ecfccb",
|
|
2582
|
+
"200": "#d9f99d",
|
|
2583
|
+
"300": "#bef264",
|
|
2584
|
+
"400": "#a3e635",
|
|
2585
|
+
"500": "#84cc16",
|
|
2586
|
+
"600": "#65a30d",
|
|
2587
|
+
"700": "#4d7c0f",
|
|
2588
|
+
"800": "#3f6212",
|
|
2589
|
+
"900": "#365314",
|
|
2590
|
+
"950": "#1a2e05"
|
|
2591
|
+
}
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
name: "Green",
|
|
2595
|
+
shades: {
|
|
2596
|
+
"50": "#f0fdf4",
|
|
2597
|
+
"100": "#dcfce7",
|
|
2598
|
+
"200": "#bbf7d0",
|
|
2599
|
+
"300": "#86efac",
|
|
2600
|
+
"400": "#4ade80",
|
|
2601
|
+
"500": "#22c55e",
|
|
2602
|
+
"600": "#16a34a",
|
|
2603
|
+
"700": "#15803d",
|
|
2604
|
+
"800": "#166534",
|
|
2605
|
+
"900": "#14532d",
|
|
2606
|
+
"950": "#052e16"
|
|
2607
|
+
}
|
|
2608
|
+
},
|
|
2609
|
+
{
|
|
2610
|
+
name: "Emerald",
|
|
2611
|
+
shades: {
|
|
2612
|
+
"50": "#ecfdf5",
|
|
2613
|
+
"100": "#d1fae5",
|
|
2614
|
+
"200": "#a7f3d0",
|
|
2615
|
+
"300": "#6ee7b7",
|
|
2616
|
+
"400": "#34d399",
|
|
2617
|
+
"500": "#10b981",
|
|
2618
|
+
"600": "#059669",
|
|
2619
|
+
"700": "#047857",
|
|
2620
|
+
"800": "#065f46",
|
|
2621
|
+
"900": "#064e3b",
|
|
2622
|
+
"950": "#022c22"
|
|
2623
|
+
}
|
|
2624
|
+
},
|
|
2625
|
+
{
|
|
2626
|
+
name: "Teal",
|
|
2627
|
+
shades: {
|
|
2628
|
+
"50": "#f0fdfa",
|
|
2629
|
+
"100": "#ccfbf1",
|
|
2630
|
+
"200": "#99f6e4",
|
|
2631
|
+
"300": "#5eead4",
|
|
2632
|
+
"400": "#2dd4bf",
|
|
2633
|
+
"500": "#14b8a6",
|
|
2634
|
+
"600": "#0d9488",
|
|
2635
|
+
"700": "#0f766e",
|
|
2636
|
+
"800": "#115e59",
|
|
2637
|
+
"900": "#134e4a",
|
|
2638
|
+
"950": "#042f2e"
|
|
2639
|
+
}
|
|
2640
|
+
},
|
|
2641
|
+
{
|
|
2642
|
+
name: "Cyan",
|
|
2643
|
+
shades: {
|
|
2644
|
+
"50": "#ecfeff",
|
|
2645
|
+
"100": "#cffafe",
|
|
2646
|
+
"200": "#a5f3fc",
|
|
2647
|
+
"300": "#67e8f9",
|
|
2648
|
+
"400": "#22d3ee",
|
|
2649
|
+
"500": "#06b6d4",
|
|
2650
|
+
"600": "#0891b2",
|
|
2651
|
+
"700": "#0e7490",
|
|
2652
|
+
"800": "#155e75",
|
|
2653
|
+
"900": "#164e63",
|
|
2654
|
+
"950": "#083344"
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
{
|
|
2658
|
+
name: "Sky",
|
|
2659
|
+
shades: {
|
|
2660
|
+
"50": "#f0f9ff",
|
|
2661
|
+
"100": "#e0f2fe",
|
|
2662
|
+
"200": "#bae6fd",
|
|
2663
|
+
"300": "#7dd3fc",
|
|
2664
|
+
"400": "#38bdf8",
|
|
2665
|
+
"500": "#0ea5e9",
|
|
2666
|
+
"600": "#0284c7",
|
|
2667
|
+
"700": "#0369a1",
|
|
2668
|
+
"800": "#075985",
|
|
2669
|
+
"900": "#0c4a6e",
|
|
2670
|
+
"950": "#082f49"
|
|
2671
|
+
}
|
|
2672
|
+
},
|
|
2673
|
+
{
|
|
2674
|
+
name: "Blue",
|
|
2675
|
+
shades: {
|
|
2676
|
+
"50": "#eff6ff",
|
|
2677
|
+
"100": "#dbeafe",
|
|
2678
|
+
"200": "#bfdbfe",
|
|
2679
|
+
"300": "#93c5fd",
|
|
2680
|
+
"400": "#60a5fa",
|
|
2681
|
+
"500": "#3b82f6",
|
|
2682
|
+
"600": "#2563eb",
|
|
2683
|
+
"700": "#1d4ed8",
|
|
2684
|
+
"800": "#1e40af",
|
|
2685
|
+
"900": "#1e3a8a",
|
|
2686
|
+
"950": "#172554"
|
|
2687
|
+
}
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
name: "Indigo",
|
|
2691
|
+
shades: {
|
|
2692
|
+
"50": "#eef2ff",
|
|
2693
|
+
"100": "#e0e7ff",
|
|
2694
|
+
"200": "#c7d2fe",
|
|
2695
|
+
"300": "#a5b4fc",
|
|
2696
|
+
"400": "#818cf8",
|
|
2697
|
+
"500": "#6366f1",
|
|
2698
|
+
"600": "#4f46e5",
|
|
2699
|
+
"700": "#4338ca",
|
|
2700
|
+
"800": "#3730a3",
|
|
2701
|
+
"900": "#312e81",
|
|
2702
|
+
"950": "#1e1b4b"
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
name: "Violet",
|
|
2707
|
+
shades: {
|
|
2708
|
+
"50": "#f5f3ff",
|
|
2709
|
+
"100": "#ede9fe",
|
|
2710
|
+
"200": "#ddd6fe",
|
|
2711
|
+
"300": "#c4b5fd",
|
|
2712
|
+
"400": "#a78bfa",
|
|
2713
|
+
"500": "#8b5cf6",
|
|
2714
|
+
"600": "#7c3aed",
|
|
2715
|
+
"700": "#6d28d9",
|
|
2716
|
+
"800": "#5b21b6",
|
|
2717
|
+
"900": "#4c1d95",
|
|
2718
|
+
"950": "#2e1065"
|
|
2719
|
+
}
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
name: "Purple",
|
|
2723
|
+
shades: {
|
|
2724
|
+
"50": "#faf5ff",
|
|
2725
|
+
"100": "#f3e8ff",
|
|
2726
|
+
"200": "#e9d5ff",
|
|
2727
|
+
"300": "#d8b4fe",
|
|
2728
|
+
"400": "#c084fc",
|
|
2729
|
+
"500": "#a855f7",
|
|
2730
|
+
"600": "#9333ea",
|
|
2731
|
+
"700": "#7e22ce",
|
|
2732
|
+
"800": "#6b21a8",
|
|
2733
|
+
"900": "#581c87",
|
|
2734
|
+
"950": "#3b0764"
|
|
2735
|
+
}
|
|
2736
|
+
},
|
|
2737
|
+
{
|
|
2738
|
+
name: "Fuchsia",
|
|
2739
|
+
shades: {
|
|
2740
|
+
"50": "#fdf4ff",
|
|
2741
|
+
"100": "#fae8ff",
|
|
2742
|
+
"200": "#f5d0fe",
|
|
2743
|
+
"300": "#f0abfc",
|
|
2744
|
+
"400": "#e879f9",
|
|
2745
|
+
"500": "#d946ef",
|
|
2746
|
+
"600": "#c026d3",
|
|
2747
|
+
"700": "#a21caf",
|
|
2748
|
+
"800": "#86198f",
|
|
2749
|
+
"900": "#701a75",
|
|
2750
|
+
"950": "#4a044e"
|
|
2751
|
+
}
|
|
2752
|
+
},
|
|
2753
|
+
{
|
|
2754
|
+
name: "Pink",
|
|
2755
|
+
shades: {
|
|
2756
|
+
"50": "#fdf2f8",
|
|
2757
|
+
"100": "#fce7f3",
|
|
2758
|
+
"200": "#fbcfe8",
|
|
2759
|
+
"300": "#f9a8d4",
|
|
2760
|
+
"400": "#f472b6",
|
|
2761
|
+
"500": "#ec4899",
|
|
2762
|
+
"600": "#db2777",
|
|
2763
|
+
"700": "#be185d",
|
|
2764
|
+
"800": "#9d174d",
|
|
2765
|
+
"900": "#831843",
|
|
2766
|
+
"950": "#500724"
|
|
2767
|
+
}
|
|
2768
|
+
},
|
|
2769
|
+
{
|
|
2770
|
+
name: "Rose",
|
|
2771
|
+
shades: {
|
|
2772
|
+
"50": "#fff1f2",
|
|
2773
|
+
"100": "#ffe4e6",
|
|
2774
|
+
"200": "#fecdd3",
|
|
2775
|
+
"300": "#fda4af",
|
|
2776
|
+
"400": "#fb7185",
|
|
2777
|
+
"500": "#f43f5e",
|
|
2778
|
+
"600": "#e11d48",
|
|
2779
|
+
"700": "#be123c",
|
|
2780
|
+
"800": "#9f1239",
|
|
2781
|
+
"900": "#881337",
|
|
2782
|
+
"950": "#4c0519"
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
];
|
|
2786
|
+
|
|
2787
|
+
// src/color-util/tailwind.ts
|
|
2788
|
+
function getColorFromTailwind(tailwindColorString) {
|
|
2789
|
+
const [colorName, shade] = tailwindColorString.split("-");
|
|
2790
|
+
const colorGroup = tailwindColors.find((group) => group.name.toLowerCase() === (colorName ?? "").toLowerCase());
|
|
2791
|
+
if (colorGroup) {
|
|
2792
|
+
const shadeEntry = Object.entries(colorGroup.shades).find(([key]) => key === shade);
|
|
2793
|
+
if (shadeEntry) {
|
|
2794
|
+
return shadeEntry[1];
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
return "";
|
|
2798
|
+
}
|
|
2799
|
+
function findNearestTailwindColor(inputColor) {
|
|
2800
|
+
let measure;
|
|
2801
|
+
if (typeof inputColor === "string") {
|
|
2802
|
+
const rgb = parseHexOrRgb(inputColor);
|
|
2803
|
+
if (!rgb) return "";
|
|
2804
|
+
measure = (hexValue) => {
|
|
2805
|
+
const target = parseHexOrRgb(hexValue);
|
|
2806
|
+
return target ? rgbDelta(rgb, target) : Infinity;
|
|
2807
|
+
};
|
|
2808
|
+
} else {
|
|
2809
|
+
measure = (hexValue) => inputColor.delta(hexValue);
|
|
2810
|
+
}
|
|
2811
|
+
let nearestColor = "";
|
|
2812
|
+
let smallestDistance = Infinity;
|
|
2813
|
+
tailwindColors.forEach((colorGroup) => {
|
|
2814
|
+
Object.entries(colorGroup.shades).forEach(([shade, hexValue]) => {
|
|
2815
|
+
const distance = measure(hexValue);
|
|
2816
|
+
if (distance < smallestDistance) {
|
|
2817
|
+
smallestDistance = distance;
|
|
2818
|
+
nearestColor = `${colorGroup.name.toLowerCase()}-${shade}`;
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
});
|
|
2822
|
+
return nearestColor;
|
|
2823
|
+
}
|
|
2824
|
+
function formatTailwindColor(tailwindColor) {
|
|
2825
|
+
const tailwindColorNames = [
|
|
2826
|
+
"Slate",
|
|
2827
|
+
"Gray",
|
|
2828
|
+
"Zinc",
|
|
2829
|
+
"Neutral",
|
|
2830
|
+
"Stone",
|
|
2831
|
+
"Red",
|
|
2832
|
+
"Orange",
|
|
2833
|
+
"Amber",
|
|
2834
|
+
"Yellow",
|
|
2835
|
+
"Lime",
|
|
2836
|
+
"Green",
|
|
2837
|
+
"Emerald",
|
|
2838
|
+
"Teal",
|
|
2839
|
+
"Cyan",
|
|
2840
|
+
"Blue",
|
|
2841
|
+
"Indigo",
|
|
2842
|
+
"Violet",
|
|
2843
|
+
"Sky",
|
|
2844
|
+
"Purple",
|
|
2845
|
+
"Fuchsia",
|
|
2846
|
+
"Pink",
|
|
2847
|
+
"Rose"
|
|
2848
|
+
];
|
|
2849
|
+
const splitIndex = tailwindColor.search(/\d/);
|
|
2850
|
+
if (splitIndex === -1) {
|
|
2851
|
+
const colorName = tailwindColorNames.find((color) => tailwindColor.toLowerCase().includes(color.toLowerCase()));
|
|
2852
|
+
if (colorName) {
|
|
2853
|
+
return getColorFromTailwind(`${colorName.toLowerCase()}-500`);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
if (splitIndex > 0) {
|
|
2857
|
+
const colorNamePart = tailwindColor.slice(0, splitIndex);
|
|
2858
|
+
let shadePart = tailwindColor.slice(splitIndex);
|
|
2859
|
+
const matchedColors = tailwindColorNames.filter((color) => colorNamePart.toLowerCase().includes(color.toLowerCase()));
|
|
2860
|
+
matchedColors.sort((a, b) => colorNamePart.toLowerCase().indexOf(a.toLowerCase()) - colorNamePart.toLowerCase().indexOf(b.toLowerCase()));
|
|
2861
|
+
if (matchedColors.length > 0) {
|
|
2862
|
+
const colorName = matchedColors[0].toLowerCase();
|
|
2863
|
+
let shade = Math.round(parseInt(shadePart) / 100) * 100;
|
|
2864
|
+
const formattedColor = getColorFromTailwind(`${colorName}-${shade.toString()}`);
|
|
2865
|
+
if (formattedColor) {
|
|
2866
|
+
return formattedColor;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
return "";
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
// src/color-util/formats.ts
|
|
2874
|
+
function formatHex(hex) {
|
|
2875
|
+
return hex.startsWith("#") ? hex : `#${hex}`;
|
|
2876
|
+
}
|
|
2877
|
+
function formatRgbString(rgb) {
|
|
2878
|
+
const rgbValues = rgb.replace(/[^\d,]/g, "").split(",");
|
|
2879
|
+
if (rgbValues.length === 3) {
|
|
2880
|
+
return `rgb(${rgbValues[0].trim()}, ${rgbValues[1].trim()}, ${rgbValues[2].trim()})`;
|
|
2881
|
+
}
|
|
2882
|
+
return "";
|
|
2883
|
+
}
|
|
2884
|
+
function formatRgbObject(rgbObject) {
|
|
2885
|
+
const regex = /["']?r["']?\s*[:=]\s*(\d+)\s*[,]\s*["']?g["']?\s*[:=]\s*(\d+)\s*[,]\s*["']?b["']?\s*[:=]\s*(\d+)(?:\s*[,]\s*["']?a["']?\s*[:=]\s*(\d+))?/i;
|
|
2886
|
+
const match = rgbObject.match(regex);
|
|
2887
|
+
if (match) {
|
|
2888
|
+
const r = match[1], g = match[2], b = match[3], a = match[4] || 1;
|
|
2889
|
+
return `{"r":${r},"g":${g},"b":${b},"a":${a}}`;
|
|
2890
|
+
}
|
|
2891
|
+
return "";
|
|
2892
|
+
}
|
|
2893
|
+
function formatHslObject(hslObject) {
|
|
2894
|
+
const regex = /["']?h["']?\s*[:=]\s*(\d+)\s*,\s*["']?s["']?\s*[:=]\s*(\d+)\s*,\s*["']?l["']?\s*[:=]\s*(\d+)(?:\s*,\s*["']?a["']?\s*[:=]\s*(\d+))?/i;
|
|
2895
|
+
const match = hslObject.match(regex);
|
|
2896
|
+
if (match) {
|
|
2897
|
+
const h = match[1], s = match[2], l = match[3], a = match[4] || 1;
|
|
2898
|
+
return `{"h":${h},"s":${s},"l":${l},"a":${a}}`;
|
|
2899
|
+
}
|
|
2900
|
+
return "";
|
|
2901
|
+
}
|
|
2902
|
+
function formatHslString(hslString) {
|
|
2903
|
+
const hslValues = hslString.replace(/[^\d,%]/g, "").split(/\s*,\s*/);
|
|
2904
|
+
if (hslValues.length === 3) {
|
|
2905
|
+
return `hsl(${hslValues[0]}, ${hslValues[1]}%, ${hslValues[2]}%)`;
|
|
2906
|
+
}
|
|
2907
|
+
return "";
|
|
2908
|
+
}
|
|
2909
|
+
function formatHsvString(hsvString) {
|
|
2910
|
+
const hsvValues = hsvString.replace(/[^\d%\s]/g, "").split(/\s+/).map((value) => parseInt(value.replace("%", ""), 10));
|
|
2911
|
+
if (hsvValues.length === 3 && hsvValues.every((val) => !isNaN(val))) {
|
|
2912
|
+
return `hsv(${hsvValues[0]}, ${hsvValues[1]}%, ${hsvValues[2]}%)`;
|
|
2913
|
+
}
|
|
2914
|
+
return "";
|
|
2915
|
+
}
|
|
2916
|
+
function formatRegularCmykString(cmykString) {
|
|
2917
|
+
const cmykValues = cmykString.replace(/[^\d,]/g, "").split(/\s*,\s*/);
|
|
2918
|
+
if (cmykValues.length === 4 && cmykValues.every((val) => !isNaN(Number(val)))) {
|
|
2919
|
+
return `cmyk(${cmykValues[0]}, ${cmykValues[1]}, ${cmykValues[2]}, ${cmykValues[3]})`;
|
|
2920
|
+
}
|
|
2921
|
+
return "";
|
|
2922
|
+
}
|
|
2923
|
+
function formatCmykObject(cmykObject) {
|
|
2924
|
+
const regex = /["']?c["']?\s*[:=]\s*(\d+)\s*,\s*["']?m["']?\s*[:=]\s*(\d+)\s*,\s*["']?y["']?\s*[:=]\s*(\d+)\s*,\s*["']?k["']?\s*[:=]\s*(\d+)(?:\s*,\s*["']?a["']?\s*[:=]\s*(\d+))?/i;
|
|
2925
|
+
const match = cmykObject.match(regex);
|
|
2926
|
+
if (match) {
|
|
2927
|
+
const c = match[1], m = match[2], y = match[3], k = match[4], a = match[5] || 1;
|
|
2928
|
+
return `{"c":${c},"m":${m},"y":${y},"k":${k},"a":${a}}`;
|
|
2929
|
+
}
|
|
2930
|
+
return "";
|
|
2931
|
+
}
|
|
2932
|
+
function formatCmykString(cmykString) {
|
|
2933
|
+
const cmykValues = cmykString.replace(/[^\d%\s]/g, "").split(/\s+/).map((value) => parseInt(value.replace("%", ""), 10));
|
|
2934
|
+
if (cmykValues.length === 4 && cmykValues.every((val) => !isNaN(val))) {
|
|
2935
|
+
return `device-cmyk(${cmykValues[0]}% ${cmykValues[1]}% ${cmykValues[2]}% ${cmykValues[3]}%)`;
|
|
2936
|
+
}
|
|
2937
|
+
return "";
|
|
2938
|
+
}
|
|
2939
|
+
function isDeviceCmyk(cmykString) {
|
|
2940
|
+
const regex = /device-cmyk\(\s*\d+%\s+\d+%\s+\d+%\s+\d+%\s*\)/;
|
|
2941
|
+
return regex.test(cmykString);
|
|
2942
|
+
}
|
|
2943
|
+
function formatHwbString(hwbString) {
|
|
2944
|
+
const hwbValues = hwbString.replace(/[^\d,%\s]/g, "").split(/\s+/);
|
|
2945
|
+
if (hwbValues.length === 3) {
|
|
2946
|
+
return `hwb(${hwbValues[0]}, ${hwbValues[1]}%, ${hwbValues[2]}%)`;
|
|
2947
|
+
}
|
|
2948
|
+
return "";
|
|
2949
|
+
}
|
|
2950
|
+
function formatLabString(labString) {
|
|
2951
|
+
const match = labString.match(/(?:lab\()?\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*\)?/i);
|
|
2952
|
+
if (match) {
|
|
2953
|
+
const [, l, a, b] = match;
|
|
2954
|
+
return `lab(${l} ${a} ${b})`;
|
|
2955
|
+
}
|
|
2956
|
+
return "";
|
|
2957
|
+
}
|
|
2958
|
+
function formatLchString(lchString) {
|
|
2959
|
+
const match = lchString.match(/(?:lch\()?\s*(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)\s*\)?/i);
|
|
2960
|
+
if (match) {
|
|
2961
|
+
const [, l, c, h] = match;
|
|
2962
|
+
return `lch(${l} ${c} ${h})`;
|
|
2963
|
+
}
|
|
2964
|
+
return "";
|
|
2965
|
+
}
|
|
2966
|
+
function formatHexWith0x(hex) {
|
|
2967
|
+
if (hex.startsWith("0x")) {
|
|
2968
|
+
return `#${hex.slice(2)}`;
|
|
2969
|
+
}
|
|
2970
|
+
return "";
|
|
2971
|
+
}
|
|
2972
|
+
|
|
2973
|
+
// src/color-util/normalize.ts
|
|
2974
|
+
function createColorNormalizer({ isValid }) {
|
|
2975
|
+
return function normalizeColorInput(colorInput) {
|
|
2976
|
+
if (isValid(colorInput)) {
|
|
2977
|
+
return { value: colorInput, type: "standard" };
|
|
2978
|
+
}
|
|
2979
|
+
if (isDeviceCmyk(colorInput)) {
|
|
2980
|
+
return { value: colorInput, type: "device-cmyk" };
|
|
2981
|
+
}
|
|
2982
|
+
const hex = formatHex(colorInput);
|
|
2983
|
+
if (isValid(hex)) {
|
|
2984
|
+
return { value: hex, type: "hex" };
|
|
2985
|
+
}
|
|
2986
|
+
const rgbString = formatRgbString(colorInput);
|
|
2987
|
+
if (isValid(rgbString)) {
|
|
2988
|
+
return { value: rgbString, type: "rgb" };
|
|
2989
|
+
}
|
|
2990
|
+
const rgbObject = formatRgbObject(colorInput);
|
|
2991
|
+
if (rgbObject !== "") {
|
|
2992
|
+
return { value: rgbObject, type: "rgb-object" };
|
|
2993
|
+
}
|
|
2994
|
+
const hslObject = formatHslObject(colorInput);
|
|
2995
|
+
if (hslObject !== "") {
|
|
2996
|
+
return { value: hslObject, type: "hsl-object" };
|
|
2997
|
+
}
|
|
2998
|
+
const hslString = formatHslString(colorInput);
|
|
2999
|
+
if (isValid(hslString)) {
|
|
3000
|
+
return { value: hslString, type: "hsl" };
|
|
3001
|
+
}
|
|
3002
|
+
const hsvString = formatHsvString(colorInput);
|
|
3003
|
+
if (isValid(hsvString)) {
|
|
3004
|
+
return { value: hsvString, type: "hsv" };
|
|
3005
|
+
}
|
|
3006
|
+
const tailwindColor = formatTailwindColor(colorInput);
|
|
3007
|
+
if (tailwindColor !== "") {
|
|
3008
|
+
return { value: tailwindColor, type: "tailwind" };
|
|
3009
|
+
}
|
|
3010
|
+
const regularCmykString = formatRegularCmykString(colorInput);
|
|
3011
|
+
if (regularCmykString !== "") {
|
|
3012
|
+
return { value: regularCmykString, type: "cmyk" };
|
|
3013
|
+
}
|
|
3014
|
+
const cmykObject = formatCmykObject(colorInput);
|
|
3015
|
+
if (cmykObject !== "") {
|
|
3016
|
+
return { value: cmykObject, type: "cmyk-object" };
|
|
3017
|
+
}
|
|
3018
|
+
const cmykString = formatCmykString(colorInput);
|
|
3019
|
+
if (isValid(cmykString)) {
|
|
3020
|
+
return { value: cmykString, type: "cmyk-percentage" };
|
|
3021
|
+
}
|
|
3022
|
+
const hwbString = formatHwbString(colorInput);
|
|
3023
|
+
if (isValid(hwbString)) {
|
|
3024
|
+
return { value: hwbString, type: "hwb" };
|
|
3025
|
+
}
|
|
3026
|
+
const labString = formatLabString(colorInput);
|
|
3027
|
+
if (labString !== "") {
|
|
3028
|
+
return { value: labString, type: "lab" };
|
|
3029
|
+
}
|
|
3030
|
+
const lchString = formatLchString(colorInput);
|
|
3031
|
+
if (lchString !== "") {
|
|
3032
|
+
return { value: lchString, type: "lch" };
|
|
3033
|
+
}
|
|
3034
|
+
const hexWith0x = formatHexWith0x(colorInput);
|
|
3035
|
+
if (isValid(hexWith0x)) {
|
|
3036
|
+
return { value: hexWith0x, type: "hex-0x" };
|
|
3037
|
+
}
|
|
3038
|
+
return null;
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
// src/qr.ts
|
|
3043
|
+
var MAX_EDGE = 1600;
|
|
3044
|
+
function nativeDetector() {
|
|
3045
|
+
const ctor = globalThis.BarcodeDetector;
|
|
3046
|
+
return typeof ctor === "function" ? ctor : null;
|
|
3047
|
+
}
|
|
3048
|
+
function hasNativeQrDetector() {
|
|
3049
|
+
return nativeDetector() !== null;
|
|
3050
|
+
}
|
|
3051
|
+
async function decodeNative(source) {
|
|
3052
|
+
const Ctor = nativeDetector();
|
|
3053
|
+
if (!Ctor) return null;
|
|
3054
|
+
try {
|
|
3055
|
+
const detector = new Ctor({ formats: ["qr_code"] });
|
|
3056
|
+
const results = await detector.detect(source);
|
|
3057
|
+
const value = results.find((r) => r.rawValue)?.rawValue;
|
|
3058
|
+
return value ? value : null;
|
|
3059
|
+
} catch {
|
|
3060
|
+
return null;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
async function decodeWithJsQr(frame) {
|
|
3064
|
+
const { default: jsQR } = await import("jsqr");
|
|
3065
|
+
const both = jsQR(frame.data, frame.width, frame.height, {
|
|
3066
|
+
inversionAttempts: "attemptBoth"
|
|
3067
|
+
});
|
|
3068
|
+
return both?.data ? both.data : null;
|
|
3069
|
+
}
|
|
3070
|
+
function toImageData(source, width, height) {
|
|
3071
|
+
if (!width || !height) return null;
|
|
3072
|
+
const scale = Math.min(1, MAX_EDGE / Math.max(width, height));
|
|
3073
|
+
const w = Math.max(1, Math.round(width * scale));
|
|
3074
|
+
const h = Math.max(1, Math.round(height * scale));
|
|
3075
|
+
const canvas = document.createElement("canvas");
|
|
3076
|
+
canvas.width = w;
|
|
3077
|
+
canvas.height = h;
|
|
3078
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
3079
|
+
if (!ctx) return null;
|
|
3080
|
+
ctx.drawImage(source, 0, 0, w, h);
|
|
3081
|
+
return ctx.getImageData(0, 0, w, h);
|
|
3082
|
+
}
|
|
3083
|
+
async function decodeQrFromImageData(frame) {
|
|
3084
|
+
return await decodeWithJsQr(frame) ?? null;
|
|
3085
|
+
}
|
|
3086
|
+
async function decodeQrFromElement(element) {
|
|
3087
|
+
const width = element instanceof HTMLVideoElement ? element.videoWidth : element.width;
|
|
3088
|
+
const height = element instanceof HTMLVideoElement ? element.videoHeight : element.height;
|
|
3089
|
+
if (!width || !height) return null;
|
|
3090
|
+
const native = await decodeNative(element);
|
|
3091
|
+
if (native) return native;
|
|
3092
|
+
const frame = toImageData(element, width, height);
|
|
3093
|
+
return frame ? decodeQrFromImageData(frame) : null;
|
|
3094
|
+
}
|
|
3095
|
+
async function decodeQrFromImageFile(file) {
|
|
3096
|
+
const direct = await decodeNative(file);
|
|
3097
|
+
if (direct) return direct;
|
|
3098
|
+
let bitmap = null;
|
|
3099
|
+
try {
|
|
3100
|
+
bitmap = await createImageBitmap(file);
|
|
3101
|
+
} catch {
|
|
3102
|
+
throw new Error("That file could not be read as an image.");
|
|
3103
|
+
}
|
|
3104
|
+
try {
|
|
3105
|
+
const viaBitmap = await decodeNative(bitmap);
|
|
3106
|
+
if (viaBitmap) return viaBitmap;
|
|
3107
|
+
const frame = toImageData(bitmap, bitmap.width, bitmap.height);
|
|
3108
|
+
return frame ? decodeQrFromImageData(frame) : null;
|
|
3109
|
+
} finally {
|
|
3110
|
+
bitmap.close();
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
//# sourceMappingURL=index.cjs.map
|