@panphora/clayjs 1.13.1 → 1.15.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.
- package/THIRD-PARTY-NOTICES.md +19 -0
- package/dist/clay.standalone.js +69385 -40992
- package/dist/server/checkpoint.cjs +1 -0
- package/dist/server/checkpoint.mjs +8 -0
- package/dist/server/core.cjs +72210 -0
- package/dist/server/rooms.cjs +1 -0
- package/dist/server/rooms.mjs +66 -0
- package/docs/guide.md +127 -99
- package/entries/clay.js +22 -0
- package/entries/sap.js +5 -5
- package/package.json +16 -2
- package/packed-contract.json +18 -3
- package/src/core/boot-phase.js +6 -0
- package/src/core/conflict-download.js +12 -3
- package/src/core/host-meta.js +75 -0
- package/src/core/persist.js +11 -0
- package/src/core/save-core.js +266 -31
- package/src/core/save.js +154 -36
- package/src/core/snapshot.js +51 -13
- package/src/core/style-comparisons.js +37 -0
- package/src/dom/insert-style-tag.js +11 -12
- package/src/lib/derived.js +116 -0
- package/src/lib/effect.js +130 -0
- package/src/lib/mutation.js +3 -1
- package/src/lib/style-adoption.js +9 -0
- package/src/lib/style.js +47 -0
- package/src/live/fast-lane.js +347 -60
- package/src/live/local-lane.js +32 -6
- package/src/live/ops.js +77 -0
- package/src/live/provenance.js +129 -0
- package/src/live/save-history.js +188 -0
- package/src/live/server-lane.js +9 -2
- package/src/live/store.js +194 -16
- package/src/loader-logic.js +16 -4
- package/src/loader.js +37 -1
- package/src/page/address.js +20 -0
- package/src/page/adopt-boot.js +189 -0
- package/src/page/adopt.js +240 -0
- package/src/page/ai-tool.js +188 -0
- package/src/page/announce.js +21 -0
- package/src/page/blocks.js +1170 -0
- package/src/page/blocksel.js +269 -0
- package/src/page/board-drag.js +226 -0
- package/src/page/board-panel.js +353 -0
- package/src/page/board-style.js +146 -0
- package/src/page/board-view.js +447 -0
- package/src/page/board.js +41 -0
- package/src/page/chart.js +220 -0
- package/src/page/checklist.js +143 -0
- package/src/page/chrome.js +587 -0
- package/src/page/csv-import.js +169 -0
- package/src/page/drag.js +202 -0
- package/src/page/drop.js +112 -0
- package/src/page/editor.js +386 -0
- package/src/page/highlight.js +268 -0
- package/src/page/inline.js +60 -1
- package/src/page/items.js +159 -0
- package/src/page/link.js +247 -0
- package/src/page/markdown.js +71 -0
- package/src/page/media.js +570 -0
- package/src/page/merge.js +130 -0
- package/src/page/model.js +71 -9
- package/src/page/placeholder.js +23 -0
- package/src/page/projection.js +647 -0
- package/src/page/recover.js +193 -0
- package/src/page/rich/cell.js +16 -0
- package/src/page/rich/chart.js +104 -0
- package/src/page/rich/csv.js +118 -0
- package/src/page/rich/page-tool.js +86 -0
- package/src/page/rich/tool-runtime.js +195 -0
- package/src/page/save-projection.js +53 -0
- package/src/page/save-states.js +85 -0
- package/src/page/schema.js +12 -5
- package/src/page/table-kinds.js +20 -0
- package/src/page/table-paste.js +57 -0
- package/src/page/table-select.js +244 -0
- package/src/page/table-style.js +25 -0
- package/src/page/table.js +650 -0
- package/src/page/themes/content.css +46 -0
- package/src/page/themes/light-1.css +269 -0
- package/src/page/themes/light-2.css +211 -0
- package/src/page/themes/light-3.css +130 -0
- package/src/page/themes/light-4.css +299 -0
- package/src/page/tool-data.js +25 -0
- package/src/page/tool-view.js +68 -0
- package/src/page/tools.js +133 -0
- package/src/page/view.js +136 -0
- package/src/page/ybind/author.js +25 -0
- package/src/page/ybind/binding.js +92 -0
- package/src/page/ybind/board-commands.js +170 -0
- package/src/page/ybind/board.js +89 -0
- package/src/page/ybind/bridge.js +382 -0
- package/src/page/ybind/conversion.js +26 -0
- package/src/page/ybind/core.js +7 -0
- package/src/page/ybind/decode.js +208 -0
- package/src/page/ybind/doc.js +58 -0
- package/src/page/ybind/external.js +190 -0
- package/src/page/ybind/group.js +124 -0
- package/src/page/ybind/index.js +12 -0
- package/src/page/ybind/layout.js +27 -0
- package/src/page/ybind/marks.js +18 -0
- package/src/page/ybind/migrate.js +152 -0
- package/src/page/ybind/moves.js +288 -0
- package/src/page/ybind/origins.js +257 -0
- package/src/page/ybind/parts.js +44 -0
- package/src/page/ybind/projection-map.js +92 -0
- package/src/page/ybind/provider.js +285 -0
- package/src/page/ybind/recovery.js +411 -0
- package/src/page/ybind/seed.js +27 -0
- package/src/page/ybind/table.js +166 -0
- package/src/page/ybind/tokens.js +93 -0
- package/src/page/ybind/walk.js +333 -0
- package/src/page/ybind/writer.js +307 -0
- package/src/plugins/derive.js +12 -0
- package/src/plugins/indicator.js +16 -6
- package/src/plugins/live.js +742 -65
- package/src/plugins/page.js +547 -0
- package/src/plugins/undo.js +62 -4
- package/src/server/checkpoint/index.js +218 -0
- package/src/server/rooms/index.js +59 -0
- package/src/server/rooms/live-data-ops.js +369 -0
- package/src/server/rooms/live-data-rooms.js +965 -0
- package/src/server/rooms/live-data-tags.js +49 -0
- package/src/server/rooms/live-page-history.js +41 -0
- package/src/server/rooms/live-provenance.js +142 -0
- package/src/server/rooms/page-admission.js +79 -0
- package/src/server/rooms/page-conversion.js +62 -0
- package/src/server/rooms/page-rebase.js +108 -0
- package/src/server/rooms/page-state.js +91 -0
- package/src/server/rooms/page-write-admission.js +63 -0
- package/src/server/rooms/page-write-guard.js +23 -0
- package/src/server/rooms/recent.js +18 -0
- package/src/server/rooms/spec-wire.js +17 -0
- package/src/standalone.js +22 -0
- package/src/sync/conflict-revert.js +2 -0
- package/src/sync/conflicts.js +2 -2
- package/src/sync/live-sync.js +36 -8
- package/src/sync/stream.js +62 -16
- package/src/ui/index.js +4 -0
- package/src/ui/toast.js +21 -5
- package/src/vendor/pierre/pierre.vendor.js +3 -0
- package/src/vendor/sap-core.vendor.js +9 -0
- package/src/vendor/sap-ownership.vendor.js +111 -0
- package/src/vendor/yjs.vendor.js +7 -0
- package/src/page/binding.js +0 -647
- package/src/page/replay.js +0 -578
|
@@ -0,0 +1,386 @@
|
|
|
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
|
+
// Editor chrome, injected on every edit-mode boot and kept out of the saved file:
|
|
342
|
+
// the reader theme <link> styles the page for visitors.
|
|
343
|
+
styleWithLegacyId(STYLE_ID, PAGE_EDITOR_CSS, STYLE_ID, ["editor-ui"]);
|
|
344
|
+
const shell = doc.createElement("div");
|
|
345
|
+
shell.className = "page-shell";
|
|
346
|
+
shell.setAttribute("clay", "editor-ui");
|
|
347
|
+
shell.setAttribute("sap-ignore", "");
|
|
348
|
+
mount.appendChild(shell);
|
|
349
|
+
let afterStep = false;
|
|
350
|
+
const view = new pmView.EditorView(shell, {
|
|
351
|
+
state: pageState(blocks, { upload, highlight, doc: initialDoc, history: useHistory, undo, redo, onAiTool, toolHost }),
|
|
352
|
+
attributes: { class: "page-editor" },
|
|
353
|
+
editable: () => editable,
|
|
354
|
+
nodeViews: {
|
|
355
|
+
image: (node, v, getPos) => new ImageView(node, v, getPos),
|
|
356
|
+
file: (node, v) => new FileView(node, v),
|
|
357
|
+
code_block: (node, v, getPos) => new CodeBlockView(node, v, getPos),
|
|
358
|
+
table: (node, v, getPos) => new TableView(node, v, getPos),
|
|
359
|
+
chart: (node, v, getPos) => new ChartView(node, v, getPos),
|
|
360
|
+
tool: (node, v, getPos) => new ToolView(node, v, getPos, { host: toolHost, revise: reviseTool }),
|
|
361
|
+
table_cell: (node, v, getPos) => new TableCellView(node, v, getPos),
|
|
362
|
+
...(board ? { board: (node, v, getPos) => new BoardView(node, v, getPos, { commands: board, undo, redo, panel: cardPanel(v) }) } : {}),
|
|
363
|
+
},
|
|
364
|
+
handleDOMEvents: {
|
|
365
|
+
mousedown: checkboxClick,
|
|
366
|
+
dragstart(v, event) {
|
|
367
|
+
const sel = v.state.selection;
|
|
368
|
+
if (!(sel instanceof state.NodeSelection || sel instanceof BlockSelection)) return false;
|
|
369
|
+
event.preventDefault();
|
|
370
|
+
return true;
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
dispatchTransaction(tr) {
|
|
374
|
+
if (dispatch) {
|
|
375
|
+
dispatch(tr);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (afterStep && tr.docChanged && !tr.getMeta(STEP)) history.closeHistory(tr);
|
|
379
|
+
if (tr.docChanged) afterStep = !!tr.getMeta(STEP);
|
|
380
|
+
const before = view.state.doc;
|
|
381
|
+
view.updateState(view.state.apply(tr));
|
|
382
|
+
if (onChange && view.state.doc !== before) onChange(blockMap(view.state.doc));
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
return view;
|
|
386
|
+
}
|