@june-ai/june-ui 0.2.4 → 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 +1626 -291
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1428 -258
- 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";
|
|
@@ -1090,18 +1095,1164 @@ function DocumentDraft({ title, words, state }) {
|
|
|
1090
1095
|
|
|
1091
1096
|
// src/components/document-editor/document-editor.tsx
|
|
1092
1097
|
import { useEditor } from "@tiptap/react";
|
|
1093
|
-
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
|
+
}
|
|
1094
1952
|
|
|
1095
1953
|
// src/components/document-editor/document-content.tsx
|
|
1096
1954
|
import { EditorContent } from "@tiptap/react";
|
|
1097
|
-
import {
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
+
] });
|
|
1100
2251
|
}
|
|
1101
2252
|
|
|
1102
2253
|
// src/components/document-editor/document-export.ts
|
|
1103
2254
|
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1104
|
-
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}}';
|
|
1105
2256
|
function buildHtmlDocument(title, bodyHtml) {
|
|
1106
2257
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(
|
|
1107
2258
|
title
|
|
@@ -1114,7 +2265,7 @@ var textResult = (text, filename, mimeType) => ({
|
|
|
1114
2265
|
blob: new Blob([text], { type: `${mimeType};charset=utf-8` })
|
|
1115
2266
|
});
|
|
1116
2267
|
var exportHtml = async (ctx) => {
|
|
1117
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
2268
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1118
2269
|
return textResult(
|
|
1119
2270
|
buildHtmlDocument(ctx.title, docToHtml2(ctx.json)),
|
|
1120
2271
|
`${ctx.basename}.html`,
|
|
@@ -1122,7 +2273,7 @@ var exportHtml = async (ctx) => {
|
|
|
1122
2273
|
);
|
|
1123
2274
|
};
|
|
1124
2275
|
var exportMarkdown = async (ctx) => {
|
|
1125
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
2276
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1126
2277
|
return textResult(
|
|
1127
2278
|
`# ${ctx.title}
|
|
1128
2279
|
|
|
@@ -1133,12 +2284,39 @@ ${docToMarkdown2(ctx.json)}
|
|
|
1133
2284
|
);
|
|
1134
2285
|
};
|
|
1135
2286
|
var exportTxt = async (ctx) => {
|
|
1136
|
-
const { docToText } = await import("./document-engine-
|
|
2287
|
+
const { docToText } = await import("./document-engine-IAY2T6B3.js");
|
|
1137
2288
|
return textResult(`${ctx.title}
|
|
1138
2289
|
|
|
1139
2290
|
${docToText(ctx.json)}
|
|
1140
2291
|
`, `${ctx.basename}.txt`, "text/plain");
|
|
1141
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
|
+
}
|
|
1142
2320
|
var ORDERED_REF = "june-ordered";
|
|
1143
2321
|
var DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
1144
2322
|
function runs(d, node, fmt) {
|
|
@@ -1152,14 +2330,21 @@ function runs(d, node, fmt) {
|
|
|
1152
2330
|
const marks = child.marks ?? [];
|
|
1153
2331
|
const has = (type) => marks.some((m) => m.type === type);
|
|
1154
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;
|
|
1155
2336
|
const run = new d.TextRun({
|
|
1156
2337
|
text: child.text,
|
|
1157
2338
|
bold: fmt.bold || has("bold") || void 0,
|
|
1158
2339
|
italics: has("italic") || void 0,
|
|
1159
2340
|
underline: has("underline") || link ? {} : void 0,
|
|
1160
2341
|
strike: has("strike") || void 0,
|
|
1161
|
-
|
|
1162
|
-
|
|
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
|
|
1163
2348
|
});
|
|
1164
2349
|
const href = typeof link?.attrs?.href === "string" ? sanitizeHref(link.attrs.href) : null;
|
|
1165
2350
|
out.push(href ? new d.ExternalHyperlink({ link: href, children: [run] }) : run);
|
|
@@ -1228,6 +2413,100 @@ function table(d, node, state) {
|
|
|
1228
2413
|
if (rows.length === 0) return new d.Paragraph("");
|
|
1229
2414
|
return new d.Table({ rows, width: { size: 100, type: d.WidthType.PERCENTAGE } });
|
|
1230
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
|
+
}
|
|
1231
2510
|
function blocks(d, nodes, fmt, state) {
|
|
1232
2511
|
const headings = [d.HeadingLevel.HEADING_1, d.HeadingLevel.HEADING_2, d.HeadingLevel.HEADING_3];
|
|
1233
2512
|
const out = [];
|
|
@@ -1262,6 +2541,12 @@ function blocks(d, nodes, fmt, state) {
|
|
|
1262
2541
|
case "table":
|
|
1263
2542
|
out.push(table(d, node, state));
|
|
1264
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;
|
|
1265
2550
|
default:
|
|
1266
2551
|
if (node.content) out.push(...blocks(d, node.content, fmt, state));
|
|
1267
2552
|
}
|
|
@@ -1270,7 +2555,7 @@ function blocks(d, nodes, fmt, state) {
|
|
|
1270
2555
|
}
|
|
1271
2556
|
var exportDocx = async (ctx) => {
|
|
1272
2557
|
const d = await import("docx");
|
|
1273
|
-
const state = { instance: 0 };
|
|
2558
|
+
const state = { instance: 0, images: await resolveImages(ctx.json) };
|
|
1274
2559
|
const children = [
|
|
1275
2560
|
new d.Paragraph({ text: ctx.title, heading: d.HeadingLevel.TITLE }),
|
|
1276
2561
|
...blocks(d, ctx.json.content, {}, state)
|
|
@@ -1321,7 +2606,7 @@ function printHtml(html) {
|
|
|
1321
2606
|
});
|
|
1322
2607
|
}
|
|
1323
2608
|
var exportPdf = async (ctx) => {
|
|
1324
|
-
const { docToHtml: docToHtml2 } = await import("./document-engine-
|
|
2609
|
+
const { docToHtml: docToHtml2 } = await import("./document-engine-IAY2T6B3.js");
|
|
1325
2610
|
await printHtml(buildHtmlDocument(ctx.title, docToHtml2(ctx.json)));
|
|
1326
2611
|
return { filename: `${ctx.basename}.pdf`, mimeType: "application/pdf", handled: true };
|
|
1327
2612
|
};
|
|
@@ -1359,7 +2644,7 @@ function downloadResult(result) {
|
|
|
1359
2644
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1360
2645
|
}
|
|
1361
2646
|
async function copyDocumentToClipboard(doc) {
|
|
1362
|
-
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-
|
|
2647
|
+
const { docToHtml: docToHtml2, docToText } = await import("./document-engine-IAY2T6B3.js");
|
|
1363
2648
|
const html = docToHtml2(doc.json);
|
|
1364
2649
|
const text = `${doc.title}
|
|
1365
2650
|
|
|
@@ -1596,189 +2881,6 @@ function getDefaultDocumentStore() {
|
|
|
1596
2881
|
return defaultStore;
|
|
1597
2882
|
}
|
|
1598
2883
|
|
|
1599
|
-
// src/components/document-editor/document-title.tsx
|
|
1600
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1601
|
-
function DocumentTitle({ value, disabled, onChange }) {
|
|
1602
|
-
return /* @__PURE__ */ jsx6(
|
|
1603
|
-
"input",
|
|
1604
|
-
{
|
|
1605
|
-
className: "june-doc__title",
|
|
1606
|
-
"aria-label": "Document title",
|
|
1607
|
-
value,
|
|
1608
|
-
disabled,
|
|
1609
|
-
placeholder: "Untitled document",
|
|
1610
|
-
onChange: (e) => onChange(e.target.value)
|
|
1611
|
-
}
|
|
1612
|
-
);
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
// src/components/document-editor/document-toolbar.tsx
|
|
1616
|
-
import { useEditorState } from "@tiptap/react";
|
|
1617
|
-
import { useState as useState2 } from "react";
|
|
1618
|
-
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1619
|
-
var FORMAT_LABELS = {
|
|
1620
|
-
html: "HTML",
|
|
1621
|
-
markdown: "Markdown",
|
|
1622
|
-
txt: "Plain text",
|
|
1623
|
-
docx: "Word (.docx)",
|
|
1624
|
-
pdf: "PDF (print)"
|
|
1625
|
-
};
|
|
1626
|
-
function Btn({ label, active, disabled, toggle, onClick, children }) {
|
|
1627
|
-
return /* @__PURE__ */ jsx7(
|
|
1628
|
-
"button",
|
|
1629
|
-
{
|
|
1630
|
-
type: "button",
|
|
1631
|
-
className: "june-doc__btn",
|
|
1632
|
-
"aria-label": label,
|
|
1633
|
-
title: label,
|
|
1634
|
-
"aria-pressed": toggle ? !!active : void 0,
|
|
1635
|
-
"data-active": active || void 0,
|
|
1636
|
-
disabled,
|
|
1637
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
1638
|
-
onClick,
|
|
1639
|
-
children
|
|
1640
|
-
}
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
function DocumentToolbar(props) {
|
|
1644
|
-
const { editor, editable, exportFormats, canSave, saveState, onExport, onCopy, onSave } = props;
|
|
1645
|
-
const [linkOpen, setLinkOpen] = useState2(false);
|
|
1646
|
-
const [linkValue, setLinkValue] = useState2("");
|
|
1647
|
-
const s = useEditorState({
|
|
1648
|
-
editor,
|
|
1649
|
-
selector: ({ editor: ed }) => ed ? {
|
|
1650
|
-
bold: ed.isActive("bold"),
|
|
1651
|
-
italic: ed.isActive("italic"),
|
|
1652
|
-
underline: ed.isActive("underline"),
|
|
1653
|
-
strike: ed.isActive("strike"),
|
|
1654
|
-
bullet: ed.isActive("bulletList"),
|
|
1655
|
-
ordered: ed.isActive("orderedList"),
|
|
1656
|
-
quote: ed.isActive("blockquote"),
|
|
1657
|
-
link: ed.isActive("link"),
|
|
1658
|
-
linkHref: ed.getAttributes("link").href ?? "",
|
|
1659
|
-
h1: ed.isActive("heading", { level: 1 }),
|
|
1660
|
-
h2: ed.isActive("heading", { level: 2 }),
|
|
1661
|
-
h3: ed.isActive("heading", { level: 3 }),
|
|
1662
|
-
left: ed.isActive({ textAlign: "left" }),
|
|
1663
|
-
center: ed.isActive({ textAlign: "center" }),
|
|
1664
|
-
right: ed.isActive({ textAlign: "right" }),
|
|
1665
|
-
justify: ed.isActive({ textAlign: "justify" }),
|
|
1666
|
-
inTable: ed.isActive("table"),
|
|
1667
|
-
canUndo: ed.can().undo(),
|
|
1668
|
-
canRedo: ed.can().redo()
|
|
1669
|
-
} : null
|
|
1670
|
-
});
|
|
1671
|
-
const run = () => editor?.chain().focus();
|
|
1672
|
-
const off = !editor || !editable;
|
|
1673
|
-
const heading = s?.h1 ? "1" : s?.h2 ? "2" : s?.h3 ? "3" : "p";
|
|
1674
|
-
const openLink = () => {
|
|
1675
|
-
setLinkValue(s?.linkHref ?? "");
|
|
1676
|
-
setLinkOpen(true);
|
|
1677
|
-
};
|
|
1678
|
-
const applyLink = () => {
|
|
1679
|
-
const href = sanitizeHref(linkValue);
|
|
1680
|
-
if (href) run()?.extendMarkRange("link").setLink({ href }).run();
|
|
1681
|
-
setLinkOpen(false);
|
|
1682
|
-
};
|
|
1683
|
-
const removeLink = () => {
|
|
1684
|
-
run()?.extendMarkRange("link").unsetLink().run();
|
|
1685
|
-
setLinkOpen(false);
|
|
1686
|
-
};
|
|
1687
|
-
return /* @__PURE__ */ jsxs3("div", { className: "june-doc__toolbar", role: "toolbar", "aria-label": "Document formatting", children: [
|
|
1688
|
-
editable ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1689
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1690
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Undo", disabled: off || !s?.canUndo, onClick: () => run()?.undo().run(), children: "\u21B6" }),
|
|
1691
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Redo", disabled: off || !s?.canRedo, onClick: () => run()?.redo().run(), children: "\u21B7" })
|
|
1692
|
-
] }),
|
|
1693
|
-
/* @__PURE__ */ jsx7("div", { className: "june-doc__group", children: /* @__PURE__ */ jsxs3(
|
|
1694
|
-
"select",
|
|
1695
|
-
{
|
|
1696
|
-
className: "june-doc__select",
|
|
1697
|
-
"aria-label": "Text style",
|
|
1698
|
-
disabled: off,
|
|
1699
|
-
value: heading,
|
|
1700
|
-
onChange: (e) => {
|
|
1701
|
-
const v = e.target.value;
|
|
1702
|
-
if (v === "p") run()?.setParagraph().run();
|
|
1703
|
-
else run()?.toggleHeading({ level: Number(v) }).run();
|
|
1704
|
-
},
|
|
1705
|
-
children: [
|
|
1706
|
-
/* @__PURE__ */ jsx7("option", { value: "p", children: "Paragraph" }),
|
|
1707
|
-
/* @__PURE__ */ jsx7("option", { value: "1", children: "Heading 1" }),
|
|
1708
|
-
/* @__PURE__ */ jsx7("option", { value: "2", children: "Heading 2" }),
|
|
1709
|
-
/* @__PURE__ */ jsx7("option", { value: "3", children: "Heading 3" })
|
|
1710
|
-
]
|
|
1711
|
-
}
|
|
1712
|
-
) }),
|
|
1713
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1714
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Bold", toggle: true, active: s?.bold, disabled: off, onClick: () => run()?.toggleBold().run(), children: /* @__PURE__ */ jsx7("b", { children: "B" }) }),
|
|
1715
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Italic", toggle: true, active: s?.italic, disabled: off, onClick: () => run()?.toggleItalic().run(), children: /* @__PURE__ */ jsx7("i", { children: "I" }) }),
|
|
1716
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Underline", toggle: true, active: s?.underline, disabled: off, onClick: () => run()?.toggleUnderline().run(), children: /* @__PURE__ */ jsx7("u", { children: "U" }) }),
|
|
1717
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Strikethrough", toggle: true, active: s?.strike, disabled: off, onClick: () => run()?.toggleStrike().run(), children: /* @__PURE__ */ jsx7("s", { children: "S" }) })
|
|
1718
|
-
] }),
|
|
1719
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1720
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Bullet list", toggle: true, active: s?.bullet, disabled: off, onClick: () => run()?.toggleBulletList().run(), children: "\u2022 List" }),
|
|
1721
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Numbered list", toggle: true, active: s?.ordered, disabled: off, onClick: () => run()?.toggleOrderedList().run(), children: "1. List" }),
|
|
1722
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Blockquote", toggle: true, active: s?.quote, disabled: off, onClick: () => run()?.toggleBlockquote().run(), children: "Quote" }),
|
|
1723
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Link", toggle: true, active: s?.link, disabled: off, onClick: openLink, children: "Link" })
|
|
1724
|
-
] }),
|
|
1725
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1726
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align left", toggle: true, active: s?.left, disabled: off, onClick: () => run()?.setTextAlign("left").run(), children: "L" }),
|
|
1727
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align center", toggle: true, active: s?.center, disabled: off, onClick: () => run()?.setTextAlign("center").run(), children: "C" }),
|
|
1728
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Align right", toggle: true, active: s?.right, disabled: off, onClick: () => run()?.setTextAlign("right").run(), children: "R" }),
|
|
1729
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Justify", toggle: true, active: s?.justify, disabled: off, onClick: () => run()?.setTextAlign("justify").run(), children: "J" })
|
|
1730
|
-
] }),
|
|
1731
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group", children: [
|
|
1732
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Insert table", disabled: off, onClick: () => run()?.insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), children: "Table" }),
|
|
1733
|
-
s?.inTable ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1734
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Add row below", disabled: off, onClick: () => run()?.addRowAfter().run(), children: "+Row" }),
|
|
1735
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Add column right", disabled: off, onClick: () => run()?.addColumnAfter().run(), children: "+Col" }),
|
|
1736
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete row", disabled: off, onClick: () => run()?.deleteRow().run(), children: "\u2212Row" }),
|
|
1737
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete column", disabled: off, onClick: () => run()?.deleteColumn().run(), children: "\u2212Col" }),
|
|
1738
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Delete table", disabled: off, onClick: () => run()?.deleteTable().run(), children: "Del table" })
|
|
1739
|
-
] }) : null
|
|
1740
|
-
] })
|
|
1741
|
-
] }) : null,
|
|
1742
|
-
/* @__PURE__ */ jsxs3("div", { className: "june-doc__group june-doc__group--end", children: [
|
|
1743
|
-
/* @__PURE__ */ jsx7(Btn, { label: "Copy document", onClick: onCopy, children: "Copy" }),
|
|
1744
|
-
exportFormats.length > 0 ? /* @__PURE__ */ jsxs3(
|
|
1745
|
-
"select",
|
|
1746
|
-
{
|
|
1747
|
-
className: "june-doc__select",
|
|
1748
|
-
"aria-label": "Export document",
|
|
1749
|
-
value: "",
|
|
1750
|
-
onChange: (e) => e.target.value && onExport(e.target.value),
|
|
1751
|
-
children: [
|
|
1752
|
-
/* @__PURE__ */ jsx7("option", { value: "", children: "Export\u2026" }),
|
|
1753
|
-
exportFormats.map((f) => /* @__PURE__ */ jsx7("option", { value: f, children: FORMAT_LABELS[f] ?? f }, f))
|
|
1754
|
-
]
|
|
1755
|
-
}
|
|
1756
|
-
) : null,
|
|
1757
|
-
canSave ? /* @__PURE__ */ jsx7(Btn, { label: "Save", disabled: saveState === "saving" || saveState === "saved" || saveState === "idle", onClick: onSave, children: "Save" }) : null
|
|
1758
|
-
] }),
|
|
1759
|
-
linkOpen ? /* @__PURE__ */ jsxs3("div", { className: "june-doc__linkbar", children: [
|
|
1760
|
-
/* @__PURE__ */ jsx7(
|
|
1761
|
-
"input",
|
|
1762
|
-
{
|
|
1763
|
-
"aria-label": "Link address",
|
|
1764
|
-
className: "june-doc__input",
|
|
1765
|
-
placeholder: "https://example.com",
|
|
1766
|
-
value: linkValue,
|
|
1767
|
-
autoFocus: true,
|
|
1768
|
-
onChange: (e) => setLinkValue(e.target.value),
|
|
1769
|
-
onKeyDown: (e) => {
|
|
1770
|
-
if (e.key === "Enter") applyLink();
|
|
1771
|
-
if (e.key === "Escape") setLinkOpen(false);
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
),
|
|
1775
|
-
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: applyLink, children: "Apply" }),
|
|
1776
|
-
s?.link ? /* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: removeLink, children: "Remove" }) : null,
|
|
1777
|
-
/* @__PURE__ */ jsx7("button", { type: "button", className: "june-doc__btn", onClick: () => setLinkOpen(false), children: "Cancel" })
|
|
1778
|
-
] }) : null
|
|
1779
|
-
] });
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
2884
|
// src/components/document-editor/document-types.ts
|
|
1783
2885
|
var ALL_EXPORT_FORMATS = [
|
|
1784
2886
|
"html",
|
|
@@ -1789,7 +2891,7 @@ var ALL_EXPORT_FORMATS = [
|
|
|
1789
2891
|
];
|
|
1790
2892
|
|
|
1791
2893
|
// src/components/document-editor/document-editor.tsx
|
|
1792
|
-
import { jsx as
|
|
2894
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1793
2895
|
var snapshotOf = (r) => ({
|
|
1794
2896
|
documentId: r.id,
|
|
1795
2897
|
title: r.title,
|
|
@@ -1804,6 +2906,8 @@ var SAVE_LABEL = {
|
|
|
1804
2906
|
saved: "Saved",
|
|
1805
2907
|
error: "Save failed"
|
|
1806
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;
|
|
1807
2911
|
function DocumentEditorImpl(props) {
|
|
1808
2912
|
const {
|
|
1809
2913
|
documentId,
|
|
@@ -1818,7 +2922,7 @@ function DocumentEditorImpl(props) {
|
|
|
1818
2922
|
} = props;
|
|
1819
2923
|
const ctxOptions = useDocumentOptions();
|
|
1820
2924
|
const options = useMemo4(() => ({ ...ctxOptions, ...props.options }), [ctxOptions, props.options]);
|
|
1821
|
-
const optionsRef =
|
|
2925
|
+
const optionsRef = useRef7(options);
|
|
1822
2926
|
optionsRef.current = options;
|
|
1823
2927
|
const store = useMemo4(() => options.store ?? getDefaultDocumentStore(), [options.store]);
|
|
1824
2928
|
const instanceId = useId();
|
|
@@ -1833,22 +2937,37 @@ function DocumentEditorImpl(props) {
|
|
|
1833
2937
|
return { json: null, error: toJuneError(e, "DOCUMENT_ERROR") };
|
|
1834
2938
|
}
|
|
1835
2939
|
}, [contentKey]);
|
|
1836
|
-
|
|
2940
|
+
useEffect5(() => {
|
|
1837
2941
|
if (parsed.error) optionsRef.current.onError?.(parsed.error);
|
|
1838
2942
|
}, [parsed.error]);
|
|
1839
2943
|
const title = useDocumentValue(store, documentId, (r) => r?.title ?? titleProp);
|
|
1840
2944
|
const pending = useDocumentValue(store, documentId, (r) => r?.pending ?? null);
|
|
1841
2945
|
const words = useDocumentValue(store, documentId, (r) => r ? wordCountOf(r.json) : 0);
|
|
1842
2946
|
const recordStatus = useDocumentValue(store, documentId, (r) => r?.status ?? status);
|
|
1843
|
-
const [saveState, setSaveState] =
|
|
1844
|
-
const [notice, setNotice] =
|
|
1845
|
-
const
|
|
1846
|
-
const
|
|
1847
|
-
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);
|
|
1848
2967
|
const flash = useCallback3((message) => {
|
|
1849
2968
|
setNotice(message);
|
|
1850
2969
|
}, []);
|
|
1851
|
-
|
|
2970
|
+
useEffect5(() => {
|
|
1852
2971
|
if (!notice) return;
|
|
1853
2972
|
const t = setTimeout(() => setNotice(null), 3e3);
|
|
1854
2973
|
return () => clearTimeout(t);
|
|
@@ -1860,13 +2979,13 @@ function DocumentEditorImpl(props) {
|
|
|
1860
2979
|
},
|
|
1861
2980
|
[store, documentId]
|
|
1862
2981
|
);
|
|
1863
|
-
const saveRef =
|
|
2982
|
+
const saveRef = useRef7(async () => {
|
|
1864
2983
|
});
|
|
1865
2984
|
const autosave = useMemo4(() => {
|
|
1866
2985
|
const delay = options.autosaveDelay ?? 1500;
|
|
1867
2986
|
return delay > 0 ? debounce(() => void saveRef.current(), delay) : null;
|
|
1868
2987
|
}, [options.autosaveDelay]);
|
|
1869
|
-
|
|
2988
|
+
useEffect5(() => () => autosave?.flush(), [autosave]);
|
|
1870
2989
|
saveRef.current = async () => {
|
|
1871
2990
|
const record = store.get(documentId);
|
|
1872
2991
|
const onSave = optionsRef.current.onSave;
|
|
@@ -1897,9 +3016,9 @@ function DocumentEditorImpl(props) {
|
|
|
1897
3016
|
setSaveState("dirty");
|
|
1898
3017
|
autosave?.();
|
|
1899
3018
|
}, [autosave]);
|
|
1900
|
-
const [initialJson] =
|
|
3019
|
+
const [initialJson] = useState6(() => store.get(documentId)?.json ?? parsed.json ?? emptyDoc());
|
|
1901
3020
|
const extensions = useMemo4(() => createDocumentExtensions(), []);
|
|
1902
|
-
const onUpdateRef =
|
|
3021
|
+
const onUpdateRef = useRef7(() => {
|
|
1903
3022
|
});
|
|
1904
3023
|
onUpdateRef.current = (json) => {
|
|
1905
3024
|
store.edit(documentId, json, instanceId);
|
|
@@ -1921,10 +3040,10 @@ function DocumentEditorImpl(props) {
|
|
|
1921
3040
|
},
|
|
1922
3041
|
onUpdate: ({ editor: ed }) => onUpdateRef.current(ed.getJSON())
|
|
1923
3042
|
});
|
|
1924
|
-
|
|
3043
|
+
useEffect5(() => {
|
|
1925
3044
|
editor?.setEditable(editable);
|
|
1926
3045
|
}, [editor, editable]);
|
|
1927
|
-
|
|
3046
|
+
useEffect5(() => {
|
|
1928
3047
|
if (!parsed.json) return;
|
|
1929
3048
|
const outcome = store.receive({
|
|
1930
3049
|
id: documentId,
|
|
@@ -1945,7 +3064,7 @@ function DocumentEditorImpl(props) {
|
|
|
1945
3064
|
});
|
|
1946
3065
|
}
|
|
1947
3066
|
}, [store, documentId, titleProp, parsed.json, documentType, status, emitChange, markDirty]);
|
|
1948
|
-
|
|
3067
|
+
useEffect5(() => {
|
|
1949
3068
|
if (!editor) return;
|
|
1950
3069
|
const apply = () => {
|
|
1951
3070
|
const record = store.get(documentId);
|
|
@@ -1962,7 +3081,7 @@ function DocumentEditorImpl(props) {
|
|
|
1962
3081
|
apply();
|
|
1963
3082
|
return store.subscribe(apply);
|
|
1964
3083
|
}, [editor, store, documentId, instanceId]);
|
|
1965
|
-
|
|
3084
|
+
useEffect5(() => {
|
|
1966
3085
|
if (!editor || readyRef.current) return;
|
|
1967
3086
|
const record = store.get(documentId);
|
|
1968
3087
|
if (!record) return;
|
|
@@ -2006,67 +3125,114 @@ function DocumentEditorImpl(props) {
|
|
|
2006
3125
|
};
|
|
2007
3126
|
const formats = (exportFormats ?? [...ALL_EXPORT_FORMATS]).filter((f) => listDocumentExporters().includes(f));
|
|
2008
3127
|
if (parsed.error && !store.get(documentId)) {
|
|
2009
|
-
return /* @__PURE__ */
|
|
2010
|
-
/* @__PURE__ */
|
|
2011
|
-
/* @__PURE__ */
|
|
2012
|
-
/* @__PURE__ */
|
|
2013
|
-
/* @__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 })
|
|
2014
3133
|
] })
|
|
2015
3134
|
] });
|
|
2016
3135
|
}
|
|
2017
|
-
if (!editor) return /* @__PURE__ */
|
|
3136
|
+
if (!editor) return /* @__PURE__ */ jsx13(DocumentDraft, { title, state: "loading" });
|
|
2018
3137
|
const isDraft = recordStatus === "draft";
|
|
2019
|
-
|
|
3138
|
+
const classes = ["june-doc", fullscreen ? "june-doc--fullscreen" : "", className ?? ""].filter(Boolean).join(" ");
|
|
3139
|
+
return /* @__PURE__ */ jsxs9(
|
|
2020
3140
|
"div",
|
|
2021
3141
|
{
|
|
2022
|
-
className:
|
|
3142
|
+
className: classes,
|
|
2023
3143
|
"data-theme": resolvedTheme,
|
|
2024
3144
|
"data-status": recordStatus,
|
|
3145
|
+
role: fullscreen ? "dialog" : void 0,
|
|
3146
|
+
"aria-modal": fullscreen || void 0,
|
|
3147
|
+
"aria-label": fullscreen ? title : void 0,
|
|
2025
3148
|
onKeyDown: (e) => {
|
|
2026
|
-
|
|
3149
|
+
const mod = e.ctrlKey || e.metaKey;
|
|
3150
|
+
if (mod && e.key.toLowerCase() === "s" && options.onSave) {
|
|
2027
3151
|
e.preventDefault();
|
|
2028
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);
|
|
2029
3158
|
}
|
|
2030
3159
|
},
|
|
2031
3160
|
children: [
|
|
2032
|
-
/* @__PURE__ */
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
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: [
|
|
2038
3180
|
"The assistant suggested changes that overlap your edits (",
|
|
2039
3181
|
pending.conflicts,
|
|
2040
3182
|
" ",
|
|
2041
3183
|
pending.conflicts === 1 ? "section" : "sections",
|
|
2042
3184
|
")."
|
|
2043
3185
|
] }),
|
|
2044
|
-
/* @__PURE__ */
|
|
2045
|
-
/* @__PURE__ */
|
|
2046
|
-
/* @__PURE__ */
|
|
2047
|
-
/* @__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" })
|
|
2048
3190
|
] })
|
|
2049
3191
|
] }) : null,
|
|
2050
|
-
/* @__PURE__ */
|
|
3192
|
+
/* @__PURE__ */ jsx13(
|
|
2051
3193
|
DocumentToolbar,
|
|
2052
3194
|
{
|
|
2053
3195
|
editor,
|
|
2054
3196
|
editable,
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
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
|
|
2061
3206
|
}
|
|
2062
3207
|
),
|
|
2063
|
-
/* @__PURE__ */
|
|
2064
|
-
/* @__PURE__ */
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
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
|
+
] })
|
|
2068
3226
|
] }),
|
|
2069
|
-
/* @__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
|
+
] })
|
|
2070
3236
|
] })
|
|
2071
3237
|
]
|
|
2072
3238
|
}
|
|
@@ -2074,7 +3240,7 @@ function DocumentEditorImpl(props) {
|
|
|
2074
3240
|
}
|
|
2075
3241
|
|
|
2076
3242
|
// src/components/document-editor/document-component.tsx
|
|
2077
|
-
import { jsx as
|
|
3243
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
2078
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.';
|
|
2079
3245
|
var documentProps = z.object({
|
|
2080
3246
|
documentId: z.string().describe("Stable id. Reuse it when revising the same document."),
|
|
@@ -2088,12 +3254,12 @@ var documentProps = z.object({
|
|
|
2088
3254
|
function DocumentEditor(props) {
|
|
2089
3255
|
const { isStreaming } = useJuneRuntime();
|
|
2090
3256
|
if (props.generating || isStreaming) {
|
|
2091
|
-
return /* @__PURE__ */
|
|
3257
|
+
return /* @__PURE__ */ jsx14(DocumentDraft, { title: props.title, state: "drafting" });
|
|
2092
3258
|
}
|
|
2093
|
-
return /* @__PURE__ */
|
|
3259
|
+
return /* @__PURE__ */ jsx14(DocumentEditorImpl, { ...props });
|
|
2094
3260
|
}
|
|
2095
3261
|
function OpenUIDocument({ props }) {
|
|
2096
|
-
return /* @__PURE__ */
|
|
3262
|
+
return /* @__PURE__ */ jsx14(
|
|
2097
3263
|
DocumentEditor,
|
|
2098
3264
|
{
|
|
2099
3265
|
documentId: props.documentId,
|
|
@@ -2113,11 +3279,15 @@ var JuneDocumentEditorComponent = defineComponent2({
|
|
|
2113
3279
|
component: OpenUIDocument
|
|
2114
3280
|
});
|
|
2115
3281
|
function withDocumentEditor(library) {
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
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
|
+
}
|
|
2121
3291
|
}
|
|
2122
3292
|
|
|
2123
3293
|
// src/components/document-editor/document-instructions.ts
|
|
@@ -2164,7 +3334,7 @@ function documentPromptOptions(options = {}) {
|
|
|
2164
3334
|
var attr = (value) => value.replace(/"/g, """);
|
|
2165
3335
|
async function serializeDocumentsForAgent(options = {}) {
|
|
2166
3336
|
const store = options.store ?? getDefaultDocumentStore();
|
|
2167
|
-
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-
|
|
3337
|
+
const { docToMarkdown: docToMarkdown2 } = await import("./document-engine-IAY2T6B3.js");
|
|
2168
3338
|
return store.list().filter((r) => !options.ids || options.ids.includes(r.id)).map(
|
|
2169
3339
|
(r) => `<document id="${attr(r.id)}" title="${attr(r.title)}"${r.documentType ? ` type="${attr(r.documentType)}"` : ""}>
|
|
2170
3340
|
${docToMarkdown2(r.json)}
|
|
@@ -2177,9 +3347,9 @@ import { openuiLibrary } from "@openuidev/react-ui";
|
|
|
2177
3347
|
var juneLibrary = withDocumentEditor(openuiLibrary);
|
|
2178
3348
|
|
|
2179
3349
|
// src/core/JuneUI.tsx
|
|
2180
|
-
import { jsx as
|
|
3350
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
2181
3351
|
function JuneUI({ library = juneLibrary, ...props }) {
|
|
2182
|
-
return /* @__PURE__ */
|
|
3352
|
+
return /* @__PURE__ */ jsx15(JuneRenderer, { library, ...props });
|
|
2183
3353
|
}
|
|
2184
3354
|
export {
|
|
2185
3355
|
ALL_EXPORT_FORMATS,
|