@june-ai/june-ui 0.2.3 → 0.2.5
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 +21 -0
- package/dist/chunk-CZE533PM.js +343 -0
- package/dist/{document-engine-C6ILFMRP.js → document-engine-IAY2T6B3.js} +1 -1
- package/dist/index.cjs +1628 -288
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +1430 -255
- package/dist/june.css +436 -118
- package/package.json +6 -1
- package/dist/chunk-HFHRSNIL.js +0 -174
package/dist/index.js
CHANGED
|
@@ -6,13 +6,18 @@ import {
|
|
|
6
6
|
docToHtml,
|
|
7
7
|
docToMarkdown,
|
|
8
8
|
emptyDoc,
|
|
9
|
+
getSearchState,
|
|
10
|
+
gotoMatch,
|
|
9
11
|
isJuneError,
|
|
10
12
|
parseDocumentContent,
|
|
13
|
+
replaceAll,
|
|
14
|
+
replaceCurrent,
|
|
11
15
|
sanitizeHref,
|
|
16
|
+
setSearch,
|
|
12
17
|
slugify,
|
|
13
18
|
toJuneError,
|
|
14
19
|
wordCountOf
|
|
15
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-CZE533PM.js";
|
|
16
21
|
|
|
17
22
|
// src/renderer/renderer.tsx
|
|
18
23
|
import { Renderer } from "@openuidev/react-lang";
|
|
@@ -203,6 +208,7 @@ var ATTACHABLE = /* @__PURE__ */ new Set([
|
|
|
203
208
|
"CodeBlock",
|
|
204
209
|
"CompositeCardBlock",
|
|
205
210
|
"ContextCardBlock",
|
|
211
|
+
"DocumentEditor",
|
|
206
212
|
"EditableTable",
|
|
207
213
|
"EntityList",
|
|
208
214
|
"Form",
|
|
@@ -1089,18 +1095,1164 @@ function DocumentDraft({ title, words, state }) {
|
|
|
1089
1095
|
|
|
1090
1096
|
// src/components/document-editor/document-editor.tsx
|
|
1091
1097
|
import { useEditor } from "@tiptap/react";
|
|
1092
|
-
import { useCallback as useCallback3, useEffect as
|
|
1098
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useId, useMemo as useMemo4, useRef as useRef7, useState as useState6 } from "react";
|
|
1099
|
+
|
|
1100
|
+
// src/components/document-editor/document-bubble.tsx
|
|
1101
|
+
import { useEditorState as useEditorState2 } from "@tiptap/react";
|
|
1102
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
1103
|
+
|
|
1104
|
+
// src/components/document-editor/document-toolbar.tsx
|
|
1105
|
+
import { useEditorState } from "@tiptap/react";
|
|
1106
|
+
import { useRef as useRef4, useState as useState3 } from "react";
|
|
1107
|
+
|
|
1108
|
+
// src/components/document-editor/document-icons.tsx
|
|
1109
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1110
|
+
var PATHS = {
|
|
1111
|
+
undo: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1112
|
+
/* @__PURE__ */ jsx5("path", { d: "M9 14 4 9l5-5" }),
|
|
1113
|
+
/* @__PURE__ */ jsx5("path", { d: "M4 9h10.5a5.5 5.5 0 0 1 0 11H11" })
|
|
1114
|
+
] }),
|
|
1115
|
+
redo: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1116
|
+
/* @__PURE__ */ jsx5("path", { d: "m15 14 5-5-5-5" }),
|
|
1117
|
+
/* @__PURE__ */ jsx5("path", { d: "M20 9H9.5a5.5 5.5 0 0 0 0 11H13" })
|
|
1118
|
+
] }),
|
|
1119
|
+
bold: /* @__PURE__ */ jsx5("path", { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" }),
|
|
1120
|
+
italic: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1121
|
+
/* @__PURE__ */ jsx5("line", { x1: "19", x2: "10", y1: "4", y2: "4" }),
|
|
1122
|
+
/* @__PURE__ */ jsx5("line", { x1: "14", x2: "5", y1: "20", y2: "20" }),
|
|
1123
|
+
/* @__PURE__ */ jsx5("line", { x1: "15", x2: "9", y1: "4", y2: "20" })
|
|
1124
|
+
] }),
|
|
1125
|
+
underline: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1126
|
+
/* @__PURE__ */ jsx5("path", { d: "M6 4v6a6 6 0 0 0 12 0V4" }),
|
|
1127
|
+
/* @__PURE__ */ jsx5("line", { x1: "4", x2: "20", y1: "20", y2: "20" })
|
|
1128
|
+
] }),
|
|
1129
|
+
strike: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1130
|
+
/* @__PURE__ */ jsx5("path", { d: "M16 4H9a3 3 0 0 0-2.83 4" }),
|
|
1131
|
+
/* @__PURE__ */ jsx5("path", { d: "M14 12a4 4 0 0 1 0 8H6" }),
|
|
1132
|
+
/* @__PURE__ */ jsx5("line", { x1: "4", x2: "20", y1: "12", y2: "12" })
|
|
1133
|
+
] }),
|
|
1134
|
+
code: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1135
|
+
/* @__PURE__ */ jsx5("path", { d: "m16 18 6-6-6-6" }),
|
|
1136
|
+
/* @__PURE__ */ jsx5("path", { d: "m8 6-6 6 6 6" })
|
|
1137
|
+
] }),
|
|
1138
|
+
codeBlock: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1139
|
+
/* @__PURE__ */ jsx5("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
1140
|
+
/* @__PURE__ */ jsx5("path", { d: "m10 9-3 3 3 3" }),
|
|
1141
|
+
/* @__PURE__ */ jsx5("path", { d: "m14 15 3-3-3-3" })
|
|
1142
|
+
] }),
|
|
1143
|
+
superscript: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1144
|
+
/* @__PURE__ */ jsx5("path", { d: "m4 19 8-8" }),
|
|
1145
|
+
/* @__PURE__ */ jsx5("path", { d: "m12 19-8-8" }),
|
|
1146
|
+
/* @__PURE__ */ jsx5("path", { d: "M20 12h-4c0-1.5.44-2 1.5-2.5S20 8.33 20 7c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })
|
|
1147
|
+
] }),
|
|
1148
|
+
subscript: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1149
|
+
/* @__PURE__ */ jsx5("path", { d: "m4 5 8 8" }),
|
|
1150
|
+
/* @__PURE__ */ jsx5("path", { d: "m12 5-8 8" }),
|
|
1151
|
+
/* @__PURE__ */ jsx5("path", { d: "M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })
|
|
1152
|
+
] }),
|
|
1153
|
+
alignLeft: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1154
|
+
/* @__PURE__ */ jsx5("path", { d: "M15 12H3" }),
|
|
1155
|
+
/* @__PURE__ */ jsx5("path", { d: "M17 18H3" }),
|
|
1156
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 6H3" })
|
|
1157
|
+
] }),
|
|
1158
|
+
alignCenter: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1159
|
+
/* @__PURE__ */ jsx5("path", { d: "M17 12H7" }),
|
|
1160
|
+
/* @__PURE__ */ jsx5("path", { d: "M19 18H5" }),
|
|
1161
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 6H3" })
|
|
1162
|
+
] }),
|
|
1163
|
+
alignRight: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1164
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 12H9" }),
|
|
1165
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 18H7" }),
|
|
1166
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 6H3" })
|
|
1167
|
+
] }),
|
|
1168
|
+
alignJustify: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1169
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 12h18" }),
|
|
1170
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 18h18" }),
|
|
1171
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 6h18" })
|
|
1172
|
+
] }),
|
|
1173
|
+
list: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1174
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 12h.01" }),
|
|
1175
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 18h.01" }),
|
|
1176
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 6h.01" }),
|
|
1177
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 12h13" }),
|
|
1178
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 18h13" }),
|
|
1179
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 6h13" })
|
|
1180
|
+
] }),
|
|
1181
|
+
listOrdered: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1182
|
+
/* @__PURE__ */ jsx5("path", { d: "M10 12h11" }),
|
|
1183
|
+
/* @__PURE__ */ jsx5("path", { d: "M10 18h11" }),
|
|
1184
|
+
/* @__PURE__ */ jsx5("path", { d: "M10 6h11" }),
|
|
1185
|
+
/* @__PURE__ */ jsx5("path", { d: "M4 10h2" }),
|
|
1186
|
+
/* @__PURE__ */ jsx5("path", { d: "M4 6h1v4" }),
|
|
1187
|
+
/* @__PURE__ */ jsx5("path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
|
|
1188
|
+
] }),
|
|
1189
|
+
outdent: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1190
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 12H11" }),
|
|
1191
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 18H11" }),
|
|
1192
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 6H11" }),
|
|
1193
|
+
/* @__PURE__ */ jsx5("path", { d: "m7 8-4 4 4 4" })
|
|
1194
|
+
] }),
|
|
1195
|
+
indent: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1196
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 12H11" }),
|
|
1197
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 18H11" }),
|
|
1198
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 6H11" }),
|
|
1199
|
+
/* @__PURE__ */ jsx5("path", { d: "m3 8 4 4-4 4" })
|
|
1200
|
+
] }),
|
|
1201
|
+
quote: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1202
|
+
/* @__PURE__ */ jsx5("path", { d: "M17 6H3" }),
|
|
1203
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 12H8" }),
|
|
1204
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 18H8" }),
|
|
1205
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 12v6" })
|
|
1206
|
+
] }),
|
|
1207
|
+
pilcrow: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1208
|
+
/* @__PURE__ */ jsx5("path", { d: "M13 4v16" }),
|
|
1209
|
+
/* @__PURE__ */ jsx5("path", { d: "M17 4v16" }),
|
|
1210
|
+
/* @__PURE__ */ jsx5("path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" })
|
|
1211
|
+
] }),
|
|
1212
|
+
pageBreak: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1213
|
+
/* @__PURE__ */ jsx5("line", { x1: "3", x2: "21", y1: "12", y2: "12" }),
|
|
1214
|
+
/* @__PURE__ */ jsx5("polyline", { points: "8 8 12 4 16 8" }),
|
|
1215
|
+
/* @__PURE__ */ jsx5("polyline", { points: "16 16 12 20 8 16" })
|
|
1216
|
+
] }),
|
|
1217
|
+
divider: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1218
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 12h18" }),
|
|
1219
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 6h8", opacity: ".45" }),
|
|
1220
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 18h8", opacity: ".45" })
|
|
1221
|
+
] }),
|
|
1222
|
+
layout: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1223
|
+
/* @__PURE__ */ jsx5("rect", { width: "18", height: "7", x: "3", y: "3", rx: "1" }),
|
|
1224
|
+
/* @__PURE__ */ jsx5("rect", { width: "9", height: "7", x: "3", y: "14", rx: "1" }),
|
|
1225
|
+
/* @__PURE__ */ jsx5("rect", { width: "5", height: "7", x: "16", y: "14", rx: "1" })
|
|
1226
|
+
] }),
|
|
1227
|
+
search: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1228
|
+
/* @__PURE__ */ jsx5("circle", { cx: "11", cy: "11", r: "8" }),
|
|
1229
|
+
/* @__PURE__ */ jsx5("path", { d: "m21 21-4.3-4.3" })
|
|
1230
|
+
] }),
|
|
1231
|
+
palette: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1232
|
+
/* @__PURE__ */ jsx5("circle", { cx: "13.5", cy: "6.5", r: ".5", fill: "currentColor" }),
|
|
1233
|
+
/* @__PURE__ */ jsx5("circle", { cx: "17.5", cy: "10.5", r: ".5", fill: "currentColor" }),
|
|
1234
|
+
/* @__PURE__ */ jsx5("circle", { cx: "8.5", cy: "7.5", r: ".5", fill: "currentColor" }),
|
|
1235
|
+
/* @__PURE__ */ jsx5("circle", { cx: "6.5", cy: "12.5", r: ".5", fill: "currentColor" }),
|
|
1236
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.93 0 1.65-.75 1.65-1.69 0-.44-.18-.84-.44-1.13-.29-.29-.44-.65-.44-1.13a1.64 1.64 0 0 1 1.67-1.67h2c3.05 0 5.55-2.5 5.55-5.55C21.97 6.01 17.46 2 12 2z" })
|
|
1237
|
+
] }),
|
|
1238
|
+
highlighter: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1239
|
+
/* @__PURE__ */ jsx5("path", { d: "m9 11-6 6v3h9l3-3" }),
|
|
1240
|
+
/* @__PURE__ */ jsx5("path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
|
|
1241
|
+
] }),
|
|
1242
|
+
link: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1243
|
+
/* @__PURE__ */ jsx5("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
1244
|
+
/* @__PURE__ */ jsx5("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
1245
|
+
] }),
|
|
1246
|
+
image: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1247
|
+
/* @__PURE__ */ jsx5("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
1248
|
+
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "2" }),
|
|
1249
|
+
/* @__PURE__ */ jsx5("path", { d: "m21 15-3.09-3.09a2 2 0 0 0-2.82 0L6 21" })
|
|
1250
|
+
] }),
|
|
1251
|
+
table: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1252
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 3v18" }),
|
|
1253
|
+
/* @__PURE__ */ jsx5("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
|
|
1254
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 9h18" }),
|
|
1255
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 15h18" })
|
|
1256
|
+
] }),
|
|
1257
|
+
eraser: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1258
|
+
/* @__PURE__ */ jsx5("path", { d: "m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21" }),
|
|
1259
|
+
/* @__PURE__ */ jsx5("path", { d: "M22 21H7" }),
|
|
1260
|
+
/* @__PURE__ */ jsx5("path", { d: "m5 11 9 9" })
|
|
1261
|
+
] }),
|
|
1262
|
+
save: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1263
|
+
/* @__PURE__ */ jsx5("path", { d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" }),
|
|
1264
|
+
/* @__PURE__ */ jsx5("path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" }),
|
|
1265
|
+
/* @__PURE__ */ jsx5("path", { d: "M7 3v4a1 1 0 0 0 1 1h7" })
|
|
1266
|
+
] }),
|
|
1267
|
+
download: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1268
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
1269
|
+
/* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
|
|
1270
|
+
/* @__PURE__ */ jsx5("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
|
|
1271
|
+
] }),
|
|
1272
|
+
chevronDown: /* @__PURE__ */ jsx5("path", { d: "m6 9 6 6 6-6" }),
|
|
1273
|
+
chevronUp: /* @__PURE__ */ jsx5("path", { d: "m18 15-6-6-6 6" }),
|
|
1274
|
+
close: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1275
|
+
/* @__PURE__ */ jsx5("path", { d: "M18 6 6 18" }),
|
|
1276
|
+
/* @__PURE__ */ jsx5("path", { d: "m6 6 12 12" })
|
|
1277
|
+
] }),
|
|
1278
|
+
maximize: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1279
|
+
/* @__PURE__ */ jsx5("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
|
|
1280
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
|
|
1281
|
+
/* @__PURE__ */ jsx5("path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
|
|
1282
|
+
/* @__PURE__ */ jsx5("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
|
|
1283
|
+
] }),
|
|
1284
|
+
copy: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1285
|
+
/* @__PURE__ */ jsx5("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2" }),
|
|
1286
|
+
/* @__PURE__ */ jsx5("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
|
|
1287
|
+
] }),
|
|
1288
|
+
file: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1289
|
+
/* @__PURE__ */ jsx5("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
|
|
1290
|
+
/* @__PURE__ */ jsx5("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })
|
|
1291
|
+
] }),
|
|
1292
|
+
plus: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1293
|
+
/* @__PURE__ */ jsx5("path", { d: "M5 12h14" }),
|
|
1294
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 5v14" })
|
|
1295
|
+
] }),
|
|
1296
|
+
minus: /* @__PURE__ */ jsx5("path", { d: "M5 12h14" }),
|
|
1297
|
+
check: /* @__PURE__ */ jsx5("path", { d: "M20 6 9 17l-5-5" }),
|
|
1298
|
+
sparkles: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1299
|
+
/* @__PURE__ */ jsx5("path", { d: "M9.94 14.06 8 20l-1.94-5.94L0 12l6.06-1.94L8 4l1.94 6.06L16 12z", transform: "translate(3 -1) scale(.85)" }),
|
|
1300
|
+
/* @__PURE__ */ jsx5("path", { d: "M20 3v4" }),
|
|
1301
|
+
/* @__PURE__ */ jsx5("path", { d: "M22 5h-4" })
|
|
1302
|
+
] }),
|
|
1303
|
+
upload: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1304
|
+
/* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
1305
|
+
/* @__PURE__ */ jsx5("polyline", { points: "17 8 12 3 7 8" }),
|
|
1306
|
+
/* @__PURE__ */ jsx5("line", { x1: "12", x2: "12", y1: "3", y2: "15" })
|
|
1307
|
+
] })
|
|
1308
|
+
};
|
|
1309
|
+
function Icon({ name, size = 16 }) {
|
|
1310
|
+
return /* @__PURE__ */ jsx5(
|
|
1311
|
+
"svg",
|
|
1312
|
+
{
|
|
1313
|
+
className: "june-doc__icon",
|
|
1314
|
+
width: size,
|
|
1315
|
+
height: size,
|
|
1316
|
+
viewBox: "0 0 24 24",
|
|
1317
|
+
fill: "none",
|
|
1318
|
+
stroke: "currentColor",
|
|
1319
|
+
strokeWidth: 2,
|
|
1320
|
+
strokeLinecap: "round",
|
|
1321
|
+
strokeLinejoin: "round",
|
|
1322
|
+
"aria-hidden": "true",
|
|
1323
|
+
focusable: "false",
|
|
1324
|
+
children: PATHS[name]
|
|
1325
|
+
}
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// src/components/document-editor/document-popover.tsx
|
|
1330
|
+
import { useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
1331
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1332
|
+
function Dropdown({ trigger, children, align = "start", className, onOpen }) {
|
|
1333
|
+
const [open, setOpen] = useState2(false);
|
|
1334
|
+
const anchor = useRef3(null);
|
|
1335
|
+
useEffect2(() => {
|
|
1336
|
+
if (!open) return;
|
|
1337
|
+
const onDown = (e) => {
|
|
1338
|
+
if (anchor.current && !anchor.current.contains(e.target)) setOpen(false);
|
|
1339
|
+
};
|
|
1340
|
+
document.addEventListener("mousedown", onDown);
|
|
1341
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
1342
|
+
}, [open]);
|
|
1343
|
+
const toggle = () => setOpen((v) => {
|
|
1344
|
+
if (!v) onOpen?.();
|
|
1345
|
+
return !v;
|
|
1346
|
+
});
|
|
1347
|
+
const close = () => setOpen(false);
|
|
1348
|
+
return /* @__PURE__ */ jsxs4(
|
|
1349
|
+
"div",
|
|
1350
|
+
{
|
|
1351
|
+
ref: anchor,
|
|
1352
|
+
className: "june-doc__anchor",
|
|
1353
|
+
onKeyDown: (e) => {
|
|
1354
|
+
if (e.key === "Escape" && open) {
|
|
1355
|
+
e.stopPropagation();
|
|
1356
|
+
close();
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
children: [
|
|
1360
|
+
trigger({ open, toggle }),
|
|
1361
|
+
open ? /* @__PURE__ */ jsx6("div", { className: `june-doc__pop june-doc__pop--${align}${className ? ` ${className}` : ""}`, children: children(close) }) : null
|
|
1362
|
+
]
|
|
1363
|
+
}
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/components/document-editor/document-toolbar.tsx
|
|
1368
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1369
|
+
var FONT_FAMILIES = [
|
|
1370
|
+
{ label: "Default", value: "" },
|
|
1371
|
+
{ label: "Arial", value: "Arial, Helvetica, sans-serif" },
|
|
1372
|
+
{ label: "Calibri", value: "Calibri, Carlito, sans-serif" },
|
|
1373
|
+
{ label: "Georgia", value: "Georgia, serif" },
|
|
1374
|
+
{ label: "Garamond", value: "Garamond, 'EB Garamond', serif" },
|
|
1375
|
+
{ label: "Times New Roman", value: "'Times New Roman', Times, serif" },
|
|
1376
|
+
{ label: "Verdana", value: "Verdana, Geneva, sans-serif" },
|
|
1377
|
+
{ label: "Trebuchet MS", value: "'Trebuchet MS', sans-serif" },
|
|
1378
|
+
{ label: "Courier New", value: "'Courier New', Courier, monospace" }
|
|
1379
|
+
];
|
|
1380
|
+
var FONT_SIZES = [8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 36, 48, 60, 72];
|
|
1381
|
+
var TEXT_COLORS = [
|
|
1382
|
+
"#111827",
|
|
1383
|
+
"#374151",
|
|
1384
|
+
"#6b7280",
|
|
1385
|
+
"#9ca3af",
|
|
1386
|
+
"#dc2626",
|
|
1387
|
+
"#ea580c",
|
|
1388
|
+
"#ca8a04",
|
|
1389
|
+
"#16a34a",
|
|
1390
|
+
"#0d9488",
|
|
1391
|
+
"#0284c7",
|
|
1392
|
+
"#2563eb",
|
|
1393
|
+
"#4f46e5",
|
|
1394
|
+
"#9333ea",
|
|
1395
|
+
"#db2777",
|
|
1396
|
+
"#7f1d1d",
|
|
1397
|
+
"#1e3a8a"
|
|
1398
|
+
];
|
|
1399
|
+
var HIGHLIGHT_COLORS = [
|
|
1400
|
+
"#fef08a",
|
|
1401
|
+
"#fde68a",
|
|
1402
|
+
"#fed7aa",
|
|
1403
|
+
"#fecaca",
|
|
1404
|
+
"#fbcfe8",
|
|
1405
|
+
"#e9d5ff",
|
|
1406
|
+
"#c7d2fe",
|
|
1407
|
+
"#bfdbfe",
|
|
1408
|
+
"#a5f3fc",
|
|
1409
|
+
"#bbf7d0",
|
|
1410
|
+
"#d9f99d",
|
|
1411
|
+
"#e5e7eb"
|
|
1412
|
+
];
|
|
1413
|
+
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
1414
|
+
function Btn({ label, icon, active, disabled, toggle, onClick, children, className, text }) {
|
|
1415
|
+
return /* @__PURE__ */ jsxs5(
|
|
1416
|
+
"button",
|
|
1417
|
+
{
|
|
1418
|
+
type: "button",
|
|
1419
|
+
className: `june-doc__btn${icon && !text && !children ? " june-doc__btn--icon" : ""}${className ? ` ${className}` : ""}`,
|
|
1420
|
+
"aria-label": label,
|
|
1421
|
+
title: label,
|
|
1422
|
+
"aria-pressed": toggle ? !!active : void 0,
|
|
1423
|
+
"data-active": active || void 0,
|
|
1424
|
+
disabled,
|
|
1425
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1426
|
+
onClick,
|
|
1427
|
+
children: [
|
|
1428
|
+
icon ? /* @__PURE__ */ jsx7(Icon, { name: icon }) : null,
|
|
1429
|
+
children ?? (text ? /* @__PURE__ */ jsx7("span", { children: label }) : null)
|
|
1430
|
+
]
|
|
1431
|
+
}
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
function Group({ label, children }) {
|
|
1435
|
+
return /* @__PURE__ */ jsxs5("div", { className: "june-doc__group", role: "group", "aria-label": label, children: [
|
|
1436
|
+
label ? /* @__PURE__ */ jsx7("span", { className: "june-doc__group-label", "aria-hidden": "true", children: label }) : null,
|
|
1437
|
+
children
|
|
1438
|
+
] });
|
|
1439
|
+
}
|
|
1440
|
+
function Field({ label, children }) {
|
|
1441
|
+
return /* @__PURE__ */ jsxs5("label", { className: "june-doc__field", children: [
|
|
1442
|
+
/* @__PURE__ */ jsx7("span", { className: "june-doc__field-label", children: label }),
|
|
1443
|
+
children
|
|
1444
|
+
] });
|
|
1445
|
+
}
|
|
1446
|
+
function ColorPicker({ label, icon, colors, current, disabled, resetLabel, onPick, onReset }) {
|
|
1447
|
+
return /* @__PURE__ */ jsx7(
|
|
1448
|
+
Dropdown,
|
|
1449
|
+
{
|
|
1450
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ jsxs5(
|
|
1451
|
+
"button",
|
|
1452
|
+
{
|
|
1453
|
+
type: "button",
|
|
1454
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--color",
|
|
1455
|
+
"aria-label": label,
|
|
1456
|
+
title: label,
|
|
1457
|
+
"aria-expanded": open,
|
|
1458
|
+
"aria-haspopup": "dialog",
|
|
1459
|
+
disabled,
|
|
1460
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1461
|
+
onClick: toggle,
|
|
1462
|
+
children: [
|
|
1463
|
+
/* @__PURE__ */ jsx7(Icon, { name: icon }),
|
|
1464
|
+
/* @__PURE__ */ jsx7("span", { className: "june-doc__color-bar", style: { background: current ?? "transparent" } })
|
|
1465
|
+
]
|
|
1466
|
+
}
|
|
1467
|
+
),
|
|
1468
|
+
children: (close) => /* @__PURE__ */ jsxs5("div", { className: "june-doc__swatches-wrap", role: "dialog", "aria-label": label, children: [
|
|
1469
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__pop-title", children: label }),
|
|
1470
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__swatches", children: colors.map((c) => /* @__PURE__ */ jsx7(
|
|
1471
|
+
"button",
|
|
1472
|
+
{
|
|
1473
|
+
type: "button",
|
|
1474
|
+
className: "june-doc__swatch",
|
|
1475
|
+
style: { background: c },
|
|
1476
|
+
"aria-label": c,
|
|
1477
|
+
title: c,
|
|
1478
|
+
"data-active": current?.toLowerCase() === c ? true : void 0,
|
|
1479
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1480
|
+
onClick: () => {
|
|
1481
|
+
onPick(c);
|
|
1482
|
+
close();
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
c
|
|
1486
|
+
)) }),
|
|
1487
|
+
/* @__PURE__ */ jsxs5("div", { className: "june-doc__pop-row", children: [
|
|
1488
|
+
/* @__PURE__ */ jsx7(
|
|
1489
|
+
"button",
|
|
1490
|
+
{
|
|
1491
|
+
type: "button",
|
|
1492
|
+
className: "june-doc__btn june-doc__btn--ghost",
|
|
1493
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1494
|
+
onClick: () => {
|
|
1495
|
+
onReset();
|
|
1496
|
+
close();
|
|
1497
|
+
},
|
|
1498
|
+
children: resetLabel
|
|
1499
|
+
}
|
|
1500
|
+
),
|
|
1501
|
+
/* @__PURE__ */ jsxs5("label", { className: "june-doc__custom-color", title: "Custom color", children: [
|
|
1502
|
+
/* @__PURE__ */ jsx7("span", { children: "Custom" }),
|
|
1503
|
+
/* @__PURE__ */ jsx7(
|
|
1504
|
+
"input",
|
|
1505
|
+
{
|
|
1506
|
+
type: "color",
|
|
1507
|
+
"aria-label": `${label}: custom`,
|
|
1508
|
+
defaultValue: current && /^#[0-9a-f]{6}$/i.test(current) ? current : "#000000",
|
|
1509
|
+
onChange: (e) => onPick(e.target.value)
|
|
1510
|
+
}
|
|
1511
|
+
)
|
|
1512
|
+
] })
|
|
1513
|
+
] })
|
|
1514
|
+
] })
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
function LinkPanel({
|
|
1519
|
+
editor,
|
|
1520
|
+
href,
|
|
1521
|
+
isLink,
|
|
1522
|
+
close
|
|
1523
|
+
}) {
|
|
1524
|
+
const [value, setValue] = useState3(href);
|
|
1525
|
+
const apply = () => {
|
|
1526
|
+
const safe = sanitizeHref(value);
|
|
1527
|
+
if (safe) editor.chain().focus().extendMarkRange("link").setLink({ href: safe }).run();
|
|
1528
|
+
close();
|
|
1529
|
+
};
|
|
1530
|
+
const remove = () => {
|
|
1531
|
+
editor.chain().focus().extendMarkRange("link").unsetLink().run();
|
|
1532
|
+
close();
|
|
1533
|
+
};
|
|
1534
|
+
return /* @__PURE__ */ jsxs5("div", { className: "june-doc__form", role: "dialog", "aria-label": "Edit link", children: [
|
|
1535
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__pop-title", children: "Link" }),
|
|
1536
|
+
/* @__PURE__ */ jsx7(
|
|
1537
|
+
"input",
|
|
1538
|
+
{
|
|
1539
|
+
"aria-label": "Link address",
|
|
1540
|
+
className: "june-doc__input",
|
|
1541
|
+
placeholder: "https://example.com",
|
|
1542
|
+
value,
|
|
1543
|
+
autoFocus: true,
|
|
1544
|
+
onChange: (e) => setValue(e.target.value),
|
|
1545
|
+
onKeyDown: (e) => {
|
|
1546
|
+
if (e.key === "Enter") {
|
|
1547
|
+
e.preventDefault();
|
|
1548
|
+
apply();
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
/* @__PURE__ */ jsxs5("div", { className: "june-doc__pop-row june-doc__pop-row--end", children: [
|
|
1554
|
+
isLink ? /* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn june-doc__btn--ghost", onClick: remove, children: "Remove" }) : null,
|
|
1555
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: close, children: "Cancel" }),
|
|
1556
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn june-doc__btn--primary", onClick: apply, children: "Apply" })
|
|
1557
|
+
] })
|
|
1558
|
+
] });
|
|
1559
|
+
}
|
|
1560
|
+
function ImagePanel({ editor, close, onNotice }) {
|
|
1561
|
+
const [url, setUrl] = useState3("");
|
|
1562
|
+
const [alt, setAlt] = useState3("");
|
|
1563
|
+
const file = useRef4(null);
|
|
1564
|
+
const insert = (src) => {
|
|
1565
|
+
editor.chain().focus().setImage({ src, alt: alt || void 0 }).run();
|
|
1566
|
+
close();
|
|
1567
|
+
};
|
|
1568
|
+
const fromUrl = () => {
|
|
1569
|
+
const safe = sanitizeHref(url);
|
|
1570
|
+
if (!safe || !/^https?:/i.test(safe)) {
|
|
1571
|
+
onNotice("Enter an http(s) image address");
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
insert(safe);
|
|
1575
|
+
};
|
|
1576
|
+
const fromFile = (f) => {
|
|
1577
|
+
if (!f) return;
|
|
1578
|
+
if (!f.type.startsWith("image/")) return onNotice("That file is not an image");
|
|
1579
|
+
if (f.size > MAX_IMAGE_BYTES) return onNotice("Images must be 8 MB or smaller");
|
|
1580
|
+
const reader = new FileReader();
|
|
1581
|
+
reader.onload = () => typeof reader.result === "string" && insert(reader.result);
|
|
1582
|
+
reader.onerror = () => onNotice("The image could not be read");
|
|
1583
|
+
reader.readAsDataURL(f);
|
|
1584
|
+
};
|
|
1585
|
+
return /* @__PURE__ */ jsxs5("div", { className: "june-doc__form", role: "dialog", "aria-label": "Insert image", children: [
|
|
1586
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__pop-title", children: "Image" }),
|
|
1587
|
+
/* @__PURE__ */ jsxs5(
|
|
1588
|
+
"button",
|
|
1589
|
+
{
|
|
1590
|
+
type: "button",
|
|
1591
|
+
className: "june-doc__dropzone",
|
|
1592
|
+
onClick: () => file.current?.click(),
|
|
1593
|
+
onDragOver: (e) => e.preventDefault(),
|
|
1594
|
+
onDrop: (e) => {
|
|
1595
|
+
e.preventDefault();
|
|
1596
|
+
fromFile(e.dataTransfer.files[0]);
|
|
1597
|
+
},
|
|
1598
|
+
children: [
|
|
1599
|
+
/* @__PURE__ */ jsx7(Icon, { name: "upload", size: 18 }),
|
|
1600
|
+
/* @__PURE__ */ jsx7("span", { children: "Upload or drop an image" })
|
|
1601
|
+
]
|
|
1602
|
+
}
|
|
1603
|
+
),
|
|
1604
|
+
/* @__PURE__ */ jsx7(
|
|
1605
|
+
"input",
|
|
1606
|
+
{
|
|
1607
|
+
ref: file,
|
|
1608
|
+
type: "file",
|
|
1609
|
+
accept: "image/*",
|
|
1610
|
+
hidden: true,
|
|
1611
|
+
onChange: (e) => fromFile(e.target.files?.[0])
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__or", children: "or" }),
|
|
1615
|
+
/* @__PURE__ */ jsx7(
|
|
1616
|
+
"input",
|
|
1617
|
+
{
|
|
1618
|
+
"aria-label": "Image address",
|
|
1619
|
+
className: "june-doc__input",
|
|
1620
|
+
placeholder: "https://example.com/image.png",
|
|
1621
|
+
value: url,
|
|
1622
|
+
onChange: (e) => setUrl(e.target.value),
|
|
1623
|
+
onKeyDown: (e) => e.key === "Enter" && fromUrl()
|
|
1624
|
+
}
|
|
1625
|
+
),
|
|
1626
|
+
/* @__PURE__ */ jsx7(
|
|
1627
|
+
"input",
|
|
1628
|
+
{
|
|
1629
|
+
"aria-label": "Alternative text",
|
|
1630
|
+
className: "june-doc__input",
|
|
1631
|
+
placeholder: "Alt text (optional)",
|
|
1632
|
+
value: alt,
|
|
1633
|
+
onChange: (e) => setAlt(e.target.value)
|
|
1634
|
+
}
|
|
1635
|
+
),
|
|
1636
|
+
/* @__PURE__ */ jsxs5("div", { className: "june-doc__pop-row june-doc__pop-row--end", children: [
|
|
1637
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: close, children: "Cancel" }),
|
|
1638
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn june-doc__btn--primary", disabled: !url.trim(), onClick: fromUrl, children: "Insert" })
|
|
1639
|
+
] })
|
|
1640
|
+
] });
|
|
1641
|
+
}
|
|
1642
|
+
function TablePanel({ editor, close }) {
|
|
1643
|
+
const [hover, setHover] = useState3([0, 0]);
|
|
1644
|
+
const N = 8;
|
|
1645
|
+
return /* @__PURE__ */ jsxs5("div", { className: "june-doc__form", role: "dialog", "aria-label": "Insert table", children: [
|
|
1646
|
+
/* @__PURE__ */ jsxs5("div", { className: "june-doc__pop-title", children: [
|
|
1647
|
+
"Table ",
|
|
1648
|
+
hover[0] ? `${hover[0]} \xD7 ${hover[1]}` : ""
|
|
1649
|
+
] }),
|
|
1650
|
+
/* @__PURE__ */ jsx7("div", { className: "june-doc__grid", onMouseLeave: () => setHover([0, 0]), children: Array.from({ length: N * N }, (_, i) => {
|
|
1651
|
+
const r = Math.floor(i / N) + 1;
|
|
1652
|
+
const c = i % N + 1;
|
|
1653
|
+
return /* @__PURE__ */ jsx7(
|
|
1654
|
+
"button",
|
|
1655
|
+
{
|
|
1656
|
+
type: "button",
|
|
1657
|
+
className: "june-doc__cell",
|
|
1658
|
+
"aria-label": `${r} by ${c} table`,
|
|
1659
|
+
"data-active": r <= hover[0] && c <= hover[1] ? true : void 0,
|
|
1660
|
+
onMouseEnter: () => setHover([r, c]),
|
|
1661
|
+
onFocus: () => setHover([r, c]),
|
|
1662
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1663
|
+
onClick: () => {
|
|
1664
|
+
editor.chain().focus().insertTable({ rows: r, cols: c, withHeaderRow: true }).run();
|
|
1665
|
+
close();
|
|
1666
|
+
}
|
|
1667
|
+
},
|
|
1668
|
+
i
|
|
1669
|
+
);
|
|
1670
|
+
}) })
|
|
1671
|
+
] });
|
|
1672
|
+
}
|
|
1673
|
+
var px = (v) => typeof v === "string" ? v.replace(/px$/, "") : "";
|
|
1674
|
+
function DocumentToolbar(props) {
|
|
1675
|
+
const { editor, editable, expanded, onToggleExpanded, showMarks, onToggleMarks, layout, onToggleLayout, findOpen, onToggleFind, onNotice } = props;
|
|
1676
|
+
const s = useEditorState({
|
|
1677
|
+
editor,
|
|
1678
|
+
selector: ({ editor: ed }) => {
|
|
1679
|
+
if (!ed) return null;
|
|
1680
|
+
const ts = ed.getAttributes("textStyle");
|
|
1681
|
+
return {
|
|
1682
|
+
bold: ed.isActive("bold"),
|
|
1683
|
+
italic: ed.isActive("italic"),
|
|
1684
|
+
underline: ed.isActive("underline"),
|
|
1685
|
+
strike: ed.isActive("strike"),
|
|
1686
|
+
code: ed.isActive("code"),
|
|
1687
|
+
sup: ed.isActive("superscript"),
|
|
1688
|
+
sub: ed.isActive("subscript"),
|
|
1689
|
+
bullet: ed.isActive("bulletList"),
|
|
1690
|
+
ordered: ed.isActive("orderedList"),
|
|
1691
|
+
quote: ed.isActive("blockquote"),
|
|
1692
|
+
codeBlock: ed.isActive("codeBlock"),
|
|
1693
|
+
link: ed.isActive("link"),
|
|
1694
|
+
linkHref: ed.getAttributes("link").href ?? "",
|
|
1695
|
+
h1: ed.isActive("heading", { level: 1 }),
|
|
1696
|
+
h2: ed.isActive("heading", { level: 2 }),
|
|
1697
|
+
h3: ed.isActive("heading", { level: 3 }),
|
|
1698
|
+
left: ed.isActive({ textAlign: "left" }),
|
|
1699
|
+
center: ed.isActive({ textAlign: "center" }),
|
|
1700
|
+
right: ed.isActive({ textAlign: "right" }),
|
|
1701
|
+
justify: ed.isActive({ textAlign: "justify" }),
|
|
1702
|
+
inTable: ed.isActive("table"),
|
|
1703
|
+
color: ts.color ?? null,
|
|
1704
|
+
fontFamily: ts.fontFamily ?? "",
|
|
1705
|
+
fontSize: px(ts.fontSize),
|
|
1706
|
+
highlight: ed.isActive("highlight") ? ed.getAttributes("highlight").color ?? "#fef08a" : null,
|
|
1707
|
+
canUndo: ed.can().undo(),
|
|
1708
|
+
canRedo: ed.can().redo(),
|
|
1709
|
+
canSink: ed.can().sinkListItem("listItem"),
|
|
1710
|
+
canLift: ed.can().liftListItem("listItem"),
|
|
1711
|
+
canMerge: ed.can().mergeCells(),
|
|
1712
|
+
canSplit: ed.can().splitCell()
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1716
|
+
const run = () => editor?.chain().focus();
|
|
1717
|
+
const off = !editor || !editable;
|
|
1718
|
+
const style = s?.codeBlock ? "code" : s?.h1 ? "1" : s?.h2 ? "2" : s?.h3 ? "3" : s?.quote ? "quote" : "p";
|
|
1719
|
+
const setStyle = (v) => {
|
|
1720
|
+
if (!editor || !s) return;
|
|
1721
|
+
let c = editor.chain().focus();
|
|
1722
|
+
if (v === "quote") {
|
|
1723
|
+
if (!s.quote) c = c.setParagraph().toggleBlockquote();
|
|
1724
|
+
} else if (v === "code") {
|
|
1725
|
+
if (!s.codeBlock) c = c.toggleCodeBlock();
|
|
1726
|
+
} else {
|
|
1727
|
+
if (s.quote) c = c.lift("blockquote");
|
|
1728
|
+
c = v === "p" ? c.setParagraph() : c.setHeading({ level: Number(v) });
|
|
1729
|
+
}
|
|
1730
|
+
c.run();
|
|
1731
|
+
};
|
|
1732
|
+
const fontKnown = FONT_FAMILIES.some((f) => f.value === s?.fontFamily);
|
|
1733
|
+
const sizes = s?.fontSize && !FONT_SIZES.includes(Number(s.fontSize)) ? [...FONT_SIZES, Number(s.fontSize)].sort((a, b) => a - b) : FONT_SIZES;
|
|
1734
|
+
if (!editable) return null;
|
|
1735
|
+
return /* @__PURE__ */ jsxs5("div", { className: "june-doc__ribbon", role: "toolbar", "aria-label": "Document formatting", children: [
|
|
1736
|
+
/* @__PURE__ */ jsxs5("div", { className: "june-doc__row", children: [
|
|
1737
|
+
/* @__PURE__ */ jsxs5(Group, { label: "History", children: [
|
|
1738
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Undo", icon: "undo", disabled: off || !s?.canUndo, onClick: () => run()?.undo().run() }),
|
|
1739
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Redo", icon: "redo", disabled: off || !s?.canRedo, onClick: () => run()?.redo().run() })
|
|
1740
|
+
] }),
|
|
1741
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Format", children: [
|
|
1742
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Bold", icon: "bold", toggle: true, active: s?.bold, disabled: off, onClick: () => run()?.toggleBold().run() }),
|
|
1743
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Italic", icon: "italic", toggle: true, active: s?.italic, disabled: off, onClick: () => run()?.toggleItalic().run() }),
|
|
1744
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Underline", icon: "underline", toggle: true, active: s?.underline, disabled: off, onClick: () => run()?.toggleUnderline().run() }),
|
|
1745
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Strikethrough", icon: "strike", toggle: true, active: s?.strike, disabled: off, onClick: () => run()?.toggleStrike().run() }),
|
|
1746
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Inline code", icon: "code", toggle: true, active: s?.code, disabled: off, onClick: () => run()?.toggleCode().run() }),
|
|
1747
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Superscript", icon: "superscript", toggle: true, active: s?.sup, disabled: off, onClick: () => run()?.toggleSuperscript().run() }),
|
|
1748
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Subscript", icon: "subscript", toggle: true, active: s?.sub, disabled: off, onClick: () => run()?.toggleSubscript().run() })
|
|
1749
|
+
] }),
|
|
1750
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Align", children: [
|
|
1751
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Align left", icon: "alignLeft", toggle: true, active: s?.left, disabled: off, onClick: () => run()?.setTextAlign("left").run() }),
|
|
1752
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Align center", icon: "alignCenter", toggle: true, active: s?.center, disabled: off, onClick: () => run()?.setTextAlign("center").run() }),
|
|
1753
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Align right", icon: "alignRight", toggle: true, active: s?.right, disabled: off, onClick: () => run()?.setTextAlign("right").run() }),
|
|
1754
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Justify", icon: "alignJustify", toggle: true, active: s?.justify, disabled: off, onClick: () => run()?.setTextAlign("justify").run() })
|
|
1755
|
+
] }),
|
|
1756
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Lists", children: [
|
|
1757
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Bullet list", icon: "list", toggle: true, active: s?.bullet, disabled: off, onClick: () => run()?.toggleBulletList().run() }),
|
|
1758
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Numbered list", icon: "listOrdered", toggle: true, active: s?.ordered, disabled: off, onClick: () => run()?.toggleOrderedList().run() }),
|
|
1759
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Decrease indent", icon: "outdent", disabled: off || !s?.canLift, onClick: () => run()?.liftListItem("listItem").run() }),
|
|
1760
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Increase indent", icon: "indent", disabled: off || !s?.canSink, onClick: () => run()?.sinkListItem("listItem").run() })
|
|
1761
|
+
] }),
|
|
1762
|
+
/* @__PURE__ */ jsxs5(
|
|
1763
|
+
"button",
|
|
1764
|
+
{
|
|
1765
|
+
type: "button",
|
|
1766
|
+
className: "june-doc__btn june-doc__more",
|
|
1767
|
+
"aria-expanded": expanded,
|
|
1768
|
+
"aria-label": expanded ? "Show fewer tools" : "Show more tools",
|
|
1769
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1770
|
+
onClick: onToggleExpanded,
|
|
1771
|
+
children: [
|
|
1772
|
+
expanded ? "Less" : "More",
|
|
1773
|
+
/* @__PURE__ */ jsx7(Icon, { name: expanded ? "chevronUp" : "chevronDown", size: 14 })
|
|
1774
|
+
]
|
|
1775
|
+
}
|
|
1776
|
+
)
|
|
1777
|
+
] }),
|
|
1778
|
+
expanded ? /* @__PURE__ */ jsxs5("div", { className: "june-doc__row june-doc__row--secondary", children: [
|
|
1779
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Style", children: [
|
|
1780
|
+
/* @__PURE__ */ jsx7(Field, { label: "Style", children: /* @__PURE__ */ jsxs5("select", { className: "june-doc__select", "aria-label": "Text style", disabled: off, value: style, onChange: (e) => setStyle(e.target.value), children: [
|
|
1781
|
+
/* @__PURE__ */ jsx7("option", { value: "p", children: "Paragraph" }),
|
|
1782
|
+
/* @__PURE__ */ jsx7("option", { value: "1", children: "Heading 1" }),
|
|
1783
|
+
/* @__PURE__ */ jsx7("option", { value: "2", children: "Heading 2" }),
|
|
1784
|
+
/* @__PURE__ */ jsx7("option", { value: "3", children: "Heading 3" }),
|
|
1785
|
+
/* @__PURE__ */ jsx7("option", { value: "quote", children: "Quote" }),
|
|
1786
|
+
/* @__PURE__ */ jsx7("option", { value: "code", children: "Code block" })
|
|
1787
|
+
] }) }),
|
|
1788
|
+
/* @__PURE__ */ jsx7(Field, { label: "Font", children: /* @__PURE__ */ jsxs5(
|
|
1789
|
+
"select",
|
|
1790
|
+
{
|
|
1791
|
+
className: "june-doc__select june-doc__select--font",
|
|
1792
|
+
"aria-label": "Font family",
|
|
1793
|
+
disabled: off,
|
|
1794
|
+
value: s?.fontFamily ?? "",
|
|
1795
|
+
onChange: (e) => {
|
|
1796
|
+
const v = e.target.value;
|
|
1797
|
+
if (v) run()?.setFontFamily(v).run();
|
|
1798
|
+
else run()?.unsetFontFamily().run();
|
|
1799
|
+
},
|
|
1800
|
+
children: [
|
|
1801
|
+
FONT_FAMILIES.map((f) => /* @__PURE__ */ jsx7("option", { value: f.value, style: { fontFamily: f.value || void 0 }, children: f.label }, f.label)),
|
|
1802
|
+
!fontKnown && s?.fontFamily ? /* @__PURE__ */ jsx7("option", { value: s.fontFamily, children: s.fontFamily.split(",")[0].replace(/['"]/g, "") }) : null
|
|
1803
|
+
]
|
|
1804
|
+
}
|
|
1805
|
+
) }),
|
|
1806
|
+
/* @__PURE__ */ jsx7(Field, { label: "Size", children: /* @__PURE__ */ jsxs5(
|
|
1807
|
+
"select",
|
|
1808
|
+
{
|
|
1809
|
+
className: "june-doc__select june-doc__select--size",
|
|
1810
|
+
"aria-label": "Font size",
|
|
1811
|
+
disabled: off,
|
|
1812
|
+
value: s?.fontSize ?? "",
|
|
1813
|
+
onChange: (e) => {
|
|
1814
|
+
const v = e.target.value;
|
|
1815
|
+
if (v) run()?.setFontSize(`${v}px`).run();
|
|
1816
|
+
else run()?.unsetFontSize().run();
|
|
1817
|
+
},
|
|
1818
|
+
children: [
|
|
1819
|
+
/* @__PURE__ */ jsx7("option", { value: "", children: "Auto" }),
|
|
1820
|
+
sizes.map((n) => /* @__PURE__ */ jsx7("option", { value: String(n), children: n }, n))
|
|
1821
|
+
]
|
|
1822
|
+
}
|
|
1823
|
+
) })
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Show formatting marks", icon: "pilcrow", toggle: true, active: showMarks, onClick: onToggleMarks, text: true, className: "june-doc__btn--marks", children: /* @__PURE__ */ jsx7("span", { children: "Paragraph" }) }),
|
|
1826
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Document", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Insert page break", icon: "pageBreak", disabled: off, onClick: () => run()?.setPageBreak().run() }),
|
|
1828
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Insert divider", icon: "divider", disabled: off, onClick: () => run()?.setHorizontalRule().run() }),
|
|
1829
|
+
/* @__PURE__ */ jsx7(Btn, { label: layout === "page" ? "Switch to full-width layout" : "Switch to page layout", icon: "layout", toggle: true, active: layout === "fluid", onClick: onToggleLayout }),
|
|
1830
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Find and replace", icon: "search", toggle: true, active: findOpen, onClick: onToggleFind })
|
|
1831
|
+
] }),
|
|
1832
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Color", children: [
|
|
1833
|
+
/* @__PURE__ */ jsx7(
|
|
1834
|
+
ColorPicker,
|
|
1835
|
+
{
|
|
1836
|
+
label: "Text color",
|
|
1837
|
+
icon: "palette",
|
|
1838
|
+
colors: TEXT_COLORS,
|
|
1839
|
+
current: s?.color ?? null,
|
|
1840
|
+
disabled: off,
|
|
1841
|
+
resetLabel: "Default color",
|
|
1842
|
+
onPick: (c) => run()?.setColor(c).run(),
|
|
1843
|
+
onReset: () => run()?.unsetColor().run()
|
|
1844
|
+
}
|
|
1845
|
+
),
|
|
1846
|
+
/* @__PURE__ */ jsx7(
|
|
1847
|
+
ColorPicker,
|
|
1848
|
+
{
|
|
1849
|
+
label: "Highlight",
|
|
1850
|
+
icon: "highlighter",
|
|
1851
|
+
colors: HIGHLIGHT_COLORS,
|
|
1852
|
+
current: s?.highlight ?? null,
|
|
1853
|
+
disabled: off,
|
|
1854
|
+
resetLabel: "No highlight",
|
|
1855
|
+
onPick: (c) => run()?.setHighlight({ color: c }).run(),
|
|
1856
|
+
onReset: () => run()?.unsetHighlight().run()
|
|
1857
|
+
}
|
|
1858
|
+
)
|
|
1859
|
+
] }),
|
|
1860
|
+
/* @__PURE__ */ jsxs5(Group, { label: "Insert", children: [
|
|
1861
|
+
editor ? /* @__PURE__ */ jsx7(
|
|
1862
|
+
Dropdown,
|
|
1863
|
+
{
|
|
1864
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ jsx7(Btn, { label: "Link", icon: "link", toggle: true, active: s?.link || open, disabled: off, onClick: toggle }),
|
|
1865
|
+
children: (close) => /* @__PURE__ */ jsx7(LinkPanel, { editor, href: s?.linkHref ?? "", isLink: !!s?.link, close })
|
|
1866
|
+
}
|
|
1867
|
+
) : null,
|
|
1868
|
+
editor ? /* @__PURE__ */ jsx7(Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ jsx7(Btn, { label: "Insert image", icon: "image", active: open, disabled: off, onClick: toggle }), children: (close) => /* @__PURE__ */ jsx7(ImagePanel, { editor, close, onNotice }) }) : null,
|
|
1869
|
+
editor ? /* @__PURE__ */ jsx7(Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ jsx7(Btn, { label: "Insert table", icon: "table", active: open, disabled: off, onClick: toggle }), children: (close) => /* @__PURE__ */ jsx7(TablePanel, { editor, close }) }) : null
|
|
1870
|
+
] }),
|
|
1871
|
+
/* @__PURE__ */ jsx7(Group, { label: "Actions", children: /* @__PURE__ */ jsx7(Btn, { label: "Clear formatting", icon: "eraser", disabled: off, onClick: () => run()?.unsetAllMarks().clearNodes().run() }) })
|
|
1872
|
+
] }) : null,
|
|
1873
|
+
s?.inTable ? /* @__PURE__ */ jsx7("div", { className: "june-doc__row june-doc__row--context", children: /* @__PURE__ */ jsxs5(Group, { label: "Table", children: [
|
|
1874
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Add row below", disabled: off, onClick: () => run()?.addRowAfter().run(), children: "+ Row" }),
|
|
1875
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Add column right", disabled: off, onClick: () => run()?.addColumnAfter().run(), children: "+ Column" }),
|
|
1876
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Delete row", disabled: off, onClick: () => run()?.deleteRow().run(), children: "\u2212 Row" }),
|
|
1877
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Delete column", disabled: off, onClick: () => run()?.deleteColumn().run(), children: "\u2212 Column" }),
|
|
1878
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Merge cells", disabled: off || !s?.canMerge, onClick: () => run()?.mergeCells().run(), children: "Merge" }),
|
|
1879
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Split cell", disabled: off || !s?.canSplit, onClick: () => run()?.splitCell().run(), children: "Split" }),
|
|
1880
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Toggle header row", disabled: off, onClick: () => run()?.toggleHeaderRow().run(), children: "Header row" }),
|
|
1881
|
+
/* @__PURE__ */ jsx7(Btn, { label: "Delete table", disabled: off, onClick: () => run()?.deleteTable().run(), className: "june-doc__btn--danger", children: "Delete table" })
|
|
1882
|
+
] }) }) : null
|
|
1883
|
+
] });
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/components/document-editor/document-bubble.tsx
|
|
1887
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1888
|
+
function DocumentBubbleMenu({ editor }) {
|
|
1889
|
+
const s = useEditorState2({
|
|
1890
|
+
editor,
|
|
1891
|
+
selector: ({ editor: ed }) => ({
|
|
1892
|
+
bold: ed.isActive("bold"),
|
|
1893
|
+
italic: ed.isActive("italic"),
|
|
1894
|
+
underline: ed.isActive("underline"),
|
|
1895
|
+
strike: ed.isActive("strike"),
|
|
1896
|
+
link: ed.isActive("link"),
|
|
1897
|
+
linkHref: ed.getAttributes("link").href ?? "",
|
|
1898
|
+
color: ed.getAttributes("textStyle").color ?? null,
|
|
1899
|
+
highlight: ed.isActive("highlight") ? ed.getAttributes("highlight").color ?? "#fef08a" : null
|
|
1900
|
+
})
|
|
1901
|
+
});
|
|
1902
|
+
const run = () => editor.chain().focus();
|
|
1903
|
+
return /* @__PURE__ */ jsxs6(
|
|
1904
|
+
BubbleMenu,
|
|
1905
|
+
{
|
|
1906
|
+
editor,
|
|
1907
|
+
className: "june-doc__bubble",
|
|
1908
|
+
options: { placement: "top", offset: 8 },
|
|
1909
|
+
shouldShow: ({ editor: ed, state }) => {
|
|
1910
|
+
const { empty } = state.selection;
|
|
1911
|
+
if (empty || !ed.isEditable) return false;
|
|
1912
|
+
if (ed.isActive("image") || ed.isActive("pageBreak") || ed.isActive("codeBlock")) return false;
|
|
1913
|
+
return state.doc.textBetween(state.selection.from, state.selection.to).trim().length > 0;
|
|
1914
|
+
},
|
|
1915
|
+
children: [
|
|
1916
|
+
/* @__PURE__ */ jsx8(Btn, { label: "Bold", icon: "bold", toggle: true, active: s.bold, onClick: () => run().toggleBold().run() }),
|
|
1917
|
+
/* @__PURE__ */ jsx8(Btn, { label: "Italic", icon: "italic", toggle: true, active: s.italic, onClick: () => run().toggleItalic().run() }),
|
|
1918
|
+
/* @__PURE__ */ jsx8(Btn, { label: "Underline", icon: "underline", toggle: true, active: s.underline, onClick: () => run().toggleUnderline().run() }),
|
|
1919
|
+
/* @__PURE__ */ jsx8(Btn, { label: "Strikethrough", icon: "strike", toggle: true, active: s.strike, onClick: () => run().toggleStrike().run() }),
|
|
1920
|
+
/* @__PURE__ */ jsx8("span", { className: "june-doc__bubble-sep", "aria-hidden": "true" }),
|
|
1921
|
+
/* @__PURE__ */ jsx8(Dropdown, { trigger: ({ open, toggle }) => /* @__PURE__ */ jsx8(Btn, { label: "Link", icon: "link", toggle: true, active: s.link || open, onClick: toggle }), children: (close) => /* @__PURE__ */ jsx8(LinkPanel, { editor, href: s.linkHref, isLink: s.link, close }) }),
|
|
1922
|
+
/* @__PURE__ */ jsx8(
|
|
1923
|
+
ColorPicker,
|
|
1924
|
+
{
|
|
1925
|
+
label: "Text color",
|
|
1926
|
+
icon: "palette",
|
|
1927
|
+
colors: TEXT_COLORS,
|
|
1928
|
+
current: s.color,
|
|
1929
|
+
resetLabel: "Default color",
|
|
1930
|
+
onPick: (c) => run().setColor(c).run(),
|
|
1931
|
+
onReset: () => run().unsetColor().run()
|
|
1932
|
+
}
|
|
1933
|
+
),
|
|
1934
|
+
/* @__PURE__ */ jsx8(
|
|
1935
|
+
ColorPicker,
|
|
1936
|
+
{
|
|
1937
|
+
label: "Highlight",
|
|
1938
|
+
icon: "highlighter",
|
|
1939
|
+
colors: HIGHLIGHT_COLORS,
|
|
1940
|
+
current: s.highlight,
|
|
1941
|
+
resetLabel: "No highlight",
|
|
1942
|
+
onPick: (c) => run().setHighlight({ color: c }).run(),
|
|
1943
|
+
onReset: () => run().unsetHighlight().run()
|
|
1944
|
+
}
|
|
1945
|
+
),
|
|
1946
|
+
/* @__PURE__ */ jsx8("span", { className: "june-doc__bubble-sep", "aria-hidden": "true" }),
|
|
1947
|
+
/* @__PURE__ */ jsx8(Btn, { label: "Clear formatting", icon: "eraser", onClick: () => run().unsetAllMarks().run() })
|
|
1948
|
+
]
|
|
1949
|
+
}
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1093
1952
|
|
|
1094
1953
|
// src/components/document-editor/document-content.tsx
|
|
1095
1954
|
import { EditorContent } from "@tiptap/react";
|
|
1096
|
-
import {
|
|
1097
|
-
|
|
1098
|
-
|
|
1955
|
+
import { useEffect as useEffect3, useRef as useRef5, useState as useState4 } from "react";
|
|
1956
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1957
|
+
var PAGE_HEIGHT = 1123;
|
|
1958
|
+
var PAGE_PAD_Y = 96;
|
|
1959
|
+
function DocumentContent({ editor, layout = "page", zoom = 1, showMarks = false, onMetrics }) {
|
|
1960
|
+
const canvas = useRef5(null);
|
|
1961
|
+
const sheet = useRef5(null);
|
|
1962
|
+
const [pages, setPages] = useState4(1);
|
|
1963
|
+
const metricsRef = useRef5(onMetrics);
|
|
1964
|
+
metricsRef.current = onMetrics;
|
|
1965
|
+
const last = useRef5({ page: 0, pages: 0 });
|
|
1966
|
+
useEffect3(() => {
|
|
1967
|
+
const c = canvas.current;
|
|
1968
|
+
const p = sheet.current;
|
|
1969
|
+
const prose = editor?.view.dom;
|
|
1970
|
+
if (!c || !p || !prose) return;
|
|
1971
|
+
const measure = () => {
|
|
1972
|
+
const contentH = prose.getBoundingClientRect().height / zoom;
|
|
1973
|
+
const total = layout === "page" ? Math.max(1, Math.ceil((contentH + PAGE_PAD_Y * 2) / PAGE_HEIGHT)) : 1;
|
|
1974
|
+
const cRect = c.getBoundingClientRect();
|
|
1975
|
+
const pRect = p.getBoundingClientRect();
|
|
1976
|
+
const y = cRect.top + c.clientHeight * 0.35 - pRect.top;
|
|
1977
|
+
const page = Math.min(total, Math.max(1, Math.floor(y / (PAGE_HEIGHT * zoom)) + 1));
|
|
1978
|
+
setPages(total);
|
|
1979
|
+
if (last.current.page !== page || last.current.pages !== total) {
|
|
1980
|
+
last.current = { page, pages: total };
|
|
1981
|
+
metricsRef.current?.({ page, pages: total });
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
measure();
|
|
1985
|
+
c.addEventListener("scroll", measure, { passive: true });
|
|
1986
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
1987
|
+
ro?.observe(prose);
|
|
1988
|
+
ro?.observe(c);
|
|
1989
|
+
editor?.on("update", measure);
|
|
1990
|
+
return () => {
|
|
1991
|
+
c.removeEventListener("scroll", measure);
|
|
1992
|
+
ro?.disconnect();
|
|
1993
|
+
editor?.off("update", measure);
|
|
1994
|
+
};
|
|
1995
|
+
}, [editor, layout, zoom]);
|
|
1996
|
+
const sheetStyle = layout === "page" ? {
|
|
1997
|
+
zoom,
|
|
1998
|
+
minHeight: pages * PAGE_HEIGHT,
|
|
1999
|
+
maxWidth: `calc(100% / ${zoom})`,
|
|
2000
|
+
"--jd-page-h": `${PAGE_HEIGHT}px`
|
|
2001
|
+
} : { zoom };
|
|
2002
|
+
return /* @__PURE__ */ jsx9("div", { ref: canvas, className: "june-doc__content june-doc__canvas", "data-layout": layout, "data-marks": showMarks || void 0, children: /* @__PURE__ */ jsx9("div", { ref: sheet, className: "june-doc__sheet", style: sheetStyle, children: /* @__PURE__ */ jsx9(EditorContent, { editor }) }) });
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
// src/components/document-editor/document-findbar.tsx
|
|
2006
|
+
import { useEditorState as useEditorState3 } from "@tiptap/react";
|
|
2007
|
+
import { useEffect as useEffect4, useRef as useRef6, useState as useState5 } from "react";
|
|
2008
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2009
|
+
function DocumentFindBar({ editor, editable, onClose, onNotice }) {
|
|
2010
|
+
const [term, setTerm] = useState5(() => {
|
|
2011
|
+
const { from, to, empty } = editor.state.selection;
|
|
2012
|
+
return empty ? "" : editor.state.doc.textBetween(from, to, " ").slice(0, 100);
|
|
2013
|
+
});
|
|
2014
|
+
const [replacement, setReplacement] = useState5("");
|
|
2015
|
+
const [caseSensitive, setCaseSensitive] = useState5(false);
|
|
2016
|
+
const input = useRef6(null);
|
|
2017
|
+
const info = useEditorState3({
|
|
2018
|
+
editor,
|
|
2019
|
+
selector: ({ editor: ed }) => {
|
|
2020
|
+
const s = getSearchState(ed);
|
|
2021
|
+
return { count: s?.matches.length ?? 0, current: s?.current ?? -1 };
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
useEffect4(() => {
|
|
2025
|
+
input.current?.focus();
|
|
2026
|
+
input.current?.select();
|
|
2027
|
+
}, []);
|
|
2028
|
+
useEffect4(() => {
|
|
2029
|
+
setSearch(editor, { term, caseSensitive, current: 0 });
|
|
2030
|
+
}, [editor, term, caseSensitive]);
|
|
2031
|
+
useEffect4(() => () => {
|
|
2032
|
+
if (!editor.isDestroyed) setSearch(editor, { term: "" });
|
|
2033
|
+
}, [editor]);
|
|
2034
|
+
const count = info?.count ?? 0;
|
|
2035
|
+
const current = info?.current ?? -1;
|
|
2036
|
+
return /* @__PURE__ */ jsxs7(
|
|
2037
|
+
"div",
|
|
2038
|
+
{
|
|
2039
|
+
className: "june-doc__findbar",
|
|
2040
|
+
role: "search",
|
|
2041
|
+
onKeyDown: (e) => {
|
|
2042
|
+
if (e.key === "Escape") {
|
|
2043
|
+
e.stopPropagation();
|
|
2044
|
+
onClose();
|
|
2045
|
+
editor.commands.focus();
|
|
2046
|
+
}
|
|
2047
|
+
},
|
|
2048
|
+
children: [
|
|
2049
|
+
/* @__PURE__ */ jsxs7("div", { className: "june-doc__find-field", children: [
|
|
2050
|
+
/* @__PURE__ */ jsx10(Icon, { name: "search", size: 14 }),
|
|
2051
|
+
/* @__PURE__ */ jsx10(
|
|
2052
|
+
"input",
|
|
2053
|
+
{
|
|
2054
|
+
ref: input,
|
|
2055
|
+
className: "june-doc__find-input",
|
|
2056
|
+
"aria-label": "Find in document",
|
|
2057
|
+
placeholder: "Find in document",
|
|
2058
|
+
value: term,
|
|
2059
|
+
onChange: (e) => setTerm(e.target.value),
|
|
2060
|
+
onKeyDown: (e) => {
|
|
2061
|
+
if (e.key === "Enter") {
|
|
2062
|
+
e.preventDefault();
|
|
2063
|
+
gotoMatch(editor, e.shiftKey ? -1 : 1);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
),
|
|
2068
|
+
/* @__PURE__ */ jsx10("span", { className: "june-doc__find-count", "aria-live": "polite", children: term ? count ? `${current + 1} of ${count}` : "No results" : "" })
|
|
2069
|
+
] }),
|
|
2070
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "june-doc__btn june-doc__btn--icon", "aria-label": "Previous match", title: "Previous (Shift+Enter)", disabled: !count, onClick: () => gotoMatch(editor, -1), children: /* @__PURE__ */ jsx10(Icon, { name: "chevronUp" }) }),
|
|
2071
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "june-doc__btn june-doc__btn--icon", "aria-label": "Next match", title: "Next (Enter)", disabled: !count, onClick: () => gotoMatch(editor, 1), children: /* @__PURE__ */ jsx10(Icon, { name: "chevronDown" }) }),
|
|
2072
|
+
/* @__PURE__ */ jsx10(
|
|
2073
|
+
"button",
|
|
2074
|
+
{
|
|
2075
|
+
type: "button",
|
|
2076
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--case",
|
|
2077
|
+
"aria-label": "Match case",
|
|
2078
|
+
title: "Match case",
|
|
2079
|
+
"aria-pressed": caseSensitive,
|
|
2080
|
+
"data-active": caseSensitive || void 0,
|
|
2081
|
+
onClick: () => setCaseSensitive((v) => !v),
|
|
2082
|
+
children: "Aa"
|
|
2083
|
+
}
|
|
2084
|
+
),
|
|
2085
|
+
editable ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
2086
|
+
/* @__PURE__ */ jsx10("span", { className: "june-doc__find-sep", "aria-hidden": "true" }),
|
|
2087
|
+
/* @__PURE__ */ jsx10(
|
|
2088
|
+
"input",
|
|
2089
|
+
{
|
|
2090
|
+
className: "june-doc__input june-doc__replace-input",
|
|
2091
|
+
"aria-label": "Replace with",
|
|
2092
|
+
placeholder: "Replace with",
|
|
2093
|
+
value: replacement,
|
|
2094
|
+
onChange: (e) => setReplacement(e.target.value),
|
|
2095
|
+
onKeyDown: (e) => {
|
|
2096
|
+
if (e.key === "Enter") {
|
|
2097
|
+
e.preventDefault();
|
|
2098
|
+
replaceCurrent(editor, replacement);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
),
|
|
2103
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "june-doc__btn", disabled: !count, onClick: () => replaceCurrent(editor, replacement), children: "Replace" }),
|
|
2104
|
+
/* @__PURE__ */ jsx10(
|
|
2105
|
+
"button",
|
|
2106
|
+
{
|
|
2107
|
+
type: "button",
|
|
2108
|
+
className: "june-doc__btn",
|
|
2109
|
+
disabled: !count,
|
|
2110
|
+
onClick: () => {
|
|
2111
|
+
const n = replaceAll(editor, replacement);
|
|
2112
|
+
onNotice(`Replaced ${n} ${n === 1 ? "match" : "matches"}`);
|
|
2113
|
+
},
|
|
2114
|
+
children: "Replace all"
|
|
2115
|
+
}
|
|
2116
|
+
)
|
|
2117
|
+
] }) : null,
|
|
2118
|
+
/* @__PURE__ */ jsx10("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost june-doc__find-close", "aria-label": "Close find", onClick: onClose, children: /* @__PURE__ */ jsx10(Icon, { name: "close" }) })
|
|
2119
|
+
]
|
|
2120
|
+
}
|
|
2121
|
+
);
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
// src/components/document-editor/document-title.tsx
|
|
2125
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
|
2126
|
+
function DocumentTitle({ value, disabled, onChange }) {
|
|
2127
|
+
return /* @__PURE__ */ jsx11(
|
|
2128
|
+
"input",
|
|
2129
|
+
{
|
|
2130
|
+
className: "june-doc__title",
|
|
2131
|
+
"aria-label": "Document title",
|
|
2132
|
+
value,
|
|
2133
|
+
disabled,
|
|
2134
|
+
placeholder: "Untitled document",
|
|
2135
|
+
onChange: (e) => onChange(e.target.value)
|
|
2136
|
+
}
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
// src/components/document-editor/document-header.tsx
|
|
2141
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2142
|
+
var FORMATS = {
|
|
2143
|
+
docx: { label: "Word document", hint: ".docx", icon: "file" },
|
|
2144
|
+
pdf: { label: "PDF", hint: "Print / Save as PDF", icon: "file" },
|
|
2145
|
+
html: { label: "Web page", hint: ".html", icon: "code" },
|
|
2146
|
+
markdown: { label: "Markdown", hint: ".md", icon: "file" },
|
|
2147
|
+
txt: { label: "Plain text", hint: ".txt", icon: "file" }
|
|
2148
|
+
};
|
|
2149
|
+
function DocumentHeader(props) {
|
|
2150
|
+
const { title, editable, isDraft, fullscreen, onToggleFullscreen, onTitle, exportFormats, canSave, saveState, onExport, onCopy, onSave } = props;
|
|
2151
|
+
return /* @__PURE__ */ jsxs8("div", { className: "june-doc__header", children: [
|
|
2152
|
+
fullscreen ? /* @__PURE__ */ jsx12("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Close full screen", title: "Close (Esc)", onClick: onToggleFullscreen, children: /* @__PURE__ */ jsx12(Icon, { name: "close", size: 18 }) }) : /* @__PURE__ */ jsx12("span", { className: "june-doc__doc-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(Icon, { name: "file", size: 16 }) }),
|
|
2153
|
+
/* @__PURE__ */ jsx12(DocumentTitle, { value: title, disabled: !editable, onChange: onTitle }),
|
|
2154
|
+
isDraft ? /* @__PURE__ */ jsxs8("span", { className: "june-doc__badge", children: [
|
|
2155
|
+
/* @__PURE__ */ jsx12(Icon, { name: "sparkles", size: 12 }),
|
|
2156
|
+
"AI draft"
|
|
2157
|
+
] }) : null,
|
|
2158
|
+
/* @__PURE__ */ jsxs8("div", { className: "june-doc__header-actions", children: [
|
|
2159
|
+
canSave ? /* @__PURE__ */ jsxs8(
|
|
2160
|
+
"button",
|
|
2161
|
+
{
|
|
2162
|
+
type: "button",
|
|
2163
|
+
className: "june-doc__btn june-doc__btn--outline",
|
|
2164
|
+
"aria-label": "Save",
|
|
2165
|
+
title: "Save (Ctrl+S)",
|
|
2166
|
+
disabled: saveState === "saving" || saveState === "saved" || saveState === "idle",
|
|
2167
|
+
onClick: onSave,
|
|
2168
|
+
children: [
|
|
2169
|
+
/* @__PURE__ */ jsx12(Icon, { name: saveState === "saved" ? "check" : "save" }),
|
|
2170
|
+
/* @__PURE__ */ jsx12("span", { children: saveState === "saving" ? "Saving\u2026" : saveState === "saved" ? "Saved" : "Save" })
|
|
2171
|
+
]
|
|
2172
|
+
}
|
|
2173
|
+
) : null,
|
|
2174
|
+
/* @__PURE__ */ jsx12(
|
|
2175
|
+
Dropdown,
|
|
2176
|
+
{
|
|
2177
|
+
align: "end",
|
|
2178
|
+
className: "june-doc__menu",
|
|
2179
|
+
trigger: ({ open, toggle }) => /* @__PURE__ */ jsxs8(
|
|
2180
|
+
"button",
|
|
2181
|
+
{
|
|
2182
|
+
type: "button",
|
|
2183
|
+
className: "june-doc__btn june-doc__btn--solid",
|
|
2184
|
+
"aria-label": "Download",
|
|
2185
|
+
"aria-haspopup": "menu",
|
|
2186
|
+
"aria-expanded": open,
|
|
2187
|
+
onClick: toggle,
|
|
2188
|
+
children: [
|
|
2189
|
+
/* @__PURE__ */ jsx12("span", { children: "Download" }),
|
|
2190
|
+
/* @__PURE__ */ jsx12(Icon, { name: "chevronDown", size: 14 })
|
|
2191
|
+
]
|
|
2192
|
+
}
|
|
2193
|
+
),
|
|
2194
|
+
children: (close) => /* @__PURE__ */ jsxs8("div", { role: "menu", "aria-label": "Download options", children: [
|
|
2195
|
+
exportFormats.map((f) => {
|
|
2196
|
+
const meta = FORMATS[f] ?? { label: f.toUpperCase(), hint: "", icon: "file" };
|
|
2197
|
+
return /* @__PURE__ */ jsxs8(
|
|
2198
|
+
"button",
|
|
2199
|
+
{
|
|
2200
|
+
type: "button",
|
|
2201
|
+
role: "menuitem",
|
|
2202
|
+
className: "june-doc__menu-item",
|
|
2203
|
+
onClick: () => {
|
|
2204
|
+
close();
|
|
2205
|
+
onExport(f);
|
|
2206
|
+
},
|
|
2207
|
+
children: [
|
|
2208
|
+
/* @__PURE__ */ jsx12(Icon, { name: meta.icon }),
|
|
2209
|
+
/* @__PURE__ */ jsx12("span", { className: "june-doc__menu-label", children: meta.label }),
|
|
2210
|
+
/* @__PURE__ */ jsx12("span", { className: "june-doc__menu-hint", children: meta.hint })
|
|
2211
|
+
]
|
|
2212
|
+
},
|
|
2213
|
+
f
|
|
2214
|
+
);
|
|
2215
|
+
}),
|
|
2216
|
+
exportFormats.length ? /* @__PURE__ */ jsx12("div", { className: "june-doc__menu-sep", role: "separator" }) : null,
|
|
2217
|
+
/* @__PURE__ */ jsxs8(
|
|
2218
|
+
"button",
|
|
2219
|
+
{
|
|
2220
|
+
type: "button",
|
|
2221
|
+
role: "menuitem",
|
|
2222
|
+
className: "june-doc__menu-item",
|
|
2223
|
+
"aria-label": "Copy document",
|
|
2224
|
+
onClick: () => {
|
|
2225
|
+
close();
|
|
2226
|
+
onCopy();
|
|
2227
|
+
},
|
|
2228
|
+
children: [
|
|
2229
|
+
/* @__PURE__ */ jsx12(Icon, { name: "copy" }),
|
|
2230
|
+
/* @__PURE__ */ jsx12("span", { className: "june-doc__menu-label", children: "Copy to clipboard" })
|
|
2231
|
+
]
|
|
2232
|
+
}
|
|
2233
|
+
)
|
|
2234
|
+
] })
|
|
2235
|
+
}
|
|
2236
|
+
),
|
|
2237
|
+
/* @__PURE__ */ jsx12(
|
|
2238
|
+
"button",
|
|
2239
|
+
{
|
|
2240
|
+
type: "button",
|
|
2241
|
+
className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost",
|
|
2242
|
+
"aria-label": fullscreen ? "Exit full screen" : "Open full screen",
|
|
2243
|
+
title: fullscreen ? "Exit full screen" : "Open full screen",
|
|
2244
|
+
"aria-pressed": fullscreen,
|
|
2245
|
+
onClick: onToggleFullscreen,
|
|
2246
|
+
children: /* @__PURE__ */ jsx12(Icon, { name: "maximize" })
|
|
2247
|
+
}
|
|
2248
|
+
)
|
|
2249
|
+
] })
|
|
2250
|
+
] });
|
|
1099
2251
|
}
|
|
1100
2252
|
|
|
1101
2253
|
// src/components/document-editor/document-export.ts
|
|
1102
2254
|
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1103
|
-
var PRINT_CSS = 'body{font-family:system-ui,-apple-system,"Segoe UI",sans-serif;line-height:1.6;color:#111827;max-width:780px;margin:32px auto;padding:0 20px}h1,h2,h3{line-height:1.25}table{border-collapse:collapse;width:100%}th,td{border:1px solid #d1d5db;padding:6px 10px;text-align:left}blockquote{border-left:4px solid #d1d5db;margin:1em 0;padding-left:1em;color:#4b5563}pre,code{font-family:ui-monospace,Menlo,monospace;background:#f3f4f6}pre{padding:12px;overflow:auto}@media print{body{margin:0;max-width:none}}';
|
|
2255
|
+
var PRINT_CSS = 'body{font-family:system-ui,-apple-system,"Segoe UI",sans-serif;line-height:1.6;color:#111827;max-width:780px;margin:32px auto;padding:0 20px}h1,h2,h3{line-height:1.25}table{border-collapse:collapse;width:100%}th,td{border:1px solid #d1d5db;padding:6px 10px;text-align:left}blockquote{border-left:4px solid #d1d5db;margin:1em 0;padding-left:1em;color:#4b5563}pre,code{font-family:ui-monospace,Menlo,monospace;background:#f3f4f6}pre{padding:12px;overflow:auto}img{max-width:100%;height:auto}mark{padding:0 .1em;border-radius:2px}div[data-page-break]{break-after:page;page-break-after:always;height:0}@media screen{div[data-page-break]{border-top:1px dashed #d1d5db;margin:24px 0}}@media print{body{margin:0;max-width:none}}';
|
|
1104
2256
|
function buildHtmlDocument(title, bodyHtml) {
|
|
1105
2257
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(
|
|
1106
2258
|
title
|
|
@@ -1113,7 +2265,7 @@ var textResult = (text, filename, mimeType) => ({
|
|
|
1113
2265
|
blob: new Blob([text], { type: `${mimeType};charset=utf-8` })
|
|
1114
2266
|
});
|
|
1115
2267
|
var exportHtml = async (ctx) => {
|
|
1116
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
2268
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1117
2269
|
return textResult(
|
|
1118
2270
|
buildHtmlDocument(ctx.title, docToHtml2(ctx.json)),
|
|
1119
2271
|
`${ctx.basename}.html`,
|
|
@@ -1121,7 +2273,7 @@ var exportHtml = async (ctx) => {
|
|
|
1121
2273
|
);
|
|
1122
2274
|
};
|
|
1123
2275
|
var exportMarkdown = async (ctx) => {
|
|
1124
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
2276
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1125
2277
|
return textResult(
|
|
1126
2278
|
`# ${ctx.title}
|
|
1127
2279
|
|
|
@@ -1132,12 +2284,39 @@ ${docToMarkdown2(ctx.json)}
|
|
|
1132
2284
|
);
|
|
1133
2285
|
};
|
|
1134
2286
|
var exportTxt = async (ctx) => {
|
|
1135
|
-
const { docToText } = await import("./document-engine-
|
|
2287
|
+
const { docToText } = await import("./document-engine-IAY2T6B3.js");
|
|
1136
2288
|
return textResult(`${ctx.title}
|
|
1137
2289
|
|
|
1138
2290
|
${docToText(ctx.json)}
|
|
1139
2291
|
`, `${ctx.basename}.txt`, "text/plain");
|
|
1140
2292
|
};
|
|
2293
|
+
function toDocxColor(value) {
|
|
2294
|
+
if (typeof value !== "string") return void 0;
|
|
2295
|
+
const v = value.trim();
|
|
2296
|
+
let m = /^#([0-9a-f]{3})$/i.exec(v);
|
|
2297
|
+
if (m) return m[1].split("").map((c) => c + c).join("").toUpperCase();
|
|
2298
|
+
m = /^#([0-9a-f]{6})$/i.exec(v);
|
|
2299
|
+
if (m) return m[1].toUpperCase();
|
|
2300
|
+
m = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(v);
|
|
2301
|
+
if (m) {
|
|
2302
|
+
return [m[1], m[2], m[3]].map((n) => Math.min(255, Number(n)).toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
2303
|
+
}
|
|
2304
|
+
return void 0;
|
|
2305
|
+
}
|
|
2306
|
+
function toHalfPoints(value) {
|
|
2307
|
+
if (typeof value !== "string") return void 0;
|
|
2308
|
+
const m = /^([\d.]+)\s*(px|pt)?$/i.exec(value.trim());
|
|
2309
|
+
if (!m) return void 0;
|
|
2310
|
+
const n = Number(m[1]);
|
|
2311
|
+
if (!Number.isFinite(n) || n <= 0) return void 0;
|
|
2312
|
+
const pt = m[2]?.toLowerCase() === "pt" ? n : n * 0.75;
|
|
2313
|
+
return Math.round(pt * 2);
|
|
2314
|
+
}
|
|
2315
|
+
function firstFontFamily(value) {
|
|
2316
|
+
if (typeof value !== "string") return void 0;
|
|
2317
|
+
const first = value.split(",")[0]?.trim().replace(/^['"]|['"]$/g, "");
|
|
2318
|
+
return first || void 0;
|
|
2319
|
+
}
|
|
1141
2320
|
var ORDERED_REF = "june-ordered";
|
|
1142
2321
|
var DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
1143
2322
|
function runs(d, node, fmt) {
|
|
@@ -1151,14 +2330,21 @@ function runs(d, node, fmt) {
|
|
|
1151
2330
|
const marks = child.marks ?? [];
|
|
1152
2331
|
const has = (type) => marks.some((m) => m.type === type);
|
|
1153
2332
|
const link = marks.find((m) => m.type === "link");
|
|
2333
|
+
const style = marks.find((m) => m.type === "textStyle")?.attrs ?? {};
|
|
2334
|
+
const highlight = marks.find((m) => m.type === "highlight");
|
|
2335
|
+
const fill = highlight ? toDocxColor(highlight.attrs?.color) ?? "FFF59D" : void 0;
|
|
1154
2336
|
const run = new d.TextRun({
|
|
1155
2337
|
text: child.text,
|
|
1156
2338
|
bold: fmt.bold || has("bold") || void 0,
|
|
1157
2339
|
italics: has("italic") || void 0,
|
|
1158
2340
|
underline: has("underline") || link ? {} : void 0,
|
|
1159
2341
|
strike: has("strike") || void 0,
|
|
1160
|
-
|
|
1161
|
-
|
|
2342
|
+
superScript: has("superscript") || void 0,
|
|
2343
|
+
subScript: has("subscript") || void 0,
|
|
2344
|
+
font: has("code") ? "Courier New" : firstFontFamily(style.fontFamily),
|
|
2345
|
+
size: toHalfPoints(style.fontSize),
|
|
2346
|
+
color: toDocxColor(style.color) ?? (link ? "0563C1" : void 0),
|
|
2347
|
+
shading: fill ? { type: d.ShadingType.CLEAR, fill, color: "auto" } : void 0
|
|
1162
2348
|
});
|
|
1163
2349
|
const href = typeof link?.attrs?.href === "string" ? sanitizeHref(link.attrs.href) : null;
|
|
1164
2350
|
out.push(href ? new d.ExternalHyperlink({ link: href, children: [run] }) : run);
|
|
@@ -1227,6 +2413,100 @@ function table(d, node, state) {
|
|
|
1227
2413
|
if (rows.length === 0) return new d.Paragraph("");
|
|
1228
2414
|
return new d.Table({ rows, width: { size: 100, type: d.WidthType.PERCENTAGE } });
|
|
1229
2415
|
}
|
|
2416
|
+
function image(d, node, state) {
|
|
2417
|
+
const src = typeof node.attrs?.src === "string" ? node.attrs.src : "";
|
|
2418
|
+
const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
|
|
2419
|
+
const img = state.images.get(src);
|
|
2420
|
+
if (img) {
|
|
2421
|
+
return [
|
|
2422
|
+
new d.Paragraph({
|
|
2423
|
+
children: [
|
|
2424
|
+
new d.ImageRun({
|
|
2425
|
+
type: img.type,
|
|
2426
|
+
data: img.data,
|
|
2427
|
+
transformation: { width: img.width, height: img.height },
|
|
2428
|
+
altText: alt ? { name: alt, description: alt, title: alt } : void 0
|
|
2429
|
+
})
|
|
2430
|
+
],
|
|
2431
|
+
spacing: { after: 120 }
|
|
2432
|
+
})
|
|
2433
|
+
];
|
|
2434
|
+
}
|
|
2435
|
+
const href = src && !src.startsWith("data:") ? sanitizeHref(src) : null;
|
|
2436
|
+
if (!href) return [];
|
|
2437
|
+
return [
|
|
2438
|
+
new d.Paragraph({
|
|
2439
|
+
children: [
|
|
2440
|
+
new d.ExternalHyperlink({
|
|
2441
|
+
link: href,
|
|
2442
|
+
children: [new d.TextRun({ text: alt || href, color: "0563C1", underline: {} })]
|
|
2443
|
+
})
|
|
2444
|
+
]
|
|
2445
|
+
})
|
|
2446
|
+
];
|
|
2447
|
+
}
|
|
2448
|
+
function collectImages(nodes, out) {
|
|
2449
|
+
for (const node of nodes ?? []) {
|
|
2450
|
+
const src = node.attrs?.src;
|
|
2451
|
+
if (node.type === "image" && typeof src === "string" && src && !out.has(src)) out.set(src, node.attrs);
|
|
2452
|
+
if (node.content) collectImages(node.content, out);
|
|
2453
|
+
}
|
|
2454
|
+
return out;
|
|
2455
|
+
}
|
|
2456
|
+
var IMAGE_TYPES = {
|
|
2457
|
+
"image/png": "png",
|
|
2458
|
+
"image/jpeg": "jpg",
|
|
2459
|
+
"image/jpg": "jpg",
|
|
2460
|
+
"image/gif": "gif",
|
|
2461
|
+
"image/bmp": "bmp"
|
|
2462
|
+
};
|
|
2463
|
+
var MAX_IMAGE_WIDTH = 600;
|
|
2464
|
+
function naturalSize(blob) {
|
|
2465
|
+
const fallback = { width: 400, height: 300 };
|
|
2466
|
+
return new Promise((resolve) => {
|
|
2467
|
+
if (typeof Image === "undefined" || typeof URL.createObjectURL !== "function") return resolve(fallback);
|
|
2468
|
+
const url = URL.createObjectURL(blob);
|
|
2469
|
+
const img = new Image();
|
|
2470
|
+
const done = (size) => {
|
|
2471
|
+
URL.revokeObjectURL(url);
|
|
2472
|
+
resolve(size);
|
|
2473
|
+
};
|
|
2474
|
+
img.onload = () => done({ width: img.naturalWidth || 400, height: img.naturalHeight || 300 });
|
|
2475
|
+
img.onerror = () => done(fallback);
|
|
2476
|
+
img.src = url;
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
async function loadImage(src, attrs) {
|
|
2480
|
+
try {
|
|
2481
|
+
const res = await fetch(src);
|
|
2482
|
+
if (!res.ok) return null;
|
|
2483
|
+
const blob = await res.blob();
|
|
2484
|
+
const type = IMAGE_TYPES[blob.type.toLowerCase()];
|
|
2485
|
+
if (!type) return null;
|
|
2486
|
+
const natural = await naturalSize(blob);
|
|
2487
|
+
let width = Number(attrs?.width) || natural.width;
|
|
2488
|
+
let height = Number(attrs?.height) || natural.height * width / natural.width;
|
|
2489
|
+
if (width > MAX_IMAGE_WIDTH) {
|
|
2490
|
+
height = height * MAX_IMAGE_WIDTH / width;
|
|
2491
|
+
width = MAX_IMAGE_WIDTH;
|
|
2492
|
+
}
|
|
2493
|
+
return { data: new Uint8Array(await blob.arrayBuffer()), type, width: Math.round(width), height: Math.round(height) };
|
|
2494
|
+
} catch {
|
|
2495
|
+
return null;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
async function resolveImages(doc) {
|
|
2499
|
+
const map = /* @__PURE__ */ new Map();
|
|
2500
|
+
if (typeof fetch !== "function") return map;
|
|
2501
|
+
const found = collectImages(doc.content, /* @__PURE__ */ new Map());
|
|
2502
|
+
await Promise.all(
|
|
2503
|
+
[...found].map(async ([src, attrs]) => {
|
|
2504
|
+
const info = await loadImage(src, attrs);
|
|
2505
|
+
if (info) map.set(src, info);
|
|
2506
|
+
})
|
|
2507
|
+
);
|
|
2508
|
+
return map;
|
|
2509
|
+
}
|
|
1230
2510
|
function blocks(d, nodes, fmt, state) {
|
|
1231
2511
|
const headings = [d.HeadingLevel.HEADING_1, d.HeadingLevel.HEADING_2, d.HeadingLevel.HEADING_3];
|
|
1232
2512
|
const out = [];
|
|
@@ -1261,6 +2541,12 @@ function blocks(d, nodes, fmt, state) {
|
|
|
1261
2541
|
case "table":
|
|
1262
2542
|
out.push(table(d, node, state));
|
|
1263
2543
|
break;
|
|
2544
|
+
case "pageBreak":
|
|
2545
|
+
out.push(new d.Paragraph({ children: [new d.PageBreak()] }));
|
|
2546
|
+
break;
|
|
2547
|
+
case "image":
|
|
2548
|
+
out.push(...image(d, node, state));
|
|
2549
|
+
break;
|
|
1264
2550
|
default:
|
|
1265
2551
|
if (node.content) out.push(...blocks(d, node.content, fmt, state));
|
|
1266
2552
|
}
|
|
@@ -1269,7 +2555,7 @@ function blocks(d, nodes, fmt, state) {
|
|
|
1269
2555
|
}
|
|
1270
2556
|
var exportDocx = async (ctx) => {
|
|
1271
2557
|
const d = await import("docx");
|
|
1272
|
-
const state = { instance: 0 };
|
|
2558
|
+
const state = { instance: 0, images: await resolveImages(ctx.json) };
|
|
1273
2559
|
const children = [
|
|
1274
2560
|
new d.Paragraph({ text: ctx.title, heading: d.HeadingLevel.TITLE }),
|
|
1275
2561
|
...blocks(d, ctx.json.content, {}, state)
|
|
@@ -1320,7 +2606,7 @@ function printHtml(html) {
|
|
|
1320
2606
|
});
|
|
1321
2607
|
}
|
|
1322
2608
|
var exportPdf = async (ctx) => {
|
|
1323
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
2609
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1324
2610
|
await printHtml(buildHtmlDocument(ctx.title, docToHtml2(ctx.json)));
|
|
1325
2611
|
return { filename: `${ctx.basename}.pdf`, mimeType: "application/pdf", handled: true };
|
|
1326
2612
|
};
|
|
@@ -1358,7 +2644,7 @@ function downloadResult(result) {
|
|
|
1358
2644
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1359
2645
|
}
|
|
1360
2646
|
async function copyDocumentToClipboard(doc) {
|
|
1361
|
-
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-
|
|
2647
|
+
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-IAY2T6B3.js");
|
|
1362
2648
|
const html = docToHtml2(doc.json);
|
|
1363
2649
|
const text = `${doc.title}
|
|
1364
2650
|
|
|
@@ -1595,189 +2881,6 @@ function getDefaultDocumentStore() {
|
|
|
1595
2881
|
return defaultStore;
|
|
1596
2882
|
}
|
|
1597
2883
|
|
|
1598
|
-
// src/components/document-editor/document-title.tsx
|
|
1599
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1600
|
-
function DocumentTitle({ value, disabled, onChange }) {
|
|
1601
|
-
return /* @__PURE__ */ jsx6(
|
|
1602
|
-
"input",
|
|
1603
|
-
{
|
|
1604
|
-
className: "june-doc__title",
|
|
1605
|
-
"aria-label": "Document title",
|
|
1606
|
-
value,
|
|
1607
|
-
disabled,
|
|
1608
|
-
placeholder: "Untitled document",
|
|
1609
|
-
onChange: (e) => onChange(e.target.value)
|
|
1610
|
-
}
|
|
1611
|
-
);
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
// src/components/document-editor/document-toolbar.tsx
|
|
1615
|
-
import { useEditorState } from "@tiptap/react";
|
|
1616
|
-
import { useState as useState2 } from "react";
|
|
1617
|
-
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1618
|
-
var FORMAT_LABELS = {
|
|
1619
|
-
html: "HTML",
|
|
1620
|
-
markdown: "Markdown",
|
|
1621
|
-
txt: "Plain text",
|
|
1622
|
-
docx: "Word (.docx)",
|
|
1623
|
-
pdf: "PDF (print)"
|
|
1624
|
-
};
|
|
1625
|
-
function Btn({ label, active, disabled, toggle, onClick, children }) {
|
|
1626
|
-
return /* @__PURE__ */ jsx7(
|
|
1627
|
-
"button",
|
|
1628
|
-
{
|
|
1629
|
-
type: "button",
|
|
1630
|
-
className: "june-doc__btn",
|
|
1631
|
-
"aria-label": label,
|
|
1632
|
-
title: label,
|
|
1633
|
-
"aria-pressed": toggle ? !!active : void 0,
|
|
1634
|
-
"data-active": active || void 0,
|
|
1635
|
-
disabled,
|
|
1636
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
1637
|
-
onClick,
|
|
1638
|
-
children
|
|
1639
|
-
}
|
|
1640
|
-
);
|
|
1641
|
-
}
|
|
1642
|
-
function DocumentToolbar(props) {
|
|
1643
|
-
const { editor, editable, exportFormats, canSave, saveState, onExport, onCopy, onSave } = props;
|
|
1644
|
-
const [linkOpen, setLinkOpen] = useState2(false);
|
|
1645
|
-
const [linkValue, setLinkValue] = useState2("");
|
|
1646
|
-
const s = useEditorState({
|
|
1647
|
-
editor,
|
|
1648
|
-
selector: ({ editor: ed }) => ed ? {
|
|
1649
|
-
bold: ed.isActive("bold"),
|
|
1650
|
-
italic: ed.isActive("italic"),
|
|
1651
|
-
underline: ed.isActive("underline"),
|
|
1652
|
-
strike: ed.isActive("strike"),
|
|
1653
|
-
bullet: ed.isActive("bulletList"),
|
|
1654
|
-
ordered: ed.isActive("orderedList"),
|
|
1655
|
-
quote: ed.isActive("blockquote"),
|
|
1656
|
-
link: ed.isActive("link"),
|
|
1657
|
-
linkHref: ed.getAttributes("link").href ?? "",
|
|
1658
|
-
h1: ed.isActive("heading", { level: 1 }),
|
|
1659
|
-
h2: ed.isActive("heading", { level: 2 }),
|
|
1660
|
-
h3: ed.isActive("heading", { level: 3 }),
|
|
1661
|
-
left: ed.isActive({ textAlign: "left" }),
|
|
1662
|
-
center: ed.isActive({ textAlign: "center" }),
|
|
1663
|
-
right: ed.isActive({ textAlign: "right" }),
|
|
1664
|
-
justify: ed.isActive({ textAlign: "justify" }),
|
|
1665
|
-
inTable: ed.isActive("table"),
|
|
1666
|
-
canUndo: ed.can().undo(),
|
|
1667
|
-
canRedo: ed.can().redo()
|
|
1668
|
-
} : null
|
|
1669
|
-
});
|
|
1670
|
-
const run = () => editor?.chain().focus();
|
|
1671
|
-
const off = !editor || !editable;
|
|
1672
|
-
const heading = s?.h1 ? "1" : s?.h2 ? "2" : s?.h3 ? "3" : "p";
|
|
1673
|
-
const openLink = () => {
|
|
1674
|
-
setLinkValue(s?.linkHref ?? "");
|
|
1675
|
-
setLinkOpen(true);
|
|
1676
|
-
};
|
|
1677
|
-
const applyLink = () => {
|
|
1678
|
-
const href = sanitizeHref(linkValue);
|
|
1679
|
-
if (href) run()?.extendMarkRange("link").setLink({ href }).run();
|
|
1680
|
-
setLinkOpen(false);
|
|
1681
|
-
};
|
|
1682
|
-
const removeLink = () => {
|
|
1683
|
-
run()?.extendMarkRange("link").unsetLink().run();
|
|
1684
|
-
setLinkOpen(false);
|
|
1685
|
-
};
|
|
1686
|
-
return /* @__PURE__ */ jsxs3("div", { className: "june-doc__toolbar", role: "toolbar", "aria-label": "Document formatting", children: [
|
|
1687
|
-
editable ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1688
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1689
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Undo", disabled: off || !s?.canUndo, onClick: () => run()?.undo().run(), children: "\u21B6" }),
|
|
1690
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Redo", disabled: off || !s?.canRedo, onClick: () => run()?.redo().run(), children: "\u21B7" })
|
|
1691
|
-
] }),
|
|
1692
|
-
/* @__PURE__ */ jsx7("div", { className: "june-doc__group", children: /* @__PURE__ */ jsxs3(
|
|
1693
|
-
"select",
|
|
1694
|
-
{
|
|
1695
|
-
className: "june-doc__select",
|
|
1696
|
-
"aria-label": "Text style",
|
|
1697
|
-
disabled: off,
|
|
1698
|
-
value: heading,
|
|
1699
|
-
onChange: (e) => {
|
|
1700
|
-
const v = e.target.value;
|
|
1701
|
-
if (v === "p") run()?.setParagraph().run();
|
|
1702
|
-
else run()?.toggleHeading({ level: Number(v) }).run();
|
|
1703
|
-
},
|
|
1704
|
-
children: [
|
|
1705
|
-
/* @__PURE__ */ jsx7("option", { value: "p", children: "Paragraph" }),
|
|
1706
|
-
/* @__PURE__ */ jsx7("option", { value: "1", children: "Heading 1" }),
|
|
1707
|
-
/* @__PURE__ */ jsx7("option", { value: "2", children: "Heading 2" }),
|
|
1708
|
-
/* @__PURE__ */ jsx7("option", { value: "3", children: "Heading 3" })
|
|
1709
|
-
]
|
|
1710
|
-
}
|
|
1711
|
-
) }),
|
|
1712
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1713
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Bold", toggle: true, active: s?.bold, disabled: off, onClick: () => run()?.toggleBold().run(), children: /* @__PURE__ */ jsx7("b", { children: "B" }) }),
|
|
1714
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Italic", toggle: true, active: s?.italic, disabled: off, onClick: () => run()?.toggleItalic().run(), children: /* @__PURE__ */ jsx7("i", { children: "I" }) }),
|
|
1715
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Underline", toggle: true, active: s?.underline, disabled: off, onClick: () => run()?.toggleUnderline().run(), children: /* @__PURE__ */ jsx7("u", { children: "U" }) }),
|
|
1716
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Strikethrough", toggle: true, active: s?.strike, disabled: off, onClick: () => run()?.toggleStrike().run(), children: /* @__PURE__ */ jsx7("s", { children: "S" }) })
|
|
1717
|
-
] }),
|
|
1718
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1719
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Bullet list", toggle: true, active: s?.bullet, disabled: off, onClick: () => run()?.toggleBulletList().run(), children: "\u2022 List" }),
|
|
1720
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Numbered list", toggle: true, active: s?.ordered, disabled: off, onClick: () => run()?.toggleOrderedList().run(), children: "1. List" }),
|
|
1721
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Blockquote", toggle: true, active: s?.quote, disabled: off, onClick: () => run()?.toggleBlockquote().run(), children: "Quote" }),
|
|
1722
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Link", toggle: true, active: s?.link, disabled: off, onClick: openLink, children: "Link" })
|
|
1723
|
-
] }),
|
|
1724
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1725
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align left", toggle: true, active: s?.left, disabled: off, onClick: () => run()?.setTextAlign("left").run(), children: "L" }),
|
|
1726
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align center", toggle: true, active: s?.center, disabled: off, onClick: () => run()?.setTextAlign("center").run(), children: "C" }),
|
|
1727
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align right", toggle: true, active: s?.right, disabled: off, onClick: () => run()?.setTextAlign("right").run(), children: "R" }),
|
|
1728
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Justify", toggle: true, active: s?.justify, disabled: off, onClick: () => run()?.setTextAlign("justify").run(), children: "J" })
|
|
1729
|
-
] }),
|
|
1730
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1731
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Insert table", disabled: off, onClick: () => run()?.insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), children: "Table" }),
|
|
1732
|
-
s?.inTable ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1733
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Add row below", disabled: off, onClick: () => run()?.addRowAfter().run(), children: "+Row" }),
|
|
1734
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Add column right", disabled: off, onClick: () => run()?.addColumnAfter().run(), children: "+Col" }),
|
|
1735
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete row", disabled: off, onClick: () => run()?.deleteRow().run(), children: "\u2212Row" }),
|
|
1736
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete column", disabled: off, onClick: () => run()?.deleteColumn().run(), children: "\u2212Col" }),
|
|
1737
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete table", disabled: off, onClick: () => run()?.deleteTable().run(), children: "Del table" })
|
|
1738
|
-
] }) : null
|
|
1739
|
-
] })
|
|
1740
|
-
] }) : null,
|
|
1741
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group june-doc__group--end", children: [
|
|
1742
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Copy document", onClick: onCopy, children: "Copy" }),
|
|
1743
|
-
exportFormats.length > 0 ? /* @__PURE__ */ jsxs3(
|
|
1744
|
-
"select",
|
|
1745
|
-
{
|
|
1746
|
-
className: "june-doc__select",
|
|
1747
|
-
"aria-label": "Export document",
|
|
1748
|
-
value: "",
|
|
1749
|
-
onChange: (e) => e.target.value && onExport(e.target.value),
|
|
1750
|
-
children: [
|
|
1751
|
-
/* @__PURE__ */ jsx7("option", { value: "", children: "Export\u2026" }),
|
|
1752
|
-
exportFormats.map((f) => /* @__PURE__ */ jsx7("option", { value: f, children: FORMAT_LABELS[f] ?? f }, f))
|
|
1753
|
-
]
|
|
1754
|
-
}
|
|
1755
|
-
) : null,
|
|
1756
|
-
canSave ? /* @__PURE__ */ jsx7(Btn, { label: "Save", disabled: saveState === "saving" || saveState === "saved" || saveState === "idle", onClick: onSave, children: "Save" }) : null
|
|
1757
|
-
] }),
|
|
1758
|
-
linkOpen ? /* @__PURE__ */ jsxs3("div", { className: "june-doc__linkbar", children: [
|
|
1759
|
-
/* @__PURE__ */ jsx7(
|
|
1760
|
-
"input",
|
|
1761
|
-
{
|
|
1762
|
-
"aria-label": "Link address",
|
|
1763
|
-
className: "june-doc__input",
|
|
1764
|
-
placeholder: "https://example.com",
|
|
1765
|
-
value: linkValue,
|
|
1766
|
-
autoFocus: true,
|
|
1767
|
-
onChange: (e) => setLinkValue(e.target.value),
|
|
1768
|
-
onKeyDown: (e) => {
|
|
1769
|
-
if (e.key === "Enter") applyLink();
|
|
1770
|
-
if (e.key === "Escape") setLinkOpen(false);
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
),
|
|
1774
|
-
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: applyLink, children: "Apply" }),
|
|
1775
|
-
s?.link ? /* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: removeLink, children: "Remove" }) : null,
|
|
1776
|
-
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: () => setLinkOpen(false), children: "Cancel" })
|
|
1777
|
-
] }) : null
|
|
1778
|
-
] });
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
2884
|
// src/components/document-editor/document-types.ts
|
|
1782
2885
|
var ALL_EXPORT_FORMATS = [
|
|
1783
2886
|
"html",
|
|
@@ -1788,7 +2891,7 @@ var ALL_EXPORT_FORMATS = [
|
|
|
1788
2891
|
];
|
|
1789
2892
|
|
|
1790
2893
|
// src/components/document-editor/document-editor.tsx
|
|
1791
|
-
import { jsx as
|
|
2894
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1792
2895
|
var snapshotOf = (r) => ({
|
|
1793
2896
|
documentId: r.id,
|
|
1794
2897
|
title: r.title,
|
|
@@ -1803,6 +2906,8 @@ var SAVE_LABEL = {
|
|
|
1803
2906
|
saved: "Saved",
|
|
1804
2907
|
error: "Save failed"
|
|
1805
2908
|
};
|
|
2909
|
+
var ZOOM_STEPS = [0.5, 0.67, 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
|
2910
|
+
var stepZoom = (z2, dir) => dir > 0 ? ZOOM_STEPS.find((s) => s > z2 + 1e-3) ?? z2 : [...ZOOM_STEPS].reverse().find((s) => s < z2 - 1e-3) ?? z2;
|
|
1806
2911
|
function DocumentEditorImpl(props) {
|
|
1807
2912
|
const {
|
|
1808
2913
|
documentId,
|
|
@@ -1817,7 +2922,7 @@ function DocumentEditorImpl(props) {
|
|
|
1817
2922
|
} = props;
|
|
1818
2923
|
const ctxOptions = useDocumentOptions();
|
|
1819
2924
|
const options = useMemo4(() => ({ ...ctxOptions, ...props.options }), [ctxOptions, props.options]);
|
|
1820
|
-
const optionsRef =
|
|
2925
|
+
const optionsRef = useRef7(options);
|
|
1821
2926
|
optionsRef.current = options;
|
|
1822
2927
|
const store = useMemo4(() => options.store ?? getDefaultDocumentStore(), [options.store]);
|
|
1823
2928
|
const instanceId = useId();
|
|
@@ -1832,22 +2937,37 @@ function DocumentEditorImpl(props) {
|
|
|
1832
2937
|
return { json: null, error: toJuneError(e, "DOCUMENT_ERROR") };
|
|
1833
2938
|
}
|
|
1834
2939
|
}, [contentKey]);
|
|
1835
|
-
|
|
2940
|
+
useEffect5(() => {
|
|
1836
2941
|
if (parsed.error) optionsRef.current.onError?.(parsed.error);
|
|
1837
2942
|
}, [parsed.error]);
|
|
1838
2943
|
const title = useDocumentValue(store, documentId, (r) => r?.title ?? titleProp);
|
|
1839
2944
|
const pending = useDocumentValue(store, documentId, (r) => r?.pending ?? null);
|
|
1840
2945
|
const words = useDocumentValue(store, documentId, (r) => r ? wordCountOf(r.json) : 0);
|
|
1841
2946
|
const recordStatus = useDocumentValue(store, documentId, (r) => r?.status ?? status);
|
|
1842
|
-
const [saveState, setSaveState] =
|
|
1843
|
-
const [notice, setNotice] =
|
|
1844
|
-
const
|
|
1845
|
-
const
|
|
1846
|
-
const
|
|
2947
|
+
const [saveState, setSaveState] = useState6("idle");
|
|
2948
|
+
const [notice, setNotice] = useState6(null);
|
|
2949
|
+
const [fullscreen, setFullscreen] = useState6(false);
|
|
2950
|
+
const [expanded, setExpanded] = useState6(true);
|
|
2951
|
+
const [showMarks, setShowMarks] = useState6(false);
|
|
2952
|
+
const [layout, setLayout] = useState6("page");
|
|
2953
|
+
const [findOpen, setFindOpen] = useState6(false);
|
|
2954
|
+
const [zoom, setZoom] = useState6(1);
|
|
2955
|
+
const [metrics, setMetrics] = useState6({ page: 1, pages: 1 });
|
|
2956
|
+
useEffect5(() => {
|
|
2957
|
+
if (!fullscreen || typeof document === "undefined") return;
|
|
2958
|
+
const prev = document.body.style.overflow;
|
|
2959
|
+
document.body.style.overflow = "hidden";
|
|
2960
|
+
return () => {
|
|
2961
|
+
document.body.style.overflow = prev;
|
|
2962
|
+
};
|
|
2963
|
+
}, [fullscreen]);
|
|
2964
|
+
const lastSavedKey = useRef7("");
|
|
2965
|
+
const seenRevision = useRef7(store.get(documentId)?.revision ?? 0);
|
|
2966
|
+
const readyRef = useRef7(false);
|
|
1847
2967
|
const flash = useCallback3((message) => {
|
|
1848
2968
|
setNotice(message);
|
|
1849
2969
|
}, []);
|
|
1850
|
-
|
|
2970
|
+
useEffect5(() => {
|
|
1851
2971
|
if (!notice) return;
|
|
1852
2972
|
const t = setTimeout(() => setNotice(null), 3e3);
|
|
1853
2973
|
return () => clearTimeout(t);
|
|
@@ -1859,13 +2979,13 @@ function DocumentEditorImpl(props) {
|
|
|
1859
2979
|
},
|
|
1860
2980
|
[store, documentId]
|
|
1861
2981
|
);
|
|
1862
|
-
const saveRef =
|
|
2982
|
+
const saveRef = useRef7(async () => {
|
|
1863
2983
|
});
|
|
1864
2984
|
const autosave = useMemo4(() => {
|
|
1865
2985
|
const delay = options.autosaveDelay ?? 1500;
|
|
1866
2986
|
return delay > 0 ? debounce(() => void saveRef.current(), delay) : null;
|
|
1867
2987
|
}, [options.autosaveDelay]);
|
|
1868
|
-
|
|
2988
|
+
useEffect5(() => () => autosave?.flush(), [autosave]);
|
|
1869
2989
|
saveRef.current = async () => {
|
|
1870
2990
|
const record = store.get(documentId);
|
|
1871
2991
|
const onSave = optionsRef.current.onSave;
|
|
@@ -1896,9 +3016,9 @@ function DocumentEditorImpl(props) {
|
|
|
1896
3016
|
setSaveState("dirty");
|
|
1897
3017
|
autosave?.();
|
|
1898
3018
|
}, [autosave]);
|
|
1899
|
-
const [initialJson] =
|
|
3019
|
+
const [initialJson] = useState6(() => store.get(documentId)?.json ?? parsed.json ?? emptyDoc());
|
|
1900
3020
|
const extensions = useMemo4(() => createDocumentExtensions(), []);
|
|
1901
|
-
const onUpdateRef =
|
|
3021
|
+
const onUpdateRef = useRef7(() => {
|
|
1902
3022
|
});
|
|
1903
3023
|
onUpdateRef.current = (json) => {
|
|
1904
3024
|
store.edit(documentId, json, instanceId);
|
|
@@ -1920,10 +3040,10 @@ function DocumentEditorImpl(props) {
|
|
|
1920
3040
|
},
|
|
1921
3041
|
onUpdate: ({ editor: ed }) => onUpdateRef.current(ed.getJSON())
|
|
1922
3042
|
});
|
|
1923
|
-
|
|
3043
|
+
useEffect5(() => {
|
|
1924
3044
|
editor?.setEditable(editable);
|
|
1925
3045
|
}, [editor, editable]);
|
|
1926
|
-
|
|
3046
|
+
useEffect5(() => {
|
|
1927
3047
|
if (!parsed.json) return;
|
|
1928
3048
|
const outcome = store.receive({
|
|
1929
3049
|
id: documentId,
|
|
@@ -1944,7 +3064,7 @@ function DocumentEditorImpl(props) {
|
|
|
1944
3064
|
});
|
|
1945
3065
|
}
|
|
1946
3066
|
}, [store, documentId, titleProp, parsed.json, documentType, status, emitChange, markDirty]);
|
|
1947
|
-
|
|
3067
|
+
useEffect5(() => {
|
|
1948
3068
|
if (!editor) return;
|
|
1949
3069
|
const apply = () => {
|
|
1950
3070
|
const record = store.get(documentId);
|
|
@@ -1961,7 +3081,7 @@ function DocumentEditorImpl(props) {
|
|
|
1961
3081
|
apply();
|
|
1962
3082
|
return store.subscribe(apply);
|
|
1963
3083
|
}, [editor, store, documentId, instanceId]);
|
|
1964
|
-
|
|
3084
|
+
useEffect5(() => {
|
|
1965
3085
|
if (!editor || readyRef.current) return;
|
|
1966
3086
|
const record = store.get(documentId);
|
|
1967
3087
|
if (!record) return;
|
|
@@ -2005,67 +3125,114 @@ function DocumentEditorImpl(props) {
|
|
|
2005
3125
|
};
|
|
2006
3126
|
const formats = (exportFormats ?? [...ALL_EXPORT_FORMATS]).filter((f) => listDocumentExporters().includes(f));
|
|
2007
3127
|
if (parsed.error && !store.get(documentId)) {
|
|
2008
|
-
return /* @__PURE__ */
|
|
2009
|
-
/* @__PURE__ */
|
|
2010
|
-
/* @__PURE__ */
|
|
2011
|
-
/* @__PURE__ */
|
|
2012
|
-
/* @__PURE__ */
|
|
3128
|
+
return /* @__PURE__ */ jsxs9("div", { className: "june-doc june-doc--error", "data-theme": resolvedTheme, role: "alert", children: [
|
|
3129
|
+
/* @__PURE__ */ jsx13("p", { className: "june-doc__draft-text", children: "This document could not be displayed." }),
|
|
3130
|
+
/* @__PURE__ */ jsxs9("details", { children: [
|
|
3131
|
+
/* @__PURE__ */ jsx13("summary", { children: "Show raw content" }),
|
|
3132
|
+
/* @__PURE__ */ jsx13("pre", { className: "june-doc__raw", children: contentKey })
|
|
2013
3133
|
] })
|
|
2014
3134
|
] });
|
|
2015
3135
|
}
|
|
2016
|
-
if (!editor) return /* @__PURE__ */
|
|
3136
|
+
if (!editor) return /* @__PURE__ */ jsx13(DocumentDraft, { title, state: "loading" });
|
|
2017
3137
|
const isDraft = recordStatus === "draft";
|
|
2018
|
-
|
|
3138
|
+
const classes = ["june-doc", fullscreen ? "june-doc--fullscreen" : "", className ?? ""].filter(Boolean).join(" ");
|
|
3139
|
+
return /* @__PURE__ */ jsxs9(
|
|
2019
3140
|
"div",
|
|
2020
3141
|
{
|
|
2021
|
-
className:
|
|
3142
|
+
className: classes,
|
|
2022
3143
|
"data-theme": resolvedTheme,
|
|
2023
3144
|
"data-status": recordStatus,
|
|
3145
|
+
role: fullscreen ? "dialog" : void 0,
|
|
3146
|
+
"aria-modal": fullscreen || void 0,
|
|
3147
|
+
"aria-label": fullscreen ? title : void 0,
|
|
2024
3148
|
onKeyDown: (e) => {
|
|
2025
|
-
|
|
3149
|
+
const mod = e.ctrlKey || e.metaKey;
|
|
3150
|
+
if (mod && e.key.toLowerCase() === "s" && options.onSave) {
|
|
2026
3151
|
e.preventDefault();
|
|
2027
3152
|
void saveRef.current();
|
|
3153
|
+
} else if (mod && e.key.toLowerCase() === "f") {
|
|
3154
|
+
e.preventDefault();
|
|
3155
|
+
setFindOpen(true);
|
|
3156
|
+
} else if (e.key === "Escape" && fullscreen && !e.defaultPrevented) {
|
|
3157
|
+
setFullscreen(false);
|
|
2028
3158
|
}
|
|
2029
3159
|
},
|
|
2030
3160
|
children: [
|
|
2031
|
-
/* @__PURE__ */
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
3161
|
+
/* @__PURE__ */ jsx13(
|
|
3162
|
+
DocumentHeader,
|
|
3163
|
+
{
|
|
3164
|
+
title,
|
|
3165
|
+
editable,
|
|
3166
|
+
isDraft,
|
|
3167
|
+
fullscreen,
|
|
3168
|
+
onToggleFullscreen: () => setFullscreen((v) => !v),
|
|
3169
|
+
onTitle: handleTitle,
|
|
3170
|
+
exportFormats: formats,
|
|
3171
|
+
canSave: !!options.onSave,
|
|
3172
|
+
saveState,
|
|
3173
|
+
onExport: (f) => void handleExport(f),
|
|
3174
|
+
onCopy: () => void handleCopy(),
|
|
3175
|
+
onSave: () => void saveRef.current()
|
|
3176
|
+
}
|
|
3177
|
+
),
|
|
3178
|
+
pending ? /* @__PURE__ */ jsxs9("div", { className: "june-doc__banner", role: "status", children: [
|
|
3179
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
2037
3180
|
"The assistant suggested changes that overlap your edits (",
|
|
2038
3181
|
pending.conflicts,
|
|
2039
3182
|
" ",
|
|
2040
3183
|
pending.conflicts === 1 ? "section" : "sections",
|
|
2041
3184
|
")."
|
|
2042
3185
|
] }),
|
|
2043
|
-
/* @__PURE__ */
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
3186
|
+
/* @__PURE__ */ jsxs9("span", { className: "june-doc__banner-actions", children: [
|
|
3187
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("merge"), children: "Merge, keep my edits" }),
|
|
3188
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("replace"), children: "Use AI version" }),
|
|
3189
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn", onClick: () => resolve("keep"), children: "Keep mine" })
|
|
2047
3190
|
] })
|
|
2048
3191
|
] }) : null,
|
|
2049
|
-
/* @__PURE__ */
|
|
3192
|
+
/* @__PURE__ */ jsx13(
|
|
2050
3193
|
DocumentToolbar,
|
|
2051
3194
|
{
|
|
2052
3195
|
editor,
|
|
2053
3196
|
editable,
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
3197
|
+
expanded,
|
|
3198
|
+
onToggleExpanded: () => setExpanded((v) => !v),
|
|
3199
|
+
showMarks,
|
|
3200
|
+
onToggleMarks: () => setShowMarks((v) => !v),
|
|
3201
|
+
layout,
|
|
3202
|
+
onToggleLayout: () => setLayout((v) => v === "page" ? "fluid" : "page"),
|
|
3203
|
+
findOpen,
|
|
3204
|
+
onToggleFind: () => setFindOpen((v) => !v),
|
|
3205
|
+
onNotice: flash
|
|
2060
3206
|
}
|
|
2061
3207
|
),
|
|
2062
|
-
/* @__PURE__ */
|
|
2063
|
-
/* @__PURE__ */
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
3208
|
+
findOpen ? /* @__PURE__ */ jsx13(DocumentFindBar, { editor, editable, onClose: () => setFindOpen(false), onNotice: flash }) : null,
|
|
3209
|
+
editable ? /* @__PURE__ */ jsx13(DocumentBubbleMenu, { editor }) : null,
|
|
3210
|
+
/* @__PURE__ */ jsx13(DocumentContent, { editor, layout, zoom, showMarks, onMetrics: setMetrics }),
|
|
3211
|
+
/* @__PURE__ */ jsxs9("div", { className: "june-doc__footer", children: [
|
|
3212
|
+
/* @__PURE__ */ jsxs9("div", { className: "june-doc__stats", children: [
|
|
3213
|
+
layout === "page" ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
3214
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
3215
|
+
"Page ",
|
|
3216
|
+
metrics.page,
|
|
3217
|
+
" of ",
|
|
3218
|
+
metrics.pages
|
|
3219
|
+
] }),
|
|
3220
|
+
/* @__PURE__ */ jsx13("span", { className: "june-doc__stats-sep", "aria-hidden": "true" })
|
|
3221
|
+
] }) : null,
|
|
3222
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
3223
|
+
words,
|
|
3224
|
+
" words"
|
|
3225
|
+
] })
|
|
2067
3226
|
] }),
|
|
2068
|
-
/* @__PURE__ */
|
|
3227
|
+
/* @__PURE__ */ jsx13("span", { className: "june-doc__status", "aria-live": "polite", "data-state": notice ? "notice" : saveState, children: notice ?? SAVE_LABEL[saveState] }),
|
|
3228
|
+
/* @__PURE__ */ jsxs9("div", { className: "june-doc__zoom", role: "group", "aria-label": "Zoom", children: [
|
|
3229
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Zoom out", disabled: zoom <= ZOOM_STEPS[0], onClick: () => setZoom(stepZoom(zoom, -1)), children: /* @__PURE__ */ jsx13(Icon, { name: "minus", size: 14 }) }),
|
|
3230
|
+
/* @__PURE__ */ jsxs9("button", { type: "button", className: "june-doc__zoom-value", "aria-label": "Reset zoom", title: "Reset zoom", onClick: () => setZoom(1), children: [
|
|
3231
|
+
Math.round(zoom * 100),
|
|
3232
|
+
"%"
|
|
3233
|
+
] }),
|
|
3234
|
+
/* @__PURE__ */ jsx13("button", { type: "button", className: "june-doc__btn june-doc__btn--icon june-doc__btn--ghost", "aria-label": "Zoom in", disabled: zoom >= ZOOM_STEPS[ZOOM_STEPS.length - 1], onClick: () => setZoom(stepZoom(zoom, 1)), children: /* @__PURE__ */ jsx13(Icon, { name: "plus", size: 14 }) })
|
|
3235
|
+
] })
|
|
2069
3236
|
] })
|
|
2070
3237
|
]
|
|
2071
3238
|
}
|
|
@@ -2073,8 +3240,8 @@ function DocumentEditorImpl(props) {
|
|
|
2073
3240
|
}
|
|
2074
3241
|
|
|
2075
3242
|
// src/components/document-editor/document-component.tsx
|
|
2076
|
-
import { jsx as
|
|
2077
|
-
var DOCUMENT_COMPONENT_DESCRIPTION = 'An editable document. DocumentEditor(documentId, title, content, documentType?, status?, editable?, exportFormats?). "content" is the full document body as one Markdown string. Reuse documentId when revising the same document. Use it only when the user asks to create or revise a document.';
|
|
3243
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
3244
|
+
var DOCUMENT_COMPONENT_DESCRIPTION = 'Custom component. An editable document. DocumentEditor(documentId, title, content, documentType?, status?, editable?, exportFormats?). Place it in the root container (Card or Stack). "content" is the full document body as one Markdown string. Reuse documentId when revising the same document. Use it only when the user asks to create or revise a document.';
|
|
2078
3245
|
var documentProps = z.object({
|
|
2079
3246
|
documentId: z.string().describe("Stable id. Reuse it when revising the same document."),
|
|
2080
3247
|
title: z.string().describe("Document title. Do not repeat it as a heading in content."),
|
|
@@ -2087,12 +3254,12 @@ var documentProps = z.object({
|
|
|
2087
3254
|
function DocumentEditor(props) {
|
|
2088
3255
|
const { isStreaming } = useJuneRuntime();
|
|
2089
3256
|
if (props.generating || isStreaming) {
|
|
2090
|
-
return /* @__PURE__ */
|
|
3257
|
+
return /* @__PURE__ */ jsx14(DocumentDraft, { title: props.title, state: "drafting" });
|
|
2091
3258
|
}
|
|
2092
|
-
return /* @__PURE__ */
|
|
3259
|
+
return /* @__PURE__ */ jsx14(DocumentEditorImpl, { ...props });
|
|
2093
3260
|
}
|
|
2094
3261
|
function OpenUIDocument({ props }) {
|
|
2095
|
-
return /* @__PURE__ */
|
|
3262
|
+
return /* @__PURE__ */ jsx14(
|
|
2096
3263
|
DocumentEditor,
|
|
2097
3264
|
{
|
|
2098
3265
|
documentId: props.documentId,
|
|
@@ -2112,7 +3279,15 @@ var JuneDocumentEditorComponent = defineComponent2({
|
|
|
2112
3279
|
component: OpenUIDocument
|
|
2113
3280
|
});
|
|
2114
3281
|
function withDocumentEditor(library) {
|
|
2115
|
-
|
|
3282
|
+
try {
|
|
3283
|
+
return library.extend({
|
|
3284
|
+
components: {
|
|
3285
|
+
add: [{ component: JuneDocumentEditorComponent, slots: ["Card"] }]
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
} catch {
|
|
3289
|
+
return library.extend({ components: { add: [JuneDocumentEditorComponent] } });
|
|
3290
|
+
}
|
|
2116
3291
|
}
|
|
2117
3292
|
|
|
2118
3293
|
// src/components/document-editor/document-instructions.ts
|
|
@@ -2159,7 +3334,7 @@ function documentPromptOptions(options = {}) {
|
|
|
2159
3334
|
var attr = (value) => value.replace(/"/g, """);
|
|
2160
3335
|
async function serializeDocumentsForAgent(options = {}) {
|
|
2161
3336
|
const store = options.store ?? getDefaultDocumentStore();
|
|
2162
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
3337
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-IAY2T6B3.js");
|
|
2163
3338
|
return store.list().filter((r) => !options.ids || options.ids.includes(r.id)).map(
|
|
2164
3339
|
(r) => `<document id="${attr(r.id)}" title="${attr(r.title)}"${r.documentType ? ` type="${attr(r.documentType)}"` : ""}>
|
|
2165
3340
|
${docToMarkdown2(r.json)}
|
|
@@ -2172,9 +3347,9 @@ import { openuiLibrary } from "@openuidev/react-ui";
|
|
|
2172
3347
|
var juneLibrary = withDocumentEditor(openuiLibrary);
|
|
2173
3348
|
|
|
2174
3349
|
// src/core/JuneUI.tsx
|
|
2175
|
-
import { jsx as
|
|
3350
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2176
3351
|
function JuneUI({ library = juneLibrary, ...props }) {
|
|
2177
|
-
return /* @__PURE__ */
|
|
3352
|
+
return /* @__PURE__ */ jsx15(JuneRenderer, { library, ...props });
|
|
2178
3353
|
}
|
|
2179
3354
|
export {
|
|
2180
3355
|
ALL_EXPORT_FORMATS,
|