blixify-ui-web 1.2.125 → 1.2.127
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/lib/components/chat/chatAnchor/index.d.ts.map +1 -1
- package/lib/components/chat/chatAnchor/index.js +10 -4
- package/lib/components/chat/chatAnchor/index.js.map +1 -1
- package/lib/components/chat/chatBase/index.d.ts +23 -0
- package/lib/components/chat/chatBase/index.d.ts.map +1 -1
- package/lib/components/chat/chatBase/index.js +266 -47
- package/lib/components/chat/chatBase/index.js.map +1 -1
- package/lib/components/chat/chatDashboard/index.d.ts +3 -1
- package/lib/components/chat/chatDashboard/index.d.ts.map +1 -1
- package/lib/components/chat/chatDashboard/index.js +47 -39
- package/lib/components/chat/chatDashboard/index.js.map +1 -1
- package/lib/components/display/newList/index.d.ts +4 -0
- package/lib/components/display/newList/index.d.ts.map +1 -1
- package/lib/components/display/newList/index.js +18 -13
- package/lib/components/display/newList/index.js.map +1 -1
- package/lib/components/input/markdown/headings.d.ts +84 -0
- package/lib/components/input/markdown/headings.d.ts.map +1 -0
- package/lib/components/input/markdown/headings.js +192 -0
- package/lib/components/input/markdown/headings.js.map +1 -0
- package/lib/components/input/markdown/index.d.ts +17 -1
- package/lib/components/input/markdown/index.d.ts.map +1 -1
- package/lib/components/input/markdown/index.js +287 -5
- package/lib/components/input/markdown/index.js.map +1 -1
- package/lib/tail.css +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A document's headings — the outline a reader navigates by.
|
|
4
|
+
*
|
|
5
|
+
* INFO: read from the markdown, not from the rendered DOM. The editor and the
|
|
6
|
+
* static renderer draw a heading through completely different paths (tiptap
|
|
7
|
+
* nodes vs markdown-it html), and an outline built from either one would only
|
|
8
|
+
* be correct in that one. The markdown is the single thing they agree on, so it
|
|
9
|
+
* is what the outline is derived from and what both of them are then labelled
|
|
10
|
+
* against.
|
|
11
|
+
*
|
|
12
|
+
* INFO: no id is ever written INTO the markdown. Ids are slugs computed from
|
|
13
|
+
* the heading text every time, so a note saved before this existed gets an
|
|
14
|
+
* outline with no migration, and adding one changes no stored byte — the same
|
|
15
|
+
* commitment the embed's `data-view` makes by omitting itself for the default.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.handleBuildHeadingAnchors = exports.handleInjectHeadingIds = exports.handleParseMarkdownHeadings = exports.handleDedupeSlug = exports.handleSlugHeading = exports.MIN_OUTLINE_HEADINGS = exports.MAX_OUTLINE_LEVEL = void 0;
|
|
19
|
+
exports.MAX_OUTLINE_LEVEL = 3;
|
|
20
|
+
/**
|
|
21
|
+
* INFO: a document with one or two headings is not navigated, it is read. The
|
|
22
|
+
* rail would cost a column of width to say what the first screen already shows.
|
|
23
|
+
*/
|
|
24
|
+
exports.MIN_OUTLINE_HEADINGS = 3;
|
|
25
|
+
var FENCE = /^\s{0,3}(`{3,}|~{3,})/;
|
|
26
|
+
var HEADING = /^(#{1,6})\s+(.*)$/;
|
|
27
|
+
/**
|
|
28
|
+
* Strips the inline markup a heading may carry, so the outline shows the words
|
|
29
|
+
* rather than the syntax around them.
|
|
30
|
+
*/
|
|
31
|
+
var handleCleanHeadingText = function (raw) {
|
|
32
|
+
return raw
|
|
33
|
+
// INFO: trailing #s of a closed atx heading — "## Title ##"
|
|
34
|
+
.replace(/\s+#+\s*$/, "")
|
|
35
|
+
// INFO: link and image syntax reduced to the text that was visible
|
|
36
|
+
.replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
37
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
38
|
+
.replace(/`([^`]*)`/g, "$1")
|
|
39
|
+
.replace(/[*_~]/g, "")
|
|
40
|
+
.replace(/<[^>]+>/g, "")
|
|
41
|
+
.replace(/\s+/g, " ")
|
|
42
|
+
.trim();
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The anchor id for a heading's text.
|
|
46
|
+
*
|
|
47
|
+
* INFO: exported because both renderers have to arrive at the same id from the
|
|
48
|
+
* same text without talking to each other — that identity is the whole
|
|
49
|
+
* mechanism, so it lives in one function rather than being spelled twice.
|
|
50
|
+
*/
|
|
51
|
+
var handleSlugHeading = function (text) {
|
|
52
|
+
var slug = handleCleanHeadingText(text)
|
|
53
|
+
.toLowerCase()
|
|
54
|
+
// INFO: punctuation is REMOVED rather than letters being kept. A
|
|
55
|
+
// `\p{L}`-style whitelist needs the unicode flag, which this library cannot
|
|
56
|
+
// use at an es5 target — and matching `[a-z0-9]` instead would slug a
|
|
57
|
+
// heading in Chinese or Malay to an empty string. Full-width punctuation is
|
|
58
|
+
// listed too, since it is what a CJK heading actually carries.
|
|
59
|
+
.replace(/["'’“”!?@#$%^&*()+=[\]{}<>,.:;/\\|`~]/g, "")
|
|
60
|
+
.replace(/[,。!?:;()【】、「」…]/g, "")
|
|
61
|
+
.replace(/\s+/g, "-")
|
|
62
|
+
.replace(/-+/g, "-")
|
|
63
|
+
.replace(/^-|-$/g, "");
|
|
64
|
+
// INFO: a heading of only punctuation would otherwise slug to "" and every
|
|
65
|
+
// such heading would share one id
|
|
66
|
+
return slug || "section";
|
|
67
|
+
};
|
|
68
|
+
exports.handleSlugHeading = handleSlugHeading;
|
|
69
|
+
/**
|
|
70
|
+
* Makes a slug unique within one document by suffixing repeats.
|
|
71
|
+
*
|
|
72
|
+
* INFO: two "Overview" headings are ordinary in a long page. Without this they
|
|
73
|
+
* share an id, and every click on the second one jumps to the first.
|
|
74
|
+
*/
|
|
75
|
+
var handleDedupeSlug = function (slug, seen) {
|
|
76
|
+
var _a;
|
|
77
|
+
var count = (_a = seen.get(slug)) !== null && _a !== void 0 ? _a : 0;
|
|
78
|
+
seen.set(slug, count + 1);
|
|
79
|
+
return count === 0 ? slug : "".concat(slug, "-").concat(count + 1);
|
|
80
|
+
};
|
|
81
|
+
exports.handleDedupeSlug = handleDedupeSlug;
|
|
82
|
+
/**
|
|
83
|
+
* Reads the outline out of a markdown document.
|
|
84
|
+
*
|
|
85
|
+
* INFO: fenced blocks are tracked and skipped, because a `# comment` inside a
|
|
86
|
+
* ```bash block and a `%% title` inside a ```mermaid one are code, not
|
|
87
|
+
* structure — a diagram-heavy page would otherwise produce an outline of its
|
|
88
|
+
* own source. The closing fence must be at least as long as the opening one,
|
|
89
|
+
* which is how a ``` block nested inside a ```` one stays inside it.
|
|
90
|
+
*/
|
|
91
|
+
var handleParseMarkdownHeadings = function (markdown) {
|
|
92
|
+
if (!markdown)
|
|
93
|
+
return [];
|
|
94
|
+
var headings = [];
|
|
95
|
+
var seen = new Map();
|
|
96
|
+
var fence = "";
|
|
97
|
+
for (var _i = 0, _a = markdown.split("\n"); _i < _a.length; _i++) {
|
|
98
|
+
var eachLine = _a[_i];
|
|
99
|
+
var fenceMatch = eachLine.match(FENCE);
|
|
100
|
+
if (fenceMatch) {
|
|
101
|
+
var marker = fenceMatch[1];
|
|
102
|
+
if (!fence) {
|
|
103
|
+
fence = marker;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
// INFO: only a fence of the same character and at least the same length
|
|
107
|
+
// closes the block
|
|
108
|
+
if (marker[0] === fence[0] && marker.length >= fence.length)
|
|
109
|
+
fence = "";
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (fence)
|
|
113
|
+
continue;
|
|
114
|
+
var headingMatch = eachLine.match(HEADING);
|
|
115
|
+
if (!headingMatch)
|
|
116
|
+
continue;
|
|
117
|
+
var level = headingMatch[1].length;
|
|
118
|
+
if (level > exports.MAX_OUTLINE_LEVEL)
|
|
119
|
+
continue;
|
|
120
|
+
var text = handleCleanHeadingText(headingMatch[2]);
|
|
121
|
+
// INFO: "###" with nothing after it is not a section anyone can navigate to
|
|
122
|
+
if (!text)
|
|
123
|
+
continue;
|
|
124
|
+
headings.push({
|
|
125
|
+
id: (0, exports.handleDedupeSlug)((0, exports.handleSlugHeading)(text), seen),
|
|
126
|
+
level: level,
|
|
127
|
+
text: text,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return headings;
|
|
131
|
+
};
|
|
132
|
+
exports.handleParseMarkdownHeadings = handleParseMarkdownHeadings;
|
|
133
|
+
/**
|
|
134
|
+
* Writes the outline's ids into rendered html, in document order.
|
|
135
|
+
*
|
|
136
|
+
* INFO: into the HTML STRING, not onto the mounted DOM. The static renderer sets
|
|
137
|
+
* its html through dangerouslySetInnerHTML, and React re-commits that html
|
|
138
|
+
* whenever it re-renders the element — which wipes every attribute added
|
|
139
|
+
* afterwards. Ids set from an effect therefore survived until the first
|
|
140
|
+
* re-render and then vanished silently, leaving an outline whose entries no
|
|
141
|
+
* longer scrolled anywhere. Being part of the html itself, these cannot be lost.
|
|
142
|
+
*
|
|
143
|
+
* INFO: the heading order is the only thing tying the two together, and it is
|
|
144
|
+
* reliable because both come from one parse of one document — the nth `<h1-3>`
|
|
145
|
+
* in the html is the nth entry in the outline. Levels deeper than the outline
|
|
146
|
+
* carry no id, which is why the pattern stops at h3.
|
|
147
|
+
*/
|
|
148
|
+
var handleInjectHeadingIds = function (html, headings) {
|
|
149
|
+
if (!headings.length)
|
|
150
|
+
return html;
|
|
151
|
+
var index = 0;
|
|
152
|
+
return html.replace(/<h([1-3])(\s[^>]*)?>/g, function (match, level, attrs) {
|
|
153
|
+
var heading = headings[index];
|
|
154
|
+
if (!heading)
|
|
155
|
+
return match;
|
|
156
|
+
index += 1;
|
|
157
|
+
return "<h".concat(level).concat(attrs !== null && attrs !== void 0 ? attrs : "", " id=\"").concat(heading.id, "\" data-heading-id=\"").concat(heading.id, "\">");
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
exports.handleInjectHeadingIds = handleInjectHeadingIds;
|
|
161
|
+
/**
|
|
162
|
+
* The id a heading node carries in the editor, as a ProseMirror decoration.
|
|
163
|
+
*
|
|
164
|
+
* INFO: a decoration rather than a node attribute or a DOM write. An attribute
|
|
165
|
+
* would reach `getHTML()` — turndown's input — and be written into the saved
|
|
166
|
+
* markdown; a DOM write is undone the next time ProseMirror redraws that node.
|
|
167
|
+
* Decorations are recomputed from the document on every redraw by construction,
|
|
168
|
+
* so an anchor cannot drift out of the view or into the file.
|
|
169
|
+
*/
|
|
170
|
+
var handleBuildHeadingAnchors = function (doc) {
|
|
171
|
+
var anchors = [];
|
|
172
|
+
var seen = new Map();
|
|
173
|
+
doc.descendants(function (node, pos) {
|
|
174
|
+
var _a, _b, _c, _d;
|
|
175
|
+
if (((_a = node.type) === null || _a === void 0 ? void 0 : _a.name) !== "heading")
|
|
176
|
+
return;
|
|
177
|
+
var level = (_c = (_b = node.attrs) === null || _b === void 0 ? void 0 : _b.level) !== null && _c !== void 0 ? _c : 1;
|
|
178
|
+
if (level > exports.MAX_OUTLINE_LEVEL)
|
|
179
|
+
return;
|
|
180
|
+
var text = ((_d = node.textContent) !== null && _d !== void 0 ? _d : "").trim();
|
|
181
|
+
if (!text)
|
|
182
|
+
return;
|
|
183
|
+
anchors.push({
|
|
184
|
+
from: pos,
|
|
185
|
+
to: pos + node.nodeSize,
|
|
186
|
+
id: (0, exports.handleDedupeSlug)((0, exports.handleSlugHeading)(text), seen),
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
return anchors;
|
|
190
|
+
};
|
|
191
|
+
exports.handleBuildHeadingAnchors = handleBuildHeadingAnchors;
|
|
192
|
+
//# sourceMappingURL=headings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headings.js","sourceRoot":"","sources":["../../../../src/components/input/markdown/headings.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { ReactElement } from "react";
|
|
2
2
|
import { MermaidLoader } from "./mermaid";
|
|
3
|
+
import { MarkdownHeading } from "./headings";
|
|
3
4
|
export type EmbedView = "grid" | "list";
|
|
4
5
|
export declare const handleParseEmbedView: (value?: string | null) => EmbedView;
|
|
5
6
|
export interface EmbedActions {
|
|
@@ -76,6 +77,16 @@ interface Props {
|
|
|
76
77
|
* until the supplied callback runs, so the host can confirm first.
|
|
77
78
|
*/
|
|
78
79
|
onRequestRemove?: (spaceId: string, remove: () => void) => void;
|
|
80
|
+
/**
|
|
81
|
+
* Called with the document's headings whenever they change, for a host that
|
|
82
|
+
* renders an outline beside the editor.
|
|
83
|
+
*
|
|
84
|
+
* INFO: the headings are read from the markdown rather than from the editor's
|
|
85
|
+
* nodes, so the list a host gets here is identical to the one MarkdownStatic
|
|
86
|
+
* reports for the same document — the editor and a published page cannot show
|
|
87
|
+
* different outlines for the same note.
|
|
88
|
+
*/
|
|
89
|
+
onHeadingsChange?: (headings: MarkdownHeading[]) => void;
|
|
79
90
|
/**
|
|
80
91
|
* Extra entries for the `/` menu, appended after the built-in formatting
|
|
81
92
|
* commands. Use for actions the host app owns, such as creating a database.
|
|
@@ -100,6 +111,11 @@ interface MarkdownStaticProps {
|
|
|
100
111
|
value: string;
|
|
101
112
|
darkMode?: boolean;
|
|
102
113
|
containerClassName?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Called with the document's headings whenever they change, for a host that
|
|
116
|
+
* renders an outline beside the page.
|
|
117
|
+
*/
|
|
118
|
+
onHeadingsChange?: (headings: MarkdownHeading[]) => void;
|
|
103
119
|
lib?: {
|
|
104
120
|
/**
|
|
105
121
|
* Loads mermaid, e.g. `() => import("mermaid")`. When given, ```mermaid
|
|
@@ -115,7 +131,7 @@ interface MarkdownStaticProps {
|
|
|
115
131
|
* many markdown fields stay responsive instead of freezing from 50+ ProseMirror
|
|
116
132
|
* instances or a heavy re-parse.
|
|
117
133
|
*/
|
|
118
|
-
export declare const MarkdownStatic: ({ id, label, labelExtra, subLabel, value, darkMode, containerClassName, lib, }: MarkdownStaticProps) => React.JSX.Element;
|
|
134
|
+
export declare const MarkdownStatic: ({ id, label, labelExtra, subLabel, value, darkMode, containerClassName, onHeadingsChange, lib, }: MarkdownStaticProps) => React.JSX.Element;
|
|
119
135
|
export declare const MarkdownInput: React.ForwardRefExoticComponent<Omit<Props, "ref"> & React.RefAttributes<unknown>>;
|
|
120
136
|
export {};
|
|
121
137
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/input/markdown/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/input/markdown/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -90,6 +90,7 @@ var TableCellsIcon_1 = __importDefault(require("@heroicons/react/24/solid/TableC
|
|
|
90
90
|
var core_1 = require("@tiptap/core");
|
|
91
91
|
var filePreview_1 = require("../../display/filePreview");
|
|
92
92
|
var state_1 = require("@tiptap/pm/state");
|
|
93
|
+
var view_1 = require("@tiptap/pm/view");
|
|
93
94
|
var extension_image_1 = __importDefault(require("@tiptap/extension-image"));
|
|
94
95
|
var extensions_1 = require("@tiptap/extensions");
|
|
95
96
|
var extension_subscript_1 = __importDefault(require("@tiptap/extension-subscript"));
|
|
@@ -108,6 +109,7 @@ var turndown_1 = __importDefault(require("turndown"));
|
|
|
108
109
|
var infoWindow_1 = require("../../advanced/infoWindow");
|
|
109
110
|
var loading_1 = require("../../display/loading");
|
|
110
111
|
var mermaid_1 = require("./mermaid");
|
|
112
|
+
var headings_1 = require("./headings");
|
|
111
113
|
var md = new markdown_it_1.default({
|
|
112
114
|
html: true,
|
|
113
115
|
breaks: true,
|
|
@@ -222,6 +224,17 @@ turndownService.addRule("image", {
|
|
|
222
224
|
var alt = img.getAttribute("alt") || "";
|
|
223
225
|
var src = img.getAttribute("src") || "";
|
|
224
226
|
var title = img.getAttribute("title") || "";
|
|
227
|
+
var width = img.getAttribute("width") || "";
|
|
228
|
+
// INFO: markdown has no syntax for a size, so a resized image is written as
|
|
229
|
+
// html — the same route <video>, the file attachment and the space embed
|
|
230
|
+
// take, and markdown-it runs with html:true so it parses back on load.
|
|
231
|
+
// Written ONLY when a width was actually set: an image nobody has resized
|
|
232
|
+
// still serialises as , so every document saved before resizing
|
|
233
|
+
// existed round-trips byte-for-byte.
|
|
234
|
+
if (width) {
|
|
235
|
+
var titleAttr = title ? " title=\"".concat(title, "\"") : "";
|
|
236
|
+
return "<img src=\"".concat(src, "\" alt=\"").concat(alt, "\"").concat(titleAttr, " width=\"").concat(width, "\" />");
|
|
237
|
+
}
|
|
225
238
|
if (title) {
|
|
226
239
|
return ".concat(src, " \"").concat(title, "\")");
|
|
227
240
|
}
|
|
@@ -771,6 +784,40 @@ var TrailingParagraph = core_1.Extension.create({
|
|
|
771
784
|
];
|
|
772
785
|
},
|
|
773
786
|
});
|
|
787
|
+
/**
|
|
788
|
+
* Gives every h1-h3 in the editor the id its outline entry scrolls to.
|
|
789
|
+
*
|
|
790
|
+
* INFO: a view decoration, so it is recomputed from the document on every redraw
|
|
791
|
+
* and never enters the document itself. The two alternatives both fail: a node
|
|
792
|
+
* attribute reaches `getHTML()` and would be written into the saved markdown,
|
|
793
|
+
* and a DOM write is undone the next time ProseMirror redraws the heading.
|
|
794
|
+
*/
|
|
795
|
+
var HeadingAnchors = core_1.Extension.create({
|
|
796
|
+
name: "headingAnchors",
|
|
797
|
+
addProseMirrorPlugins: function () {
|
|
798
|
+
return [
|
|
799
|
+
new state_1.Plugin({
|
|
800
|
+
key: new state_1.PluginKey("headingAnchors"),
|
|
801
|
+
props: {
|
|
802
|
+
// INFO: cast because the tree carries two copies of prosemirror-view —
|
|
803
|
+
// one hoisted, one under @tiptap/pm — so the DecorationSet built from
|
|
804
|
+
// `@tiptap/pm/view` is a different nominal type to the one tiptap's own
|
|
805
|
+
// Plugin signature was compiled against, despite being the same class
|
|
806
|
+
// at runtime. Importing the hoisted copy directly would fix the types
|
|
807
|
+
// by adding an undeclared dependency, which is the worse trade.
|
|
808
|
+
decorations: function (state) {
|
|
809
|
+
return view_1.DecorationSet.create(state.doc, (0, headings_1.handleBuildHeadingAnchors)(state.doc).map(function (eachAnchor) {
|
|
810
|
+
return view_1.Decoration.node(eachAnchor.from, eachAnchor.to, {
|
|
811
|
+
id: eachAnchor.id,
|
|
812
|
+
"data-heading-id": eachAnchor.id,
|
|
813
|
+
});
|
|
814
|
+
}));
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
}),
|
|
818
|
+
];
|
|
819
|
+
},
|
|
820
|
+
});
|
|
774
821
|
var SpaceEmbed = core_1.Node.create({
|
|
775
822
|
name: "spaceEmbed",
|
|
776
823
|
group: "block",
|
|
@@ -944,6 +991,206 @@ var DiagramCodeBlock = extension_code_block_1.default.extend({
|
|
|
944
991
|
return (0, react_1.ReactNodeViewRenderer)(CodeBlockComponent);
|
|
945
992
|
},
|
|
946
993
|
});
|
|
994
|
+
// INFO: below this an image is a thumbnail nobody can see the content of, and
|
|
995
|
+
// dragging past it is almost always an accident
|
|
996
|
+
var MIN_IMAGE_WIDTH = 80;
|
|
997
|
+
/**
|
|
998
|
+
* An image with drag handles on its left and right edges.
|
|
999
|
+
*
|
|
1000
|
+
* INFO: the width is previewed in local state while dragging and only committed
|
|
1001
|
+
* to the node on release. Writing it on every pointermove would put one
|
|
1002
|
+
* transaction per mouse move into the history — undo would take fifty presses to
|
|
1003
|
+
* get back — and each one fires onUpdate, which in a host with autosave is fifty
|
|
1004
|
+
* saves for one resize.
|
|
1005
|
+
*/
|
|
1006
|
+
var ResizableImageComponent = function (_a) {
|
|
1007
|
+
var _b;
|
|
1008
|
+
var node = _a.node, updateAttributes = _a.updateAttributes, editor = _a.editor, selected = _a.selected;
|
|
1009
|
+
var _c = (0, react_2.useState)(null), preview = _c[0], setPreview = _c[1];
|
|
1010
|
+
var _d = (0, react_2.useState)(false), hovered = _d[0], setHovered = _d[1];
|
|
1011
|
+
var _e = (0, react_2.useState)(null), dragging = _e[0], setDragging = _e[1];
|
|
1012
|
+
var imageRef = (0, react_2.useRef)(null);
|
|
1013
|
+
var startRef = (0, react_2.useRef)({ x: 0, width: 0 });
|
|
1014
|
+
// INFO: the pointerup handler needs the width the last move produced, and it
|
|
1015
|
+
// is registered once — reading it from state would read the value from the
|
|
1016
|
+
// render that registered it, which is the width before the drag started
|
|
1017
|
+
var previewRef = (0, react_2.useRef)(null);
|
|
1018
|
+
var _f = node.attrs, src = _f.src, alt = _f.alt, title = _f.title, width = _f.width;
|
|
1019
|
+
var editable = !!(editor === null || editor === void 0 ? void 0 : editor.isEditable);
|
|
1020
|
+
// INFO: the preview wins while dragging so the image tracks the pointer
|
|
1021
|
+
// without the document being edited on the way
|
|
1022
|
+
var shownWidth = (_b = preview !== null && preview !== void 0 ? preview : width) !== null && _b !== void 0 ? _b : null;
|
|
1023
|
+
(0, react_2.useEffect)(function () {
|
|
1024
|
+
if (!dragging)
|
|
1025
|
+
return;
|
|
1026
|
+
return handleObserveDrag();
|
|
1027
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1028
|
+
}, [dragging]);
|
|
1029
|
+
var handleObserveDrag = function () {
|
|
1030
|
+
var edge = dragging;
|
|
1031
|
+
var handleMove = function (event) {
|
|
1032
|
+
var _a, _b, _c;
|
|
1033
|
+
var delta = event.clientX - startRef.current.x;
|
|
1034
|
+
// INFO: dragging the left handle outward is dragging left, so the sign
|
|
1035
|
+
// flips — the alternative is a handle that shrinks when pulled away
|
|
1036
|
+
var next = startRef.current.width + (edge === "left" ? -delta : delta);
|
|
1037
|
+
// INFO: an event that carries no coordinate makes this NaN, and NaN is
|
|
1038
|
+
// truthy enough to be stored and falsy enough to render as no width at
|
|
1039
|
+
// all — so the image would silently lose the size it already had. There is
|
|
1040
|
+
// nothing to resize to without a coordinate, so the move is ignored.
|
|
1041
|
+
if (!Number.isFinite(next))
|
|
1042
|
+
return;
|
|
1043
|
+
// INFO: capped at the width of the COLUMN the image sits in, because a
|
|
1044
|
+
// wider image would be clipped by the page with no handle left to drag it
|
|
1045
|
+
// back. Measured from the editor element, not from the node view wrapper:
|
|
1046
|
+
// the wrapper is `fit-content` so the selection outline hugs the image,
|
|
1047
|
+
// which means measuring it caps the image at its own current width — an
|
|
1048
|
+
// image could then be shrunk but never grown again. A container that
|
|
1049
|
+
// cannot be measured is no reason to refuse the resize.
|
|
1050
|
+
var column = (_c = (_b = (_a = imageRef.current) === null || _a === void 0 ? void 0 : _a.closest(".ProseMirror")) === null || _b === void 0 ? void 0 : _b.clientWidth) !== null && _c !== void 0 ? _c : 0;
|
|
1051
|
+
var max = column > 0 ? column : Number.MAX_SAFE_INTEGER;
|
|
1052
|
+
var resolved = Math.round(Math.max(MIN_IMAGE_WIDTH, Math.min(next, max)));
|
|
1053
|
+
previewRef.current = resolved;
|
|
1054
|
+
setPreview(resolved);
|
|
1055
|
+
};
|
|
1056
|
+
/**
|
|
1057
|
+
* INFO: the single point where the document is edited. Committing here
|
|
1058
|
+
* rather than from the effect's cleanup keeps it out of a state updater —
|
|
1059
|
+
* React may run an updater more than once, and a write to the document is
|
|
1060
|
+
* not something that may happen twice.
|
|
1061
|
+
*/
|
|
1062
|
+
var handleUp = function () {
|
|
1063
|
+
var resolved = previewRef.current;
|
|
1064
|
+
previewRef.current = null;
|
|
1065
|
+
setDragging(null);
|
|
1066
|
+
setPreview(null);
|
|
1067
|
+
if (resolved !== null && resolved !== width)
|
|
1068
|
+
updateAttributes({ width: resolved });
|
|
1069
|
+
};
|
|
1070
|
+
window.addEventListener("pointermove", handleMove);
|
|
1071
|
+
window.addEventListener("pointerup", handleUp);
|
|
1072
|
+
window.addEventListener("pointercancel", handleUp);
|
|
1073
|
+
return function () {
|
|
1074
|
+
window.removeEventListener("pointermove", handleMove);
|
|
1075
|
+
window.removeEventListener("pointerup", handleUp);
|
|
1076
|
+
window.removeEventListener("pointercancel", handleUp);
|
|
1077
|
+
};
|
|
1078
|
+
};
|
|
1079
|
+
var handleStartDrag = function (edge) { return function (event) {
|
|
1080
|
+
var _a, _b, _c, _d;
|
|
1081
|
+
if (!editable)
|
|
1082
|
+
return;
|
|
1083
|
+
// INFO: capture phase, and propagation stopped. ProseMirror handles pointer
|
|
1084
|
+
// events on the editor element to move the selection and to drag a node
|
|
1085
|
+
// around the document — an image IS draggable — and its listener sits on an
|
|
1086
|
+
// ancestor of this handle, so a bubble-phase handler never runs: the drag
|
|
1087
|
+
// silently did nothing in a real browser while passing in jsdom, where no
|
|
1088
|
+
// editor view competes for the event.
|
|
1089
|
+
event.preventDefault();
|
|
1090
|
+
event.stopPropagation();
|
|
1091
|
+
// INFO: on a touch screen the finger leaves the 6px handle almost at once,
|
|
1092
|
+
// and without capture every later move goes to whatever is underneath — the
|
|
1093
|
+
// drag appeared to stick after a few pixels. Capture keeps the whole stream
|
|
1094
|
+
// coming here until release.
|
|
1095
|
+
try {
|
|
1096
|
+
(_b = (_a = event.currentTarget) === null || _a === void 0 ? void 0 : _a.setPointerCapture) === null || _b === void 0 ? void 0 : _b.call(_a, event.pointerId);
|
|
1097
|
+
}
|
|
1098
|
+
catch (err) {
|
|
1099
|
+
// INFO: a mouse event carries no pointerId in every browser; the drag
|
|
1100
|
+
// works from the window listeners regardless
|
|
1101
|
+
}
|
|
1102
|
+
startRef.current = {
|
|
1103
|
+
x: event.clientX,
|
|
1104
|
+
width: (_d = (_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.getBoundingClientRect().width) !== null && _d !== void 0 ? _d : 0,
|
|
1105
|
+
};
|
|
1106
|
+
setDragging(edge);
|
|
1107
|
+
}; };
|
|
1108
|
+
var renderHandle = function (edge) { return (react_2.default.createElement("div", { role: "presentation", "data-testid": "image-resize-".concat(edge), onPointerDownCapture: handleStartDrag(edge),
|
|
1109
|
+
// INFO: the editor must not treat a grab of the handle as a text selection
|
|
1110
|
+
// or a node drag either, and it listens for mousedown as well
|
|
1111
|
+
onMouseDownCapture: function (event) {
|
|
1112
|
+
event.preventDefault();
|
|
1113
|
+
event.stopPropagation();
|
|
1114
|
+
}, draggable: false,
|
|
1115
|
+
// INFO: the hit area is far larger than the bar it draws. A 6px target is
|
|
1116
|
+
// reachable with a mouse and not with a thumb, which is most of why
|
|
1117
|
+
// resizing felt unresponsive on a phone rather than anything in the maths.
|
|
1118
|
+
className: "absolute ".concat(edge === "left" ? "-left-1" : "-right-1", " top-1/2 flex h-16 w-8 -translate-y-1/2 cursor-ew-resize items-center justify-center"),
|
|
1119
|
+
// INFO: touch-action none is what makes a touch drag possible at all —
|
|
1120
|
+
// otherwise the browser claims the gesture as a scroll and cancels the
|
|
1121
|
+
// pointer stream after the first few pixels, which reads as the handle
|
|
1122
|
+
// being unresponsive
|
|
1123
|
+
style: { touchAction: "none" } },
|
|
1124
|
+
react_2.default.createElement("div", { className: "h-10 w-1.5 rounded-full bg-white/90 shadow ring-1 ring-gray-900/20" }))); };
|
|
1125
|
+
/**
|
|
1126
|
+
* INFO: a double click returns the image to its natural width by clearing the
|
|
1127
|
+
* attribute rather than measuring one — so it goes back to serialising as
|
|
1128
|
+
* plain ``, exactly as it did before it was ever resized.
|
|
1129
|
+
*/
|
|
1130
|
+
var handleResetWidth = function () {
|
|
1131
|
+
if (!editable || !width)
|
|
1132
|
+
return;
|
|
1133
|
+
updateAttributes({ width: null });
|
|
1134
|
+
};
|
|
1135
|
+
var showHandles = editable && (hovered || selected || !!dragging);
|
|
1136
|
+
return (react_2.default.createElement(react_1.NodeViewWrapper, { className: "my-4",
|
|
1137
|
+
// INFO: the selection outline and the handles sit on a box the size of the
|
|
1138
|
+
// image, not the width of the line, so they do not float away from it
|
|
1139
|
+
style: { width: "fit-content", maxWidth: "100%" }, onMouseEnter: function () { return setHovered(true); }, onMouseLeave: function () { return setHovered(false); },
|
|
1140
|
+
// INFO: a touch screen has no hover, so a tap has to be what reveals the
|
|
1141
|
+
// handles. ProseMirror also selects the node on tap, but only once the
|
|
1142
|
+
// editor has focus — relying on that alone left an image on a phone with
|
|
1143
|
+
// no handles at all on first touch.
|
|
1144
|
+
onPointerDown: function (event) {
|
|
1145
|
+
if (event.pointerType !== "mouse")
|
|
1146
|
+
setHovered(true);
|
|
1147
|
+
} },
|
|
1148
|
+
react_2.default.createElement("div", { className: "relative inline-block max-w-full leading-none" },
|
|
1149
|
+
react_2.default.createElement("img", { ref: imageRef, src: src, alt: alt !== null && alt !== void 0 ? alt : "", title: title !== null && title !== void 0 ? title : undefined, draggable: false, onDoubleClick: handleResetWidth, className: "block h-auto max-w-full rounded ".concat(selected ? "ring-2 ring-primary-500" : ""), style: {
|
|
1150
|
+
// INFO: no width attribute means the image draws at its own size,
|
|
1151
|
+
// capped the way it always was
|
|
1152
|
+
width: shownWidth ? "".concat(shownWidth, "px") : undefined,
|
|
1153
|
+
maxWidth: shownWidth ? "100%" : "600px",
|
|
1154
|
+
// INFO: the browser would otherwise keep showing the resize cursor
|
|
1155
|
+
// and the pointer could slip onto the image mid-drag
|
|
1156
|
+
pointerEvents: dragging ? "none" : undefined,
|
|
1157
|
+
} }),
|
|
1158
|
+
showHandles && renderHandle("left"),
|
|
1159
|
+
showHandles && renderHandle("right"))));
|
|
1160
|
+
};
|
|
1161
|
+
/**
|
|
1162
|
+
* INFO: only a width is stored, never a height — the image keeps its own aspect
|
|
1163
|
+
* ratio, which is what makes a one-axis handle enough and makes it impossible to
|
|
1164
|
+
* save a distorted image.
|
|
1165
|
+
*/
|
|
1166
|
+
var ResizableImage = extension_image_1.default.extend({
|
|
1167
|
+
addAttributes: function () {
|
|
1168
|
+
var _a;
|
|
1169
|
+
return __assign(__assign({}, (_a = this.parent) === null || _a === void 0 ? void 0 : _a.call(this)), { width: {
|
|
1170
|
+
default: null,
|
|
1171
|
+
parseHTML: function (element) {
|
|
1172
|
+
var _a, _b;
|
|
1173
|
+
var attr = element.getAttribute("width");
|
|
1174
|
+
if (attr && !Number.isNaN(Number(attr)))
|
|
1175
|
+
return Number(attr);
|
|
1176
|
+
// INFO: a document written by an older editor carries the size in the
|
|
1177
|
+
// inline style instead, so it is read back rather than being lost
|
|
1178
|
+
var style = (_b = (_a = element.style) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : "";
|
|
1179
|
+
var match = style.match(/^(\d+(?:\.\d+)?)px$/);
|
|
1180
|
+
return match ? Math.round(Number(match[1])) : null;
|
|
1181
|
+
},
|
|
1182
|
+
// INFO: the attribute, not a style — this is what turndown reads to
|
|
1183
|
+
// decide between html and ``, and what a browser honours when
|
|
1184
|
+
// the published page sets the html raw
|
|
1185
|
+
renderHTML: function (attributes) {
|
|
1186
|
+
return attributes.width ? { width: attributes.width } : {};
|
|
1187
|
+
},
|
|
1188
|
+
} });
|
|
1189
|
+
},
|
|
1190
|
+
addNodeView: function () {
|
|
1191
|
+
return (0, react_1.ReactNodeViewRenderer)(ResizableImageComponent);
|
|
1192
|
+
},
|
|
1193
|
+
});
|
|
947
1194
|
// Slash command items
|
|
948
1195
|
var slashCommands = [
|
|
949
1196
|
{
|
|
@@ -1061,7 +1308,7 @@ var SlashMenuList = function (_a) {
|
|
|
1061
1308
|
react_2.default.createElement("span", { className: "text-sm ".concat(darkMode ? "text-gray-200" : "text-gray-700") }, item.label))); })) : (react_2.default.createElement("div", { className: "px-4 py-2 text-sm text-gray-500" }, "No results"))));
|
|
1062
1309
|
};
|
|
1063
1310
|
var MarkdownInputContent = function (_a) {
|
|
1064
|
-
var innerRef = _a.innerRef, id = _a.id, value = _a.value, label = _a.label, subLabel = _a.subLabel, labelExtra = _a.labelExtra, infoElement = _a.infoElement, placeholder = _a.placeholder, optional = _a.optional, disabled = _a.disabled, containerClassName = _a.containerClassName, heightClassName = _a.heightClassName, borderless = _a.borderless, onChange = _a.onChange, compress = _a.compress, lib = _a.lib, uploadEndpoint = _a.uploadEndpoint, enableImageUpload = _a.enableImageUpload, enableVideoUpload = _a.enableVideoUpload, enableFileUpload = _a.enableFileUpload, darkMode = _a.darkMode, readOnly = _a.readOnly, customElement = _a.customElement, renderEmbed = _a.renderEmbed, onRequestRemove = _a.onRequestRemove, customCommands = _a.customCommands;
|
|
1311
|
+
var innerRef = _a.innerRef, id = _a.id, value = _a.value, label = _a.label, subLabel = _a.subLabel, labelExtra = _a.labelExtra, infoElement = _a.infoElement, placeholder = _a.placeholder, optional = _a.optional, disabled = _a.disabled, containerClassName = _a.containerClassName, heightClassName = _a.heightClassName, borderless = _a.borderless, onChange = _a.onChange, compress = _a.compress, lib = _a.lib, uploadEndpoint = _a.uploadEndpoint, enableImageUpload = _a.enableImageUpload, enableVideoUpload = _a.enableVideoUpload, enableFileUpload = _a.enableFileUpload, darkMode = _a.darkMode, readOnly = _a.readOnly, customElement = _a.customElement, renderEmbed = _a.renderEmbed, onRequestRemove = _a.onRequestRemove, onHeadingsChange = _a.onHeadingsChange, customCommands = _a.customCommands;
|
|
1065
1312
|
var _b = (0, react_2.useState)(""), error = _b[0], setError = _b[1];
|
|
1066
1313
|
var _c = (0, react_2.useState)(false), showTableMenu = _c[0], setShowTableMenu = _c[1];
|
|
1067
1314
|
var _d = (0, react_2.useState)(3), tableCols = _d[0], setTableCols = _d[1];
|
|
@@ -1228,6 +1475,7 @@ var MarkdownInputContent = function (_a) {
|
|
|
1228
1475
|
extension_list_1.TaskItem.configure({
|
|
1229
1476
|
nested: true,
|
|
1230
1477
|
}),
|
|
1478
|
+
HeadingAnchors,
|
|
1231
1479
|
SpaceEmbed.configure({
|
|
1232
1480
|
getRenderEmbed: function () { return renderEmbedRef.current; },
|
|
1233
1481
|
getOnRequestRemove: function () { return onRequestRemoveRef.current; },
|
|
@@ -1240,7 +1488,10 @@ var MarkdownInputContent = function (_a) {
|
|
|
1240
1488
|
extension_table_1.TableRow,
|
|
1241
1489
|
extension_table_1.TableHeader,
|
|
1242
1490
|
extension_table_1.TableCell,
|
|
1243
|
-
|
|
1491
|
+
// INFO: the same image node with drag handles. The style stays for the
|
|
1492
|
+
// html path — getHTML() is turndown's input, and a document opened by
|
|
1493
|
+
// anything that renders that html directly still gets the 600px cap.
|
|
1494
|
+
ResizableImage.configure({
|
|
1244
1495
|
inline: false,
|
|
1245
1496
|
allowBase64: true,
|
|
1246
1497
|
HTMLAttributes: {
|
|
@@ -1763,6 +2014,18 @@ var MarkdownInputContent = function (_a) {
|
|
|
1763
2014
|
}
|
|
1764
2015
|
}
|
|
1765
2016
|
}, [value, editor, pendingImagesCount]);
|
|
2017
|
+
/**
|
|
2018
|
+
* INFO: the outline of the document being edited. The ids the headings carry
|
|
2019
|
+
* in the view are produced by the HeadingAnchors plugin from the same slug
|
|
2020
|
+
* function, so an entry here and the anchor it scrolls to cannot disagree.
|
|
2021
|
+
*/
|
|
2022
|
+
var headings = (0, react_2.useMemo)(function () { return (0, headings_1.handleParseMarkdownHeadings)(value); }, [value]);
|
|
2023
|
+
var onHeadingsChangeRef = (0, react_2.useRef)(onHeadingsChange);
|
|
2024
|
+
onHeadingsChangeRef.current = onHeadingsChange;
|
|
2025
|
+
(0, react_2.useEffect)(function () {
|
|
2026
|
+
var _a;
|
|
2027
|
+
(_a = onHeadingsChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onHeadingsChangeRef, headings);
|
|
2028
|
+
}, [headings]);
|
|
1766
2029
|
(0, react_2.useImperativeHandle)(innerRef, function () {
|
|
1767
2030
|
var methods = {
|
|
1768
2031
|
handleSubmit: function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -2152,7 +2415,7 @@ var MarkdownInputContent = function (_a) {
|
|
|
2152
2415
|
* instances or a heavy re-parse.
|
|
2153
2416
|
*/
|
|
2154
2417
|
var MarkdownStatic = function (_a) {
|
|
2155
|
-
var id = _a.id, label = _a.label, labelExtra = _a.labelExtra, subLabel = _a.subLabel, value = _a.value, darkMode = _a.darkMode, containerClassName = _a.containerClassName, lib = _a.lib;
|
|
2418
|
+
var id = _a.id, label = _a.label, labelExtra = _a.labelExtra, subLabel = _a.subLabel, value = _a.value, darkMode = _a.darkMode, containerClassName = _a.containerClassName, onHeadingsChange = _a.onHeadingsChange, lib = _a.lib;
|
|
2156
2419
|
var contentRef = (0, react_2.useRef)(null);
|
|
2157
2420
|
var labelColor = darkMode ? "text-white" : "text-gray-700";
|
|
2158
2421
|
var subColor = darkMode ? "text-gray-300" : "text-gray-500";
|
|
@@ -2178,9 +2441,17 @@ var MarkdownStatic = function (_a) {
|
|
|
2178
2441
|
};
|
|
2179
2442
|
// INFO: markdown-it is fast (~ms/field); memoize on value so a read view with
|
|
2180
2443
|
// many fields never re-parses on re-render — the source of the repeated freeze.
|
|
2181
|
-
var
|
|
2444
|
+
var headings = (0, react_2.useMemo)(function () { return (0, headings_1.handleParseMarkdownHeadings)(value); }, [value]);
|
|
2445
|
+
var html = (0, react_2.useMemo)(function () {
|
|
2446
|
+
return value
|
|
2447
|
+
? // INFO: the ids go into the html here, not onto the DOM afterwards —
|
|
2448
|
+
// React re-commits this string on re-render and would wipe anything
|
|
2449
|
+
// added to the mounted nodes
|
|
2450
|
+
(0, headings_1.handleInjectHeadingIds)(handleRenderAttachments(md.render(value)), headings)
|
|
2451
|
+
: "";
|
|
2452
|
+
},
|
|
2182
2453
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2183
|
-
[value]);
|
|
2454
|
+
[value, headings]);
|
|
2184
2455
|
// INFO: the html is set raw, so diagrams are drawn over the mounted DOM once
|
|
2185
2456
|
// it matches this value. A document with no mermaid block never loads mermaid.
|
|
2186
2457
|
var mermaidLoader = lib === null || lib === void 0 ? void 0 : lib.mermaid;
|
|
@@ -2189,6 +2460,17 @@ var MarkdownStatic = function (_a) {
|
|
|
2189
2460
|
return;
|
|
2190
2461
|
(0, mermaid_1.handleRenderMermaidBlocks)(contentRef.current, mermaidLoader);
|
|
2191
2462
|
}, [html, mermaidLoader]);
|
|
2463
|
+
/**
|
|
2464
|
+
* INFO: the same outline the editor reports, from the same function — a
|
|
2465
|
+
* published page and the note it was published from cannot list different
|
|
2466
|
+
* sections.
|
|
2467
|
+
*/
|
|
2468
|
+
var onHeadingsChangeRef = (0, react_2.useRef)(onHeadingsChange);
|
|
2469
|
+
onHeadingsChangeRef.current = onHeadingsChange;
|
|
2470
|
+
(0, react_2.useEffect)(function () {
|
|
2471
|
+
var _a;
|
|
2472
|
+
(_a = onHeadingsChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onHeadingsChangeRef, headings);
|
|
2473
|
+
}, [headings]);
|
|
2192
2474
|
return (react_2.default.createElement("div", { className: defaultClassName },
|
|
2193
2475
|
react_2.default.createElement("div", { className: "flex flex-col" },
|
|
2194
2476
|
react_2.default.createElement("label", { className: "block text-sm font-medium ".concat(labelColor) },
|