@panphora/clayjs 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/THIRD-PARTY-NOTICES.md +19 -0
  2. package/dist/clay.standalone.js +69739 -41784
  3. package/dist/server/checkpoint.cjs +70690 -0
  4. package/dist/server/checkpoint.mjs +8 -0
  5. package/dist/server/rooms.cjs +72039 -0
  6. package/dist/server/rooms.mjs +66 -0
  7. package/docs/guide.md +95 -94
  8. package/entries/clay.js +22 -0
  9. package/package.json +16 -2
  10. package/packed-contract.json +17 -3
  11. package/src/core/conflict-download.js +11 -2
  12. package/src/core/host-meta.js +75 -0
  13. package/src/core/persist.js +11 -0
  14. package/src/core/save-core.js +266 -31
  15. package/src/core/save.js +62 -32
  16. package/src/core/snapshot.js +39 -13
  17. package/src/core/style-comparisons.js +37 -0
  18. package/src/dom/insert-style-tag.js +11 -12
  19. package/src/lib/effect.js +130 -0
  20. package/src/lib/style-adoption.js +9 -0
  21. package/src/lib/style.js +44 -0
  22. package/src/live/fast-lane.js +347 -60
  23. package/src/live/local-lane.js +32 -6
  24. package/src/live/ops.js +77 -0
  25. package/src/live/provenance.js +129 -0
  26. package/src/live/save-history.js +188 -0
  27. package/src/live/server-lane.js +9 -2
  28. package/src/live/store.js +194 -16
  29. package/src/loader-logic.js +7 -3
  30. package/src/loader.js +6 -0
  31. package/src/page/address.js +20 -0
  32. package/src/page/adopt-boot.js +189 -0
  33. package/src/page/adopt.js +240 -0
  34. package/src/page/ai-tool.js +188 -0
  35. package/src/page/announce.js +21 -0
  36. package/src/page/blocks.js +1121 -0
  37. package/src/page/blocksel.js +269 -0
  38. package/src/page/board-drag.js +226 -0
  39. package/src/page/board-panel.js +353 -0
  40. package/src/page/board-style.js +146 -0
  41. package/src/page/board-view.js +447 -0
  42. package/src/page/board.js +41 -0
  43. package/src/page/chart.js +220 -0
  44. package/src/page/checklist.js +143 -0
  45. package/src/page/chrome.js +587 -0
  46. package/src/page/csv-import.js +169 -0
  47. package/src/page/drag.js +202 -0
  48. package/src/page/drop.js +112 -0
  49. package/src/page/editor.js +384 -0
  50. package/src/page/highlight.js +268 -0
  51. package/src/page/inline.js +60 -1
  52. package/src/page/items.js +159 -0
  53. package/src/page/link.js +247 -0
  54. package/src/page/markdown.js +71 -0
  55. package/src/page/media.js +570 -0
  56. package/src/page/merge.js +130 -0
  57. package/src/page/model.js +71 -9
  58. package/src/page/placeholder.js +23 -0
  59. package/src/page/projection.js +647 -0
  60. package/src/page/recover.js +193 -0
  61. package/src/page/rich/cell.js +16 -0
  62. package/src/page/rich/chart.js +104 -0
  63. package/src/page/rich/csv.js +118 -0
  64. package/src/page/rich/page-tool.js +86 -0
  65. package/src/page/rich/tool-runtime.js +195 -0
  66. package/src/page/save-projection.js +53 -0
  67. package/src/page/save-states.js +85 -0
  68. package/src/page/schema.js +12 -5
  69. package/src/page/table-kinds.js +20 -0
  70. package/src/page/table-paste.js +57 -0
  71. package/src/page/table-select.js +244 -0
  72. package/src/page/table-style.js +25 -0
  73. package/src/page/table.js +650 -0
  74. package/src/page/themes/content.css +46 -0
  75. package/src/page/themes/light-1.css +269 -0
  76. package/src/page/themes/light-2.css +211 -0
  77. package/src/page/themes/light-3.css +130 -0
  78. package/src/page/themes/light-4.css +299 -0
  79. package/src/page/tool-data.js +25 -0
  80. package/src/page/tool-view.js +68 -0
  81. package/src/page/tools.js +133 -0
  82. package/src/page/view.js +136 -0
  83. package/src/page/ybind/author.js +25 -0
  84. package/src/page/ybind/binding.js +92 -0
  85. package/src/page/ybind/board-commands.js +170 -0
  86. package/src/page/ybind/board.js +89 -0
  87. package/src/page/ybind/bridge.js +382 -0
  88. package/src/page/ybind/conversion.js +26 -0
  89. package/src/page/ybind/core.js +7 -0
  90. package/src/page/ybind/decode.js +208 -0
  91. package/src/page/ybind/doc.js +58 -0
  92. package/src/page/ybind/external.js +190 -0
  93. package/src/page/ybind/group.js +124 -0
  94. package/src/page/ybind/index.js +12 -0
  95. package/src/page/ybind/layout.js +27 -0
  96. package/src/page/ybind/marks.js +18 -0
  97. package/src/page/ybind/migrate.js +152 -0
  98. package/src/page/ybind/moves.js +288 -0
  99. package/src/page/ybind/origins.js +257 -0
  100. package/src/page/ybind/parts.js +44 -0
  101. package/src/page/ybind/projection-map.js +92 -0
  102. package/src/page/ybind/provider.js +285 -0
  103. package/src/page/ybind/recovery.js +411 -0
  104. package/src/page/ybind/seed.js +27 -0
  105. package/src/page/ybind/table.js +166 -0
  106. package/src/page/ybind/tokens.js +93 -0
  107. package/src/page/ybind/walk.js +333 -0
  108. package/src/page/ybind/writer.js +307 -0
  109. package/src/plugins/indicator.js +16 -6
  110. package/src/plugins/live.js +742 -65
  111. package/src/plugins/page.js +547 -0
  112. package/src/plugins/undo.js +60 -3
  113. package/src/plugins/visitor-drafts/prompt.js +4 -6
  114. package/src/server/checkpoint/index.js +218 -0
  115. package/src/server/rooms/index.js +59 -0
  116. package/src/server/rooms/live-data-ops.js +369 -0
  117. package/src/server/rooms/live-data-rooms.js +965 -0
  118. package/src/server/rooms/live-data-tags.js +49 -0
  119. package/src/server/rooms/live-page-history.js +41 -0
  120. package/src/server/rooms/live-provenance.js +142 -0
  121. package/src/server/rooms/page-admission.js +79 -0
  122. package/src/server/rooms/page-conversion.js +62 -0
  123. package/src/server/rooms/page-rebase.js +108 -0
  124. package/src/server/rooms/page-state.js +91 -0
  125. package/src/server/rooms/page-write-admission.js +63 -0
  126. package/src/server/rooms/page-write-guard.js +23 -0
  127. package/src/server/rooms/recent.js +18 -0
  128. package/src/server/rooms/spec-wire.js +17 -0
  129. package/src/standalone.js +22 -0
  130. package/src/sync/live-sync.js +13 -2
  131. package/src/sync/stream.js +62 -16
  132. package/src/ui/index.js +4 -0
  133. package/src/ui/toast.js +21 -5
  134. package/src/vendor/pierre/pierre.vendor.js +3 -0
  135. package/src/vendor/yjs.vendor.js +7 -0
  136. package/src/page/binding.js +0 -647
  137. package/src/page/replay.js +0 -578
@@ -0,0 +1,384 @@
1
+ // The page editor: a ProseMirror view over the flat block document, with the
2
+ // list, toggle, move, collapse and paste behaviour of blocks.js and the chrome
3
+ // of chrome.js. Local editing only; the live record and sync attach elsewhere.
4
+ //
5
+ // Theming: the base look below is built on --page-* tokens and sits in the
6
+ // `page` cascade layer, so a theme is one plain stylesheet that sets tokens on
7
+ // .page-shell (and adds scoped rules where it needs to); unlayered theme rules
8
+ // always win. Contract: plans/.../default-page/v1/look/THEME-CONTRACT.md
9
+ import { state, view as pmView, keymap, commands, history, gapcursor, inputrules } from "../vendor/prosemirror.vendor.js";
10
+ import { schema } from "./schema.js";
11
+ import { identityPlugin, docFromBlocks, blockMap, keyBetween, newId } from "./model.js";
12
+ import { blockKeys, structurePlugin, collapsePlugin, pastePlugin, BlockSelection, STEP } from "./blocks.js";
13
+ import { chromePlugin } from "./chrome.js";
14
+ import { announcementPlugin } from "./announce.js";
15
+ import { linkPlugin } from "./link.js";
16
+ import { blockSelectionPlugin } from "./blocksel.js";
17
+ import { markdownPlugin, markdownKeys, backspaceToParagraph } from "./markdown.js";
18
+ import { placeholderPlugin } from "./placeholder.js";
19
+ import { mediaPlugin, ImageView, FileView } from "./media.js";
20
+ import { highlightPlugin, CodeBlockView } from "./highlight.js";
21
+ import { tableKeys, tablePlugin, TableView, TableCellView } from "./table.js";
22
+ import { tableSelectPlugin } from "./table-select.js";
23
+ import { chartPlugin, ChartView } from "./chart.js";
24
+ import { BoardView } from "./board-view.js";
25
+ import { boardPlugin, cardPanel } from "./board-panel.js";
26
+ import { ToolView } from "./tool-view.js";
27
+ import { createToolHost } from "./tools.js";
28
+ import { checklistPlugin, checklistKeys } from "./checklist.js";
29
+ import { csvImportPlugin } from "./csv-import.js";
30
+ import { TABLE_CSS } from "./table-style.js";
31
+ import { BOARD_CSS } from "./board-style.js";
32
+ import { styleWithLegacyId } from "../lib/style.js";
33
+
34
+ const STYLE_ID = "page-editor-style";
35
+ const INDENTS = [1, 2, 3, 4, 5, 6, 7, 8];
36
+
37
+ const LIGHT = `
38
+ --page-bg: #F7F6F2; --page-ink: #23221F; --page-ink-soft: #6B6862; --page-ink-faint: #9B968C;
39
+ --page-rule: rgba(35, 34, 31, 0.10); --page-accent: #2F5E9E; --page-link: #164583; --page-selection: rgba(88, 120, 170, 0.20);
40
+ --page-check-border: rgba(35, 34, 31, 0.35); --page-check-fill: #23221F; --page-check-ink: #F7F6F2;
41
+ --page-callout-bg: rgba(35, 34, 31, 0.04); --page-code-bg: rgba(35, 34, 31, 0.05);
42
+ --page-code-token-keyword: #8A3B6E; --page-code-token-constant: #1F5A8A; --page-code-token-string: #3E6A28; --page-code-token-string-expression: #3E6A28; --page-code-token-comment: #8B877E; --page-code-token-function: #5E4596; --page-code-token-parameter: #8A5A1E; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
43
+ --page-chrome-ink-rest: rgba(35, 34, 31, 0.20); --page-chrome-ink-moved: rgba(35, 34, 31, 0.34);
44
+ --page-chrome-bg-rest: rgba(35, 34, 31, 0.02); --page-chrome-bg-moved: rgba(35, 34, 31, 0.04);
45
+ --page-chrome-line-rest: rgba(35, 34, 31, 0.06); --page-chrome-line-moved: rgba(35, 34, 31, 0.11);
46
+ --page-pop-bg: #FBFAF7; --page-pop-ink: #23221F; --page-pop-muted: #6B6862; --page-pop-line: rgba(35, 34, 31, 0.10);
47
+ --page-pop-shadow: 0 1px 2px rgba(35, 34, 31, 0.05), 0 8px 24px rgba(35, 34, 31, 0.08); --page-pop-hover: rgba(35, 34, 31, 0.06);
48
+ --page-scrim: rgba(35, 34, 31, 0.12);
49
+ --page-danger: #9E3B2F; --page-block-selected: rgba(88, 120, 170, 0.12);`;
50
+
51
+ const DARK = `
52
+ --page-bg: #1A1A18; --page-ink: #E7E4DC; --page-ink-soft: #A3A098; --page-ink-faint: #75726B;
53
+ --page-rule: rgba(231, 228, 220, 0.10); --page-accent: #8DB0E6; --page-link: #8DB0E6; --page-selection: rgba(120, 155, 210, 0.28);
54
+ --page-check-border: rgba(231, 228, 220, 0.35); --page-check-fill: #E7E4DC; --page-check-ink: #1A1A18;
55
+ --page-callout-bg: rgba(231, 228, 220, 0.05); --page-code-bg: rgba(231, 228, 220, 0.06);
56
+ --page-code-token-keyword: #D9A0C8; --page-code-token-constant: #8DB0E6; --page-code-token-string: #A9C98B; --page-code-token-string-expression: #A9C98B; --page-code-token-comment: #75726B; --page-code-token-function: #BBA6E6; --page-code-token-parameter: #E0B47A; --page-code-token-punctuation: var(--page-ink-soft); --page-code-token-link: var(--page-link);
57
+ --page-chrome-ink-rest: rgba(231, 228, 220, 0.18); --page-chrome-ink-moved: rgba(231, 228, 220, 0.30);
58
+ --page-chrome-bg-rest: rgba(231, 228, 220, 0.02); --page-chrome-bg-moved: rgba(231, 228, 220, 0.04);
59
+ --page-chrome-line-rest: rgba(231, 228, 220, 0.06); --page-chrome-line-moved: rgba(231, 228, 220, 0.11);
60
+ --page-pop-bg: #222220; --page-pop-ink: #E7E4DC; --page-pop-muted: #A3A098; --page-pop-line: rgba(231, 228, 220, 0.10);
61
+ --page-pop-shadow: 0 1px 2px rgba(10, 10, 9, 0.3), 0 10px 28px rgba(10, 10, 9, 0.35); --page-pop-hover: rgba(231, 228, 220, 0.07);
62
+ --page-scrim: rgba(10, 10, 9, 0.35);
63
+ --page-danger: #E08A7A; --page-block-selected: rgba(120, 155, 210, 0.18);
64
+ --chart-1: #8DB0E6; --chart-2: #D9A0C8; --chart-3: #A9C98B; --chart-4: #BBA6E6; --chart-5: #E0B47A; --chart-6: #A3A098;`;
65
+
66
+ export const PAGE_EDITOR_CSS = `
67
+ @layer page {
68
+ :where(article[data-page], .page-shell) {${LIGHT}
69
+ --chart-1: #2F5E9E; --chart-2: #164583; --chart-3: #5B5953; --chart-4: #8A3B6E; --chart-5: #3E6A28; --chart-6: #8A5A1E;
70
+ --page-ink-strong: var(--page-ink); --page-caret: var(--page-accent);
71
+ --page-font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --page-font-display: var(--page-font); --page-font-heading: var(--page-font);
72
+ --page-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
73
+ --page-font-size: 17px; --page-line-height: 1.65; --page-measure: 680px; --page-pad-top: 72px;
74
+ --page-h1-size: 34px; --page-h1-weight: 600; --page-h1-tracking: -0.01em; --page-h1-line: 1.2;
75
+ --page-h2-size: 21px; --page-h2-weight: 600; --page-h3-size: 17px; --page-h3-weight: 600;
76
+ --page-block-gap: 0.35em; --page-indent: 1.6em;
77
+ --page-marker: var(--page-ink-soft); --page-bullet-size: 0.36em; --page-check-size: 16px; --page-check-radius: 4px; --page-done-ink: var(--page-ink-faint);
78
+ --page-toggle-ink: var(--page-ink-soft); --page-callout-line: transparent; --page-code-ink: var(--page-ink);
79
+ --page-chrome-shadow-rest: none; --page-chrome-shadow-moved: none; --page-chrome-radius: 7px; --page-plus-size: 24px; --page-fade: 240ms;
80
+ --page-pop-radius: 10px; --page-bar-bg: var(--page-pop-bg); --page-bar-ink: var(--page-pop-ink);
81
+ }
82
+ @media (prefers-color-scheme: dark) { :where(:root:not([data-theme="light"]) :is(article[data-page], .page-shell)) {${DARK} } }
83
+ :where(:root[data-theme="dark"] :is(article[data-page], .page-shell)) {${DARK} }
84
+
85
+ .page-shell { position: relative; background: var(--page-bg); color: var(--page-ink); font-family: var(--page-font); font-size: var(--page-font-size); line-height: var(--page-line-height); padding: 0 16px; }
86
+ .page-editor { max-width: var(--page-measure); margin: 0 auto; padding: var(--page-pad-top) 0 30vh; outline: none; white-space: pre-wrap; overflow-wrap: break-word; caret-color: var(--page-caret); }
87
+ :where(article[data-page], .page-editor) ::selection { background: var(--page-selection); }
88
+ :where(article[data-page], .page-editor) > * { margin: var(--page-block-gap) 0; margin-inline-start: calc(var(--page-depth, 0) * var(--page-indent)); }
89
+ :where(article[data-page], .page-editor) > h1 { font-family: var(--page-font-display); font-size: var(--page-h1-size); font-weight: var(--page-h1-weight); letter-spacing: var(--page-h1-tracking); line-height: var(--page-h1-line); color: var(--page-ink-strong); margin-block: 0 0.6em; }
90
+ :where(article[data-page], .page-editor) > h2 { font-family: var(--page-font-heading); font-size: var(--page-h2-size); font-weight: var(--page-h2-weight); color: var(--page-ink-strong); line-height: 1.35; margin-block-start: 1.4em; }
91
+ :where(article[data-page], .page-editor) > h3 { font-family: var(--page-font-heading); font-size: var(--page-h3-size); font-weight: var(--page-h3-weight); color: var(--page-ink-strong); margin-block-start: 1.2em; }
92
+ :where(article[data-page], .page-editor) a { color: var(--page-link, var(--page-accent)); }
93
+ :where(article[data-page], .page-editor) code { font-family: var(--page-font-mono); font-size: 0.88em; background: var(--page-code-bg); color: var(--page-code-ink); border-radius: 4px; padding: 0.1em 0.3em; }
94
+ :where(article[data-page], .page-editor) pre { font-family: var(--page-font-mono); font-size: 0.88em; background: var(--page-code-bg); color: var(--page-code-ink); border-radius: 8px; padding: 12px 14px; }
95
+ :where(article[data-page], .page-editor) pre code { background: none; padding: 0; font-size: inherit; }
96
+ :where(article[data-page], .page-editor) pre { position: relative; padding-top: 32px; white-space: pre-wrap; overflow-wrap: anywhere; }
97
+ :where(article[data-page], .page-editor) pre[data-lang] { white-space: pre; overflow-wrap: normal; }
98
+ :where(article[data-page], .page-editor) pre[data-lang] > code { display: block; overflow-x: auto; }
99
+ .page-code-lang { position: absolute; top: 5px; inset-inline-end: 6px; max-width: 120px; height: 24px; padding: 0 4px; border: 0; border-radius: 5px; background: none; color: var(--page-ink-faint); font: 12px/1 var(--page-font); opacity: 0; cursor: pointer; transition: opacity var(--page-fade); }
100
+ .page-editor pre:hover .page-code-lang, .page-editor pre:focus-within .page-code-lang, .page-code-lang:focus-visible, .page-editor pre[data-lang] .page-code-lang { opacity: 1; }
101
+ .page-code-lang:hover, .page-code-lang:focus-visible { background: var(--page-pop-hover); color: var(--page-ink-soft); }
102
+ :where(article[data-page], .page-editor) .page-callout { background: var(--page-callout-bg); border: 1px solid var(--page-callout-line); border-radius: 8px; padding: 10px 14px; }
103
+ :where(article[data-page], .page-editor) hr { border: 0; border-top: 1px solid var(--page-rule); margin-block: 1.2em; }
104
+ :where(article[data-page], .page-editor) .page-hidden { display: none; }
105
+ :where(article[data-page], .page-editor) .page-li { position: relative; --li-indent: 0; padding-inline-start: calc((var(--li-indent) + 1) * var(--page-indent)); }
106
+ ${INDENTS.map((n) => `:where(article[data-page], .page-editor) .page-li[data-indent="${n}"] { --li-indent: ${n}; }`).join("\n")}
107
+ :where(article[data-page], .page-editor) .page-li::before { position: absolute; inset-inline-start: calc(var(--li-indent) * var(--page-indent)); width: var(--page-indent); text-align: center; color: var(--page-marker); content: "\\2022"; }
108
+ :where(article[data-page], .page-editor) .page-li[data-kind="bullet"]::before { content: ""; box-sizing: border-box; width: var(--page-bullet-size); height: var(--page-bullet-size); top: calc((1em * var(--page-line-height) - var(--page-bullet-size)) / 2); inset-inline-start: calc(var(--li-indent) * var(--page-indent) + (var(--page-indent) - var(--page-bullet-size)) / 2); border-radius: 50%; background: currentColor; }
109
+ :where(article[data-page], .page-editor) .page-li[data-kind="ordered"]::before { content: attr(data-marker); font-variant-numeric: tabular-nums; }
110
+ :where(article[data-page], .page-editor) .page-li[data-kind="check"]::before { content: ""; box-sizing: border-box; width: var(--page-check-size); height: var(--page-check-size); top: calc((1em * var(--page-line-height) - var(--page-check-size)) / 2); inset-inline-start: calc(var(--li-indent) * var(--page-indent) + (var(--page-indent) - var(--page-check-size)) / 2); border: 1px solid var(--page-check-border); border-radius: var(--page-check-radius); cursor: pointer; }
111
+ :where(article[data-page], .page-editor) .page-li[data-kind="check"][data-checked="true"]::before { background: var(--page-check-fill); border-color: var(--page-check-fill); }
112
+ :where(article[data-page], .page-editor) .page-li[data-kind="check"][data-checked="true"]::after { content: ""; position: absolute; width: var(--page-check-size); height: var(--page-check-size); top: calc((1em * var(--page-line-height) - var(--page-check-size)) / 2); inset-inline-start: calc(var(--li-indent) * var(--page-indent) + (var(--page-indent) - var(--page-check-size)) / 2); background: var(--page-check-ink); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.8 8.4l2.9 2.9 5.5-6.4' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 72% no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.8 8.4l2.9 2.9 5.5-6.4' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 72% no-repeat; pointer-events: none; }
113
+ :where(article[data-page], .page-editor) .page-li[data-kind="check"][data-checked="true"] { color: var(--page-done-ink); text-decoration: line-through; }
114
+ :where(article[data-page], .page-editor) .page-toggle { position: relative; padding-inline-start: var(--page-indent); }
115
+ .page-editor .page-toggle-button { position: absolute; inset-inline-start: 0; top: 0; width: var(--page-indent); height: calc(1em * var(--page-line-height)); display: grid; place-items: center; border: 0; padding: 0; background: none; color: var(--page-toggle-ink); font: inherit; line-height: 1; cursor: pointer; transition: transform var(--page-fade); }
116
+ .page-editor .page-toggle-button::before { content: "\\25B8"; font-size: 1.4em; }
117
+ .page-editor .page-toggle[aria-expanded="true"] > .page-toggle-button { transform: rotate(90deg); }
118
+
119
+ .page-dock, .page-plus { color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); box-shadow: var(--page-chrome-shadow-rest); border-radius: var(--page-chrome-radius); transition: color var(--page-fade), background-color var(--page-fade), border-color var(--page-fade), box-shadow var(--page-fade); }
120
+ .page-shell.is-moved .page-dock, .page-shell.is-moved .page-plus { color: var(--page-chrome-ink-moved); background: var(--page-chrome-bg-moved); border-color: var(--page-chrome-line-moved); box-shadow: var(--page-chrome-shadow-moved); }
121
+ .page-dock { position: sticky; top: 12px; z-index: 3; display: flex; align-items: center; gap: 2px; width: max-content; margin: 12px auto 0; padding: 3px; }
122
+ /* One formatting bar at a time: the selection bar covers what the fixed bar offers. Hidden, not removed, so the page does not shift under the selection. */
123
+ .page-shell:has(.page-bubble:not([hidden])) .page-dock { visibility: hidden; }
124
+ .page-dock, .page-shell.is-moved .page-dock { background: var(--page-pop-bg); }
125
+ .page-dock + .page-editor { padding-top: max(16px, calc(var(--page-pad-top) - 48px)); }
126
+ .page-tool { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 6px; border: 0; border-radius: 5px; background: none; color: inherit; font: 500 13px/1 var(--page-font); cursor: pointer; }
127
+ .page-tool:hover, .page-tool[aria-pressed="true"] { background: var(--page-pop-hover); }
128
+ .page-tool-turn { gap: 4px; padding: 0 8px; }
129
+ .page-tool-sep { width: 1px; height: 16px; margin: 0 3px; background: currentColor; opacity: 0.5; }
130
+ .page-plus { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-plus-size) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px); width: var(--page-plus-size); height: var(--page-plus-size); display: grid; place-items: center; padding: 0; cursor: pointer; }
131
+ .page-plus[aria-expanded="true"] { color: var(--page-chrome-ink-moved); background: var(--page-chrome-bg-moved); }
132
+ .page-grip { position: absolute; z-index: 2; top: calc(var(--page-plus-top, 0px) - var(--page-grip-size, var(--page-plus-size)) / 2); left: calc(var(--page-plus-left, 0px) - var(--page-plus-size) - 12px - var(--page-grip-size, var(--page-plus-size)) - 2px); width: var(--page-grip-size, var(--page-plus-size)); height: var(--page-grip-size, var(--page-plus-size)); display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--page-chrome-radius); background: none; color: var(--page-chrome-ink-moved); opacity: 0; cursor: grab; touch-action: none; transition: opacity var(--page-fade); }
133
+ .page-shell.is-moved .page-grip, .page-grip.is-on, .page-grip:focus-visible, .page-grip[aria-expanded="true"] { opacity: 1; }
134
+ .page-bubble, .page-menu { position: absolute; z-index: 4; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); box-shadow: var(--page-pop-shadow); border-radius: var(--page-pop-radius); }
135
+ .page-bubble { display: flex; gap: 1px; padding: 3px; left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); transform: translate(-50%, calc(-100% - 8px)); }
136
+ .page-menu { left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); width: 260px; max-height: 340px; overflow-y: auto; padding: 6px; font-size: 14px; }
137
+ .page-menu-title { padding: 4px 10px 6px; color: var(--page-pop-muted); font-size: 12px; }
138
+ .page-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 34px; padding: 4px 10px; border: 0; border-radius: 7px; background: none; color: inherit; font: inherit; text-align: start; cursor: pointer; }
139
+ .page-menu-item[aria-selected="true"], .page-menu-item.is-active, .page-menu-item:hover { background: var(--page-pop-hover); }
140
+ .page-menu-icon { display: grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--page-pop-line); border-radius: 6px; color: var(--page-pop-muted); font-size: 12px; flex: none; }
141
+ .page-menu-hint { margin-inline-start: auto; color: var(--page-pop-muted); font-size: 12px; }
142
+ .page-menu-text { display: flex; flex-direction: column; min-width: 0; }
143
+ .page-menu-desc { color: var(--page-pop-muted); font-size: 12px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
144
+ .page-menu-empty { padding: 8px 10px; color: var(--page-pop-muted); font-size: 13px; }
145
+ .page-shell.is-dragging, .page-shell.is-dragging .page-grip { cursor: grabbing; }
146
+ .page-shell[data-drop="none"] { cursor: no-drop; }
147
+ .page-drop { position: absolute; z-index: 3; left: var(--page-drop-x, 0px); top: var(--page-drop-y, 0px); width: var(--page-drop-w, 0px); height: 2px; margin-top: -1px; border-radius: 1px; background: var(--page-drop, var(--page-accent)); pointer-events: none; }
148
+ .page-shell.is-dragging .page-block-selected { opacity: 0.45; }
149
+ .page-editor.ProseMirror-hideselection { caret-color: transparent; }
150
+ .page-editor.ProseMirror-hideselection *::selection { background: transparent; }
151
+ .page-editor .page-block-selected { background: var(--page-block-selected, rgba(88, 120, 170, 0.12)); box-shadow: -6px 0 0 var(--page-block-selected, rgba(88, 120, 170, 0.12)), 6px 0 0 var(--page-block-selected, rgba(88, 120, 170, 0.12)); border-radius: 4px; }
152
+ .page-editor .page-link-pending { background: var(--page-selection); }
153
+ .page-link { position: absolute; z-index: 4; left: var(--page-pop-x, 0px); top: var(--page-pop-y, 0px); display: flex; align-items: center; gap: 2px; padding: 4px; max-width: min(420px, calc(100% - 32px)); background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); box-shadow: var(--page-pop-shadow); border-radius: var(--page-pop-radius); font-size: 14px; }
154
+ .page-link-input { flex: 1; min-width: 220px; height: 30px; padding: 0 8px; border: 0; border-radius: 6px; background: var(--page-pop-hover); color: inherit; font: inherit; outline: none; }
155
+ .page-link-input[aria-invalid="true"] { box-shadow: inset 0 0 0 1px var(--page-danger, #9E3B2F); }
156
+ .page-link-url { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 8px; color: var(--page-pop-muted); text-decoration: none; }
157
+ .page-link-btn { display: inline-grid; place-items: center; min-width: 30px; height: 30px; padding: 0 6px; border: 0; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
158
+ .page-link-btn:hover { background: var(--page-pop-hover); }
159
+ .page-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
160
+ .page-editor .page-image { margin-inline: 0; }
161
+ .page-image-frame { position: relative; margin: 0 auto; max-width: 100%; }
162
+ .page-image-frame img { display: block; width: 100%; height: auto; border-radius: var(--page-image-radius, 6px); }
163
+ .page-image[data-broken] .page-image-frame { min-height: 80px; background: var(--page-code-bg); border-radius: var(--page-image-radius, 6px); }
164
+ .page-image-handle { position: absolute; top: 50%; inset-inline-end: 6px; width: 6px; height: 36px; margin-top: -18px; border-radius: 3px; background: var(--page-chrome-ink-moved); opacity: 0; cursor: ew-resize; touch-action: none; transition: opacity var(--page-fade); }
165
+ .page-image:hover .page-image-handle, .page-image.is-selected .page-image-handle { opacity: 1; }
166
+ .page-editor .page-image.page-block-selected { background: none; box-shadow: none; }
167
+ .page-editor .page-image.page-block-selected .page-image-frame { box-shadow: 0 0 0 6px var(--page-block-selected, rgba(88, 120, 170, 0.12)); border-radius: var(--page-image-radius, 6px); }
168
+ .page-image[data-align="left"] .page-image-frame { margin-left: 0; margin-right: auto; }
169
+ .page-image[data-align="right"] .page-image-frame { margin-left: auto; margin-right: 0; }
170
+ .page-image[data-align="left"] .page-image-caption { text-align: left; }
171
+ .page-image[data-align="right"] .page-image-caption { text-align: right; }
172
+ .page-image-align { position: absolute; top: 8px; inset-inline-end: 8px; display: none; gap: 1px; padding: 2px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); box-shadow: var(--page-pop-shadow); border-radius: 7px; }
173
+ .page-image.is-selected .page-image-align, .page-image.page-block-selected .page-image-align { display: flex; }
174
+ .page-image-align button { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 5px; background: none; color: inherit; cursor: pointer; }
175
+ .page-image-align button:hover, .page-image-align button[aria-pressed="true"] { background: var(--page-pop-hover); }
176
+ .page-image.is-selected .page-image-frame img { outline: 2px solid var(--page-selection); outline-offset: 2px; }
177
+ .page-image-caption { margin-top: 6px; color: var(--page-ink-soft); font-size: 0.88em; text-align: center; outline: none; }
178
+ .page-image-caption[data-empty]::before { content: attr(data-placeholder); color: var(--page-ink-faint); }
179
+ .ProseMirror-focused .page-placeholder::before { content: attr(data-placeholder); float: left; height: 0; color: var(--page-ink-faint); pointer-events: none; }
180
+ .page-image:not(.is-selected) .page-image-caption[data-empty]:not(:focus) { display: none; }
181
+ .page-file-link { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--page-rule); border-radius: 8px; color: var(--page-ink); text-decoration: none; }
182
+ .page-file-icon { color: var(--page-link, var(--page-accent)); }
183
+ .page-file-name { color: var(--page-link, var(--page-accent)); min-width: 0; overflow-wrap: anywhere; }
184
+ .page-file-meta { margin-inline-start: auto; color: var(--page-ink-faint); font-size: 0.88em; }
185
+ .page-upload { display: flex; align-items: center; gap: 10px; margin: var(--page-block-gap) 0; padding: 8px 12px; border: 1px dashed var(--page-rule); border-radius: 8px; color: var(--page-ink-soft); font-size: 0.88em; }
186
+ .page-upload-preview { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; opacity: 0.6; }
187
+ .page-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
188
+ .page-upload-bar { flex: 1; height: 2px; border-radius: 1px; background: var(--page-rule); overflow: hidden; }
189
+ .page-upload-bar > span { display: block; height: 100%; background: var(--page-progress, var(--page-ink-soft)); transition: width 200ms; }
190
+ .page-upload[data-state="error"] .page-upload-msg, .page-upload[data-state="embed-choice"] .page-upload-msg { flex: 1; }
191
+ .page-upload[data-state="error"] .page-upload-msg { color: var(--page-danger, #9E3B2F); }
192
+ .page-upload-btn { border: 0; padding: 4px 8px; border-radius: 6px; background: none; color: inherit; font: inherit; cursor: pointer; }
193
+ .page-upload-btn:hover { background: var(--page-pop-hover); }
194
+ .page-csv-error { margin: 8px 0; color: var(--page-danger); font-size: 14px; }
195
+ .page-editor .page-table { max-width: 100%; min-width: 0; border: 1px solid var(--page-rule); border-radius: 8px; }
196
+ .page-editor .page-chart { position: relative; max-width: 100%; min-width: 0; margin-inline: 0; color: var(--page-ink); }
197
+ .page-chart-plot svg { display: block; width: 100%; height: auto; color: var(--chart-text, var(--page-ink-soft)); font: 12px var(--page-font); }
198
+ .page-chart figcaption { color: var(--page-ink-soft); font-size: 0.88em; text-align: center; }
199
+ .page-chart-settings { display: block; margin: 8px auto 0; padding: 4px 10px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; cursor: pointer; }
200
+ .page-chart-title-input { display: block; width: min(100%, 24em); box-sizing: border-box; margin: 6px auto; padding: 4px 8px; border: 1px solid var(--page-rule); border-radius: 6px; background: var(--page-pop-bg); color: var(--page-ink); font: inherit; }
201
+ .page-tool-block { position: relative; min-height: 48px; border: 1px solid var(--page-rule); border-radius: 8px; padding: 12px; }
202
+ .page-tool-settings { position: absolute; top: 4px; inset-inline-end: 4px; border: 0; border-radius: 5px; background: var(--page-pop-bg); color: var(--page-ink-soft); font: 12px var(--page-font); padding: 5px 8px; cursor: pointer; opacity: 0; }
203
+ .page-tool-block:hover .page-tool-settings, .page-tool-block:focus-within .page-tool-settings { opacity: 1; }
204
+ .page-ai-panel { position: relative; z-index: 5; width: min(520px, 100%); box-sizing: border-box; margin: 12px auto; padding: 14px; border: 1px solid var(--page-pop-line); border-radius: 10px; background: var(--page-pop-bg); color: var(--page-pop-ink); box-shadow: var(--page-pop-shadow); }
205
+ .page-ai-panel textarea { width: 100%; min-height: 88px; box-sizing: border-box; padding: 8px; border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; resize: vertical; }
206
+ .page-ai-actions { display: flex; gap: 8px; margin-top: 8px; }
207
+ .page-ai-actions button { border: 1px solid var(--page-pop-line); border-radius: 6px; background: var(--page-pop-bg); color: inherit; font: inherit; padding: 5px 10px; cursor: pointer; }
208
+ .page-ai-status, [data-tool-note], [data-tool-error] { color: var(--page-danger); font-size: 0.85em; }
209
+ .page-table-scroll { max-width: 100%; overflow-x: auto; }
210
+ .page-table table { border-collapse: collapse; min-width: max-content; width: 100%; table-layout: auto; }
211
+ .page-table th, .page-table td { min-width: 120px; padding: 6px 10px; border-inline-end: 1px solid var(--page-rule); border-block-end: 1px solid var(--page-rule); text-align: start; white-space: pre-wrap; }
212
+ .page-table th { background: var(--page-code-bg); color: var(--page-ink-soft); font-weight: 600; }
213
+ .page-table th[data-kind="number"], .page-table td[data-kind="number"] { text-align: end; }
214
+ .page-table th[aria-sort="ascending"] button::after { content: " ↑"; }
215
+ .page-table th[aria-sort="descending"] button::after { content: " ↓"; }
216
+ .page-table th button, .page-table-row-menu, .page-check-chip { border: 0; border-radius: 5px; background: none; color: inherit; font: inherit; cursor: pointer; }
217
+ .page-table th button:hover, .page-table-row-menu:hover, .page-check-chip:hover { background: var(--page-pop-hover); }
218
+ .page-table-row-menu { opacity: 0; font-size: 12px; color: var(--page-ink-soft); }
219
+ .page-table tr:hover .page-table-row-menu, .page-table tr:focus-within .page-table-row-menu, .page-table-row-menu:focus-visible { opacity: 1; }
220
+ .page-check-controls { display: inline-flex; gap: 4px; margin-inline-start: 8px; color: var(--page-ink-soft); font-size: 0.78em; text-decoration: none; }
221
+ .page-check-controls[data-empty="true"] { opacity: 0; }
222
+ .page-li:hover .page-check-controls, .page-li:focus-within .page-check-controls { opacity: 1; }
223
+ .page-check-popup { display: inline-flex; gap: 4px; padding: 4px; background: var(--page-pop-bg); color: var(--page-pop-ink); border: 1px solid var(--page-pop-line); border-radius: var(--page-pop-radius); box-shadow: var(--page-pop-shadow); }
224
+ .page-check-popup input { font: inherit; color: inherit; background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); }
225
+ .page-check-popup.page-check-owner { display: inline-block; width: min(280px, 80vw); padding: 8px 0; vertical-align: top; }
226
+ .page-scrim { display: none; }
227
+ .page-shell [hidden] { display: none !important; }
228
+ ${TABLE_CSS}
229
+ ${BOARD_CSS}
230
+ @media (max-width: 560px) {
231
+ .page-dock { display: none; }
232
+ .page-plus { position: fixed; top: auto; left: 16px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; }
233
+ .page-grip { position: fixed; top: auto; left: 68px; bottom: calc(16px + env(safe-area-inset-bottom)); width: 44px; height: 44px; border-radius: 22px; opacity: 1; color: var(--page-chrome-ink-rest); background: var(--page-chrome-bg-rest); border: 1px solid var(--page-chrome-line-rest); }
234
+ .page-bubble { position: fixed; left: 0; right: 0; top: auto; bottom: 0; transform: none; justify-content: center; gap: 4px; padding: 6px 16px calc(6px + env(safe-area-inset-bottom)); border-radius: 0; border-width: 1px 0 0; background: var(--page-bar-bg); color: var(--page-bar-ink); }
235
+ .page-bubble .page-tool { min-width: 44px; height: 44px; }
236
+ .page-scrim { display: block; position: fixed; inset: 0; z-index: 4; background: var(--page-scrim); }
237
+ .page-menu { position: fixed; left: 0; right: 0; top: auto; bottom: 0; width: auto; max-height: 70vh; z-index: 5; padding: 8px 8px calc(8px + env(safe-area-inset-bottom)); border-radius: var(--page-pop-radius) var(--page-pop-radius) 0 0; border-width: 1px 0 0; }
238
+ .page-menu-item { min-height: 48px; font-size: 16px; }
239
+ .page-link { position: fixed; left: 0; right: 0; top: auto; bottom: 0; max-width: none; padding: 8px 16px calc(8px + env(safe-area-inset-bottom)); border-radius: 0; border-width: 1px 0 0; }
240
+ .page-link-input { min-width: 0; height: 44px; font-size: 16px; }
241
+ .page-link-btn, .page-upload-btn { min-width: 44px; height: 44px; }
242
+ .page-image-frame { width: 100% !important; }
243
+ .page-image-handle { display: none; }
244
+ .page-table th button, .page-table-row-menu, .page-check-chip, .page-check-popup button, .page-menu-item { min-height: 44px; }
245
+ }
246
+ @media (prefers-reduced-motion: reduce) {
247
+ .page-dock, .page-plus, .page-grip, .page-toggle-button, .page-image-handle, .page-upload-bar > span { transition: none; }
248
+ }
249
+ }
250
+ `;
251
+
252
+ const insertHardBreak = (st, dispatch) => {
253
+ if (dispatch) dispatch(st.tr.replaceSelectionWith(schema.nodes.hard_break.create()).scrollIntoView());
254
+ return true;
255
+ };
256
+
257
+ const editKeys = ({ history: useHistory = true, undo, redo } = {}) => ({
258
+ "Mod-z": useHistory ? history.undo : () => { undo?.(); return true; },
259
+ "Shift-Mod-z": useHistory ? history.redo : () => { redo?.(); return true; },
260
+ "Mod-y": useHistory ? history.redo : () => { redo?.(); return true; },
261
+ "Shift-Enter": insertHardBreak,
262
+ "Mod-b": commands.toggleMark(schema.marks.strong),
263
+ "Mod-i": commands.toggleMark(schema.marks.em),
264
+ });
265
+
266
+ const markKeys = {
267
+ Backspace: commands.chainCommands(inputrules.undoInputRule, backspaceToParagraph),
268
+ "Mod-e": commands.toggleMark(schema.marks.code),
269
+ "Mod-Shift-s": commands.toggleMark(schema.marks.strike),
270
+ };
271
+
272
+ const defaultUpload = (file, options) => globalThis.clay?.upload?.(file, options) ?? Promise.resolve({ ok: false, code: "unavailable", msg: "File uploads are not available in this editor" });
273
+
274
+ export function pagePlugins({ upload = defaultUpload, highlight, history: useHistory = true, undo, redo, onAiTool = null, toolHost = null } = {}) {
275
+ return [
276
+ announcementPlugin(),
277
+ chromePlugin({ onAiTool }),
278
+ toolHost && new state.Plugin({
279
+ view(view) {
280
+ let previous = view.state.doc;
281
+ return { update(next) { if (next.state.doc !== previous) { previous = next.state.doc; toolHost.tablesChanged(); } } };
282
+ },
283
+ }),
284
+ linkPlugin(),
285
+ tableSelectPlugin(),
286
+ blockSelectionPlugin(),
287
+ markdownPlugin(),
288
+ markdownKeys(),
289
+ placeholderPlugin(),
290
+ keymap.keymap(markKeys),
291
+ tableKeys,
292
+ checklistKeys,
293
+ keymap.keymap(blockKeys),
294
+ keymap.keymap(editKeys({ history: useHistory, undo, redo })),
295
+ keymap.keymap(commands.baseKeymap),
296
+ structurePlugin(),
297
+ identityPlugin(),
298
+ tablePlugin(),
299
+ chartPlugin(),
300
+ boardPlugin(),
301
+ checklistPlugin(),
302
+ csvImportPlugin({ upload }),
303
+ collapsePlugin(),
304
+ mediaPlugin({ upload }),
305
+ highlightPlugin(highlight),
306
+ pastePlugin(),
307
+ useHistory ? history.history() : null,
308
+ gapcursor.gapCursor(),
309
+ ].filter(Boolean);
310
+ }
311
+
312
+ export function pageState(blocks, options = {}) {
313
+ const record = blocks && Object.keys(blocks).length ? blocks : { [newId()]: { t: "paragraph", pos: keyBetween(), c: "" } };
314
+ const doc = options.doc ?? docFromBlocks(schema, record);
315
+ return state.EditorState.create({ doc, plugins: pagePlugins(options) });
316
+ }
317
+
318
+ // Clicking a checklist box (drawn by ::before in the item's start gutter)
319
+ // toggles `checked`.
320
+ function checkboxClick(view, event) {
321
+ const li = event.target.closest?.('.page-li[data-kind="check"]');
322
+ if (!li || !view.dom.contains(li)) return false;
323
+ const box = li.getBoundingClientRect();
324
+ const start = parseFloat(getComputedStyle(li).paddingInlineStart) || 0;
325
+ const x = getComputedStyle(li).direction === "rtl" ? box.right - event.clientX : event.clientX - box.left;
326
+ if (x < 0 || x > start) return false;
327
+ const pos = view.posAtDOM(li, 0) - 1;
328
+ const node = view.state.doc.nodeAt(pos);
329
+ if (!node) return false;
330
+ event.preventDefault();
331
+ view.dispatch(view.state.tr.setNodeMarkup(pos, null, { ...node.attrs, checked: !node.attrs.checked }));
332
+ return true;
333
+ }
334
+
335
+ // Mounts the editor in `mount`, inside a .page-shell wrapper (the surface a
336
+ // theme styles). `onChange(record)` gets the block record (blockMap) after
337
+ // every transaction that changed the document.
338
+ export function createPageEditor(mount, { blocks = null, onChange = null, upload, highlight, doc: initialDoc, dispatch, history: useHistory = true, undo, redo, editable = true, toolHost: providedToolHost = null, onAiTool = null, reviseTool = null, board = null } = {}) {
339
+ const doc = mount.ownerDocument;
340
+ const toolHost = providedToolHost ?? createToolHost({ mode: "edit" });
341
+ styleWithLegacyId(STYLE_ID, PAGE_EDITOR_CSS, STYLE_ID);
342
+ const shell = doc.createElement("div");
343
+ shell.className = "page-shell";
344
+ shell.setAttribute("clay", "editor-ui");
345
+ shell.setAttribute("sap-ignore", "");
346
+ mount.appendChild(shell);
347
+ let afterStep = false;
348
+ const view = new pmView.EditorView(shell, {
349
+ state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost }),
350
+ attributes: { class: "page-editor" },
351
+ editable: () => editable,
352
+ nodeViews: {
353
+ image: (node, v, getPos) => new ImageView(node, v, getPos),
354
+ file: (node, v) => new FileView(node, v),
355
+ code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos),
356
+ table: (node, v, getPos) => new TableView(node, v, getPos),
357
+ chart: (node, v, getPos) => new ChartView(node, v, getPos),
358
+ tool: (node, v, getPos) => new ToolView(node, v, getPos, { host: toolHost, revise: reviseTool }),
359
+ table_cell: (node, v, getPos) => new TableCellView(node, v, getPos),
360
+ ...(board ? { board: (node, v, getPos) => new BoardView(node, v, getPos, { commands: board, undo, redo, panel: cardPanel(v) }) } : {}),
361
+ },
362
+ handleDOMEvents: {
363
+ mousedown: checkboxClick,
364
+ dragstart(v, event) {
365
+ const sel = v.state.selection;
366
+ if (!(sel instanceof state.NodeSelection || sel instanceof BlockSelection)) return false;
367
+ event.preventDefault();
368
+ return true;
369
+ },
370
+ },
371
+ dispatchTransaction(tr) {
372
+ if (dispatch) {
373
+ dispatch(tr);
374
+ return;
375
+ }
376
+ if (afterStep && tr.docChanged && !tr.getMeta(STEP)) history.closeHistory(tr);
377
+ if (tr.docChanged) afterStep = !!tr.getMeta(STEP);
378
+ const before = view.state.doc;
379
+ view.updateState(view.state.apply(tr));
380
+ if (onChange && view.state.doc !== before) onChange(blockMap(view.state.doc));
381
+ },
382
+ });
383
+ return view;
384
+ }