nuvox 0.1.4 → 0.1.6
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/BUILDING-COMPONENTS.md +555 -551
- package/CHANGELOG.md +66 -147
- package/dist/chunk-HAMPWL7I.js +2457 -0
- package/dist/chunk-HAMPWL7I.js.map +1 -0
- package/dist/index.cjs +967 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +65 -1
- package/dist/pdf-viewer-CF7o-73J.d.cts +1092 -0
- package/dist/pdf-viewer-CF7o-73J.d.ts +1092 -0
- package/dist/react/index.cjs +11754 -5768
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.css +3446 -918
- package/dist/react/index.css.map +1 -1
- package/dist/react/index.d.cts +521 -5
- package/dist/react/index.d.ts +521 -5
- package/dist/react/index.js +11273 -6430
- package/dist/react/index.js.map +1 -1
- package/dist/themes/generated.css +55 -55
- package/package.json +2 -2
- package/dist/chunk-KKRVJF3Y.js +0 -1159
- package/dist/chunk-KKRVJF3Y.js.map +0 -1
- package/dist/context-menu-CcYi_Dc_.d.cts +0 -635
- package/dist/context-menu-CcYi_Dc_.d.ts +0 -635
package/dist/index.cjs
CHANGED
|
@@ -21,24 +21,45 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
DEFAULT_COLOR_PRESETS: () => DEFAULT_PRESETS,
|
|
24
|
+
DRAG_START_THRESHOLD_PX: () => DRAG_START_THRESHOLD_PX,
|
|
25
|
+
LABELED_SKIP_SECONDS: () => LABELED_SKIP_SECONDS,
|
|
26
|
+
LANGUAGE_LABELS: () => LANGUAGE_LABELS,
|
|
27
|
+
PLAYBACK_RATES: () => PLAYBACK_RATES,
|
|
24
28
|
REQUIRED_TOKEN_PATHS: () => REQUIRED_TOKEN_PATHS,
|
|
29
|
+
SKIP_SECONDS: () => SKIP_SECONDS,
|
|
25
30
|
TOKEN_CATEGORIES: () => TOKEN_CATEGORIES,
|
|
26
31
|
amber: () => amber,
|
|
32
|
+
aspectRatioFromDims: () => aspectRatioFromDims,
|
|
27
33
|
brutalism: () => brutalism,
|
|
34
|
+
computeColumnDropIndex: () => computeColumnDropIndex,
|
|
35
|
+
computeDropColumn: () => computeDropColumn,
|
|
36
|
+
computeDropIndex: () => computeDropIndex,
|
|
37
|
+
computeJustifiedRows: () => computeJustifiedRows,
|
|
28
38
|
copper: () => copper,
|
|
39
|
+
createHistory: () => createHistory,
|
|
29
40
|
cyberpunk: () => cyberpunk,
|
|
30
41
|
dark: () => dark,
|
|
31
42
|
darkGlass: () => darkGlass,
|
|
32
43
|
defineTheme: () => defineTheme,
|
|
33
44
|
emerald: () => emerald,
|
|
45
|
+
execRichTextCommand: () => execRichTextCommand,
|
|
46
|
+
findCardLocation: () => findCardLocation,
|
|
34
47
|
findMissingTokenPaths: () => findMissingTokenPaths,
|
|
48
|
+
formatMediaTime: () => formatMediaTime,
|
|
49
|
+
formatZoomPercent: () => formatZoomPercent,
|
|
35
50
|
getAccent: () => getAccent,
|
|
51
|
+
getActiveCommands: () => getActiveCommands,
|
|
52
|
+
getAdjacentColumnId: () => getAdjacentColumnId,
|
|
53
|
+
getColumnKeyboardMoveTarget: () => getColumnKeyboardMoveTarget,
|
|
54
|
+
getKeyboardMoveTarget: () => getKeyboardMoveTarget,
|
|
55
|
+
getMediaKeyAction: () => getMediaKeyAction,
|
|
36
56
|
getNextSortDirection: () => getNextSortDirection,
|
|
37
57
|
getSelectAllState: () => getSelectAllState,
|
|
38
58
|
getTheme: () => getTheme,
|
|
39
59
|
glass: () => glass,
|
|
40
60
|
gradient: () => gradient,
|
|
41
61
|
graphite: () => graphite,
|
|
62
|
+
hasLabeledSkipIcon: () => hasLabeledSkipIcon,
|
|
42
63
|
indigo: () => indigo,
|
|
43
64
|
light: () => light,
|
|
44
65
|
listAccents: () => listAccents,
|
|
@@ -46,12 +67,23 @@ __export(src_exports, {
|
|
|
46
67
|
magenta: () => magenta,
|
|
47
68
|
material: () => material,
|
|
48
69
|
minimalism: () => minimalism,
|
|
70
|
+
moveCard: () => moveCard,
|
|
71
|
+
moveColumn: () => moveColumn,
|
|
49
72
|
neobrutalism: () => neobrutalism,
|
|
73
|
+
nuvoxHighlight: () => nuvoxHighlight,
|
|
74
|
+
pushHistory: () => pushHistory,
|
|
75
|
+
redoHistory: () => redoHistory,
|
|
50
76
|
registerAccent: () => registerAccent,
|
|
51
77
|
registerTheme: () => registerTheme,
|
|
78
|
+
removeCard: () => removeCard,
|
|
79
|
+
removeColumn: () => removeColumn,
|
|
80
|
+
resolveMediaTransport: () => resolveMediaTransport,
|
|
81
|
+
resolveSkipSeconds: () => resolveSkipSeconds,
|
|
52
82
|
rose: () => rose,
|
|
83
|
+
sanitizeHtml: () => sanitizeHtml,
|
|
53
84
|
skeuomorphism: () => skeuomorphism,
|
|
54
85
|
teal: () => teal,
|
|
86
|
+
undoHistory: () => undoHistory,
|
|
55
87
|
violet: () => violet
|
|
56
88
|
});
|
|
57
89
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -213,7 +245,7 @@ var light = defineTheme({
|
|
|
213
245
|
"2xl": "0 28px 56px rgba(0,0,0,0.16)",
|
|
214
246
|
inner: "inset 0 1px 2px rgba(0,0,0,0.05)"
|
|
215
247
|
},
|
|
216
|
-
elevation: { dropdown:
|
|
248
|
+
elevation: { dropdown: 1050, popover: 1060, modal: 1e3, toast: 1200, tooltip: 1100 },
|
|
217
249
|
motion: {
|
|
218
250
|
durationFast: "120ms",
|
|
219
251
|
durationNormal: "200ms",
|
|
@@ -286,7 +318,7 @@ var dark = defineTheme({
|
|
|
286
318
|
"2xl": "0 28px 56px rgba(0,0,0,0.65)",
|
|
287
319
|
inner: "inset 0 1px 2px rgba(0,0,0,0.4)"
|
|
288
320
|
},
|
|
289
|
-
elevation: { dropdown:
|
|
321
|
+
elevation: { dropdown: 1050, popover: 1060, modal: 1e3, toast: 1200, tooltip: 1100 },
|
|
290
322
|
motion: {
|
|
291
323
|
durationFast: "120ms",
|
|
292
324
|
durationNormal: "200ms",
|
|
@@ -370,7 +402,7 @@ var glass = defineTheme({
|
|
|
370
402
|
"2xl": "0 32px 80px rgba(0,0,0,0.18)",
|
|
371
403
|
inner: "inset 0 1px 0 rgba(255,255,255,0.4)"
|
|
372
404
|
},
|
|
373
|
-
elevation: { dropdown:
|
|
405
|
+
elevation: { dropdown: 1050, popover: 1060, modal: 1e3, toast: 1200, tooltip: 1100 },
|
|
374
406
|
motion: {
|
|
375
407
|
// Slightly slower/softer than light/dark — matches "everything floats".
|
|
376
408
|
durationFast: "160ms",
|
|
@@ -890,27 +922,948 @@ function getSelectAllState(totalCount, selectedCount) {
|
|
|
890
922
|
if (selectedCount >= totalCount) return "all";
|
|
891
923
|
return "some";
|
|
892
924
|
}
|
|
925
|
+
|
|
926
|
+
// src/core/kanban.ts
|
|
927
|
+
var DRAG_START_THRESHOLD_PX = 4;
|
|
928
|
+
function findCardLocation(value, cardId) {
|
|
929
|
+
for (const column of value.columns) {
|
|
930
|
+
const index = column.cardIds.indexOf(cardId);
|
|
931
|
+
if (index !== -1) return { columnId: column.id, index };
|
|
932
|
+
}
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
function removeCard(value, cardId) {
|
|
936
|
+
const location = findCardLocation(value, cardId);
|
|
937
|
+
if (!location) return value;
|
|
938
|
+
return {
|
|
939
|
+
columns: value.columns.map(
|
|
940
|
+
(column) => column.id === location.columnId ? { ...column, cardIds: column.cardIds.filter((id) => id !== cardId) } : column
|
|
941
|
+
)
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function moveCard(value, target) {
|
|
945
|
+
const location = findCardLocation(value, target.cardId);
|
|
946
|
+
const destExists = value.columns.some((column) => column.id === target.toColumnId);
|
|
947
|
+
if (!location || !destExists) return value;
|
|
948
|
+
const withoutCard = value.columns.map(
|
|
949
|
+
(column) => column.cardIds.includes(target.cardId) ? { ...column, cardIds: column.cardIds.filter((id) => id !== target.cardId) } : column
|
|
950
|
+
);
|
|
951
|
+
return {
|
|
952
|
+
columns: withoutCard.map((column) => {
|
|
953
|
+
if (column.id !== target.toColumnId) return column;
|
|
954
|
+
const clampedIndex = Math.max(0, Math.min(target.toIndex, column.cardIds.length));
|
|
955
|
+
const cardIds = [...column.cardIds];
|
|
956
|
+
cardIds.splice(clampedIndex, 0, target.cardId);
|
|
957
|
+
return { ...column, cardIds };
|
|
958
|
+
})
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
function getAdjacentColumnId(value, columnId, direction) {
|
|
962
|
+
const index = value.columns.findIndex((column) => column.id === columnId);
|
|
963
|
+
if (index === -1) return null;
|
|
964
|
+
return value.columns[index + direction]?.id ?? null;
|
|
965
|
+
}
|
|
966
|
+
function getKeyboardMoveTarget(value, cardId, direction) {
|
|
967
|
+
const location = findCardLocation(value, cardId);
|
|
968
|
+
if (!location) return null;
|
|
969
|
+
const column = value.columns.find((c) => c.id === location.columnId);
|
|
970
|
+
if (!column) return null;
|
|
971
|
+
if (direction === "up" || direction === "down") {
|
|
972
|
+
const toIndex2 = direction === "up" ? location.index - 1 : location.index + 1;
|
|
973
|
+
if (toIndex2 < 0 || toIndex2 > column.cardIds.length - 1) return null;
|
|
974
|
+
return { cardId, toColumnId: location.columnId, toIndex: toIndex2 };
|
|
975
|
+
}
|
|
976
|
+
const adjacentColumnId = getAdjacentColumnId(value, location.columnId, direction === "prev" ? -1 : 1);
|
|
977
|
+
if (!adjacentColumnId) return null;
|
|
978
|
+
const destColumn = value.columns.find((c) => c.id === adjacentColumnId);
|
|
979
|
+
if (!destColumn) return null;
|
|
980
|
+
const toIndex = Math.min(location.index, destColumn.cardIds.length);
|
|
981
|
+
return { cardId, toColumnId: adjacentColumnId, toIndex };
|
|
982
|
+
}
|
|
983
|
+
function computeDropIndex(pointerY, cardRects) {
|
|
984
|
+
for (let i = 0; i < cardRects.length; i++) {
|
|
985
|
+
const midpoint = (cardRects[i].top + cardRects[i].bottom) / 2;
|
|
986
|
+
if (pointerY < midpoint) return i;
|
|
987
|
+
}
|
|
988
|
+
return cardRects.length;
|
|
989
|
+
}
|
|
990
|
+
function computeDropColumn(pointerX, pointerY, columns) {
|
|
991
|
+
for (const column of columns) {
|
|
992
|
+
if (pointerX >= column.left && pointerX <= column.right && pointerY >= column.top && pointerY <= column.bottom) {
|
|
993
|
+
return column.id;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
function moveColumn(value, target) {
|
|
999
|
+
const fromIndex = value.columns.findIndex((c) => c.id === target.columnId);
|
|
1000
|
+
if (fromIndex === -1) return value;
|
|
1001
|
+
const columns = [...value.columns];
|
|
1002
|
+
const [moved] = columns.splice(fromIndex, 1);
|
|
1003
|
+
const clampedIndex = Math.max(0, Math.min(target.toIndex, columns.length));
|
|
1004
|
+
columns.splice(clampedIndex, 0, moved);
|
|
1005
|
+
return { columns };
|
|
1006
|
+
}
|
|
1007
|
+
function removeColumn(value, columnId) {
|
|
1008
|
+
if (!value.columns.some((c) => c.id === columnId)) return value;
|
|
1009
|
+
return { columns: value.columns.filter((c) => c.id !== columnId) };
|
|
1010
|
+
}
|
|
1011
|
+
function getColumnKeyboardMoveTarget(value, columnId, direction) {
|
|
1012
|
+
const fromIndex = value.columns.findIndex((c) => c.id === columnId);
|
|
1013
|
+
if (fromIndex === -1) return null;
|
|
1014
|
+
const toIndex = direction === "prev" ? fromIndex - 1 : fromIndex + 1;
|
|
1015
|
+
if (toIndex < 0 || toIndex > value.columns.length - 1) return null;
|
|
1016
|
+
return { columnId, toIndex };
|
|
1017
|
+
}
|
|
1018
|
+
function computeColumnDropIndex(pointerX, columnRects) {
|
|
1019
|
+
for (let i = 0; i < columnRects.length; i++) {
|
|
1020
|
+
const midpoint = (columnRects[i].left + columnRects[i].right) / 2;
|
|
1021
|
+
if (pointerX < midpoint) return i;
|
|
1022
|
+
}
|
|
1023
|
+
return columnRects.length;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/core/code-highlight.ts
|
|
1027
|
+
function tokenizeFlat(source, grammar) {
|
|
1028
|
+
const tokens = [];
|
|
1029
|
+
const n = source.length;
|
|
1030
|
+
let i = 0;
|
|
1031
|
+
outer: while (i < n) {
|
|
1032
|
+
for (const rule of grammar.rules) {
|
|
1033
|
+
if (rule.pattern) {
|
|
1034
|
+
rule.pattern.lastIndex = i;
|
|
1035
|
+
const m = rule.pattern.exec(source);
|
|
1036
|
+
if (m && m.index === i && m[0].length > 0) {
|
|
1037
|
+
tokens.push({ type: rule.type, content: m[0] });
|
|
1038
|
+
i += m[0].length;
|
|
1039
|
+
continue outer;
|
|
1040
|
+
}
|
|
1041
|
+
} else if (rule.match) {
|
|
1042
|
+
const result = rule.match(source, i, tokens);
|
|
1043
|
+
if (result && result.length > 0) {
|
|
1044
|
+
tokens.push(...result.tokens);
|
|
1045
|
+
i += result.length;
|
|
1046
|
+
continue outer;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
const ch = source[i];
|
|
1051
|
+
const last = tokens[tokens.length - 1];
|
|
1052
|
+
if (last && last.type === "plain") last.content += ch;
|
|
1053
|
+
else tokens.push({ type: "plain", content: ch });
|
|
1054
|
+
i++;
|
|
1055
|
+
}
|
|
1056
|
+
return tokens;
|
|
1057
|
+
}
|
|
1058
|
+
function splitIntoLines(tokens) {
|
|
1059
|
+
const lines = [[]];
|
|
1060
|
+
for (const tok of tokens) {
|
|
1061
|
+
const parts = tok.content.split("\n");
|
|
1062
|
+
parts.forEach((part, idx) => {
|
|
1063
|
+
if (part.length > 0) lines[lines.length - 1].push({ type: tok.type, content: part });
|
|
1064
|
+
if (idx < parts.length - 1) lines.push([]);
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
return lines;
|
|
1068
|
+
}
|
|
1069
|
+
var KEYWORD = (words) => new RegExp(`\\b(?:${words})\\b`, "y");
|
|
1070
|
+
function matchTemplateLiteral(source, start, jsGrammar2) {
|
|
1071
|
+
if (source[start] !== "`") return null;
|
|
1072
|
+
const tokens = [{ type: "string", content: "`" }];
|
|
1073
|
+
let i = start + 1;
|
|
1074
|
+
let buf = "";
|
|
1075
|
+
const flush = () => {
|
|
1076
|
+
if (buf) {
|
|
1077
|
+
tokens.push({ type: "string", content: buf });
|
|
1078
|
+
buf = "";
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
while (i < source.length) {
|
|
1082
|
+
const ch = source[i];
|
|
1083
|
+
if (ch === "\\" && i + 1 < source.length) {
|
|
1084
|
+
buf += ch + source[i + 1];
|
|
1085
|
+
i += 2;
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (ch === "`") {
|
|
1089
|
+
flush();
|
|
1090
|
+
tokens.push({ type: "string", content: "`" });
|
|
1091
|
+
i++;
|
|
1092
|
+
return { length: i - start, tokens };
|
|
1093
|
+
}
|
|
1094
|
+
if (ch === "$" && source[i + 1] === "{") {
|
|
1095
|
+
flush();
|
|
1096
|
+
tokens.push({ type: "punctuation", content: "${" });
|
|
1097
|
+
i += 2;
|
|
1098
|
+
const exprStart = i;
|
|
1099
|
+
let depth = 1;
|
|
1100
|
+
while (i < source.length && depth > 0) {
|
|
1101
|
+
if (source[i] === "{") depth++;
|
|
1102
|
+
else if (source[i] === "}") depth--;
|
|
1103
|
+
if (depth > 0) i++;
|
|
1104
|
+
}
|
|
1105
|
+
const exprSource = source.slice(exprStart, i);
|
|
1106
|
+
tokens.push(...tokenizeFlat(exprSource, jsGrammar2));
|
|
1107
|
+
if (i < source.length) {
|
|
1108
|
+
tokens.push({ type: "punctuation", content: "}" });
|
|
1109
|
+
i++;
|
|
1110
|
+
}
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1113
|
+
buf += ch;
|
|
1114
|
+
i++;
|
|
1115
|
+
}
|
|
1116
|
+
flush();
|
|
1117
|
+
return { length: i - start, tokens };
|
|
1118
|
+
}
|
|
1119
|
+
var REGEX_EXPRESSION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1120
|
+
"return",
|
|
1121
|
+
"typeof",
|
|
1122
|
+
"instanceof",
|
|
1123
|
+
"in",
|
|
1124
|
+
"of",
|
|
1125
|
+
"new",
|
|
1126
|
+
"delete",
|
|
1127
|
+
"void",
|
|
1128
|
+
"throw",
|
|
1129
|
+
"case",
|
|
1130
|
+
"do",
|
|
1131
|
+
"else",
|
|
1132
|
+
"yield",
|
|
1133
|
+
"await"
|
|
1134
|
+
]);
|
|
1135
|
+
function matchRegexLiteral(source, start) {
|
|
1136
|
+
if (source[start] !== "/" || source[start + 1] === "/" || source[start + 1] === "*") return null;
|
|
1137
|
+
let j = start - 1;
|
|
1138
|
+
while (j >= 0 && /\s/.test(source[j])) j--;
|
|
1139
|
+
if (j >= 0) {
|
|
1140
|
+
const prevChar = source[j];
|
|
1141
|
+
const looksLikeValueEnd = /[)\]}\w$]/.test(prevChar) || prevChar === '"' || prevChar === "'" || prevChar === "`";
|
|
1142
|
+
if (looksLikeValueEnd) {
|
|
1143
|
+
const wordBefore = source.slice(0, j + 1).match(/([A-Za-z_$][\w$]*)$/)?.[1];
|
|
1144
|
+
if (!(wordBefore && REGEX_EXPRESSION_KEYWORDS.has(wordBefore))) return null;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
let i = start + 1;
|
|
1148
|
+
let inClass = false;
|
|
1149
|
+
while (i < source.length) {
|
|
1150
|
+
const ch = source[i];
|
|
1151
|
+
if (ch === "\\") {
|
|
1152
|
+
i += 2;
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
if (ch === "\n") return null;
|
|
1156
|
+
if (ch === "[") inClass = true;
|
|
1157
|
+
else if (ch === "]") inClass = false;
|
|
1158
|
+
else if (ch === "/" && !inClass) {
|
|
1159
|
+
i++;
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
i++;
|
|
1163
|
+
}
|
|
1164
|
+
if (source[i - 1] !== "/") return null;
|
|
1165
|
+
while (i < source.length && /[a-z]/i.test(source[i])) i++;
|
|
1166
|
+
return { length: i - start, tokens: [{ type: "regex", content: source.slice(start, i) }] };
|
|
1167
|
+
}
|
|
1168
|
+
function buildJsGrammar(withTags) {
|
|
1169
|
+
const grammar = { rules: [] };
|
|
1170
|
+
const rules = grammar.rules;
|
|
1171
|
+
if (withTags) {
|
|
1172
|
+
rules.push({ type: "tag", pattern: /<\/?[A-Za-z][\w.-]*/y });
|
|
1173
|
+
}
|
|
1174
|
+
rules.push(
|
|
1175
|
+
{ type: "comment", pattern: /\/\/[^\n]*|\/\*[\s\S]*?\*\//y },
|
|
1176
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'/y },
|
|
1177
|
+
{ type: "custom-template", match: (src, idx) => matchTemplateLiteral(src, idx, grammar) },
|
|
1178
|
+
{ type: "custom-regex", match: (src, idx) => matchRegexLiteral(src, idx) },
|
|
1179
|
+
{ type: "decorator", pattern: /@[A-Za-z_$][\w$]*/y },
|
|
1180
|
+
{ type: "boolean", pattern: KEYWORD("true|false|null|undefined") },
|
|
1181
|
+
{
|
|
1182
|
+
type: "keyword",
|
|
1183
|
+
pattern: KEYWORD(
|
|
1184
|
+
"const|let|var|function|return|if|else|for|while|do|switch|case|default|break|continue|class|extends|super|new|this|typeof|instanceof|in|of|try|catch|finally|throw|async|await|yield|import|export|from|as|void|delete|static|get|set|interface|type|enum|implements|public|private|protected|readonly|namespace|declare|abstract|is|keyof|infer|satisfies"
|
|
1185
|
+
)
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
type: "builtin",
|
|
1189
|
+
pattern: KEYWORD(
|
|
1190
|
+
"console|Math|JSON|Promise|Array|Object|Map|Set|WeakMap|WeakSet|String|Number|Boolean|Symbol|Error|Date|RegExp|Reflect|Proxy|globalThis|Infinity|NaN|BigInt|fetch|localStorage|sessionStorage"
|
|
1191
|
+
)
|
|
1192
|
+
},
|
|
1193
|
+
{ type: "number", pattern: /\b0[xX][0-9a-fA-F]+\b|\b\d+\.?\d*(?:[eE][+-]?\d+)?n?\b/y },
|
|
1194
|
+
{ type: "function", pattern: /[A-Za-z_$][\w$]*(?=\s*\()/y },
|
|
1195
|
+
{ type: "punctuation", pattern: /[{}()[\];,.:?]/y }
|
|
1196
|
+
);
|
|
1197
|
+
return grammar;
|
|
1198
|
+
}
|
|
1199
|
+
var jsGrammar = buildJsGrammar(false);
|
|
1200
|
+
var jsxGrammar = buildJsGrammar(true);
|
|
1201
|
+
var jsonGrammar = {
|
|
1202
|
+
rules: [
|
|
1203
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"/y },
|
|
1204
|
+
{ type: "boolean", pattern: KEYWORD("true|false|null") },
|
|
1205
|
+
{ type: "number", pattern: /-?\b\d+\.?\d*(?:[eE][+-]?\d+)?\b/y },
|
|
1206
|
+
{ type: "punctuation", pattern: /[{}[\]:,]/y }
|
|
1207
|
+
]
|
|
1208
|
+
};
|
|
1209
|
+
var cssGrammar = {
|
|
1210
|
+
rules: [
|
|
1211
|
+
{ type: "comment", pattern: /\/\*[\s\S]*?\*\//y },
|
|
1212
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'/y },
|
|
1213
|
+
{ type: "keyword", pattern: /!important\b/y },
|
|
1214
|
+
{ type: "atrule", pattern: /@[a-zA-Z-]+/y },
|
|
1215
|
+
// Custom-property declarations/usage (--brand-color, var(--x)) —
|
|
1216
|
+
// checked before the generic hex-color/selector rules below.
|
|
1217
|
+
{ type: "variable", pattern: /--[a-zA-Z_][\w-]*/y },
|
|
1218
|
+
{ type: "function", pattern: /\b(?:var|calc|rgba?|hsla?|clamp|minmax|repeat|url|translate[XYZ]?|rotate[XYZ]?|scale[XYZ]?|linear-gradient|radial-gradient)(?=\()/y },
|
|
1219
|
+
// Pseudo-classes/elements (:hover, ::before) before the plain ":" punctuation.
|
|
1220
|
+
{ type: "pseudo", pattern: /::?[a-zA-Z-]+/y },
|
|
1221
|
+
// Hex colors before the generic "#name" id-selector pattern below.
|
|
1222
|
+
{ type: "builtin", pattern: /#[0-9a-fA-F]{3,8}\b/y },
|
|
1223
|
+
{ type: "selector", pattern: /[.#][a-zA-Z_-][\w-]*/y },
|
|
1224
|
+
{ type: "property", pattern: /[a-zA-Z-]+(?=\s*:)/y },
|
|
1225
|
+
{ type: "number", pattern: /-?\d+\.?\d*(?:px|em|rem|%|vh|vw|s|ms|deg|fr)?\b/y },
|
|
1226
|
+
{ type: "punctuation", pattern: /[{}:;,()]/y }
|
|
1227
|
+
]
|
|
1228
|
+
};
|
|
1229
|
+
var htmlGrammar = {
|
|
1230
|
+
rules: [
|
|
1231
|
+
{ type: "comment", pattern: /<!--[\s\S]*?-->/y },
|
|
1232
|
+
{ type: "keyword", pattern: /<!DOCTYPE[^>]*>/iy },
|
|
1233
|
+
{ type: "tag", pattern: /<\/?[a-zA-Z][\w:-]*/y },
|
|
1234
|
+
{ type: "attrName", pattern: /[a-zA-Z-]+(?=\s*=)/y },
|
|
1235
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'/y },
|
|
1236
|
+
{ type: "builtin", pattern: /&[a-zA-Z#0-9]+;/y },
|
|
1237
|
+
{ type: "punctuation", pattern: /[<>=/]/y }
|
|
1238
|
+
]
|
|
1239
|
+
};
|
|
1240
|
+
var bashGrammar = {
|
|
1241
|
+
rules: [
|
|
1242
|
+
{ type: "comment", pattern: /#[^\n]*/y },
|
|
1243
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"|'[^'\n]*'/y },
|
|
1244
|
+
{ type: "variable", pattern: /\$\{?\w+\}?/y },
|
|
1245
|
+
{
|
|
1246
|
+
type: "keyword",
|
|
1247
|
+
pattern: KEYWORD(
|
|
1248
|
+
"if|then|else|elif|fi|for|do|done|while|case|esac|function|return|exit|export|local|echo|cd|sudo|npm|yarn|pnpm|git|mkdir|rm|cp|mv|cat|grep|sed|awk"
|
|
1249
|
+
)
|
|
1250
|
+
},
|
|
1251
|
+
{ type: "number", pattern: /\b\d+\b/y },
|
|
1252
|
+
{ type: "function", pattern: /(?<=^|\s)--?[a-zA-Z][\w-]*/y },
|
|
1253
|
+
{ type: "punctuation", pattern: /[|&;()]/y }
|
|
1254
|
+
]
|
|
1255
|
+
};
|
|
1256
|
+
var pythonGrammar = {
|
|
1257
|
+
rules: [
|
|
1258
|
+
{ type: "comment", pattern: /#[^\n]*/y },
|
|
1259
|
+
{ type: "decorator", pattern: /@[A-Za-z_][\w.]*/y },
|
|
1260
|
+
{
|
|
1261
|
+
// f/r/b/u string prefixes (f"...", rb'...', etc.) before plain strings.
|
|
1262
|
+
type: "string",
|
|
1263
|
+
pattern: /[rRbBfFuU]{0,2}(?:"""[\s\S]*?"""|'''[\s\S]*?'''|"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*')/y
|
|
1264
|
+
},
|
|
1265
|
+
{ type: "boolean", pattern: KEYWORD("True|False|None") },
|
|
1266
|
+
{
|
|
1267
|
+
type: "keyword",
|
|
1268
|
+
pattern: KEYWORD(
|
|
1269
|
+
"def|return|if|elif|else|for|while|in|not|and|or|is|import|from|as|class|try|except|finally|raise|with|lambda|yield|pass|break|continue|global|nonlocal|assert|del|async|await|self"
|
|
1270
|
+
)
|
|
1271
|
+
},
|
|
1272
|
+
{ type: "builtin", pattern: KEYWORD("print|len|range|str|int|float|list|dict|set|tuple|type|isinstance|super|open|input|enumerate|zip|map|filter|sorted|sum|min|max|any|all") },
|
|
1273
|
+
{ type: "number", pattern: /\b\d+\.?\d*\b/y },
|
|
1274
|
+
{ type: "function", pattern: /[A-Za-z_]\w*(?=\s*\()/y },
|
|
1275
|
+
{ type: "punctuation", pattern: /[:,.()[\]{}]/y }
|
|
1276
|
+
]
|
|
1277
|
+
};
|
|
1278
|
+
var markdownGrammar = {
|
|
1279
|
+
rules: [
|
|
1280
|
+
{ type: "comment", pattern: /```[\s\S]*?```/y },
|
|
1281
|
+
{ type: "keyword", pattern: /^#{1,6}[^\n]*/my },
|
|
1282
|
+
{ type: "punctuation", pattern: /^(?:-{3,}|\*{3,}|_{3,})$/my },
|
|
1283
|
+
{ type: "function", pattern: /`[^`\n]+`/y },
|
|
1284
|
+
{ type: "builtin", pattern: /\*\*[^*\n]+\*\*/y },
|
|
1285
|
+
{ type: "variable", pattern: /~~[^~\n]+~~/y },
|
|
1286
|
+
{ type: "string", pattern: /\*[^*\n]+\*/y },
|
|
1287
|
+
{ type: "tag", pattern: /!?\[[^\]\n]*\]\([^)\n]*\)/y },
|
|
1288
|
+
{ type: "punctuation", pattern: /^[-*+]\s|^\d+\.\s/my }
|
|
1289
|
+
]
|
|
1290
|
+
};
|
|
1291
|
+
var yamlGrammar = {
|
|
1292
|
+
rules: [
|
|
1293
|
+
{ type: "comment", pattern: /#[^\n]*/y },
|
|
1294
|
+
{ type: "property", pattern: /^[ \t]*[\w.-]+(?=\s*:)/my },
|
|
1295
|
+
{ type: "string", pattern: /"(?:[^"\\\n]|\\.)*"|'[^'\n]*'/y },
|
|
1296
|
+
{ type: "boolean", pattern: KEYWORD("true|false|null|yes|no|on|off|True|False|Null") },
|
|
1297
|
+
{ type: "number", pattern: /-?\b\d+\.?\d*\b/y },
|
|
1298
|
+
{ type: "variable", pattern: /[&*][\w-]+/y },
|
|
1299
|
+
{ type: "punctuation", pattern: /[:\-[\]{},]/y }
|
|
1300
|
+
]
|
|
1301
|
+
};
|
|
1302
|
+
var sqlGrammar = {
|
|
1303
|
+
rules: [
|
|
1304
|
+
{ type: "comment", pattern: /--[^\n]*|\/\*[\s\S]*?\*\//y },
|
|
1305
|
+
{ type: "string", pattern: /'(?:[^'\\\n]|\\.)*'/y },
|
|
1306
|
+
{
|
|
1307
|
+
type: "keyword",
|
|
1308
|
+
pattern: new RegExp(
|
|
1309
|
+
"\\b(?:SELECT|FROM|WHERE|INSERT|INTO|VALUES|UPDATE|SET|DELETE|CREATE|TABLE|ALTER|DROP|JOIN|INNER|LEFT|RIGHT|OUTER|ON|GROUP|BY|ORDER|HAVING|LIMIT|OFFSET|AS|AND|OR|NOT|NULL|IS|IN|LIKE|BETWEEN|DISTINCT|UNION|ALL|EXISTS|CASE|WHEN|THEN|ELSE|END|PRIMARY|KEY|FOREIGN|REFERENCES|DEFAULT|CONSTRAINT|INDEX|VIEW|WITH)\\b",
|
|
1310
|
+
"iy"
|
|
1311
|
+
)
|
|
1312
|
+
},
|
|
1313
|
+
{ type: "number", pattern: /\b\d+\.?\d*\b/y },
|
|
1314
|
+
{ type: "function", pattern: /\b[A-Za-z_]\w*(?=\s*\()/y },
|
|
1315
|
+
{ type: "punctuation", pattern: /[(),;.*=<>!]/y }
|
|
1316
|
+
]
|
|
1317
|
+
};
|
|
1318
|
+
var GRAMMARS = {
|
|
1319
|
+
javascript: jsGrammar,
|
|
1320
|
+
typescript: jsGrammar,
|
|
1321
|
+
jsx: jsxGrammar,
|
|
1322
|
+
tsx: jsxGrammar,
|
|
1323
|
+
json: jsonGrammar,
|
|
1324
|
+
css: cssGrammar,
|
|
1325
|
+
html: htmlGrammar,
|
|
1326
|
+
bash: bashGrammar,
|
|
1327
|
+
python: pythonGrammar,
|
|
1328
|
+
markdown: markdownGrammar,
|
|
1329
|
+
yaml: yamlGrammar,
|
|
1330
|
+
sql: sqlGrammar
|
|
1331
|
+
};
|
|
1332
|
+
var ALIASES = {
|
|
1333
|
+
js: "javascript",
|
|
1334
|
+
mjs: "javascript",
|
|
1335
|
+
cjs: "javascript",
|
|
1336
|
+
ts: "typescript",
|
|
1337
|
+
sh: "bash",
|
|
1338
|
+
shell: "bash",
|
|
1339
|
+
zsh: "bash",
|
|
1340
|
+
py: "python",
|
|
1341
|
+
md: "markdown",
|
|
1342
|
+
htm: "html",
|
|
1343
|
+
yml: "yaml"
|
|
1344
|
+
};
|
|
1345
|
+
var LANGUAGE_LABELS = {
|
|
1346
|
+
javascript: "JavaScript",
|
|
1347
|
+
typescript: "TypeScript",
|
|
1348
|
+
jsx: "JSX",
|
|
1349
|
+
tsx: "TSX",
|
|
1350
|
+
json: "JSON",
|
|
1351
|
+
css: "CSS",
|
|
1352
|
+
html: "HTML",
|
|
1353
|
+
bash: "Shell",
|
|
1354
|
+
python: "Python",
|
|
1355
|
+
markdown: "Markdown",
|
|
1356
|
+
yaml: "YAML",
|
|
1357
|
+
sql: "SQL",
|
|
1358
|
+
plaintext: "Plain text"
|
|
1359
|
+
};
|
|
1360
|
+
function normalizeLanguage(language) {
|
|
1361
|
+
const lower = language.trim().toLowerCase();
|
|
1362
|
+
return ALIASES[lower] ?? lower;
|
|
1363
|
+
}
|
|
1364
|
+
var nuvoxHighlight = (code, language) => {
|
|
1365
|
+
const key = normalizeLanguage(language ?? "plaintext");
|
|
1366
|
+
const grammar = GRAMMARS[key];
|
|
1367
|
+
if (!grammar) return splitIntoLines([{ type: "plain", content: code }]);
|
|
1368
|
+
try {
|
|
1369
|
+
return splitIntoLines(tokenizeFlat(code, grammar));
|
|
1370
|
+
} catch {
|
|
1371
|
+
return splitIntoLines([{ type: "plain", content: code }]);
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
|
|
1375
|
+
// src/core/rich-text.ts
|
|
1376
|
+
var MARK_TAGS = {
|
|
1377
|
+
bold: "STRONG",
|
|
1378
|
+
italic: "EM",
|
|
1379
|
+
underline: "U",
|
|
1380
|
+
strike: "S",
|
|
1381
|
+
code: "CODE"
|
|
1382
|
+
};
|
|
1383
|
+
var BLOCK_TAGS = {
|
|
1384
|
+
paragraph: "P",
|
|
1385
|
+
h1: "H1",
|
|
1386
|
+
h2: "H2",
|
|
1387
|
+
h3: "H3",
|
|
1388
|
+
blockquote: "BLOCKQUOTE",
|
|
1389
|
+
bulletList: "UL",
|
|
1390
|
+
orderedList: "OL"
|
|
1391
|
+
};
|
|
1392
|
+
var BLOCK_TAG_NAMES = /* @__PURE__ */ new Set(["P", "H1", "H2", "H3", "BLOCKQUOTE", "DIV"]);
|
|
1393
|
+
function isMark(command) {
|
|
1394
|
+
return command in MARK_TAGS;
|
|
1395
|
+
}
|
|
1396
|
+
function isBlock(command) {
|
|
1397
|
+
return command in BLOCK_TAGS;
|
|
1398
|
+
}
|
|
1399
|
+
function isCommandActive(ancestorTags, command) {
|
|
1400
|
+
if (command === "link") return ancestorTags.includes("A");
|
|
1401
|
+
if (command === "unlink") return false;
|
|
1402
|
+
if (isMark(command)) return ancestorTags.includes(MARK_TAGS[command]);
|
|
1403
|
+
if (isBlock(command)) return ancestorTags.includes(BLOCK_TAGS[command]);
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
function closestWithinRoot(node, tag, root) {
|
|
1407
|
+
let n = node;
|
|
1408
|
+
while (n && n !== root) {
|
|
1409
|
+
if (n.nodeType === Node.ELEMENT_NODE && n.tagName === tag) return n;
|
|
1410
|
+
n = n.parentNode;
|
|
1411
|
+
}
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function closestBlock(node, root) {
|
|
1415
|
+
let n = node && node.nodeType === Node.ELEMENT_NODE ? node : node?.parentNode ?? null;
|
|
1416
|
+
while (n && n !== root) {
|
|
1417
|
+
if (n.nodeType === Node.ELEMENT_NODE && BLOCK_TAG_NAMES.has(n.tagName)) return n;
|
|
1418
|
+
n = n.parentNode;
|
|
1419
|
+
}
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
function unwrapElement(el) {
|
|
1423
|
+
const parent = el.parentNode;
|
|
1424
|
+
if (!parent) return;
|
|
1425
|
+
while (el.firstChild) parent.insertBefore(el.firstChild, el);
|
|
1426
|
+
parent.removeChild(el);
|
|
1427
|
+
}
|
|
1428
|
+
function replaceTag(el, tag) {
|
|
1429
|
+
const next = document.createElement(tag);
|
|
1430
|
+
while (el.firstChild) next.appendChild(el.firstChild);
|
|
1431
|
+
el.parentNode?.replaceChild(next, el);
|
|
1432
|
+
return next;
|
|
1433
|
+
}
|
|
1434
|
+
function selectNodeContents(node, collapseToEnd = false) {
|
|
1435
|
+
const sel = window.getSelection();
|
|
1436
|
+
if (!sel) return;
|
|
1437
|
+
const range = document.createRange();
|
|
1438
|
+
range.selectNodeContents(node);
|
|
1439
|
+
if (collapseToEnd) range.collapse(false);
|
|
1440
|
+
sel.removeAllRanges();
|
|
1441
|
+
sel.addRange(range);
|
|
1442
|
+
}
|
|
1443
|
+
var ZERO_WIDTH_SPACE = "\u200B";
|
|
1444
|
+
function selectAtMarker(sel, marker, offset) {
|
|
1445
|
+
const range = document.createRange();
|
|
1446
|
+
range.setStart(marker, offset);
|
|
1447
|
+
range.collapse(true);
|
|
1448
|
+
sel.removeAllRanges();
|
|
1449
|
+
sel.addRange(range);
|
|
1450
|
+
}
|
|
1451
|
+
function togglePendingMark(root, tag) {
|
|
1452
|
+
const sel = window.getSelection();
|
|
1453
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1454
|
+
const range = sel.getRangeAt(0);
|
|
1455
|
+
const existing = closestWithinRoot(range.startContainer, tag, root);
|
|
1456
|
+
if (existing) {
|
|
1457
|
+
const marker2 = document.createTextNode(ZERO_WIDTH_SPACE);
|
|
1458
|
+
existing.parentNode?.insertBefore(marker2, existing.nextSibling);
|
|
1459
|
+
selectAtMarker(sel, marker2, 1);
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
const wrapper = document.createElement(tag);
|
|
1463
|
+
const marker = document.createTextNode(ZERO_WIDTH_SPACE);
|
|
1464
|
+
wrapper.appendChild(marker);
|
|
1465
|
+
range.insertNode(wrapper);
|
|
1466
|
+
selectAtMarker(sel, marker, 1);
|
|
1467
|
+
}
|
|
1468
|
+
function toggleMark(root, tag) {
|
|
1469
|
+
const sel = window.getSelection();
|
|
1470
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1471
|
+
if (sel.isCollapsed) {
|
|
1472
|
+
togglePendingMark(root, tag);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const range = sel.getRangeAt(0);
|
|
1476
|
+
const existing = closestWithinRoot(range.commonAncestorContainer, tag, root);
|
|
1477
|
+
if (existing) {
|
|
1478
|
+
unwrapElement(existing);
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
const wrapper = document.createElement(tag);
|
|
1482
|
+
wrapper.appendChild(range.extractContents());
|
|
1483
|
+
range.insertNode(wrapper);
|
|
1484
|
+
selectNodeContents(wrapper);
|
|
1485
|
+
}
|
|
1486
|
+
function setBlockTag(root, targetTag) {
|
|
1487
|
+
const sel = window.getSelection();
|
|
1488
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1489
|
+
const block = closestBlock(sel.getRangeAt(0).startContainer, root);
|
|
1490
|
+
if (!block) return;
|
|
1491
|
+
const next = replaceTag(block, block.tagName === targetTag ? "P" : targetTag);
|
|
1492
|
+
selectNodeContents(next, true);
|
|
1493
|
+
}
|
|
1494
|
+
function toggleList(root, listTag) {
|
|
1495
|
+
const sel = window.getSelection();
|
|
1496
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1497
|
+
const range = sel.getRangeAt(0);
|
|
1498
|
+
const li = closestWithinRoot(range.startContainer, "LI", root);
|
|
1499
|
+
if (li && li.parentElement?.tagName === listTag) {
|
|
1500
|
+
const list2 = li.parentElement;
|
|
1501
|
+
const p = document.createElement("P");
|
|
1502
|
+
while (li.firstChild) p.appendChild(li.firstChild);
|
|
1503
|
+
list2.parentNode?.replaceChild(p, list2);
|
|
1504
|
+
selectNodeContents(p, true);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
const block = closestBlock(range.startContainer, root);
|
|
1508
|
+
if (!block) return;
|
|
1509
|
+
const list = document.createElement(listTag);
|
|
1510
|
+
const item = document.createElement("LI");
|
|
1511
|
+
while (block.firstChild) item.appendChild(block.firstChild);
|
|
1512
|
+
list.appendChild(item);
|
|
1513
|
+
block.parentNode?.replaceChild(list, block);
|
|
1514
|
+
selectNodeContents(item, true);
|
|
1515
|
+
}
|
|
1516
|
+
function setLink(root, url) {
|
|
1517
|
+
const sel = window.getSelection();
|
|
1518
|
+
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return;
|
|
1519
|
+
const range = sel.getRangeAt(0);
|
|
1520
|
+
const existing = closestWithinRoot(range.commonAncestorContainer, "A", root);
|
|
1521
|
+
if (existing) {
|
|
1522
|
+
existing.setAttribute("href", url);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const a = document.createElement("A");
|
|
1526
|
+
a.setAttribute("href", url);
|
|
1527
|
+
a.setAttribute("rel", "noopener noreferrer");
|
|
1528
|
+
a.appendChild(range.extractContents());
|
|
1529
|
+
range.insertNode(a);
|
|
1530
|
+
selectNodeContents(a);
|
|
1531
|
+
}
|
|
1532
|
+
function unlink(root) {
|
|
1533
|
+
const sel = window.getSelection();
|
|
1534
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1535
|
+
const a = closestWithinRoot(sel.getRangeAt(0).commonAncestorContainer, "A", root);
|
|
1536
|
+
if (a) unwrapElement(a);
|
|
1537
|
+
}
|
|
1538
|
+
function findStyledSpanWithinRoot(node, property, root) {
|
|
1539
|
+
let n = node;
|
|
1540
|
+
while (n && n !== root) {
|
|
1541
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1542
|
+
const el = n;
|
|
1543
|
+
if (el.tagName === "SPAN" && el.style.getPropertyValue(property)) return el;
|
|
1544
|
+
}
|
|
1545
|
+
n = n.parentNode;
|
|
1546
|
+
}
|
|
1547
|
+
return null;
|
|
1548
|
+
}
|
|
1549
|
+
function setInlineStyle(root, property, value) {
|
|
1550
|
+
const sel = window.getSelection();
|
|
1551
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1552
|
+
const range = sel.getRangeAt(0);
|
|
1553
|
+
if (sel.isCollapsed) {
|
|
1554
|
+
const existing2 = findStyledSpanWithinRoot(range.startContainer, property, root);
|
|
1555
|
+
if (existing2) {
|
|
1556
|
+
existing2.style.setProperty(property, value);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
const wrapper2 = document.createElement("span");
|
|
1560
|
+
wrapper2.style.setProperty(property, value);
|
|
1561
|
+
const marker = document.createTextNode(ZERO_WIDTH_SPACE);
|
|
1562
|
+
wrapper2.appendChild(marker);
|
|
1563
|
+
range.insertNode(wrapper2);
|
|
1564
|
+
selectAtMarker(sel, marker, 1);
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
const existing = findStyledSpanWithinRoot(range.commonAncestorContainer, property, root);
|
|
1568
|
+
if (existing) {
|
|
1569
|
+
existing.style.setProperty(property, value);
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1572
|
+
const wrapper = document.createElement("span");
|
|
1573
|
+
wrapper.style.setProperty(property, value);
|
|
1574
|
+
wrapper.appendChild(range.extractContents());
|
|
1575
|
+
range.insertNode(wrapper);
|
|
1576
|
+
selectNodeContents(wrapper);
|
|
1577
|
+
}
|
|
1578
|
+
function clearInlineStyle(root, property) {
|
|
1579
|
+
const sel = window.getSelection();
|
|
1580
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
1581
|
+
const el = findStyledSpanWithinRoot(sel.getRangeAt(0).commonAncestorContainer, property, root);
|
|
1582
|
+
if (!el) return;
|
|
1583
|
+
el.style.removeProperty(property);
|
|
1584
|
+
if (!el.getAttribute("style")) unwrapElement(el);
|
|
1585
|
+
}
|
|
1586
|
+
function execRichTextCommand(root, command, value) {
|
|
1587
|
+
if (isMark(command)) toggleMark(root, MARK_TAGS[command]);
|
|
1588
|
+
else if (command === "bulletList") toggleList(root, "UL");
|
|
1589
|
+
else if (command === "orderedList") toggleList(root, "OL");
|
|
1590
|
+
else if (isBlock(command)) setBlockTag(root, BLOCK_TAGS[command]);
|
|
1591
|
+
else if (command === "link" && value) setLink(root, value);
|
|
1592
|
+
else if (command === "unlink") unlink(root);
|
|
1593
|
+
else if (command === "textColor" && value) setInlineStyle(root, "color", value);
|
|
1594
|
+
else if (command === "clearTextColor") clearInlineStyle(root, "color");
|
|
1595
|
+
else if (command === "highlight" && value) setInlineStyle(root, "background-color", value);
|
|
1596
|
+
else if (command === "clearHighlight") clearInlineStyle(root, "background-color");
|
|
1597
|
+
}
|
|
1598
|
+
function getActiveCommands(root) {
|
|
1599
|
+
const result = /* @__PURE__ */ new Set();
|
|
1600
|
+
if (typeof window === "undefined") return result;
|
|
1601
|
+
const sel = window.getSelection();
|
|
1602
|
+
if (!sel || sel.rangeCount === 0) return result;
|
|
1603
|
+
const tags = [];
|
|
1604
|
+
let node = sel.anchorNode;
|
|
1605
|
+
while (node && node !== root) {
|
|
1606
|
+
if (node.nodeType === Node.ELEMENT_NODE) tags.push(node.tagName);
|
|
1607
|
+
node = node.parentNode;
|
|
1608
|
+
}
|
|
1609
|
+
const commands = [...Object.keys(MARK_TAGS), ...Object.keys(BLOCK_TAGS), "link"];
|
|
1610
|
+
for (const command of commands) {
|
|
1611
|
+
if (isCommandActive(tags, command)) result.add(command);
|
|
1612
|
+
}
|
|
1613
|
+
return result;
|
|
1614
|
+
}
|
|
1615
|
+
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
1616
|
+
"P",
|
|
1617
|
+
"BR",
|
|
1618
|
+
"STRONG",
|
|
1619
|
+
"B",
|
|
1620
|
+
"EM",
|
|
1621
|
+
"I",
|
|
1622
|
+
"U",
|
|
1623
|
+
"S",
|
|
1624
|
+
"STRIKE",
|
|
1625
|
+
"CODE",
|
|
1626
|
+
"A",
|
|
1627
|
+
"H1",
|
|
1628
|
+
"H2",
|
|
1629
|
+
"H3",
|
|
1630
|
+
"BLOCKQUOTE",
|
|
1631
|
+
"UL",
|
|
1632
|
+
"OL",
|
|
1633
|
+
"LI",
|
|
1634
|
+
"SPAN"
|
|
1635
|
+
]);
|
|
1636
|
+
var ALLOWED_ATTRS = {
|
|
1637
|
+
A: ["href"],
|
|
1638
|
+
SPAN: ["style"]
|
|
1639
|
+
};
|
|
1640
|
+
var ALLOWED_STYLE_PROPS = /* @__PURE__ */ new Set(["color", "background-color"]);
|
|
1641
|
+
var SAFE_COLOR_VALUE = /^(#[0-9a-fA-F]{3,8}|rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)|rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*[\d.]+\s*\)|hsl\([^();]*\)|hsla\([^();]*\)|[a-zA-Z]+)$/;
|
|
1642
|
+
function sanitizeStyleAttr(value) {
|
|
1643
|
+
const out = [];
|
|
1644
|
+
for (const decl of value.split(";")) {
|
|
1645
|
+
const colon = decl.indexOf(":");
|
|
1646
|
+
if (colon === -1) continue;
|
|
1647
|
+
const prop = decl.slice(0, colon).trim().toLowerCase();
|
|
1648
|
+
const val = decl.slice(colon + 1).trim();
|
|
1649
|
+
if (ALLOWED_STYLE_PROPS.has(prop) && SAFE_COLOR_VALUE.test(val)) {
|
|
1650
|
+
out.push(`${prop}: ${val}`);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return out.join("; ");
|
|
1654
|
+
}
|
|
1655
|
+
var STRIP_ENTIRELY = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "IFRAME", "OBJECT", "EMBED"]);
|
|
1656
|
+
function sanitizeElement(el) {
|
|
1657
|
+
for (const child of Array.from(el.childNodes)) {
|
|
1658
|
+
if (child.nodeType === Node.TEXT_NODE) continue;
|
|
1659
|
+
if (child.nodeType !== Node.ELEMENT_NODE) {
|
|
1660
|
+
child.parentNode?.removeChild(child);
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
const childEl = child;
|
|
1664
|
+
if (STRIP_ENTIRELY.has(childEl.tagName)) {
|
|
1665
|
+
childEl.parentNode?.removeChild(childEl);
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
if (!ALLOWED_TAGS.has(childEl.tagName)) {
|
|
1669
|
+
sanitizeElement(childEl);
|
|
1670
|
+
const parent = childEl.parentNode;
|
|
1671
|
+
while (childEl.firstChild) parent?.insertBefore(childEl.firstChild, childEl);
|
|
1672
|
+
parent?.removeChild(childEl);
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1675
|
+
const allowedAttrs = ALLOWED_ATTRS[childEl.tagName] ?? [];
|
|
1676
|
+
for (const attr of Array.from(childEl.attributes)) {
|
|
1677
|
+
const name = attr.name.toLowerCase();
|
|
1678
|
+
if (!allowedAttrs.includes(name)) {
|
|
1679
|
+
childEl.removeAttribute(attr.name);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
if (name === "style") {
|
|
1683
|
+
const cleaned = sanitizeStyleAttr(attr.value);
|
|
1684
|
+
if (cleaned) childEl.setAttribute("style", cleaned);
|
|
1685
|
+
else childEl.removeAttribute("style");
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
if (childEl.tagName === "A") {
|
|
1689
|
+
const href = childEl.getAttribute("href") ?? "";
|
|
1690
|
+
const safe = /^https?:\/\//i.test(href) || href.startsWith("/") || href.startsWith("#") || href.startsWith("mailto:");
|
|
1691
|
+
if (!safe) childEl.removeAttribute("href");
|
|
1692
|
+
childEl.setAttribute("rel", "noopener noreferrer");
|
|
1693
|
+
childEl.setAttribute("target", "_blank");
|
|
1694
|
+
}
|
|
1695
|
+
sanitizeElement(childEl);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
function sanitizeHtml(html) {
|
|
1699
|
+
if (typeof DOMParser === "undefined") return "";
|
|
1700
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
1701
|
+
sanitizeElement(doc.body);
|
|
1702
|
+
return doc.body.innerHTML;
|
|
1703
|
+
}
|
|
1704
|
+
var HISTORY_LIMIT = 100;
|
|
1705
|
+
function createHistory() {
|
|
1706
|
+
return { past: [], future: [] };
|
|
1707
|
+
}
|
|
1708
|
+
function pushHistory(history, previousValue) {
|
|
1709
|
+
return { past: [...history.past, previousValue].slice(-HISTORY_LIMIT), future: [] };
|
|
1710
|
+
}
|
|
1711
|
+
function undoHistory(history, currentValue) {
|
|
1712
|
+
if (history.past.length === 0) return null;
|
|
1713
|
+
const value = history.past[history.past.length - 1];
|
|
1714
|
+
return {
|
|
1715
|
+
history: { past: history.past.slice(0, -1), future: [currentValue, ...history.future] },
|
|
1716
|
+
value
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
function redoHistory(history, currentValue) {
|
|
1720
|
+
if (history.future.length === 0) return null;
|
|
1721
|
+
const value = history.future[0];
|
|
1722
|
+
return {
|
|
1723
|
+
history: { past: [...history.past, currentValue], future: history.future.slice(1) },
|
|
1724
|
+
value
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// src/core/gallery.ts
|
|
1729
|
+
function aspectRatioFromDims(width, height) {
|
|
1730
|
+
if (!width || !height || width <= 0 || height <= 0) return 1;
|
|
1731
|
+
return width / height;
|
|
1732
|
+
}
|
|
1733
|
+
function computeJustifiedRows(aspectRatios, containerWidth, targetRowHeight, gap) {
|
|
1734
|
+
if (aspectRatios.length === 0 || containerWidth <= 0 || targetRowHeight <= 0) return [];
|
|
1735
|
+
const rows = [];
|
|
1736
|
+
let rowIndices = [];
|
|
1737
|
+
let rowNaturalWidths = [];
|
|
1738
|
+
let rowWidthSum = 0;
|
|
1739
|
+
function flushRow(scale) {
|
|
1740
|
+
if (rowIndices.length === 0) return;
|
|
1741
|
+
const gapsWidth = gap * (rowIndices.length - 1);
|
|
1742
|
+
const availableWidth = containerWidth - gapsWidth;
|
|
1743
|
+
const naturalTotal = rowWidthSum;
|
|
1744
|
+
const scaleFactor = scale ? availableWidth / naturalTotal : 1;
|
|
1745
|
+
rows.push({
|
|
1746
|
+
indices: rowIndices,
|
|
1747
|
+
height: targetRowHeight * scaleFactor,
|
|
1748
|
+
widths: rowNaturalWidths.map((w) => w * scaleFactor)
|
|
1749
|
+
});
|
|
1750
|
+
rowIndices = [];
|
|
1751
|
+
rowNaturalWidths = [];
|
|
1752
|
+
rowWidthSum = 0;
|
|
1753
|
+
}
|
|
1754
|
+
for (let i = 0; i < aspectRatios.length; i++) {
|
|
1755
|
+
const naturalWidth = aspectRatios[i] * targetRowHeight;
|
|
1756
|
+
const gapsWidth = gap * rowIndices.length;
|
|
1757
|
+
if (rowIndices.length > 0 && rowWidthSum + naturalWidth + gapsWidth > containerWidth) {
|
|
1758
|
+
flushRow(true);
|
|
1759
|
+
}
|
|
1760
|
+
rowIndices.push(i);
|
|
1761
|
+
rowNaturalWidths.push(naturalWidth);
|
|
1762
|
+
rowWidthSum += naturalWidth;
|
|
1763
|
+
}
|
|
1764
|
+
const finalGapsWidth = gap * (rowIndices.length - 1);
|
|
1765
|
+
const overflows = rowWidthSum + finalGapsWidth > containerWidth;
|
|
1766
|
+
flushRow(overflows);
|
|
1767
|
+
return rows;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
// src/core/media.ts
|
|
1771
|
+
var PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
|
|
1772
|
+
function formatMediaTime(seconds) {
|
|
1773
|
+
if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
|
|
1774
|
+
const total = Math.floor(seconds);
|
|
1775
|
+
const h = Math.floor(total / 3600);
|
|
1776
|
+
const m = Math.floor(total % 3600 / 60);
|
|
1777
|
+
const s = total % 60;
|
|
1778
|
+
const paddedS = String(s).padStart(2, "0");
|
|
1779
|
+
if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${paddedS}`;
|
|
1780
|
+
return `${m}:${paddedS}`;
|
|
1781
|
+
}
|
|
1782
|
+
var SKIP_SECONDS = 10;
|
|
1783
|
+
var LABELED_SKIP_SECONDS = [5, 10, 15, 30];
|
|
1784
|
+
function hasLabeledSkipIcon(seconds) {
|
|
1785
|
+
return LABELED_SKIP_SECONDS.includes(seconds);
|
|
1786
|
+
}
|
|
1787
|
+
function resolveSkipSeconds(skipSeconds) {
|
|
1788
|
+
if (skipSeconds === void 0) return SKIP_SECONDS;
|
|
1789
|
+
if (!Number.isFinite(skipSeconds) || skipSeconds <= 0) return SKIP_SECONDS;
|
|
1790
|
+
return skipSeconds;
|
|
1791
|
+
}
|
|
1792
|
+
function resolveMediaTransport(props) {
|
|
1793
|
+
const showNext = props.onNext !== void 0;
|
|
1794
|
+
const showPrevious = props.onPrevious !== void 0;
|
|
1795
|
+
return {
|
|
1796
|
+
skipSeconds: resolveSkipSeconds(props.skipSeconds),
|
|
1797
|
+
showNext,
|
|
1798
|
+
showPrevious,
|
|
1799
|
+
nextDisabled: showNext && (props.hasNext ?? true) === false,
|
|
1800
|
+
previousDisabled: showPrevious && (props.hasPrevious ?? true) === false
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
function getMediaKeyAction(key) {
|
|
1804
|
+
switch (key) {
|
|
1805
|
+
case " ":
|
|
1806
|
+
case "Spacebar":
|
|
1807
|
+
return "toggle-play";
|
|
1808
|
+
case "ArrowLeft":
|
|
1809
|
+
return "seek-back";
|
|
1810
|
+
case "ArrowRight":
|
|
1811
|
+
return "seek-forward";
|
|
1812
|
+
case "ArrowUp":
|
|
1813
|
+
return "volume-up";
|
|
1814
|
+
case "ArrowDown":
|
|
1815
|
+
return "volume-down";
|
|
1816
|
+
default:
|
|
1817
|
+
return null;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// src/core/pdf-viewer.ts
|
|
1822
|
+
function formatZoomPercent(scale) {
|
|
1823
|
+
return scale === "auto" ? "Fit" : `${Math.round(scale * 100)}%`;
|
|
1824
|
+
}
|
|
893
1825
|
// Annotate the CommonJS export names for ESM import in node:
|
|
894
1826
|
0 && (module.exports = {
|
|
895
1827
|
DEFAULT_COLOR_PRESETS,
|
|
1828
|
+
DRAG_START_THRESHOLD_PX,
|
|
1829
|
+
LABELED_SKIP_SECONDS,
|
|
1830
|
+
LANGUAGE_LABELS,
|
|
1831
|
+
PLAYBACK_RATES,
|
|
896
1832
|
REQUIRED_TOKEN_PATHS,
|
|
1833
|
+
SKIP_SECONDS,
|
|
897
1834
|
TOKEN_CATEGORIES,
|
|
898
1835
|
amber,
|
|
1836
|
+
aspectRatioFromDims,
|
|
899
1837
|
brutalism,
|
|
1838
|
+
computeColumnDropIndex,
|
|
1839
|
+
computeDropColumn,
|
|
1840
|
+
computeDropIndex,
|
|
1841
|
+
computeJustifiedRows,
|
|
900
1842
|
copper,
|
|
1843
|
+
createHistory,
|
|
901
1844
|
cyberpunk,
|
|
902
1845
|
dark,
|
|
903
1846
|
darkGlass,
|
|
904
1847
|
defineTheme,
|
|
905
1848
|
emerald,
|
|
1849
|
+
execRichTextCommand,
|
|
1850
|
+
findCardLocation,
|
|
906
1851
|
findMissingTokenPaths,
|
|
1852
|
+
formatMediaTime,
|
|
1853
|
+
formatZoomPercent,
|
|
907
1854
|
getAccent,
|
|
1855
|
+
getActiveCommands,
|
|
1856
|
+
getAdjacentColumnId,
|
|
1857
|
+
getColumnKeyboardMoveTarget,
|
|
1858
|
+
getKeyboardMoveTarget,
|
|
1859
|
+
getMediaKeyAction,
|
|
908
1860
|
getNextSortDirection,
|
|
909
1861
|
getSelectAllState,
|
|
910
1862
|
getTheme,
|
|
911
1863
|
glass,
|
|
912
1864
|
gradient,
|
|
913
1865
|
graphite,
|
|
1866
|
+
hasLabeledSkipIcon,
|
|
914
1867
|
indigo,
|
|
915
1868
|
light,
|
|
916
1869
|
listAccents,
|
|
@@ -918,12 +1871,23 @@ function getSelectAllState(totalCount, selectedCount) {
|
|
|
918
1871
|
magenta,
|
|
919
1872
|
material,
|
|
920
1873
|
minimalism,
|
|
1874
|
+
moveCard,
|
|
1875
|
+
moveColumn,
|
|
921
1876
|
neobrutalism,
|
|
1877
|
+
nuvoxHighlight,
|
|
1878
|
+
pushHistory,
|
|
1879
|
+
redoHistory,
|
|
922
1880
|
registerAccent,
|
|
923
1881
|
registerTheme,
|
|
1882
|
+
removeCard,
|
|
1883
|
+
removeColumn,
|
|
1884
|
+
resolveMediaTransport,
|
|
1885
|
+
resolveSkipSeconds,
|
|
924
1886
|
rose,
|
|
1887
|
+
sanitizeHtml,
|
|
925
1888
|
skeuomorphism,
|
|
926
1889
|
teal,
|
|
1890
|
+
undoHistory,
|
|
927
1891
|
violet
|
|
928
1892
|
});
|
|
929
1893
|
//# sourceMappingURL=index.cjs.map
|