bot commited on
Commit
2f60066
·
1 Parent(s): 6a3cab2

add paste image support

Browse files
Files changed (9) hide show
  1. .DS_Store +0 -0
  2. app.js +428 -34
  3. assets/app.bundle.js +0 -0
  4. index.html +36 -2
  5. package.json +11 -2
  6. pnpm-lock.yaml +935 -0
  7. src/data/state-utils.mjs +1 -1
  8. styles.css +205 -5
  9. vite.config.js +15 -0
.DS_Store ADDED
Binary file (6.15 kB). View file
 
app.js CHANGED
@@ -1,3 +1,10 @@
 
 
 
 
 
 
 
1
  const uiStorageKey = "memo-ui-state-v1";
2
 
3
  if (new URLSearchParams(window.location.search).get("device") === "mobile") {
@@ -6,7 +13,7 @@ if (new URLSearchParams(window.location.search).get("device") === "mobile") {
6
 
7
  const starterNotes = [
8
  {
9
- id: crypto.randomUUID(),
10
  folder: "notes",
11
  body: "欢迎使用备忘录\n\n左侧选择文件夹,中间选择笔记,右侧直接编辑。第一行会自动作为标题。",
12
  createdAt: Date.now() - 1000 * 60 * 60 * 24,
@@ -14,7 +21,7 @@ const starterNotes = [
14
  version: 1
15
  },
16
  {
17
- id: crypto.randomUUID(),
18
  folder: "notes",
19
  body: "待办清单\n\n- 记录想法\n- 整理项目\n- 做一个真正好用的 Web 版笔记",
20
  createdAt: Date.now() - 1000 * 60 * 60 * 3,
@@ -32,8 +39,26 @@ let saveTimerNoteId = null;
32
  let saveInFlightNoteId = null;
33
  let queuedSaveNoteId = null;
34
  let saveStatusTimer = null;
 
 
35
  const saveDelay = 700;
36
- const maxNoteBodyLength = 1024 * 1024;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  const els = {
38
  authGate: document.querySelector("#authGate"),
39
  authForm: document.querySelector("#authForm"),
@@ -73,6 +98,8 @@ const els = {
73
  mobileSearchInput: document.querySelector("#mobileSearchInput"),
74
  mobileSearchWrap: document.querySelector("#mobileSearchWrap"),
75
  mobileEdit: document.querySelector("#mobileEdit"),
 
 
76
  mobilePreviewToggle: document.querySelector("#mobilePreviewToggle"),
77
  mobileSave: document.querySelector("#mobileSave"),
78
  mobileMore: document.querySelector("#mobileMore"),
@@ -84,6 +111,8 @@ const els = {
84
  appShell: document.querySelector(".app-shell"),
85
  editor: document.querySelector("#editor"),
86
  noteFolderSelect: document.querySelector("#noteFolderSelect"),
 
 
87
  editorPreviewToggle: document.querySelector("#editorPreviewToggle"),
88
  saveNote: document.querySelector("#saveNote"),
89
  saveStatus: document.querySelector("#saveStatus"),
@@ -92,6 +121,7 @@ const els = {
92
  editorPane: document.querySelector(".editor-pane")
93
  };
94
 
 
95
  render();
96
  if (authStatus.authenticated) {
97
  hideAuthGate();
@@ -130,7 +160,7 @@ els.authForm.addEventListener("submit", async (event) => {
130
  els.newNote.addEventListener("click", async () => {
131
  const now = Date.now();
132
  const note = {
133
- id: crypto.randomUUID(),
134
  folder: getWriteFolderId(),
135
  body: "",
136
  createdAt: now,
@@ -226,6 +256,24 @@ els.mobileSave.addEventListener("click", () => {
226
 
227
  els.mobilePreviewToggle.addEventListener("click", togglePreviewMode);
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  els.mobileMore.addEventListener("click", (event) => {
230
  event.stopPropagation();
231
  els.mobileActionMenu.hidden = !els.mobileActionMenu.hidden;
@@ -247,9 +295,15 @@ els.mobileMenuDelete.addEventListener("click", () => {
247
  });
248
 
249
  document.addEventListener("click", (event) => {
250
- if (els.mobileActionMenu.hidden) return;
251
- if (event.target.closest("#mobileActions")) return;
252
- els.mobileActionMenu.hidden = true;
 
 
 
 
 
 
253
  });
254
 
255
  els.newFolder.addEventListener("click", () => {
@@ -259,23 +313,6 @@ els.newFolder.addEventListener("click", () => {
259
  });
260
  });
261
 
262
- els.editor.addEventListener("input", (event) => {
263
- const note = getSelectedNote();
264
- if (!note) return;
265
-
266
- if (event.target.value.length > maxNoteBodyLength) {
267
- event.target.value = event.target.value.slice(0, maxNoteBodyLength);
268
- setSaveStatus("内容已达上限", "error");
269
- }
270
-
271
- note.body = event.target.value;
272
- note.updatedAt = Date.now();
273
- saveSelectedNote();
274
- renderListOnly();
275
- updateEditorMeta(note);
276
- renderMarkdownPreview(note);
277
- });
278
-
279
  els.noteFolderSelect.addEventListener("change", (event) => {
280
  moveSelectedNoteToFolder(event.target.value);
281
  });
@@ -379,10 +416,239 @@ function moveSelectedNoteToFolder(nextFolder) {
379
  function togglePreviewMode() {
380
  state.previewMode = !state.previewMode;
381
  saveUiState();
 
382
  renderEditor();
383
  renderMobileChrome();
384
  }
385
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  document.addEventListener("keydown", (event) => {
387
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "n") {
388
  event.preventDefault();
@@ -400,9 +666,13 @@ document.addEventListener("keydown", (event) => {
400
  if (getSelectedNote()) saveSelectedNote({ immediate: true });
401
  }
402
 
403
- if (event.key === "Delete" && document.activeElement !== els.editor) {
404
  els.deleteNote.click();
405
  }
 
 
 
 
406
  });
407
 
408
  async function loadState() {
@@ -878,9 +1148,10 @@ function renderNoteButton(note) {
878
  function renderEditor() {
879
  const note = getSelectedNote();
880
  els.editorPane.classList.toggle("empty", !note);
881
- els.editor.disabled = !note;
882
  els.saveNote.disabled = !note;
883
  els.mobileSave.disabled = !note;
 
 
884
  els.editorPreviewToggle.disabled = !note;
885
  els.mobilePreviewToggle.disabled = !note;
886
  els.mobileMore.disabled = !note;
@@ -894,7 +1165,9 @@ function renderEditor() {
894
 
895
  if (!note) {
896
  els.mobileActionMenu.hidden = true;
897
- els.editor.value = "";
 
 
898
  els.editor.hidden = false;
899
  els.editedAt.textContent = "";
900
  els.markdownPreview.hidden = true;
@@ -905,14 +1178,29 @@ function renderEditor() {
905
  return;
906
  }
907
 
908
- if (els.editor.value !== note.body) {
909
- els.editor.value = note.body;
910
- }
911
  els.editor.hidden = state.previewMode;
912
  els.markdownPreview.hidden = !state.previewMode;
913
  renderMarkdownPreview(note);
914
  renderNoteFolderSelect(note);
915
  updateEditorMeta(note);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  }
917
 
918
  function renderNoteFolderSelect(note) {
@@ -1038,7 +1326,7 @@ async function submitFolderCreate(name) {
1038
  const previousActiveFolder = state.activeFolder;
1039
  const previousSelectedId = state.selectedId;
1040
  const folder = {
1041
- id: crypto.randomUUID(),
1042
  name,
1043
  createdAt: now,
1044
  updatedAt: now,
@@ -1132,6 +1420,20 @@ function defaultFolders() {
1132
  return [{ id: "notes", name: "备忘录", version: 1 }];
1133
  }
1134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1135
  function renderIcons() {
1136
  window.lucide?.createIcons({
1137
  attrs: {
@@ -1142,7 +1444,85 @@ function renderIcons() {
1142
 
1143
  function renderMarkdownPreview(note) {
1144
  if (!state.previewMode) return;
1145
- els.markdownPreview.innerHTML = markdownToHtml(note.body);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1146
  }
1147
 
1148
  function markdownToHtml(markdown) {
@@ -1233,6 +1613,7 @@ function renderInlineMarkdown(value) {
1233
  .replace(/`([^`]+)`/g, "<code>$1</code>")
1234
  .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
1235
  .replace(/\*([^*]+)\*/g, "<em>$1</em>")
 
1236
  .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>');
1237
  }
1238
 
@@ -1246,16 +1627,29 @@ function escapeHtml(value) {
1246
  }
1247
 
1248
  function getTitle(note) {
1249
- const firstLine = note.body.split(/\n/).find((line) => line.trim());
1250
  return firstLine ? firstLine.trim().slice(0, 80) : "新建笔记";
1251
  }
1252
 
1253
  function getPreview(note) {
1254
- const lines = note.body.split(/\n/).map((line) => line.trim()).filter(Boolean);
1255
  const text = lines.slice(1).join(" ") || "无附加文本";
1256
  return text.slice(0, 140);
1257
  }
1258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1259
  function formatListDate(value) {
1260
  const date = new Date(value);
1261
  const today = new Date();
 
1
+ import { Editor } from "@tiptap/core";
2
+ import TiptapImage from "@tiptap/extension-image";
3
+ import TextAlign from "@tiptap/extension-text-align";
4
+ import { Markdown } from "@tiptap/markdown";
5
+ import StarterKit from "@tiptap/starter-kit";
6
+ import { stringify as stringifyUuid } from "uuid";
7
+
8
  const uiStorageKey = "memo-ui-state-v1";
9
 
10
  if (new URLSearchParams(window.location.search).get("device") === "mobile") {
 
13
 
14
  const starterNotes = [
15
  {
16
+ id: createId(),
17
  folder: "notes",
18
  body: "欢迎使用备忘录\n\n左侧选择文件夹,中间选择笔记,右侧直接编辑。第一行会自动作为标题。",
19
  createdAt: Date.now() - 1000 * 60 * 60 * 24,
 
21
  version: 1
22
  },
23
  {
24
+ id: createId(),
25
  folder: "notes",
26
  body: "待办清单\n\n- 记录想法\n- 整理项目\n- 做一个真正好用的 Web 版笔记",
27
  createdAt: Date.now() - 1000 * 60 * 60 * 3,
 
39
  let saveInFlightNoteId = null;
40
  let queuedSaveNoteId = null;
41
  let saveStatusTimer = null;
42
+ let tiptapEditor = null;
43
+ let syncingEditor = false;
44
  const saveDelay = 700;
45
+ const maxNoteBodyLength = 10 * 1024 * 1024;
46
+ const maxPastedImageDimension = 1600;
47
+ const pastedImageQuality = 0.82;
48
+ const AlignedImage = TiptapImage.extend({
49
+ addAttributes() {
50
+ return {
51
+ ...this.parent?.(),
52
+ align: {
53
+ default: "center",
54
+ parseHTML: (element) => element.getAttribute("data-align") || "center",
55
+ renderHTML: (attributes) => ({
56
+ "data-align": attributes.align || "center"
57
+ })
58
+ }
59
+ };
60
+ }
61
+ });
62
  const els = {
63
  authGate: document.querySelector("#authGate"),
64
  authForm: document.querySelector("#authForm"),
 
98
  mobileSearchInput: document.querySelector("#mobileSearchInput"),
99
  mobileSearchWrap: document.querySelector("#mobileSearchWrap"),
100
  mobileEdit: document.querySelector("#mobileEdit"),
101
+ mobileEditorMenuToggle: document.querySelector("#mobileEditorMenuToggle"),
102
+ mobileEditorFormatMenu: document.querySelector("#mobileEditorFormatMenu"),
103
  mobilePreviewToggle: document.querySelector("#mobilePreviewToggle"),
104
  mobileSave: document.querySelector("#mobileSave"),
105
  mobileMore: document.querySelector("#mobileMore"),
 
111
  appShell: document.querySelector(".app-shell"),
112
  editor: document.querySelector("#editor"),
113
  noteFolderSelect: document.querySelector("#noteFolderSelect"),
114
+ editorMenuToggle: document.querySelector("#editorMenuToggle"),
115
+ editorFormatMenu: document.querySelector("#editorFormatMenu"),
116
  editorPreviewToggle: document.querySelector("#editorPreviewToggle"),
117
  saveNote: document.querySelector("#saveNote"),
118
  saveStatus: document.querySelector("#saveStatus"),
 
121
  editorPane: document.querySelector(".editor-pane")
122
  };
123
 
124
+ initTiptapEditor();
125
  render();
126
  if (authStatus.authenticated) {
127
  hideAuthGate();
 
160
  els.newNote.addEventListener("click", async () => {
161
  const now = Date.now();
162
  const note = {
163
+ id: createId(),
164
  folder: getWriteFolderId(),
165
  body: "",
166
  createdAt: now,
 
256
 
257
  els.mobilePreviewToggle.addEventListener("click", togglePreviewMode);
258
 
259
+ els.editorMenuToggle.addEventListener("click", (event) => {
260
+ event.stopPropagation();
261
+ toggleEditorMenu("desktop");
262
+ });
263
+
264
+ els.mobileEditorMenuToggle.addEventListener("click", (event) => {
265
+ event.stopPropagation();
266
+ toggleEditorMenu("mobile");
267
+ });
268
+
269
+ [...document.querySelectorAll("[data-editor-command]")].forEach((button) => {
270
+ button.addEventListener("click", (event) => {
271
+ event.preventDefault();
272
+ event.stopPropagation();
273
+ runEditorCommand(button.dataset.editorCommand);
274
+ });
275
+ });
276
+
277
  els.mobileMore.addEventListener("click", (event) => {
278
  event.stopPropagation();
279
  els.mobileActionMenu.hidden = !els.mobileActionMenu.hidden;
 
295
  });
296
 
297
  document.addEventListener("click", (event) => {
298
+ if (!els.mobileActionMenu.hidden && !event.target.closest("#mobileActions")) {
299
+ els.mobileActionMenu.hidden = true;
300
+ }
301
+ if (!event.target.closest(".editor-menu-wrap")) {
302
+ els.editorFormatMenu.hidden = true;
303
+ }
304
+ if (!event.target.closest(".mobile-editor-menu-wrap")) {
305
+ els.mobileEditorFormatMenu.hidden = true;
306
+ }
307
  });
308
 
309
  els.newFolder.addEventListener("click", () => {
 
313
  });
314
  });
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  els.noteFolderSelect.addEventListener("change", (event) => {
317
  moveSelectedNoteToFolder(event.target.value);
318
  });
 
416
  function togglePreviewMode() {
417
  state.previewMode = !state.previewMode;
418
  saveUiState();
419
+ hideEditorMenus();
420
  renderEditor();
421
  renderMobileChrome();
422
  }
423
 
424
+ function toggleEditorMenu(target) {
425
+ const note = getSelectedNote();
426
+ if (!note || state.previewMode) return;
427
+ const menu = target === "mobile" ? els.mobileEditorFormatMenu : els.editorFormatMenu;
428
+ const otherMenu = target === "mobile" ? els.editorFormatMenu : els.mobileEditorFormatMenu;
429
+ otherMenu.hidden = true;
430
+ menu.hidden = !menu.hidden;
431
+ els.editorMenuToggle.classList.toggle("active", !els.editorFormatMenu.hidden);
432
+ els.mobileEditorMenuToggle.classList.toggle("active", !els.mobileEditorFormatMenu.hidden);
433
+ renderEditorMenuState();
434
+ }
435
+
436
+ function hideEditorMenus() {
437
+ els.editorFormatMenu.hidden = true;
438
+ els.mobileEditorFormatMenu.hidden = true;
439
+ els.editorMenuToggle.classList.remove("active");
440
+ els.mobileEditorMenuToggle.classList.remove("active");
441
+ }
442
+
443
+ function runEditorCommand(command) {
444
+ if (!tiptapEditor || state.previewMode || !getSelectedNote()) return;
445
+ const align = {
446
+ alignLeft: "left",
447
+ alignCenter: "center",
448
+ alignRight: "right"
449
+ }[command];
450
+
451
+ if (align) {
452
+ setCurrentAlignment(align);
453
+ renderEditorMenuState();
454
+ return;
455
+ }
456
+
457
+ const chain = tiptapEditor.chain().focus();
458
+ const commands = {
459
+ paragraph: () => chain.setParagraph().run(),
460
+ heading1: () => chain.toggleHeading({ level: 1 }).run(),
461
+ heading2: () => chain.toggleHeading({ level: 2 }).run(),
462
+ bold: () => chain.toggleBold().run(),
463
+ italic: () => chain.toggleItalic().run(),
464
+ bulletList: () => chain.toggleBulletList().run(),
465
+ orderedList: () => chain.toggleOrderedList().run()
466
+ };
467
+
468
+ commands[command]?.();
469
+ renderEditorMenuState();
470
+ }
471
+
472
+ function setCurrentAlignment(align) {
473
+ if (isImageSelected()) {
474
+ setImageAlignment(align);
475
+ return;
476
+ }
477
+ tiptapEditor.chain().focus().setTextAlign(align).run();
478
+ }
479
+
480
+ function setImageAlignment(align) {
481
+ if (!isImageSelected()) return;
482
+ tiptapEditor.chain().focus().updateAttributes("image", { align }).run();
483
+ }
484
+
485
+ function renderEditorMenuState(editor = tiptapEditor) {
486
+ if (!editor) return;
487
+ const disabled = !getSelectedNote() || state.previewMode;
488
+ els.editorMenuToggle.disabled = disabled;
489
+ els.mobileEditorMenuToggle.disabled = disabled;
490
+ if (disabled) hideEditorMenus();
491
+
492
+ const activeCommands = new Set();
493
+ if (editor.isActive("paragraph")) activeCommands.add("paragraph");
494
+ if (editor.isActive("heading", { level: 1 })) activeCommands.add("heading1");
495
+ if (editor.isActive("heading", { level: 2 })) activeCommands.add("heading2");
496
+ if (editor.isActive("bold")) activeCommands.add("bold");
497
+ if (editor.isActive("italic")) activeCommands.add("italic");
498
+ if (editor.isActive("bulletList")) activeCommands.add("bulletList");
499
+ if (editor.isActive("orderedList")) activeCommands.add("orderedList");
500
+
501
+ const imageAlign = getSelectedImageAlign();
502
+ const textAlign = editor.getAttributes("paragraph").textAlign || editor.getAttributes("heading").textAlign;
503
+ const align = imageAlign || textAlign || "left";
504
+ activeCommands.add(`align${align[0].toUpperCase()}${align.slice(1)}`);
505
+
506
+ document.querySelectorAll("[data-editor-command]").forEach((button) => {
507
+ const isActive = activeCommands.has(button.dataset.editorCommand);
508
+ button.classList.toggle("active", isActive);
509
+ button.disabled = disabled;
510
+ });
511
+ }
512
+
513
+ function isImageSelected() {
514
+ return tiptapEditor?.state.selection.node?.type.name === "image";
515
+ }
516
+
517
+ function getSelectedImageAlign() {
518
+ if (!isImageSelected()) return "";
519
+ return tiptapEditor.state.selection.node.attrs.align || "center";
520
+ }
521
+
522
+ function initTiptapEditor() {
523
+ tiptapEditor = new Editor({
524
+ element: els.editor,
525
+ extensions: [
526
+ StarterKit,
527
+ AlignedImage.configure({
528
+ allowBase64: true,
529
+ HTMLAttributes: {
530
+ class: "memo-image"
531
+ }
532
+ }),
533
+ TextAlign.configure({
534
+ types: ["heading", "paragraph"]
535
+ }),
536
+ Markdown
537
+ ],
538
+ content: "",
539
+ editorProps: {
540
+ handlePaste(_view, event) {
541
+ const files = [...(event.clipboardData?.files || [])].filter((file) => file.type.startsWith("image/"));
542
+ if (!files.length) return false;
543
+ event.preventDefault();
544
+ insertPastedImages(files);
545
+ return true;
546
+ }
547
+ },
548
+ onUpdate({ editor }) {
549
+ if (syncingEditor) return;
550
+ const note = getSelectedNote();
551
+ if (!note) return;
552
+
553
+ const nextBody = normalizeEditorHtml(editor.getHTML());
554
+ if (nextBody.length > maxNoteBodyLength) {
555
+ setSaveStatus("内容已达上限", "error");
556
+ return;
557
+ }
558
+
559
+ note.body = nextBody;
560
+ note.updatedAt = Date.now();
561
+ saveSelectedNote();
562
+ renderListOnly();
563
+ updateEditorMeta(note);
564
+ renderMarkdownPreview(note);
565
+ renderEditorMenuState(editor);
566
+ },
567
+ onSelectionUpdate({ editor }) {
568
+ renderEditorMenuState(editor);
569
+ }
570
+ });
571
+ }
572
+
573
+ async function insertPastedImages(files) {
574
+ if (!tiptapEditor || !getSelectedNote()) return;
575
+ setSaveStatus("处理图片");
576
+
577
+ for (const file of files) {
578
+ try {
579
+ const dataUrl = await imageFileToDataUrl(file);
580
+ const currentLength = normalizeEditorHtml(tiptapEditor.getHTML()).length;
581
+ if (currentLength + dataUrl.length > maxNoteBodyLength) {
582
+ setSaveStatus("图片太大", "error");
583
+ continue;
584
+ }
585
+
586
+ tiptapEditor
587
+ .chain()
588
+ .focus()
589
+ .insertContent([
590
+ {
591
+ type: "image",
592
+ attrs: {
593
+ src: dataUrl,
594
+ alt: file.name || "粘贴的图片",
595
+ title: file.name || null,
596
+ align: "center"
597
+ }
598
+ },
599
+ { type: "paragraph" }
600
+ ])
601
+ .run();
602
+ } catch (error) {
603
+ setSaveStatus("图片粘贴失败", "error");
604
+ console.error("Failed to paste image.", error);
605
+ }
606
+ }
607
+ }
608
+
609
+ async function imageFileToDataUrl(file) {
610
+ if (file.type === "image/gif") {
611
+ return await readFileAsDataUrl(file);
612
+ }
613
+
614
+ const image = await loadImage(file);
615
+ const scale = Math.min(
616
+ 1,
617
+ maxPastedImageDimension / Math.max(image.naturalWidth || image.width, image.naturalHeight || image.height)
618
+ );
619
+ const width = Math.max(1, Math.round((image.naturalWidth || image.width) * scale));
620
+ const height = Math.max(1, Math.round((image.naturalHeight || image.height) * scale));
621
+ const canvas = document.createElement("canvas");
622
+ canvas.width = width;
623
+ canvas.height = height;
624
+ const context = canvas.getContext("2d");
625
+ if (!context) throw new Error("Canvas is unavailable.");
626
+ context.drawImage(image, 0, 0, width, height);
627
+ URL.revokeObjectURL(image.src);
628
+
629
+ const webp = canvas.toDataURL("image/webp", pastedImageQuality);
630
+ if (webp.startsWith("data:image/webp")) return webp;
631
+ return canvas.toDataURL("image/jpeg", pastedImageQuality);
632
+ }
633
+
634
+ function loadImage(file) {
635
+ return new Promise((resolve, reject) => {
636
+ const image = new Image();
637
+ image.onload = () => resolve(image);
638
+ image.onerror = reject;
639
+ image.src = URL.createObjectURL(file);
640
+ });
641
+ }
642
+
643
+ function readFileAsDataUrl(file) {
644
+ return new Promise((resolve, reject) => {
645
+ const reader = new FileReader();
646
+ reader.onload = () => resolve(String(reader.result || ""));
647
+ reader.onerror = reject;
648
+ reader.readAsDataURL(file);
649
+ });
650
+ }
651
+
652
  document.addEventListener("keydown", (event) => {
653
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "n") {
654
  event.preventDefault();
 
666
  if (getSelectedNote()) saveSelectedNote({ immediate: true });
667
  }
668
 
669
+ if (event.key === "Delete" && !els.editor.contains(document.activeElement)) {
670
  els.deleteNote.click();
671
  }
672
+
673
+ if (event.key === "Escape") {
674
+ hideEditorMenus();
675
+ }
676
  });
677
 
678
  async function loadState() {
 
1148
  function renderEditor() {
1149
  const note = getSelectedNote();
1150
  els.editorPane.classList.toggle("empty", !note);
 
1151
  els.saveNote.disabled = !note;
1152
  els.mobileSave.disabled = !note;
1153
+ els.editorMenuToggle.disabled = !note || state.previewMode;
1154
+ els.mobileEditorMenuToggle.disabled = !note || state.previewMode;
1155
  els.editorPreviewToggle.disabled = !note;
1156
  els.mobilePreviewToggle.disabled = !note;
1157
  els.mobileMore.disabled = !note;
 
1165
 
1166
  if (!note) {
1167
  els.mobileActionMenu.hidden = true;
1168
+ hideEditorMenus();
1169
+ setEditorContent("");
1170
+ tiptapEditor?.setEditable(false, false);
1171
  els.editor.hidden = false;
1172
  els.editedAt.textContent = "";
1173
  els.markdownPreview.hidden = true;
 
1178
  return;
1179
  }
1180
 
1181
+ setEditorContent(note.body);
1182
+ tiptapEditor?.setEditable(!state.previewMode, false);
 
1183
  els.editor.hidden = state.previewMode;
1184
  els.markdownPreview.hidden = !state.previewMode;
1185
  renderMarkdownPreview(note);
1186
  renderNoteFolderSelect(note);
1187
  updateEditorMeta(note);
1188
+ renderEditorMenuState();
1189
+ }
1190
+
1191
+ function setEditorContent(body) {
1192
+ if (!tiptapEditor) return;
1193
+ const currentHtml = normalizeEditorHtml(tiptapEditor.getHTML());
1194
+ if (currentHtml === normalizeEditorHtml(body) || (!isHtmlNoteBody(body) && currentHtml === normalizeEditorHtml(markdownToHtml(body)))) {
1195
+ return;
1196
+ }
1197
+
1198
+ syncingEditor = true;
1199
+ tiptapEditor.commands.setContent(body || "", {
1200
+ contentType: isHtmlNoteBody(body) ? "html" : "markdown",
1201
+ emitUpdate: false
1202
+ });
1203
+ syncingEditor = false;
1204
  }
1205
 
1206
  function renderNoteFolderSelect(note) {
 
1326
  const previousActiveFolder = state.activeFolder;
1327
  const previousSelectedId = state.selectedId;
1328
  const folder = {
1329
+ id: createId(),
1330
  name,
1331
  createdAt: now,
1332
  updatedAt: now,
 
1420
  return [{ id: "notes", name: "备忘录", version: 1 }];
1421
  }
1422
 
1423
+ function createId() {
1424
+ const bytes = new Uint8Array(16);
1425
+ if (globalThis.crypto?.getRandomValues) {
1426
+ globalThis.crypto.getRandomValues(bytes);
1427
+ } else {
1428
+ for (let index = 0; index < bytes.length; index += 1) {
1429
+ bytes[index] = Math.floor(Math.random() * 256);
1430
+ }
1431
+ }
1432
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
1433
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
1434
+ return stringifyUuid(bytes);
1435
+ }
1436
+
1437
  function renderIcons() {
1438
  window.lucide?.createIcons({
1439
  attrs: {
 
1444
 
1445
  function renderMarkdownPreview(note) {
1446
  if (!state.previewMode) return;
1447
+ els.markdownPreview.innerHTML = isHtmlNoteBody(note.body)
1448
+ ? sanitizeRichHtml(note.body)
1449
+ : markdownToHtml(note.body);
1450
+ }
1451
+
1452
+ function normalizeEditorHtml(value) {
1453
+ const html = String(value || "").trim();
1454
+ return html === "<p></p>" ? "" : html;
1455
+ }
1456
+
1457
+ function isHtmlNoteBody(value) {
1458
+ return /<\/?(p|h[1-6]|ul|ol|li|blockquote|pre|code|strong|em|s|u|a|img|br|hr)\b/i.test(String(value || ""));
1459
+ }
1460
+
1461
+ function sanitizeRichHtml(html) {
1462
+ const template = document.createElement("template");
1463
+ template.innerHTML = String(html || "");
1464
+ const allowedTags = new Set([
1465
+ "A",
1466
+ "BLOCKQUOTE",
1467
+ "BR",
1468
+ "CODE",
1469
+ "DIV",
1470
+ "EM",
1471
+ "H1",
1472
+ "H2",
1473
+ "H3",
1474
+ "H4",
1475
+ "H5",
1476
+ "H6",
1477
+ "HR",
1478
+ "IMG",
1479
+ "LI",
1480
+ "OL",
1481
+ "P",
1482
+ "PRE",
1483
+ "S",
1484
+ "STRONG",
1485
+ "U",
1486
+ "UL"
1487
+ ]);
1488
+
1489
+ [...template.content.querySelectorAll("*")].forEach((node) => {
1490
+ if (!allowedTags.has(node.tagName)) {
1491
+ node.replaceWith(...node.childNodes);
1492
+ return;
1493
+ }
1494
+
1495
+ const href = node.getAttribute("href") || "";
1496
+ const src = node.getAttribute("src") || "";
1497
+ const alt = node.getAttribute("alt") || "";
1498
+ const style = node.getAttribute("style") || "";
1499
+ const textAlign = style.match(/text-align:\s*(left|center|right)/i)?.[1] || "";
1500
+ const imageAlign = node.getAttribute("data-align") || node.getAttribute("align") || "";
1501
+ [...node.attributes].forEach((attribute) => node.removeAttribute(attribute.name));
1502
+ if (textAlign && ["P", "H1", "H2", "H3", "H4", "H5", "H6"].includes(node.tagName)) {
1503
+ node.setAttribute("style", `text-align: ${textAlign}`);
1504
+ }
1505
+ if (node.tagName === "A") {
1506
+ if (/^(https?:|mailto:)/i.test(href)) {
1507
+ node.setAttribute("href", href);
1508
+ node.setAttribute("target", "_blank");
1509
+ node.setAttribute("rel", "noreferrer");
1510
+ }
1511
+ }
1512
+ if (node.tagName === "IMG") {
1513
+ if (/^(data:image\/|https?:\/\/)/i.test(src)) {
1514
+ node.setAttribute("src", src);
1515
+ node.setAttribute("alt", alt);
1516
+ if (["left", "center", "right"].includes(imageAlign)) {
1517
+ node.setAttribute("data-align", imageAlign);
1518
+ }
1519
+ } else {
1520
+ node.remove();
1521
+ }
1522
+ }
1523
+ });
1524
+
1525
+ return template.innerHTML;
1526
  }
1527
 
1528
  function markdownToHtml(markdown) {
 
1613
  .replace(/`([^`]+)`/g, "<code>$1</code>")
1614
  .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
1615
  .replace(/\*([^*]+)\*/g, "<em>$1</em>")
1616
+ .replace(/!\[([^\]]*)\]\((data:image\/[^)\s]+|https?:\/\/[^)\s]+)\)/g, '<img src="$2" alt="$1">')
1617
  .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>');
1618
  }
1619
 
 
1627
  }
1628
 
1629
  function getTitle(note) {
1630
+ const firstLine = getNotePlainText(note).split(/\n/).find((line) => line.trim());
1631
  return firstLine ? firstLine.trim().slice(0, 80) : "新建笔记";
1632
  }
1633
 
1634
  function getPreview(note) {
1635
+ const lines = getNotePlainText(note).split(/\n/).map((line) => line.trim()).filter(Boolean);
1636
  const text = lines.slice(1).join(" ") || "无附加文本";
1637
  return text.slice(0, 140);
1638
  }
1639
 
1640
+ function getNotePlainText(note) {
1641
+ if (!isHtmlNoteBody(note.body)) return note.body;
1642
+ const template = document.createElement("template");
1643
+ template.innerHTML = sanitizeRichHtml(note.body);
1644
+ const imageCount = template.content.querySelectorAll("img").length;
1645
+ const blockSelector = "p,h1,h2,h3,h4,h5,h6,li,blockquote,pre";
1646
+ const blocks = [...template.content.querySelectorAll(blockSelector)]
1647
+ .map((node) => node.textContent.trim())
1648
+ .filter(Boolean);
1649
+ const text = blocks.length ? blocks.join("\n") : template.content.textContent || "";
1650
+ return `${text}${imageCount ? `\n${imageCount} 张图片` : ""}`;
1651
+ }
1652
+
1653
  function formatListDate(value) {
1654
  const date = new Date(value);
1655
  const today = new Date();
assets/app.bundle.js ADDED
The diff for this file is too large to render. See raw diff
 
index.html CHANGED
@@ -120,6 +120,23 @@
120
  <time id="editedAt"></time>
121
  <span class="save-status" id="saveStatus" aria-live="polite"></span>
122
  <select id="noteFolderSelect" aria-label="移动笔记到文件夹"></select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  <button class="editor-preview-button" id="editorPreviewToggle" type="button" aria-label="Markdown 预览" title="Markdown 预览">
124
  <i data-lucide="eye" aria-hidden="true"></i>
125
  </button>
@@ -127,7 +144,7 @@
127
  <i data-lucide="save" aria-hidden="true"></i>
128
  </button>
129
  </div>
130
- <textarea id="editor" spellcheck="true" aria-label="笔记正文"></textarea>
131
  <article class="markdown-preview" id="markdownPreview" hidden></article>
132
  <div class="empty-state" id="emptyState">
133
  <strong>没有选择笔记</strong>
@@ -142,6 +159,23 @@
142
  <button class="mobile-edit-button" id="mobileEdit" type="button" aria-label="新建笔记" title="新建笔记">
143
  <i data-lucide="square-pen" aria-hidden="true"></i>
144
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  <button class="mobile-preview-button" id="mobilePreviewToggle" type="button" aria-label="Markdown 预览" title="Markdown 预览">
146
  <i data-lucide="eye" aria-hidden="true"></i>
147
  </button>
@@ -152,6 +186,6 @@
152
  </div>
153
 
154
  <script src="/node_modules/lucide/dist/umd/lucide.js"></script>
155
- <script type="module" src="app.js"></script>
156
  </body>
157
  </html>
 
120
  <time id="editedAt"></time>
121
  <span class="save-status" id="saveStatus" aria-live="polite"></span>
122
  <select id="noteFolderSelect" aria-label="移动笔记到文件夹"></select>
123
+ <div class="editor-menu-wrap">
124
+ <button class="editor-menu-button" id="editorMenuToggle" type="button" aria-label="编辑菜单" title="编辑菜单">
125
+ <i data-lucide="type" aria-hidden="true"></i>
126
+ </button>
127
+ <div class="editor-format-menu" id="editorFormatMenu" hidden>
128
+ <button type="button" data-editor-command="paragraph" aria-label="正文" title="正文"><i data-lucide="pilcrow" aria-hidden="true"></i></button>
129
+ <button type="button" data-editor-command="heading1" aria-label="一级标题" title="一级标题"><i data-lucide="heading-1" aria-hidden="true"></i></button>
130
+ <button type="button" data-editor-command="heading2" aria-label="二级标题" title="二级标题"><i data-lucide="heading-2" aria-hidden="true"></i></button>
131
+ <button type="button" data-editor-command="bold" aria-label="加粗" title="加粗"><i data-lucide="bold" aria-hidden="true"></i></button>
132
+ <button type="button" data-editor-command="italic" aria-label="斜体" title="斜体"><i data-lucide="italic" aria-hidden="true"></i></button>
133
+ <button type="button" data-editor-command="bulletList" aria-label="无序列表" title="无序列表"><i data-lucide="list" aria-hidden="true"></i></button>
134
+ <button type="button" data-editor-command="orderedList" aria-label="有序列表" title="有序列表"><i data-lucide="list-ordered" aria-hidden="true"></i></button>
135
+ <button type="button" data-editor-command="alignLeft" aria-label="左对齐" title="左对齐"><i data-lucide="align-left" aria-hidden="true"></i></button>
136
+ <button type="button" data-editor-command="alignCenter" aria-label="居中" title="居中"><i data-lucide="align-center" aria-hidden="true"></i></button>
137
+ <button type="button" data-editor-command="alignRight" aria-label="右对齐" title="右对齐"><i data-lucide="align-right" aria-hidden="true"></i></button>
138
+ </div>
139
+ </div>
140
  <button class="editor-preview-button" id="editorPreviewToggle" type="button" aria-label="Markdown 预览" title="Markdown 预览">
141
  <i data-lucide="eye" aria-hidden="true"></i>
142
  </button>
 
144
  <i data-lucide="save" aria-hidden="true"></i>
145
  </button>
146
  </div>
147
+ <div id="editor" spellcheck="true" aria-label="笔记正文"></div>
148
  <article class="markdown-preview" id="markdownPreview" hidden></article>
149
  <div class="empty-state" id="emptyState">
150
  <strong>没有选择笔记</strong>
 
159
  <button class="mobile-edit-button" id="mobileEdit" type="button" aria-label="新建笔记" title="新建笔记">
160
  <i data-lucide="square-pen" aria-hidden="true"></i>
161
  </button>
162
+ <div class="mobile-editor-menu-wrap">
163
+ <button class="mobile-editor-menu-button" id="mobileEditorMenuToggle" type="button" aria-label="编辑菜单" title="编辑菜单">
164
+ <i data-lucide="type" aria-hidden="true"></i>
165
+ </button>
166
+ <div class="mobile-editor-format-menu" id="mobileEditorFormatMenu" hidden>
167
+ <button type="button" data-editor-command="paragraph" aria-label="正文" title="正文"><i data-lucide="pilcrow" aria-hidden="true"></i></button>
168
+ <button type="button" data-editor-command="heading1" aria-label="一级标题" title="一级标题"><i data-lucide="heading-1" aria-hidden="true"></i></button>
169
+ <button type="button" data-editor-command="heading2" aria-label="二级标题" title="二级标题"><i data-lucide="heading-2" aria-hidden="true"></i></button>
170
+ <button type="button" data-editor-command="bold" aria-label="加粗" title="加粗"><i data-lucide="bold" aria-hidden="true"></i></button>
171
+ <button type="button" data-editor-command="italic" aria-label="斜体" title="斜体"><i data-lucide="italic" aria-hidden="true"></i></button>
172
+ <button type="button" data-editor-command="bulletList" aria-label="无序列表" title="无序列表"><i data-lucide="list" aria-hidden="true"></i></button>
173
+ <button type="button" data-editor-command="orderedList" aria-label="有序列表" title="有序列表"><i data-lucide="list-ordered" aria-hidden="true"></i></button>
174
+ <button type="button" data-editor-command="alignLeft" aria-label="左对齐" title="左对齐"><i data-lucide="align-left" aria-hidden="true"></i></button>
175
+ <button type="button" data-editor-command="alignCenter" aria-label="居中" title="居中"><i data-lucide="align-center" aria-hidden="true"></i></button>
176
+ <button type="button" data-editor-command="alignRight" aria-label="右对齐" title="右对齐"><i data-lucide="align-right" aria-hidden="true"></i></button>
177
+ </div>
178
+ </div>
179
  <button class="mobile-preview-button" id="mobilePreviewToggle" type="button" aria-label="Markdown 预览" title="Markdown 预览">
180
  <i data-lucide="eye" aria-hidden="true"></i>
181
  </button>
 
186
  </div>
187
 
188
  <script src="/node_modules/lucide/dist/umd/lucide.js"></script>
189
+ <script type="module" src="assets/app.bundle.js"></script>
190
  </body>
191
  </html>
package.json CHANGED
@@ -1,12 +1,21 @@
1
  {
2
  "type": "module",
3
  "scripts": {
4
- "dev": "node server.mjs",
 
5
  "seed:mock": "node scripts/seed-mock.mjs"
6
  },
7
  "dependencies": {
8
  "@hono/node-server": "^2.0.4",
 
 
 
 
 
 
9
  "hono": "^4.12.23",
10
- "lucide": "^1.16.0"
 
 
11
  }
12
  }
 
1
  {
2
  "type": "module",
3
  "scripts": {
4
+ "build": "vite build",
5
+ "dev": "pnpm run build && node server.mjs",
6
  "seed:mock": "node scripts/seed-mock.mjs"
7
  },
8
  "dependencies": {
9
  "@hono/node-server": "^2.0.4",
10
+ "@tiptap/core": "^3.26.1",
11
+ "@tiptap/extension-image": "^3.26.1",
12
+ "@tiptap/extension-text-align": "^3.26.1",
13
+ "@tiptap/markdown": "^3.26.1",
14
+ "@tiptap/pm": "^3.26.1",
15
+ "@tiptap/starter-kit": "^3.26.1",
16
  "hono": "^4.12.23",
17
+ "lucide": "^1.16.0",
18
+ "uuid": "^14.0.0",
19
+ "vite": "^8.0.16"
20
  }
21
  }
pnpm-lock.yaml CHANGED
@@ -11,34 +11,969 @@ importers:
11
  '@hono/node-server':
12
  specifier: ^2.0.4
13
  version: 2.0.4(hono@4.12.23)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  hono:
15
  specifier: ^4.12.23
16
  version: 4.12.23
17
  lucide:
18
  specifier: ^1.16.0
19
  version: 1.16.0
 
 
 
 
 
 
20
 
21
  packages:
22
 
 
 
 
 
 
 
 
 
 
23
  '@hono/node-server@2.0.4':
24
  resolution: {integrity: sha512-Ut3y0dMMPWy6bZ2kVfx25EOVbZlm15dhF4mOsezMlhpNHy+4MkU1qN9Y6lnruYi4wPmFzimGX2X7LF/FwHli4A==}
25
  engines: {node: '>=20'}
26
  peerDependencies:
27
  hono: ^4
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  hono@4.12.23:
30
  resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==}
31
  engines: {node: '>=16.9.0'}
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  lucide@1.16.0:
34
  resolution: {integrity: sha512-20QvduCJTB7e7K9WVvoLBuKPsYZ8d6ptwe9PIdTFiZmjVTPdFWQreBNyNCM9QQtGnqHR0PLiEQdxyhYsL1LdoA==}
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  snapshots:
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  '@hono/node-server@2.0.4(hono@4.12.23)':
39
  dependencies:
40
  hono: 4.12.23
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  hono@4.12.23: {}
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  lucide@1.16.0: {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  '@hono/node-server':
12
  specifier: ^2.0.4
13
  version: 2.0.4(hono@4.12.23)
14
+ '@tiptap/core':
15
+ specifier: ^3.26.1
16
+ version: 3.26.1(@tiptap/pm@3.26.1)
17
+ '@tiptap/extension-image':
18
+ specifier: ^3.26.1
19
+ version: 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
20
+ '@tiptap/extension-text-align':
21
+ specifier: ^3.26.1
22
+ version: 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
23
+ '@tiptap/markdown':
24
+ specifier: ^3.26.1
25
+ version: 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
26
+ '@tiptap/pm':
27
+ specifier: ^3.26.1
28
+ version: 3.26.1
29
+ '@tiptap/starter-kit':
30
+ specifier: ^3.26.1
31
+ version: 3.26.1
32
  hono:
33
  specifier: ^4.12.23
34
  version: 4.12.23
35
  lucide:
36
  specifier: ^1.16.0
37
  version: 1.16.0
38
+ uuid:
39
+ specifier: ^14.0.0
40
+ version: 14.0.0
41
+ vite:
42
+ specifier: ^8.0.16
43
+ version: 8.0.16
44
 
45
  packages:
46
 
47
+ '@emnapi/core@1.10.0':
48
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
49
+
50
+ '@emnapi/runtime@1.10.0':
51
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
52
+
53
+ '@emnapi/wasi-threads@1.2.1':
54
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
55
+
56
  '@hono/node-server@2.0.4':
57
  resolution: {integrity: sha512-Ut3y0dMMPWy6bZ2kVfx25EOVbZlm15dhF4mOsezMlhpNHy+4MkU1qN9Y6lnruYi4wPmFzimGX2X7LF/FwHli4A==}
58
  engines: {node: '>=20'}
59
  peerDependencies:
60
  hono: ^4
61
 
62
+ '@napi-rs/wasm-runtime@1.1.5':
63
+ resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
64
+ peerDependencies:
65
+ '@emnapi/core': ^1.7.1
66
+ '@emnapi/runtime': ^1.7.1
67
+
68
+ '@oxc-project/types@0.133.0':
69
+ resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
70
+
71
+ '@rolldown/binding-android-arm64@1.0.3':
72
+ resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
73
+ engines: {node: ^20.19.0 || >=22.12.0}
74
+ cpu: [arm64]
75
+ os: [android]
76
+
77
+ '@rolldown/binding-darwin-arm64@1.0.3':
78
+ resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==}
79
+ engines: {node: ^20.19.0 || >=22.12.0}
80
+ cpu: [arm64]
81
+ os: [darwin]
82
+
83
+ '@rolldown/binding-darwin-x64@1.0.3':
84
+ resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==}
85
+ engines: {node: ^20.19.0 || >=22.12.0}
86
+ cpu: [x64]
87
+ os: [darwin]
88
+
89
+ '@rolldown/binding-freebsd-x64@1.0.3':
90
+ resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==}
91
+ engines: {node: ^20.19.0 || >=22.12.0}
92
+ cpu: [x64]
93
+ os: [freebsd]
94
+
95
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
96
+ resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==}
97
+ engines: {node: ^20.19.0 || >=22.12.0}
98
+ cpu: [arm]
99
+ os: [linux]
100
+
101
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
102
+ resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==}
103
+ engines: {node: ^20.19.0 || >=22.12.0}
104
+ cpu: [arm64]
105
+ os: [linux]
106
+
107
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
108
+ resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
109
+ engines: {node: ^20.19.0 || >=22.12.0}
110
+ cpu: [arm64]
111
+ os: [linux]
112
+
113
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
114
+ resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
115
+ engines: {node: ^20.19.0 || >=22.12.0}
116
+ cpu: [ppc64]
117
+ os: [linux]
118
+
119
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
120
+ resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
121
+ engines: {node: ^20.19.0 || >=22.12.0}
122
+ cpu: [s390x]
123
+ os: [linux]
124
+
125
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
126
+ resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
127
+ engines: {node: ^20.19.0 || >=22.12.0}
128
+ cpu: [x64]
129
+ os: [linux]
130
+
131
+ '@rolldown/binding-linux-x64-musl@1.0.3':
132
+ resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
133
+ engines: {node: ^20.19.0 || >=22.12.0}
134
+ cpu: [x64]
135
+ os: [linux]
136
+
137
+ '@rolldown/binding-openharmony-arm64@1.0.3':
138
+ resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
139
+ engines: {node: ^20.19.0 || >=22.12.0}
140
+ cpu: [arm64]
141
+ os: [openharmony]
142
+
143
+ '@rolldown/binding-wasm32-wasi@1.0.3':
144
+ resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==}
145
+ engines: {node: ^20.19.0 || >=22.12.0}
146
+ cpu: [wasm32]
147
+
148
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
149
+ resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==}
150
+ engines: {node: ^20.19.0 || >=22.12.0}
151
+ cpu: [arm64]
152
+ os: [win32]
153
+
154
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
155
+ resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==}
156
+ engines: {node: ^20.19.0 || >=22.12.0}
157
+ cpu: [x64]
158
+ os: [win32]
159
+
160
+ '@rolldown/pluginutils@1.0.1':
161
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
162
+
163
+ '@tiptap/core@3.26.1':
164
+ resolution: {integrity: sha512-TX9PyPqBoix0qDLjtok/bddtdSy54QhzLVha405C07V+WySOpH3s/pWYkywehZQY0SQtcrcY4MNSCeQjCbA28A==}
165
+ peerDependencies:
166
+ '@tiptap/pm': 3.26.1
167
+
168
+ '@tiptap/extension-blockquote@3.26.1':
169
+ resolution: {integrity: sha512-WaKjKmUaadgvZDDBk9JOn/oidlOFr6booqJIWHGL5S0aUUTKHS19oGfKQq/l9Z1y1niaRePk0Y4fy/jxCnfKPA==}
170
+ peerDependencies:
171
+ '@tiptap/core': 3.26.1
172
+
173
+ '@tiptap/extension-bold@3.26.1':
174
+ resolution: {integrity: sha512-VIlF2sAiV6K009pcIDotfY8mvsPaq90dxeG9Q0ZIqfMD958TUCqjHw4MGYZf0/FgP12xksBfmcR7W312xgUf9Q==}
175
+ peerDependencies:
176
+ '@tiptap/core': 3.26.1
177
+
178
+ '@tiptap/extension-bullet-list@3.26.1':
179
+ resolution: {integrity: sha512-JB6bEJJHxXNAXEXTIAN3/j70p1ARHdeMfhzshGZswWKUWtDibTCrspIp7p1VNeiuVtJ/HB6PpFkGi7yWtQ3RTg==}
180
+ peerDependencies:
181
+ '@tiptap/extension-list': 3.26.1
182
+
183
+ '@tiptap/extension-code-block@3.26.1':
184
+ resolution: {integrity: sha512-NY7SYqcrqDVYTSWyaNGdSfCims6pOHoRQ2Rh4DEFb/rb8gLVkqbLZhcHzQCVfinlPqgV3xWF6cYMORwmnlBkXQ==}
185
+ peerDependencies:
186
+ '@tiptap/core': 3.26.1
187
+ '@tiptap/pm': 3.26.1
188
+
189
+ '@tiptap/extension-code@3.26.1':
190
+ resolution: {integrity: sha512-t9/VR5k3rGPyhcGau9YvVgaAQ+nP9R9WzS996bQQ7GIrMOTSXb0FWwoQFBiYl83V6VA16Tlj/oScC7SFlA8lvA==}
191
+ peerDependencies:
192
+ '@tiptap/core': 3.26.1
193
+
194
+ '@tiptap/extension-document@3.26.1':
195
+ resolution: {integrity: sha512-6W2vZjvi0Mv+4xEtwMDGhWwo7FotWR6eKfmntmduvehWevFpMxOKcTtyotjLigfZv738y50YWmvbaPuAPJG3BA==}
196
+ peerDependencies:
197
+ '@tiptap/core': 3.26.1
198
+
199
+ '@tiptap/extension-dropcursor@3.26.1':
200
+ resolution: {integrity: sha512-eVq3BvFIa3YD+pBIlj1i72vYEixlegGVKHnSYiVF2ovkQOSAH9sca7pkq6WgV1sMTCyWCU8e+WznTqtydvHUWA==}
201
+ peerDependencies:
202
+ '@tiptap/extensions': 3.26.1
203
+
204
+ '@tiptap/extension-gapcursor@3.26.1':
205
+ resolution: {integrity: sha512-BWW1yMQQA4TbEU0LLK+4cd9ebLTuZG5KjHwFMBRD/bGiRW9V1gTWFsCqThBbczcANoQiZK9pn5/4Ad/rGM3HUg==}
206
+ peerDependencies:
207
+ '@tiptap/extensions': 3.26.1
208
+
209
+ '@tiptap/extension-hard-break@3.26.1':
210
+ resolution: {integrity: sha512-gzNb1e/fK6HN+ko1axsrasjK7F1q0Bnm0G4ZY/0eq7pV7s1wZuwoCiGbvUx/9LCFKRV6+94FTqlb0A3NbYN36g==}
211
+ peerDependencies:
212
+ '@tiptap/core': 3.26.1
213
+
214
+ '@tiptap/extension-heading@3.26.1':
215
+ resolution: {integrity: sha512-eRlv9XxzUL8FobKAiF1WjP35CT2QpbcxxeyYFF7BmGEONvKI7r5g7JGwyGli4Cvclh70h8w6JuoXSmGUVEU65A==}
216
+ peerDependencies:
217
+ '@tiptap/core': 3.26.1
218
+
219
+ '@tiptap/extension-horizontal-rule@3.26.1':
220
+ resolution: {integrity: sha512-l9lPZYeSmY90y/2GkQcKaICFD5Atr8sx2SzJGkQzpNC9tRxZXyAHnfJE3OjBkspuGzjWIN0DimxBj4ibz58sKw==}
221
+ peerDependencies:
222
+ '@tiptap/core': 3.26.1
223
+ '@tiptap/pm': 3.26.1
224
+
225
+ '@tiptap/extension-image@3.26.1':
226
+ resolution: {integrity: sha512-IjoT+kRK4a1sTImvUz257yfk5l9kMxXxfxCfix5AUKdiWyn8SGUjJZapLICcZVY05UDqXmwsBvBK9lHkKX5ERg==}
227
+ peerDependencies:
228
+ '@tiptap/core': 3.26.1
229
+
230
+ '@tiptap/extension-italic@3.26.1':
231
+ resolution: {integrity: sha512-cLKYvOLToWEkJkAPspgIZ/PYDzAxacLm1VWcAq1tO1QDQCDe2Kw+y/zsGlyYEq/aKsAgpp4JNopBwAXRXxt2/A==}
232
+ peerDependencies:
233
+ '@tiptap/core': 3.26.1
234
+
235
+ '@tiptap/extension-link@3.26.1':
236
+ resolution: {integrity: sha512-aLLGLgikuhLFHRbjfUC6D4gRg+NUty4uhW7YkyVl8AxxPME47dPbCOX4H6uLCjEZcn3WnfNuCTr6HCTl0KEmGA==}
237
+ peerDependencies:
238
+ '@tiptap/core': 3.26.1
239
+ '@tiptap/pm': 3.26.1
240
+
241
+ '@tiptap/extension-list-item@3.26.1':
242
+ resolution: {integrity: sha512-5gLXJUiP763NA6i4HgrtcwUDXPP8820hsaBQyF1Y1VsXNi02uW9FVLe3RZK8jF0NZUNh9CqD0gogYJCbKOUU8A==}
243
+ peerDependencies:
244
+ '@tiptap/extension-list': 3.26.1
245
+
246
+ '@tiptap/extension-list-keymap@3.26.1':
247
+ resolution: {integrity: sha512-EReSayePO6SIxtRbxx+7KfBQreWHvoZmMb3O/RemfT8W6J0hCG5N/Rh8Z12+YZOnCDRXJ4RzFpAikYka3E54jQ==}
248
+ peerDependencies:
249
+ '@tiptap/extension-list': 3.26.1
250
+
251
+ '@tiptap/extension-list@3.26.1':
252
+ resolution: {integrity: sha512-06nOjnyXpzMO8Ys5k3IbYsDsKib1mv2OtaxBYX1/1uvRyOKwUX5tqDLb/qigic0LIANNL73lkNC8Z8XPeG4Tkg==}
253
+ peerDependencies:
254
+ '@tiptap/core': 3.26.1
255
+ '@tiptap/pm': 3.26.1
256
+
257
+ '@tiptap/extension-ordered-list@3.26.1':
258
+ resolution: {integrity: sha512-LeFPeFwb7ylkQVuuaHj+niu7WhWHpjDOi1GKZJE/ohOa2lgt7P221HMqhUzPiDlXOExN72oWTNmXUlT0ymCTkw==}
259
+ peerDependencies:
260
+ '@tiptap/extension-list': 3.26.1
261
+
262
+ '@tiptap/extension-paragraph@3.26.1':
263
+ resolution: {integrity: sha512-OkBeYUNM3eTzjm3z6IcC3NHryOX8g3eGNI86P/B+tFoFQSRuzLsKZU50ARCfIiLLg812NjcqujeJ1eX3BKDZrw==}
264
+ peerDependencies:
265
+ '@tiptap/core': 3.26.1
266
+
267
+ '@tiptap/extension-strike@3.26.1':
268
+ resolution: {integrity: sha512-7hmQ2mBsA+75GRrJIKYxb+10H23mblEQSGGsv9Ptl7JLaGmj+8sv2HGQGSUT9QBiBVprxaYTqyWFXQC9akfLWg==}
269
+ peerDependencies:
270
+ '@tiptap/core': 3.26.1
271
+
272
+ '@tiptap/extension-text-align@3.26.1':
273
+ resolution: {integrity: sha512-Ptbu1FyASFqIrbw/p9D6zLt8G7seDh4X408qU9FIrun7PE79o+amB5LWI/SzHFuVk3OMI+g7jE/mMDhNUYsqVA==}
274
+ peerDependencies:
275
+ '@tiptap/core': 3.26.1
276
+
277
+ '@tiptap/extension-text@3.26.1':
278
+ resolution: {integrity: sha512-Gocui5WvcCCJJIX17gdOVCSdYi5H4fDwaR0qkMAUZPq5kJCdrfl+vNpt8BTt53Bk+/QumiUW21fhQ184w7RoeQ==}
279
+ peerDependencies:
280
+ '@tiptap/core': 3.26.1
281
+
282
+ '@tiptap/extension-underline@3.26.1':
283
+ resolution: {integrity: sha512-HUHtQ+DRWDM0opW7Nk3YQwrLzw876hMU7cr1X/ZTG+8Bp+AKHihlwU+bqrPgG5St0mqASyUEhHQ/vK5PlnUYOQ==}
284
+ peerDependencies:
285
+ '@tiptap/core': 3.26.1
286
+
287
+ '@tiptap/extensions@3.26.1':
288
+ resolution: {integrity: sha512-PmRaoe6bebTgz/ZQrjmzwZMST1d9js9ZTiKnUXeXl3Fm+V5U/c3TbbKDfqmL63qPQdjtShDMHi9tYuv+c77OFQ==}
289
+ peerDependencies:
290
+ '@tiptap/core': 3.26.1
291
+ '@tiptap/pm': 3.26.1
292
+
293
+ '@tiptap/markdown@3.26.1':
294
+ resolution: {integrity: sha512-PpAi3hZqZnb7IsCiRnD6rZfauj8O19fvSzRRdx99Uwx14VnhznbO3WKpUMyleuLz5KjClidqqtKMQWDM6Wt0dA==}
295
+ peerDependencies:
296
+ '@tiptap/core': 3.26.1
297
+ '@tiptap/pm': 3.26.1
298
+
299
+ '@tiptap/pm@3.26.1':
300
+ resolution: {integrity: sha512-48cJQRbvr9Ux0+IgM1BR5vOLU5hkC+n+uerdQy2JjrIRKpYE/huU8fQFm6PoRppoKYfilklzb29elsQ+n2TA+g==}
301
+
302
+ '@tiptap/starter-kit@3.26.1':
303
+ resolution: {integrity: sha512-A0zsvwGU9exLND34F8e8KqUXFSfs835tNN+VC+ZT3yNeaO/WXnlh/Cgal1F6pHHbcxy7RV2CRwJU5S3cWLPxrA==}
304
+
305
+ '@tybys/wasm-util@0.10.2':
306
+ resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
307
+
308
+ detect-libc@2.1.2:
309
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
310
+ engines: {node: '>=8'}
311
+
312
+ fdir@6.5.0:
313
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
314
+ engines: {node: '>=12.0.0'}
315
+ peerDependencies:
316
+ picomatch: ^3 || ^4
317
+ peerDependenciesMeta:
318
+ picomatch:
319
+ optional: true
320
+
321
+ fsevents@2.3.3:
322
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
323
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
324
+ os: [darwin]
325
+
326
  hono@4.12.23:
327
  resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==}
328
  engines: {node: '>=16.9.0'}
329
 
330
+ lightningcss-android-arm64@1.32.0:
331
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
332
+ engines: {node: '>= 12.0.0'}
333
+ cpu: [arm64]
334
+ os: [android]
335
+
336
+ lightningcss-darwin-arm64@1.32.0:
337
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
338
+ engines: {node: '>= 12.0.0'}
339
+ cpu: [arm64]
340
+ os: [darwin]
341
+
342
+ lightningcss-darwin-x64@1.32.0:
343
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
344
+ engines: {node: '>= 12.0.0'}
345
+ cpu: [x64]
346
+ os: [darwin]
347
+
348
+ lightningcss-freebsd-x64@1.32.0:
349
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
350
+ engines: {node: '>= 12.0.0'}
351
+ cpu: [x64]
352
+ os: [freebsd]
353
+
354
+ lightningcss-linux-arm-gnueabihf@1.32.0:
355
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
356
+ engines: {node: '>= 12.0.0'}
357
+ cpu: [arm]
358
+ os: [linux]
359
+
360
+ lightningcss-linux-arm64-gnu@1.32.0:
361
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
362
+ engines: {node: '>= 12.0.0'}
363
+ cpu: [arm64]
364
+ os: [linux]
365
+
366
+ lightningcss-linux-arm64-musl@1.32.0:
367
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
368
+ engines: {node: '>= 12.0.0'}
369
+ cpu: [arm64]
370
+ os: [linux]
371
+
372
+ lightningcss-linux-x64-gnu@1.32.0:
373
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
374
+ engines: {node: '>= 12.0.0'}
375
+ cpu: [x64]
376
+ os: [linux]
377
+
378
+ lightningcss-linux-x64-musl@1.32.0:
379
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
380
+ engines: {node: '>= 12.0.0'}
381
+ cpu: [x64]
382
+ os: [linux]
383
+
384
+ lightningcss-win32-arm64-msvc@1.32.0:
385
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
386
+ engines: {node: '>= 12.0.0'}
387
+ cpu: [arm64]
388
+ os: [win32]
389
+
390
+ lightningcss-win32-x64-msvc@1.32.0:
391
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
392
+ engines: {node: '>= 12.0.0'}
393
+ cpu: [x64]
394
+ os: [win32]
395
+
396
+ lightningcss@1.32.0:
397
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
398
+ engines: {node: '>= 12.0.0'}
399
+
400
+ linkifyjs@4.3.3:
401
+ resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==}
402
+
403
  lucide@1.16.0:
404
  resolution: {integrity: sha512-20QvduCJTB7e7K9WVvoLBuKPsYZ8d6ptwe9PIdTFiZmjVTPdFWQreBNyNCM9QQtGnqHR0PLiEQdxyhYsL1LdoA==}
405
 
406
+ marked@17.0.6:
407
+ resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==}
408
+ engines: {node: '>= 20'}
409
+ hasBin: true
410
+
411
+ nanoid@3.3.12:
412
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
413
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
414
+ hasBin: true
415
+
416
+ orderedmap@2.1.1:
417
+ resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
418
+
419
+ picocolors@1.1.1:
420
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
421
+
422
+ picomatch@4.0.4:
423
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
424
+ engines: {node: '>=12'}
425
+
426
+ postcss@8.5.15:
427
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
428
+ engines: {node: ^10 || ^12 || >=14}
429
+
430
+ prosemirror-changeset@2.4.1:
431
+ resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==}
432
+
433
+ prosemirror-commands@1.7.1:
434
+ resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
435
+
436
+ prosemirror-dropcursor@1.8.2:
437
+ resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
438
+
439
+ prosemirror-gapcursor@1.4.1:
440
+ resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
441
+
442
+ prosemirror-history@1.5.0:
443
+ resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
444
+
445
+ prosemirror-inputrules@1.5.1:
446
+ resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
447
+
448
+ prosemirror-keymap@1.2.3:
449
+ resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
450
+
451
+ prosemirror-model@1.25.8:
452
+ resolution: {integrity: sha512-BswA4BLSFEiORV6Vjj/yZBXDbos1zTEnhyeSSgT8psGFhstQS7UJ8/WOLiDos9Byaee27+tml0/DuMNxYR84zg==}
453
+
454
+ prosemirror-schema-list@1.5.1:
455
+ resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
456
+
457
+ prosemirror-state@1.4.4:
458
+ resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
459
+
460
+ prosemirror-tables@1.8.5:
461
+ resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
462
+
463
+ prosemirror-transform@1.12.0:
464
+ resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
465
+
466
+ prosemirror-view@1.41.9:
467
+ resolution: {integrity: sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==}
468
+
469
+ rolldown@1.0.3:
470
+ resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
471
+ engines: {node: ^20.19.0 || >=22.12.0}
472
+ hasBin: true
473
+
474
+ rope-sequence@1.3.4:
475
+ resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
476
+
477
+ source-map-js@1.2.1:
478
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
479
+ engines: {node: '>=0.10.0'}
480
+
481
+ tinyglobby@0.2.17:
482
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
483
+ engines: {node: '>=12.0.0'}
484
+
485
+ tslib@2.8.1:
486
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
487
+
488
+ uuid@14.0.0:
489
+ resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
490
+ hasBin: true
491
+
492
+ vite@8.0.16:
493
+ resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==}
494
+ engines: {node: ^20.19.0 || >=22.12.0}
495
+ hasBin: true
496
+ peerDependencies:
497
+ '@types/node': ^20.19.0 || >=22.12.0
498
+ '@vitejs/devtools': ^0.1.18
499
+ esbuild: ^0.27.0 || ^0.28.0
500
+ jiti: '>=1.21.0'
501
+ less: ^4.0.0
502
+ sass: ^1.70.0
503
+ sass-embedded: ^1.70.0
504
+ stylus: '>=0.54.8'
505
+ sugarss: ^5.0.0
506
+ terser: ^5.16.0
507
+ tsx: ^4.8.1
508
+ yaml: ^2.4.2
509
+ peerDependenciesMeta:
510
+ '@types/node':
511
+ optional: true
512
+ '@vitejs/devtools':
513
+ optional: true
514
+ esbuild:
515
+ optional: true
516
+ jiti:
517
+ optional: true
518
+ less:
519
+ optional: true
520
+ sass:
521
+ optional: true
522
+ sass-embedded:
523
+ optional: true
524
+ stylus:
525
+ optional: true
526
+ sugarss:
527
+ optional: true
528
+ terser:
529
+ optional: true
530
+ tsx:
531
+ optional: true
532
+ yaml:
533
+ optional: true
534
+
535
+ w3c-keyname@2.2.8:
536
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
537
+
538
  snapshots:
539
 
540
+ '@emnapi/core@1.10.0':
541
+ dependencies:
542
+ '@emnapi/wasi-threads': 1.2.1
543
+ tslib: 2.8.1
544
+ optional: true
545
+
546
+ '@emnapi/runtime@1.10.0':
547
+ dependencies:
548
+ tslib: 2.8.1
549
+ optional: true
550
+
551
+ '@emnapi/wasi-threads@1.2.1':
552
+ dependencies:
553
+ tslib: 2.8.1
554
+ optional: true
555
+
556
  '@hono/node-server@2.0.4(hono@4.12.23)':
557
  dependencies:
558
  hono: 4.12.23
559
 
560
+ '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
561
+ dependencies:
562
+ '@emnapi/core': 1.10.0
563
+ '@emnapi/runtime': 1.10.0
564
+ '@tybys/wasm-util': 0.10.2
565
+ optional: true
566
+
567
+ '@oxc-project/types@0.133.0': {}
568
+
569
+ '@rolldown/binding-android-arm64@1.0.3':
570
+ optional: true
571
+
572
+ '@rolldown/binding-darwin-arm64@1.0.3':
573
+ optional: true
574
+
575
+ '@rolldown/binding-darwin-x64@1.0.3':
576
+ optional: true
577
+
578
+ '@rolldown/binding-freebsd-x64@1.0.3':
579
+ optional: true
580
+
581
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
582
+ optional: true
583
+
584
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
585
+ optional: true
586
+
587
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
588
+ optional: true
589
+
590
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
591
+ optional: true
592
+
593
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
594
+ optional: true
595
+
596
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
597
+ optional: true
598
+
599
+ '@rolldown/binding-linux-x64-musl@1.0.3':
600
+ optional: true
601
+
602
+ '@rolldown/binding-openharmony-arm64@1.0.3':
603
+ optional: true
604
+
605
+ '@rolldown/binding-wasm32-wasi@1.0.3':
606
+ dependencies:
607
+ '@emnapi/core': 1.10.0
608
+ '@emnapi/runtime': 1.10.0
609
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
610
+ optional: true
611
+
612
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
613
+ optional: true
614
+
615
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
616
+ optional: true
617
+
618
+ '@rolldown/pluginutils@1.0.1': {}
619
+
620
+ '@tiptap/core@3.26.1(@tiptap/pm@3.26.1)':
621
+ dependencies:
622
+ '@tiptap/pm': 3.26.1
623
+
624
+ '@tiptap/extension-blockquote@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
625
+ dependencies:
626
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
627
+
628
+ '@tiptap/extension-bold@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
629
+ dependencies:
630
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
631
+
632
+ '@tiptap/extension-bullet-list@3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
633
+ dependencies:
634
+ '@tiptap/extension-list': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
635
+
636
+ '@tiptap/extension-code-block@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
637
+ dependencies:
638
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
639
+ '@tiptap/pm': 3.26.1
640
+
641
+ '@tiptap/extension-code@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
642
+ dependencies:
643
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
644
+
645
+ '@tiptap/extension-document@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
646
+ dependencies:
647
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
648
+
649
+ '@tiptap/extension-dropcursor@3.26.1(@tiptap/extensions@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
650
+ dependencies:
651
+ '@tiptap/extensions': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
652
+
653
+ '@tiptap/extension-gapcursor@3.26.1(@tiptap/extensions@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
654
+ dependencies:
655
+ '@tiptap/extensions': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
656
+
657
+ '@tiptap/extension-hard-break@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
658
+ dependencies:
659
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
660
+
661
+ '@tiptap/extension-heading@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
662
+ dependencies:
663
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
664
+
665
+ '@tiptap/extension-horizontal-rule@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
666
+ dependencies:
667
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
668
+ '@tiptap/pm': 3.26.1
669
+
670
+ '@tiptap/extension-image@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
671
+ dependencies:
672
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
673
+
674
+ '@tiptap/extension-italic@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
675
+ dependencies:
676
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
677
+
678
+ '@tiptap/extension-link@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
679
+ dependencies:
680
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
681
+ '@tiptap/pm': 3.26.1
682
+ linkifyjs: 4.3.3
683
+
684
+ '@tiptap/extension-list-item@3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
685
+ dependencies:
686
+ '@tiptap/extension-list': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
687
+
688
+ '@tiptap/extension-list-keymap@3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
689
+ dependencies:
690
+ '@tiptap/extension-list': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
691
+
692
+ '@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
693
+ dependencies:
694
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
695
+ '@tiptap/pm': 3.26.1
696
+
697
+ '@tiptap/extension-ordered-list@3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))':
698
+ dependencies:
699
+ '@tiptap/extension-list': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
700
+
701
+ '@tiptap/extension-paragraph@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
702
+ dependencies:
703
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
704
+
705
+ '@tiptap/extension-strike@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
706
+ dependencies:
707
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
708
+
709
+ '@tiptap/extension-text-align@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
710
+ dependencies:
711
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
712
+
713
+ '@tiptap/extension-text@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
714
+ dependencies:
715
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
716
+
717
+ '@tiptap/extension-underline@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))':
718
+ dependencies:
719
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
720
+
721
+ '@tiptap/extensions@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
722
+ dependencies:
723
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
724
+ '@tiptap/pm': 3.26.1
725
+
726
+ '@tiptap/markdown@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)':
727
+ dependencies:
728
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
729
+ '@tiptap/pm': 3.26.1
730
+ marked: 17.0.6
731
+
732
+ '@tiptap/pm@3.26.1':
733
+ dependencies:
734
+ prosemirror-changeset: 2.4.1
735
+ prosemirror-commands: 1.7.1
736
+ prosemirror-dropcursor: 1.8.2
737
+ prosemirror-gapcursor: 1.4.1
738
+ prosemirror-history: 1.5.0
739
+ prosemirror-inputrules: 1.5.1
740
+ prosemirror-keymap: 1.2.3
741
+ prosemirror-model: 1.25.8
742
+ prosemirror-schema-list: 1.5.1
743
+ prosemirror-state: 1.4.4
744
+ prosemirror-tables: 1.8.5
745
+ prosemirror-transform: 1.12.0
746
+ prosemirror-view: 1.41.9
747
+
748
+ '@tiptap/starter-kit@3.26.1':
749
+ dependencies:
750
+ '@tiptap/core': 3.26.1(@tiptap/pm@3.26.1)
751
+ '@tiptap/extension-blockquote': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
752
+ '@tiptap/extension-bold': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
753
+ '@tiptap/extension-bullet-list': 3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
754
+ '@tiptap/extension-code': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
755
+ '@tiptap/extension-code-block': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
756
+ '@tiptap/extension-document': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
757
+ '@tiptap/extension-dropcursor': 3.26.1(@tiptap/extensions@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
758
+ '@tiptap/extension-gapcursor': 3.26.1(@tiptap/extensions@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
759
+ '@tiptap/extension-hard-break': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
760
+ '@tiptap/extension-heading': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
761
+ '@tiptap/extension-horizontal-rule': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
762
+ '@tiptap/extension-italic': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
763
+ '@tiptap/extension-link': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
764
+ '@tiptap/extension-list': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
765
+ '@tiptap/extension-list-item': 3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
766
+ '@tiptap/extension-list-keymap': 3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
767
+ '@tiptap/extension-ordered-list': 3.26.1(@tiptap/extension-list@3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1))
768
+ '@tiptap/extension-paragraph': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
769
+ '@tiptap/extension-strike': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
770
+ '@tiptap/extension-text': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
771
+ '@tiptap/extension-underline': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))
772
+ '@tiptap/extensions': 3.26.1(@tiptap/core@3.26.1(@tiptap/pm@3.26.1))(@tiptap/pm@3.26.1)
773
+ '@tiptap/pm': 3.26.1
774
+
775
+ '@tybys/wasm-util@0.10.2':
776
+ dependencies:
777
+ tslib: 2.8.1
778
+ optional: true
779
+
780
+ detect-libc@2.1.2: {}
781
+
782
+ fdir@6.5.0(picomatch@4.0.4):
783
+ optionalDependencies:
784
+ picomatch: 4.0.4
785
+
786
+ fsevents@2.3.3:
787
+ optional: true
788
+
789
  hono@4.12.23: {}
790
 
791
+ lightningcss-android-arm64@1.32.0:
792
+ optional: true
793
+
794
+ lightningcss-darwin-arm64@1.32.0:
795
+ optional: true
796
+
797
+ lightningcss-darwin-x64@1.32.0:
798
+ optional: true
799
+
800
+ lightningcss-freebsd-x64@1.32.0:
801
+ optional: true
802
+
803
+ lightningcss-linux-arm-gnueabihf@1.32.0:
804
+ optional: true
805
+
806
+ lightningcss-linux-arm64-gnu@1.32.0:
807
+ optional: true
808
+
809
+ lightningcss-linux-arm64-musl@1.32.0:
810
+ optional: true
811
+
812
+ lightningcss-linux-x64-gnu@1.32.0:
813
+ optional: true
814
+
815
+ lightningcss-linux-x64-musl@1.32.0:
816
+ optional: true
817
+
818
+ lightningcss-win32-arm64-msvc@1.32.0:
819
+ optional: true
820
+
821
+ lightningcss-win32-x64-msvc@1.32.0:
822
+ optional: true
823
+
824
+ lightningcss@1.32.0:
825
+ dependencies:
826
+ detect-libc: 2.1.2
827
+ optionalDependencies:
828
+ lightningcss-android-arm64: 1.32.0
829
+ lightningcss-darwin-arm64: 1.32.0
830
+ lightningcss-darwin-x64: 1.32.0
831
+ lightningcss-freebsd-x64: 1.32.0
832
+ lightningcss-linux-arm-gnueabihf: 1.32.0
833
+ lightningcss-linux-arm64-gnu: 1.32.0
834
+ lightningcss-linux-arm64-musl: 1.32.0
835
+ lightningcss-linux-x64-gnu: 1.32.0
836
+ lightningcss-linux-x64-musl: 1.32.0
837
+ lightningcss-win32-arm64-msvc: 1.32.0
838
+ lightningcss-win32-x64-msvc: 1.32.0
839
+
840
+ linkifyjs@4.3.3: {}
841
+
842
  lucide@1.16.0: {}
843
+
844
+ marked@17.0.6: {}
845
+
846
+ nanoid@3.3.12: {}
847
+
848
+ orderedmap@2.1.1: {}
849
+
850
+ picocolors@1.1.1: {}
851
+
852
+ picomatch@4.0.4: {}
853
+
854
+ postcss@8.5.15:
855
+ dependencies:
856
+ nanoid: 3.3.12
857
+ picocolors: 1.1.1
858
+ source-map-js: 1.2.1
859
+
860
+ prosemirror-changeset@2.4.1:
861
+ dependencies:
862
+ prosemirror-transform: 1.12.0
863
+
864
+ prosemirror-commands@1.7.1:
865
+ dependencies:
866
+ prosemirror-model: 1.25.8
867
+ prosemirror-state: 1.4.4
868
+ prosemirror-transform: 1.12.0
869
+
870
+ prosemirror-dropcursor@1.8.2:
871
+ dependencies:
872
+ prosemirror-state: 1.4.4
873
+ prosemirror-transform: 1.12.0
874
+ prosemirror-view: 1.41.9
875
+
876
+ prosemirror-gapcursor@1.4.1:
877
+ dependencies:
878
+ prosemirror-keymap: 1.2.3
879
+ prosemirror-model: 1.25.8
880
+ prosemirror-state: 1.4.4
881
+ prosemirror-view: 1.41.9
882
+
883
+ prosemirror-history@1.5.0:
884
+ dependencies:
885
+ prosemirror-state: 1.4.4
886
+ prosemirror-transform: 1.12.0
887
+ prosemirror-view: 1.41.9
888
+ rope-sequence: 1.3.4
889
+
890
+ prosemirror-inputrules@1.5.1:
891
+ dependencies:
892
+ prosemirror-state: 1.4.4
893
+ prosemirror-transform: 1.12.0
894
+
895
+ prosemirror-keymap@1.2.3:
896
+ dependencies:
897
+ prosemirror-state: 1.4.4
898
+ w3c-keyname: 2.2.8
899
+
900
+ prosemirror-model@1.25.8:
901
+ dependencies:
902
+ orderedmap: 2.1.1
903
+
904
+ prosemirror-schema-list@1.5.1:
905
+ dependencies:
906
+ prosemirror-model: 1.25.8
907
+ prosemirror-state: 1.4.4
908
+ prosemirror-transform: 1.12.0
909
+
910
+ prosemirror-state@1.4.4:
911
+ dependencies:
912
+ prosemirror-model: 1.25.8
913
+ prosemirror-transform: 1.12.0
914
+ prosemirror-view: 1.41.9
915
+
916
+ prosemirror-tables@1.8.5:
917
+ dependencies:
918
+ prosemirror-keymap: 1.2.3
919
+ prosemirror-model: 1.25.8
920
+ prosemirror-state: 1.4.4
921
+ prosemirror-transform: 1.12.0
922
+ prosemirror-view: 1.41.9
923
+
924
+ prosemirror-transform@1.12.0:
925
+ dependencies:
926
+ prosemirror-model: 1.25.8
927
+
928
+ prosemirror-view@1.41.9:
929
+ dependencies:
930
+ prosemirror-model: 1.25.8
931
+ prosemirror-state: 1.4.4
932
+ prosemirror-transform: 1.12.0
933
+
934
+ rolldown@1.0.3:
935
+ dependencies:
936
+ '@oxc-project/types': 0.133.0
937
+ '@rolldown/pluginutils': 1.0.1
938
+ optionalDependencies:
939
+ '@rolldown/binding-android-arm64': 1.0.3
940
+ '@rolldown/binding-darwin-arm64': 1.0.3
941
+ '@rolldown/binding-darwin-x64': 1.0.3
942
+ '@rolldown/binding-freebsd-x64': 1.0.3
943
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.3
944
+ '@rolldown/binding-linux-arm64-gnu': 1.0.3
945
+ '@rolldown/binding-linux-arm64-musl': 1.0.3
946
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.3
947
+ '@rolldown/binding-linux-s390x-gnu': 1.0.3
948
+ '@rolldown/binding-linux-x64-gnu': 1.0.3
949
+ '@rolldown/binding-linux-x64-musl': 1.0.3
950
+ '@rolldown/binding-openharmony-arm64': 1.0.3
951
+ '@rolldown/binding-wasm32-wasi': 1.0.3
952
+ '@rolldown/binding-win32-arm64-msvc': 1.0.3
953
+ '@rolldown/binding-win32-x64-msvc': 1.0.3
954
+
955
+ rope-sequence@1.3.4: {}
956
+
957
+ source-map-js@1.2.1: {}
958
+
959
+ tinyglobby@0.2.17:
960
+ dependencies:
961
+ fdir: 6.5.0(picomatch@4.0.4)
962
+ picomatch: 4.0.4
963
+
964
+ tslib@2.8.1:
965
+ optional: true
966
+
967
+ uuid@14.0.0: {}
968
+
969
+ vite@8.0.16:
970
+ dependencies:
971
+ lightningcss: 1.32.0
972
+ picomatch: 4.0.4
973
+ postcss: 8.5.15
974
+ rolldown: 1.0.3
975
+ tinyglobby: 0.2.17
976
+ optionalDependencies:
977
+ fsevents: 2.3.3
978
+
979
+ w3c-keyname@2.2.8: {}
src/data/state-utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const maxNoteBodyLength = 1024 * 1024;
2
 
3
  export function createSeedState(now = Date.now()) {
4
  return {
 
1
+ export const maxNoteBodyLength = 10 * 1024 * 1024;
2
 
3
  export function createSeedState(now = Date.now()) {
4
  return {
styles.css CHANGED
@@ -516,7 +516,7 @@ textarea {
516
 
517
  .editor-meta {
518
  display: grid;
519
- grid-template-columns: 1fr auto auto auto auto;
520
  align-items: center;
521
  gap: 8px;
522
  height: 48px;
@@ -559,6 +559,7 @@ textarea {
559
  }
560
 
561
  .editor-save-button,
 
562
  .editor-preview-button {
563
  display: grid;
564
  place-items: center;
@@ -572,22 +573,26 @@ textarea {
572
  }
573
 
574
  .editor-save-button:hover:not(:disabled),
 
575
  .editor-preview-button:hover:not(:disabled) {
576
  background: var(--selected-soft);
577
  border-color: #eed08b;
578
  }
579
 
580
- .editor-preview-button.active {
 
581
  background: var(--selected);
582
  border-color: #e3bd5d;
583
  }
584
 
585
  .editor-save-button:disabled,
 
586
  .editor-preview-button:disabled {
587
  color: #b9b4aa;
588
  }
589
 
590
  .editor-save-button svg,
 
591
  .editor-preview-button svg {
592
  width: 18px;
593
  height: 18px;
@@ -598,7 +603,74 @@ textarea {
598
  stroke-width: 2;
599
  }
600
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  .mobile-folder-select,
 
602
  .mobile-save-button,
603
  .mobile-preview-button {
604
  display: none;
@@ -615,7 +687,7 @@ textarea {
615
  padding: 8px 56px 36px;
616
  border: 0;
617
  outline: 0;
618
- resize: none;
619
  background: transparent;
620
  color: var(--text);
621
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
@@ -623,6 +695,94 @@ textarea {
623
  line-height: 1.55;
624
  }
625
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
  .markdown-preview {
627
  flex: 1;
628
  min-height: 0;
@@ -831,6 +991,7 @@ textarea {
831
  }
832
 
833
  #noteFolderSelect,
 
834
  .editor-preview-button,
835
  .editor-save-button {
836
  display: none;
@@ -976,6 +1137,7 @@ html.force-mobile .save-status {
976
  }
977
 
978
  html.force-mobile #noteFolderSelect,
 
979
  html.force-mobile .editor-preview-button,
980
  html.force-mobile .editor-save-button {
981
  display: none;
@@ -1153,6 +1315,7 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-folder-select {
1153
  }
1154
 
1155
  .mobile-edit-button,
 
1156
  .mobile-preview-button,
1157
  .mobile-save-button {
1158
  display: grid;
@@ -1167,20 +1330,34 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-folder-select {
1167
  }
1168
 
1169
  .mobile-save-button:disabled,
 
1170
  .mobile-preview-button:disabled {
1171
  color: #b9b4aa;
1172
  }
1173
 
1174
  .mobile-save-button,
 
1175
  .mobile-preview-button {
1176
  display: none;
1177
  }
1178
 
 
 
 
 
 
 
 
 
 
 
 
1179
  .mobile-preview-button.active {
1180
  background: var(--selected);
1181
  }
1182
 
1183
  .mobile-edit-button svg,
 
1184
  .mobile-preview-button svg,
1185
  .mobile-save-button svg {
1186
  width: 22px;
@@ -1208,7 +1385,7 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-folder-select {
1208
  }
1209
 
1210
  .app-shell[data-mobile-view="editor"] .mobile-bottom {
1211
- grid-template-columns: 44px 44px 44px;
1212
  justify-content: end;
1213
  }
1214
 
@@ -1220,6 +1397,10 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-folder-select {
1220
  display: grid;
1221
  }
1222
 
 
 
 
 
1223
  .app-shell[data-mobile-view="editor"] .mobile-preview-button {
1224
  display: grid;
1225
  }
@@ -1341,6 +1522,7 @@ html.force-mobile .mobile-back span {
1341
 
1342
  html.force-mobile .mobile-back svg,
1343
  html.force-mobile .mobile-edit-button svg,
 
1344
  html.force-mobile .mobile-preview-button svg,
1345
  html.force-mobile .mobile-save-button svg {
1346
  width: 22px;
@@ -1447,6 +1629,7 @@ html.force-mobile .mobile-search input {
1447
  }
1448
 
1449
  html.force-mobile .mobile-edit-button,
 
1450
  html.force-mobile .mobile-preview-button,
1451
  html.force-mobile .mobile-save-button {
1452
  display: grid;
@@ -1460,15 +1643,28 @@ html.force-mobile .mobile-save-button {
1460
  }
1461
 
1462
  html.force-mobile .mobile-save-button:disabled,
 
1463
  html.force-mobile .mobile-preview-button:disabled {
1464
  color: #b9b4aa;
1465
  }
1466
 
1467
  html.force-mobile .mobile-save-button,
 
1468
  html.force-mobile .mobile-preview-button {
1469
  display: none;
1470
  }
1471
 
 
 
 
 
 
 
 
 
 
 
 
1472
  html.force-mobile .mobile-preview-button.active {
1473
  background: var(--selected);
1474
  }
@@ -1489,7 +1685,7 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .editor-pane {
1489
  }
1490
 
1491
  html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-bottom {
1492
- grid-template-columns: 44px 44px 44px;
1493
  justify-content: end;
1494
  }
1495
 
@@ -1501,6 +1697,10 @@ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-save-button {
1501
  display: grid;
1502
  }
1503
 
 
 
 
 
1504
  html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-preview-button {
1505
  display: grid;
1506
  }
 
516
 
517
  .editor-meta {
518
  display: grid;
519
+ grid-template-columns: 1fr auto auto auto auto auto;
520
  align-items: center;
521
  gap: 8px;
522
  height: 48px;
 
559
  }
560
 
561
  .editor-save-button,
562
+ .editor-menu-button,
563
  .editor-preview-button {
564
  display: grid;
565
  place-items: center;
 
573
  }
574
 
575
  .editor-save-button:hover:not(:disabled),
576
+ .editor-menu-button:hover:not(:disabled),
577
  .editor-preview-button:hover:not(:disabled) {
578
  background: var(--selected-soft);
579
  border-color: #eed08b;
580
  }
581
 
582
+ .editor-preview-button.active,
583
+ .editor-menu-button.active {
584
  background: var(--selected);
585
  border-color: #e3bd5d;
586
  }
587
 
588
  .editor-save-button:disabled,
589
+ .editor-menu-button:disabled,
590
  .editor-preview-button:disabled {
591
  color: #b9b4aa;
592
  }
593
 
594
  .editor-save-button svg,
595
+ .editor-menu-button svg,
596
  .editor-preview-button svg {
597
  width: 18px;
598
  height: 18px;
 
603
  stroke-width: 2;
604
  }
605
 
606
+ .editor-menu-wrap {
607
+ position: relative;
608
+ }
609
+
610
+ .editor-format-menu,
611
+ .mobile-editor-format-menu {
612
+ display: grid;
613
+ grid-template-columns: repeat(5, 32px);
614
+ gap: 4px;
615
+ padding: 6px;
616
+ border: 1px solid var(--line);
617
+ border-radius: 8px;
618
+ background: rgba(248, 247, 243, 0.98);
619
+ box-shadow: 0 12px 30px rgba(31, 29, 25, 0.16);
620
+ }
621
+
622
+ .editor-format-menu {
623
+ position: absolute;
624
+ z-index: 26;
625
+ top: 36px;
626
+ right: 0;
627
+ }
628
+
629
+ .editor-format-menu[hidden],
630
+ .mobile-editor-format-menu[hidden] {
631
+ display: none;
632
+ }
633
+
634
+ .editor-format-menu button,
635
+ .mobile-editor-format-menu button {
636
+ position: relative;
637
+ display: grid;
638
+ place-items: center;
639
+ width: 32px;
640
+ height: 32px;
641
+ border: 1px solid transparent;
642
+ border-radius: 6px;
643
+ background: transparent;
644
+ color: var(--text);
645
+ }
646
+
647
+ .editor-format-menu button:hover:not(:disabled),
648
+ .mobile-editor-format-menu button:hover:not(:disabled),
649
+ .editor-format-menu button.active,
650
+ .mobile-editor-format-menu button.active {
651
+ border-color: #eed08b;
652
+ background: var(--selected-soft);
653
+ color: var(--accent-strong);
654
+ }
655
+
656
+ .editor-format-menu button:disabled,
657
+ .mobile-editor-format-menu button:disabled {
658
+ color: #b9b4aa;
659
+ }
660
+
661
+ .editor-format-menu svg,
662
+ .mobile-editor-format-menu svg {
663
+ width: 17px;
664
+ height: 17px;
665
+ fill: none;
666
+ stroke: currentColor;
667
+ stroke-linecap: round;
668
+ stroke-linejoin: round;
669
+ stroke-width: 2;
670
+ }
671
+
672
  .mobile-folder-select,
673
+ .mobile-editor-menu-wrap,
674
  .mobile-save-button,
675
  .mobile-preview-button {
676
  display: none;
 
687
  padding: 8px 56px 36px;
688
  border: 0;
689
  outline: 0;
690
+ overflow: auto;
691
  background: transparent;
692
  color: var(--text);
693
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
 
695
  line-height: 1.55;
696
  }
697
 
698
+ #editor .ProseMirror {
699
+ min-height: 100%;
700
+ outline: 0;
701
+ white-space: pre-wrap;
702
+ }
703
+
704
+ #editor .ProseMirror > *:first-child {
705
+ margin-top: 0;
706
+ }
707
+
708
+ #editor .ProseMirror > *:last-child {
709
+ margin-bottom: 0;
710
+ }
711
+
712
+ #editor p,
713
+ #editor ul,
714
+ #editor ol,
715
+ #editor blockquote,
716
+ #editor pre {
717
+ margin: 0.55em 0;
718
+ }
719
+
720
+ #editor h1,
721
+ #editor h2,
722
+ #editor h3 {
723
+ margin: 0.8em 0 0.35em;
724
+ line-height: 1.2;
725
+ }
726
+
727
+ #editor code,
728
+ #editor pre {
729
+ border-radius: 6px;
730
+ background: #f1f0ec;
731
+ }
732
+
733
+ #editor code {
734
+ padding: 2px 4px;
735
+ }
736
+
737
+ #editor pre {
738
+ overflow: auto;
739
+ padding: 10px;
740
+ }
741
+
742
+ #editor blockquote {
743
+ border-left: 3px solid #d7d2c8;
744
+ padding-left: 12px;
745
+ color: #6e675d;
746
+ }
747
+
748
+ #editor img,
749
+ .markdown-preview img {
750
+ display: block;
751
+ max-width: min(100%, 720px);
752
+ height: auto;
753
+ margin: 12px auto;
754
+ border: 1px solid #ddd7cc;
755
+ border-radius: 7px;
756
+ background: white;
757
+ }
758
+
759
+ #editor img[data-align="left"],
760
+ .markdown-preview img[data-align="left"] {
761
+ margin-right: auto;
762
+ margin-left: 0;
763
+ }
764
+
765
+ #editor img[data-align="center"],
766
+ .markdown-preview img[data-align="center"] {
767
+ margin-right: auto;
768
+ margin-left: auto;
769
+ }
770
+
771
+ #editor img[data-align="right"],
772
+ .markdown-preview img[data-align="right"] {
773
+ margin-right: 0;
774
+ margin-left: auto;
775
+ }
776
+
777
+ #editor .ProseMirror-selectednode {
778
+ outline: 3px solid rgba(216, 150, 33, 0.34);
779
+ outline-offset: 3px;
780
+ }
781
+
782
+ #editor img.ProseMirror-selectednode {
783
+ box-shadow: 0 0 0 3px rgba(216, 150, 33, 0.22);
784
+ }
785
+
786
  .markdown-preview {
787
  flex: 1;
788
  min-height: 0;
 
991
  }
992
 
993
  #noteFolderSelect,
994
+ .editor-menu-wrap,
995
  .editor-preview-button,
996
  .editor-save-button {
997
  display: none;
 
1137
  }
1138
 
1139
  html.force-mobile #noteFolderSelect,
1140
+ html.force-mobile .editor-menu-wrap,
1141
  html.force-mobile .editor-preview-button,
1142
  html.force-mobile .editor-save-button {
1143
  display: none;
 
1315
  }
1316
 
1317
  .mobile-edit-button,
1318
+ .mobile-editor-menu-button,
1319
  .mobile-preview-button,
1320
  .mobile-save-button {
1321
  display: grid;
 
1330
  }
1331
 
1332
  .mobile-save-button:disabled,
1333
+ .mobile-editor-menu-button:disabled,
1334
  .mobile-preview-button:disabled {
1335
  color: #b9b4aa;
1336
  }
1337
 
1338
  .mobile-save-button,
1339
+ .mobile-editor-menu-wrap,
1340
  .mobile-preview-button {
1341
  display: none;
1342
  }
1343
 
1344
+ .mobile-editor-menu-wrap {
1345
+ position: relative;
1346
+ }
1347
+
1348
+ .mobile-editor-format-menu {
1349
+ position: absolute;
1350
+ z-index: 28;
1351
+ right: -96px;
1352
+ bottom: 48px;
1353
+ }
1354
+
1355
  .mobile-preview-button.active {
1356
  background: var(--selected);
1357
  }
1358
 
1359
  .mobile-edit-button svg,
1360
+ .mobile-editor-menu-button svg,
1361
  .mobile-preview-button svg,
1362
  .mobile-save-button svg {
1363
  width: 22px;
 
1385
  }
1386
 
1387
  .app-shell[data-mobile-view="editor"] .mobile-bottom {
1388
+ grid-template-columns: 44px 44px 44px 44px;
1389
  justify-content: end;
1390
  }
1391
 
 
1397
  display: grid;
1398
  }
1399
 
1400
+ .app-shell[data-mobile-view="editor"] .mobile-editor-menu-wrap {
1401
+ display: block;
1402
+ }
1403
+
1404
  .app-shell[data-mobile-view="editor"] .mobile-preview-button {
1405
  display: grid;
1406
  }
 
1522
 
1523
  html.force-mobile .mobile-back svg,
1524
  html.force-mobile .mobile-edit-button svg,
1525
+ html.force-mobile .mobile-editor-menu-button svg,
1526
  html.force-mobile .mobile-preview-button svg,
1527
  html.force-mobile .mobile-save-button svg {
1528
  width: 22px;
 
1629
  }
1630
 
1631
  html.force-mobile .mobile-edit-button,
1632
+ html.force-mobile .mobile-editor-menu-button,
1633
  html.force-mobile .mobile-preview-button,
1634
  html.force-mobile .mobile-save-button {
1635
  display: grid;
 
1643
  }
1644
 
1645
  html.force-mobile .mobile-save-button:disabled,
1646
+ html.force-mobile .mobile-editor-menu-button:disabled,
1647
  html.force-mobile .mobile-preview-button:disabled {
1648
  color: #b9b4aa;
1649
  }
1650
 
1651
  html.force-mobile .mobile-save-button,
1652
+ html.force-mobile .mobile-editor-menu-wrap,
1653
  html.force-mobile .mobile-preview-button {
1654
  display: none;
1655
  }
1656
 
1657
+ html.force-mobile .mobile-editor-menu-wrap {
1658
+ position: relative;
1659
+ }
1660
+
1661
+ html.force-mobile .mobile-editor-format-menu {
1662
+ position: absolute;
1663
+ z-index: 28;
1664
+ right: -96px;
1665
+ bottom: 48px;
1666
+ }
1667
+
1668
  html.force-mobile .mobile-preview-button.active {
1669
  background: var(--selected);
1670
  }
 
1685
  }
1686
 
1687
  html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-bottom {
1688
+ grid-template-columns: 44px 44px 44px 44px;
1689
  justify-content: end;
1690
  }
1691
 
 
1697
  display: grid;
1698
  }
1699
 
1700
+ html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-editor-menu-wrap {
1701
+ display: block;
1702
+ }
1703
+
1704
  html.force-mobile .app-shell[data-mobile-view="editor"] .mobile-preview-button {
1705
  display: grid;
1706
  }
vite.config.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from "vite";
2
+
3
+ export default defineConfig({
4
+ build: {
5
+ codeSplitting: false,
6
+ emptyOutDir: false,
7
+ outDir: "assets",
8
+ lib: {
9
+ entry: "app.js",
10
+ formats: ["es"],
11
+ fileName: () => "app.bundle.js"
12
+ },
13
+ rollupOptions: {}
14
+ }
15
+ });