@aicayzer/inkkit 0.0.18 → 0.1.1
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/NOTICE +4 -2
- package/README.md +11 -7
- package/dist/THIRD-PARTY-NOTICES.txt +2742 -0
- package/dist/chunks/core-CYp5ONPO.js +56519 -0
- package/dist/chunks/highlight-BW9W8PtS.js +14122 -0
- package/dist/core.d.ts +2 -5
- package/dist/core.js +2 -2
- package/dist/editor.d.ts +229 -47
- package/dist/index.js +5 -3
- package/dist/style.css +231 -202
- package/dist/types.d.ts +143 -18
- package/package.json +16 -13
- package/dist/autolink.d.ts +0 -1
- package/dist/autolink.js +0 -18
- package/dist/callouts.d.ts +0 -25
- package/dist/callouts.js +0 -288
- package/dist/clipboard.d.ts +0 -11
- package/dist/clipboard.js +0 -261
- package/dist/code-fences.d.ts +0 -8
- package/dist/code-fences.js +0 -171
- package/dist/comments.d.ts +0 -76
- package/dist/comments.js +0 -545
- package/dist/cursor.d.ts +0 -2
- package/dist/cursor.js +0 -40
- package/dist/decorations.d.ts +0 -2
- package/dist/decorations.js +0 -67
- package/dist/dialect.d.ts +0 -9
- package/dist/dialect.js +0 -446
- package/dist/drop-placement.d.ts +0 -19
- package/dist/drop-placement.js +0 -47
- package/dist/editor.js +0 -2673
- package/dist/files.d.ts +0 -40
- package/dist/files.js +0 -133
- package/dist/highlight.d.ts +0 -1
- package/dist/highlight.js +0 -136
- package/dist/html-import.d.ts +0 -13
- package/dist/html-import.js +0 -477
- package/dist/image-blocks.d.ts +0 -86
- package/dist/image-blocks.js +0 -329
- package/dist/images.d.ts +0 -51
- package/dist/images.js +0 -398
- package/dist/inline-highlight.d.ts +0 -6
- package/dist/inline-highlight.js +0 -112
- package/dist/labels.d.ts +0 -36
- package/dist/labels.js +0 -34
- package/dist/link-safety.d.ts +0 -17
- package/dist/link-safety.js +0 -29
- package/dist/linked-syntax.d.ts +0 -48
- package/dist/linked-syntax.js +0 -320
- package/dist/literals.d.ts +0 -20
- package/dist/literals.js +0 -216
- package/dist/mermaid.d.ts +0 -28
- package/dist/mermaid.js +0 -404
- package/dist/outline.d.ts +0 -22
- package/dist/outline.js +0 -79
- package/dist/paste-html.d.ts +0 -31
- package/dist/paste-html.js +0 -462
- package/dist/paste.d.ts +0 -33
- package/dist/paste.js +0 -591
- package/dist/portable-files.d.ts +0 -10
- package/dist/portable-files.js +0 -36
- package/dist/preserve.d.ts +0 -38
- package/dist/preserve.js +0 -1396
- package/dist/print.d.ts +0 -7
- package/dist/print.js +0 -406
- package/dist/reference-clipboard.d.ts +0 -8
- package/dist/reference-clipboard.js +0 -201
- package/dist/references.d.ts +0 -16
- package/dist/references.js +0 -418
- package/dist/scroll.d.ts +0 -15
- package/dist/scroll.js +0 -27
- package/dist/search.d.ts +0 -15
- package/dist/search.js +0 -202
- package/dist/selection.d.ts +0 -1
- package/dist/selection.js +0 -59
- package/dist/source.d.ts +0 -16
- package/dist/source.js +0 -57
- package/dist/spreadsheet.d.ts +0 -8
- package/dist/spreadsheet.js +0 -289
- package/dist/table-alignment.d.ts +0 -2
- package/dist/table-alignment.js +0 -46
- package/dist/tables.d.ts +0 -27
- package/dist/tables.js +0 -344
- package/dist/tasks.d.ts +0 -3
- package/dist/tasks.js +0 -51
- package/dist/text-ranges.d.ts +0 -29
- package/dist/text-ranges.js +0 -256
- package/dist/types.js +0 -8
- package/dist/wiki-view.d.ts +0 -21
- package/dist/wiki-view.js +0 -78
package/dist/mermaid.js
DELETED
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { editorLabels } from './labels.js';
|
|
2
|
-
import DOMPurify from 'dompurify';
|
|
3
|
-
import { Plugin, PluginKey } from '@milkdown/kit/prose/state';
|
|
4
|
-
import { Decoration, DecorationSet } from '@milkdown/kit/prose/view';
|
|
5
|
-
import { $prose } from '@milkdown/kit/utils';
|
|
6
|
-
import { InkKitError } from './types.js';
|
|
7
|
-
export function isMermaid(node) {
|
|
8
|
-
return node.type.name === 'code_block' && node.attrs.language === 'mermaid';
|
|
9
|
-
}
|
|
10
|
-
const refusal = 'HTML, styles, actions, resources and diagram configuration are disabled. The source is retained.';
|
|
11
|
-
// CSS functions that can load an image or display another element. Mermaid's
|
|
12
|
-
// own styles use none of them apart from url() for in-document markers.
|
|
13
|
-
const cssResource = /url\s*\(\s*[^#\s]|(?:image-set|cross-fade|image|element|src)\s*\(/i;
|
|
14
|
-
// Annotation syntax in the positions the pinned grammars read it: class
|
|
15
|
-
// annotations on their own line, before a class name or after `class Name`, and
|
|
16
|
-
// the state diagram fork, join and choice markers after `state Name`.
|
|
17
|
-
const annotationName = String.raw `\w(?:[\w -]*\w)?`;
|
|
18
|
-
const classAnnotation = new RegExp(String.raw `(?<=^[ \t]*(?:class[ \t]+[^\s<>"]+(?:\["[^"\n<>]*"\])?[ \t]+)?)<<(${annotationName})>>(?=[ \t{]|$)`, 'gm');
|
|
19
|
-
const stateAnnotation = /(?<=^[ \t]*state[ \t]+[^\s<>"]+[ \t]+)<<(fork|join|choice)>>(?=[ \t]*$)/gim;
|
|
20
|
-
const stateBrackets = /^[ \t]*state[ \t].*\[\[(fork|join|choice)\]\]/gim;
|
|
21
|
-
// Separates the annotations that Mermaid may read from the rest of the source,
|
|
22
|
-
// which must pass the HTML check. Whether each one became an annotation is
|
|
23
|
-
// checked in the parse result.
|
|
24
|
-
function annotations(source) {
|
|
25
|
-
const header = /^\s*(?:%%[^\n]*\n\s*)*(classDiagram|stateDiagram)/.exec(source)?.[1];
|
|
26
|
-
const pattern = header === 'classDiagram'
|
|
27
|
-
? classAnnotation
|
|
28
|
-
: header === 'stateDiagram'
|
|
29
|
-
? stateAnnotation
|
|
30
|
-
: undefined;
|
|
31
|
-
if (!pattern)
|
|
32
|
-
return { names: [], rest: source };
|
|
33
|
-
const names = [];
|
|
34
|
-
const rest = source.replace(pattern, (_, name) => {
|
|
35
|
-
names.push(header === 'stateDiagram' ? name.toLowerCase() : name);
|
|
36
|
-
return '';
|
|
37
|
-
});
|
|
38
|
-
// The class lexer skips comments, so a comment after << would hide an
|
|
39
|
-
// annotation name from the HTML check.
|
|
40
|
-
if (header === 'classDiagram' && /<<\s*%%/.test(rest))
|
|
41
|
-
throw new Error(refusal);
|
|
42
|
-
return { names, rest };
|
|
43
|
-
}
|
|
44
|
-
export function validateDiagram(source) {
|
|
45
|
-
if (source.length > 30000)
|
|
46
|
-
throw new Error('The diagram exceeds the 30,000 character rendering limit.');
|
|
47
|
-
if (!/^\s*(?:%%[^\n]*\n\s*)*(?:flowchart|graph|sequenceDiagram|classDiagram|stateDiagram(?:-v2)?|erDiagram|pie)\b/.test(source))
|
|
48
|
-
throw new Error('This diagram type is not supported. The source is retained.');
|
|
49
|
-
const found = annotations(source);
|
|
50
|
-
// Mermaid can load image bytes before sanitising its result, so resource and
|
|
51
|
-
// configuration syntax is refused anywhere in the source, labels included.
|
|
52
|
-
// Statements are checked separately, in Mermaid's parse result.
|
|
53
|
-
if (/%%\{|^\s*---|<\s*[a-z!/?]|&(?:#\w+|[a-z]+);|@\{|\$\$|(?:https?|data|file|javascript|vbscript|blob):|\/\/|url\s*\(|image-set\s*\(|cross-fade\s*\(|@import|\\/im.test(found.rest))
|
|
54
|
-
throw new Error(refusal);
|
|
55
|
-
return found;
|
|
56
|
-
}
|
|
57
|
-
const entries = (value) => value instanceof Map ? [...value.values()] : Array.isArray(value) ? value : [];
|
|
58
|
-
const filled = (value) => value instanceof Map
|
|
59
|
-
? value.size > 0
|
|
60
|
-
: Array.isArray(value)
|
|
61
|
-
? value.length > 0
|
|
62
|
-
: typeof value === 'object' && value !== null
|
|
63
|
-
? Object.keys(value).length > 0
|
|
64
|
-
: Boolean(value);
|
|
65
|
-
// In strict mode a flowchart records a click handler only as this class.
|
|
66
|
-
const clickable = (classes) => (Array.isArray(classes) ? classes : String(classes ?? '').split(/\s+/))
|
|
67
|
-
.map(String)
|
|
68
|
-
.includes('clickable');
|
|
69
|
-
const unchecked = () => new Error('The diagram could not be checked. The source is retained.');
|
|
70
|
-
// Visits each object reachable from node once, until visit returns true.
|
|
71
|
-
function walk(node, visit, seen = new Set()) {
|
|
72
|
-
if (typeof node !== 'object' || node === null || seen.has(node))
|
|
73
|
-
return false;
|
|
74
|
-
seen.add(node);
|
|
75
|
-
if (visit(node))
|
|
76
|
-
return true;
|
|
77
|
-
return Object.values(node).some((value) => walk(value, visit, seen));
|
|
78
|
-
}
|
|
79
|
-
const statementIn = (node, kinds) => walk(node, (entry) => kinds.includes(String(entry.stmt)));
|
|
80
|
-
// Refuses styles, class definitions, link styles, links, click handlers,
|
|
81
|
-
// tooltips, icons and images wherever the grammar accepted them. The names are
|
|
82
|
-
// those of the pinned Mermaid's diagram databases; if one is missing, the
|
|
83
|
-
// diagram is refused rather than passed unchecked.
|
|
84
|
-
function refuseDirectives({ type, db }) {
|
|
85
|
-
const store = db;
|
|
86
|
-
const field = (name) => {
|
|
87
|
-
if (!(name in store))
|
|
88
|
-
throw unchecked();
|
|
89
|
-
return store[name];
|
|
90
|
-
};
|
|
91
|
-
const read = (name) => {
|
|
92
|
-
const accessor = field(name);
|
|
93
|
-
if (typeof accessor !== 'function')
|
|
94
|
-
throw unchecked();
|
|
95
|
-
return accessor.call(db);
|
|
96
|
-
};
|
|
97
|
-
let found;
|
|
98
|
-
switch (type) {
|
|
99
|
-
case 'flowchart-v2': {
|
|
100
|
-
const edges = read('getEdges');
|
|
101
|
-
found =
|
|
102
|
-
filled(read('getClasses')) ||
|
|
103
|
-
filled(field('tooltips')) ||
|
|
104
|
-
filled(edges.defaultStyle) ||
|
|
105
|
-
edges.defaultInterpolate !== undefined ||
|
|
106
|
-
entries(read('getVertices')).some((vertex) => filled(vertex.styles) ||
|
|
107
|
-
Boolean(vertex.link || vertex.haveCallback || vertex.icon || vertex.img) ||
|
|
108
|
-
clickable(vertex.classes)) ||
|
|
109
|
-
entries(edges).some((edge) => filled(edge.style) ||
|
|
110
|
-
edge.interpolate !== undefined ||
|
|
111
|
-
clickable(edge.classes)) ||
|
|
112
|
-
entries(read('getSubGraphs')).some((graph) => clickable(graph.classes));
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
case 'stateDiagram':
|
|
116
|
-
found =
|
|
117
|
-
statementIn(field('rootDoc'), ['style', 'classDef', 'click']) ||
|
|
118
|
-
filled(read('getClasses')) ||
|
|
119
|
-
filled(read('getLinks')) ||
|
|
120
|
-
entries(read('getStates')).some((state) => filled(state.styles) || filled(state.textStyles));
|
|
121
|
-
break;
|
|
122
|
-
case 'classDiagram':
|
|
123
|
-
found =
|
|
124
|
-
filled(field('styleClasses')) ||
|
|
125
|
-
entries(read('getClasses')).some((node) => filled(node.styles) ||
|
|
126
|
-
Boolean(node.link || node.haveCallback || node.tooltip) ||
|
|
127
|
-
clickable(node.cssClasses));
|
|
128
|
-
break;
|
|
129
|
-
case 'er':
|
|
130
|
-
found =
|
|
131
|
-
filled(read('getClasses')) ||
|
|
132
|
-
entries(read('getEntities')).some((entity) => filled(entity.cssStyles)) ||
|
|
133
|
-
entries(read('getSubGraphs')).some((graph) => filled(graph.cssStyles));
|
|
134
|
-
break;
|
|
135
|
-
case 'sequence':
|
|
136
|
-
found = entries(read('getActors')).some((actor) => filled(actor.links) || filled(actor.properties));
|
|
137
|
-
break;
|
|
138
|
-
case 'pie':
|
|
139
|
-
found = false;
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
throw new Error('This diagram type is not supported. The source is retained.');
|
|
143
|
-
}
|
|
144
|
-
if (found)
|
|
145
|
-
throw new Error(refusal);
|
|
146
|
-
}
|
|
147
|
-
// Each annotation taken out of the source check must be one that Mermaid read
|
|
148
|
-
// as an annotation. One read as text, in a note or a multi-line label, would
|
|
149
|
-
// put angle brackets in a label.
|
|
150
|
-
function refuseStrayAnnotations({ type, db }, source, names) {
|
|
151
|
-
if (names.length === 0)
|
|
152
|
-
return;
|
|
153
|
-
const store = db;
|
|
154
|
-
const expected = [...names];
|
|
155
|
-
const parsed = [];
|
|
156
|
-
if (type === 'classDiagram') {
|
|
157
|
-
if (typeof store.getClasses !== 'function')
|
|
158
|
-
throw unchecked();
|
|
159
|
-
for (const node of entries(store.getClasses.call(db))) {
|
|
160
|
-
if (!Array.isArray(node.annotations))
|
|
161
|
-
throw unchecked();
|
|
162
|
-
parsed.push(...node.annotations.map(String));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else if (type === 'stateDiagram') {
|
|
166
|
-
if (!('rootDoc' in store))
|
|
167
|
-
throw unchecked();
|
|
168
|
-
walk(store.rootDoc, ({ stmt, type }) => {
|
|
169
|
-
if (stmt === 'state' && /^(?:fork|join|choice)$/.test(String(type)))
|
|
170
|
-
parsed.push(String(type));
|
|
171
|
-
});
|
|
172
|
-
// [[fork]] syntax on a state line makes the same states without angle
|
|
173
|
-
// brackets.
|
|
174
|
-
for (const [, name] of source.matchAll(stateBrackets))
|
|
175
|
-
expected.push(name.toLowerCase());
|
|
176
|
-
}
|
|
177
|
-
else
|
|
178
|
-
throw new Error(refusal);
|
|
179
|
-
if (parsed.sort().join('\n') !== expected.sort().join('\n'))
|
|
180
|
-
throw new Error(refusal);
|
|
181
|
-
}
|
|
182
|
-
let loadMermaid;
|
|
183
|
-
// The package's main entry registers Mermaid. The core entry does not, so a
|
|
184
|
-
// host that imports only the core entry does not bundle it.
|
|
185
|
-
export function registerMermaid(load) {
|
|
186
|
-
loadMermaid = load;
|
|
187
|
-
}
|
|
188
|
-
export const diagramsAvailable = () => loadMermaid !== undefined;
|
|
189
|
-
let sequence = 0;
|
|
190
|
-
let queue = Promise.resolve();
|
|
191
|
-
const cache = new Map();
|
|
192
|
-
export function safeDiagramSVG(svg) {
|
|
193
|
-
const clean = DOMPurify.sanitize(svg, {
|
|
194
|
-
USE_PROFILES: { svg: true, svgFilters: true },
|
|
195
|
-
FORBID_TAGS: ['foreignObject', 'image', 'a', 'script', 'animate', 'set'],
|
|
196
|
-
FORBID_ATTR: ['href', 'xlink:href'],
|
|
197
|
-
});
|
|
198
|
-
const template = document.createElement('template');
|
|
199
|
-
template.innerHTML = clean;
|
|
200
|
-
const root = template.content.querySelector('svg');
|
|
201
|
-
if (!root)
|
|
202
|
-
throw new Error('The renderer did not produce a diagram.');
|
|
203
|
-
for (const element of [root, ...root.querySelectorAll('*')]) {
|
|
204
|
-
for (const attribute of [...element.attributes]) {
|
|
205
|
-
if (/^on/i.test(attribute.name) ||
|
|
206
|
-
/(?:https?|data|file|javascript|blob):|@import|\\/i.test(attribute.value) ||
|
|
207
|
-
cssResource.test(attribute.value))
|
|
208
|
-
element.removeAttribute(attribute.name);
|
|
209
|
-
}
|
|
210
|
-
if (element.tagName.toLowerCase() === 'style' &&
|
|
211
|
-
(/@import|(?:https?|data|file):|\\/i.test(element.textContent ?? '') ||
|
|
212
|
-
cssResource.test(element.textContent ?? '')))
|
|
213
|
-
element.remove();
|
|
214
|
-
}
|
|
215
|
-
root.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
216
|
-
return root.outerHTML;
|
|
217
|
-
}
|
|
218
|
-
export function renderDiagram(source) {
|
|
219
|
-
const load = loadMermaid;
|
|
220
|
-
if (!load)
|
|
221
|
-
return Promise.reject(new Error('Diagram rendering is not included in this build. The source is retained.'));
|
|
222
|
-
let names;
|
|
223
|
-
try {
|
|
224
|
-
names = validateDiagram(source).names;
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
return Promise.reject(error);
|
|
228
|
-
}
|
|
229
|
-
const previous = cache.get(source);
|
|
230
|
-
if (previous)
|
|
231
|
-
return previous;
|
|
232
|
-
const rendered = queue.then(async () => {
|
|
233
|
-
const mermaid = await load();
|
|
234
|
-
mermaid.initialize({
|
|
235
|
-
startOnLoad: false,
|
|
236
|
-
securityLevel: 'strict',
|
|
237
|
-
htmlLabels: false,
|
|
238
|
-
suppressErrorRendering: true,
|
|
239
|
-
fontFamily: 'Arial, sans-serif',
|
|
240
|
-
altFontFamily: 'Arial, sans-serif',
|
|
241
|
-
layout: 'dagre',
|
|
242
|
-
look: 'classic',
|
|
243
|
-
theme: 'default',
|
|
244
|
-
maxTextSize: 30000,
|
|
245
|
-
maxEdges: 500,
|
|
246
|
-
flowchart: { htmlLabels: false },
|
|
247
|
-
sequence: {
|
|
248
|
-
actorFontFamily: 'Arial, sans-serif',
|
|
249
|
-
noteFontFamily: 'Arial, sans-serif',
|
|
250
|
-
messageFontFamily: 'Arial, sans-serif',
|
|
251
|
-
},
|
|
252
|
-
secure: [
|
|
253
|
-
'securityLevel',
|
|
254
|
-
'htmlLabels',
|
|
255
|
-
'theme',
|
|
256
|
-
'themeCSS',
|
|
257
|
-
'themeVariables',
|
|
258
|
-
'fontFamily',
|
|
259
|
-
'dompurifyConfig',
|
|
260
|
-
'flowchart',
|
|
261
|
-
'startOnLoad',
|
|
262
|
-
'maxTextSize',
|
|
263
|
-
'maxEdges',
|
|
264
|
-
'secure',
|
|
265
|
-
'altFontFamily',
|
|
266
|
-
'layout',
|
|
267
|
-
'look',
|
|
268
|
-
'sequence',
|
|
269
|
-
'class',
|
|
270
|
-
'state',
|
|
271
|
-
'er',
|
|
272
|
-
'pie',
|
|
273
|
-
'suppressErrorRendering',
|
|
274
|
-
],
|
|
275
|
-
});
|
|
276
|
-
// getDiagramFromText is the pinned Mermaid's own parser, which render runs
|
|
277
|
-
// again on the same text; the public parse does not return the diagram. It
|
|
278
|
-
// skips render's configuration step, so the two parse alike only because
|
|
279
|
-
// the source check refuses %%{ directives and front matter.
|
|
280
|
-
const parsed = await mermaid.mermaidAPI.getDiagramFromText(source);
|
|
281
|
-
refuseDirectives(parsed);
|
|
282
|
-
refuseStrayAnnotations(parsed, source, names);
|
|
283
|
-
// Mermaid measures text while laying out, so the holder must be attached
|
|
284
|
-
// and rendered. Such a holder can load CSS resources before sanitising,
|
|
285
|
-
// which is why the source and the parse result are checked first.
|
|
286
|
-
const holder = document.createElement('div');
|
|
287
|
-
holder.style.cssText =
|
|
288
|
-
'position:fixed;left:-100000px;top:0;width:1200px;pointer-events:none';
|
|
289
|
-
holder.setAttribute('aria-hidden', 'true');
|
|
290
|
-
document.body.append(holder);
|
|
291
|
-
try {
|
|
292
|
-
const result = await mermaid.render(`inkkitDiagram${++sequence}`, source, holder);
|
|
293
|
-
return safeDiagramSVG(result.svg);
|
|
294
|
-
}
|
|
295
|
-
finally {
|
|
296
|
-
holder.remove();
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
queue = rendered.catch(() => undefined);
|
|
300
|
-
cache.set(source, rendered);
|
|
301
|
-
void rendered.catch(() => {
|
|
302
|
-
if (cache.get(source) === rendered)
|
|
303
|
-
cache.delete(source);
|
|
304
|
-
});
|
|
305
|
-
if (cache.size > 64)
|
|
306
|
-
cache.delete(cache.keys().next().value);
|
|
307
|
-
return rendered;
|
|
308
|
-
}
|
|
309
|
-
export async function diagramImage(source) {
|
|
310
|
-
const svg = await renderDiagram(source);
|
|
311
|
-
const parsed = new DOMParser().parseFromString(svg, 'image/svg+xml').documentElement;
|
|
312
|
-
const bounds = (parsed.getAttribute('viewBox') ?? '')
|
|
313
|
-
.split(/[\s,]+/)
|
|
314
|
-
.map(Number);
|
|
315
|
-
const width = bounds[2] || Number.parseFloat(parsed.getAttribute('width') ?? '');
|
|
316
|
-
const height = bounds[3] || Number.parseFloat(parsed.getAttribute('height') ?? '');
|
|
317
|
-
if (!(width > 0 && height > 0 && Number.isFinite(width + height)))
|
|
318
|
-
throw new Error('The rendered diagram has no usable dimensions.');
|
|
319
|
-
const scale = Math.min(2, 4096 / width, 4096 / height);
|
|
320
|
-
const canvas = document.createElement('canvas');
|
|
321
|
-
canvas.width = Math.max(1, Math.ceil(width * scale));
|
|
322
|
-
canvas.height = Math.max(1, Math.ceil(height * scale));
|
|
323
|
-
const context = canvas.getContext('2d');
|
|
324
|
-
if (!context)
|
|
325
|
-
throw new Error('Portable diagram output is unavailable.');
|
|
326
|
-
parsed.setAttribute('width', String(width));
|
|
327
|
-
parsed.setAttribute('height', String(height));
|
|
328
|
-
const image = new Image();
|
|
329
|
-
const url = URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(parsed)], {
|
|
330
|
-
type: 'image/svg+xml',
|
|
331
|
-
}));
|
|
332
|
-
try {
|
|
333
|
-
await new Promise((resolve, reject) => {
|
|
334
|
-
image.onload = () => resolve();
|
|
335
|
-
image.onerror = () => reject(new Error('The diagram image could not be rendered.'));
|
|
336
|
-
image.src = url;
|
|
337
|
-
});
|
|
338
|
-
context.fillStyle = '#ffffff';
|
|
339
|
-
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
340
|
-
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
341
|
-
const data = canvas.toDataURL('image/png').split(',')[1];
|
|
342
|
-
if (!data)
|
|
343
|
-
throw new Error('The diagram image could not be exported.');
|
|
344
|
-
return {
|
|
345
|
-
bytes: Uint8Array.from(atob(data), (character) => character.charCodeAt(0)),
|
|
346
|
-
mimeType: 'image/png',
|
|
347
|
-
filename: 'diagram.png',
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
finally {
|
|
351
|
-
URL.revokeObjectURL(url);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
export function mermaidPreview(onError) {
|
|
355
|
-
return $prose((ctx) => new Plugin({
|
|
356
|
-
key: new PluginKey('mermaidPreview'),
|
|
357
|
-
props: {
|
|
358
|
-
decorations(state) {
|
|
359
|
-
const widgets = [];
|
|
360
|
-
state.doc.descendants((node, pos) => {
|
|
361
|
-
if (!isMermaid(node))
|
|
362
|
-
return true;
|
|
363
|
-
const source = node.textContent;
|
|
364
|
-
widgets.push(Decoration.widget(pos + node.nodeSize, () => {
|
|
365
|
-
const preview = document.createElement('div');
|
|
366
|
-
preview.className = 'inkkit-mermaid-preview';
|
|
367
|
-
preview.contentEditable = 'false';
|
|
368
|
-
preview.textContent = editorLabels(ctx).diagramRendering;
|
|
369
|
-
preview.setAttribute('role', 'status');
|
|
370
|
-
preview.setAttribute('aria-live', 'polite');
|
|
371
|
-
let active = true;
|
|
372
|
-
preview.cancel =
|
|
373
|
-
() => {
|
|
374
|
-
active = false;
|
|
375
|
-
};
|
|
376
|
-
void renderDiagram(source).then((svg) => {
|
|
377
|
-
if (active) {
|
|
378
|
-
preview.innerHTML = svg;
|
|
379
|
-
preview.dataset.state = 'ready';
|
|
380
|
-
}
|
|
381
|
-
}, (error) => {
|
|
382
|
-
if (!active)
|
|
383
|
-
return;
|
|
384
|
-
const message = error instanceof Error
|
|
385
|
-
? error.message
|
|
386
|
-
: 'The diagram could not be rendered.';
|
|
387
|
-
preview.textContent = `${editorLabels(ctx).diagramUnavailable} ${message}`;
|
|
388
|
-
preview.dataset.state = 'error';
|
|
389
|
-
onError(new InkKitError('diagram-unavailable', message));
|
|
390
|
-
});
|
|
391
|
-
return preview;
|
|
392
|
-
}, {
|
|
393
|
-
key: `mermaid:${source}`,
|
|
394
|
-
side: -1,
|
|
395
|
-
ignoreSelection: true,
|
|
396
|
-
destroy: (node) => node.cancel?.(),
|
|
397
|
-
}));
|
|
398
|
-
return false;
|
|
399
|
-
});
|
|
400
|
-
return DecorationSet.create(state.doc, widgets);
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
}));
|
|
404
|
-
}
|
package/dist/outline.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { Ctx } from '@milkdown/kit/ctx';
|
|
2
|
-
import type { Node as ProseNode } from '@milkdown/kit/prose/model';
|
|
3
|
-
import type { EditorView } from '@milkdown/kit/prose/view';
|
|
4
|
-
export interface Heading {
|
|
5
|
-
readonly id: string;
|
|
6
|
-
readonly level: number;
|
|
7
|
-
readonly text: string;
|
|
8
|
-
readonly documentId: string;
|
|
9
|
-
readonly generation: number;
|
|
10
|
-
readonly revision: number;
|
|
11
|
-
}
|
|
12
|
-
export interface OutlineContext {
|
|
13
|
-
documentId: string;
|
|
14
|
-
generation: number;
|
|
15
|
-
revision: number;
|
|
16
|
-
epoch: number;
|
|
17
|
-
mode: 'source' | 'formatted';
|
|
18
|
-
}
|
|
19
|
-
export declare function collectFormattedHeadings(doc: ProseNode, context: OutlineContext): readonly Heading[];
|
|
20
|
-
export declare function collectSourceHeadings(ctx: Ctx, source: string, context: OutlineContext): readonly Heading[];
|
|
21
|
-
export declare function sourceHeadingPosition(ctx: Ctx, source: string, requested: Heading, context: OutlineContext): number;
|
|
22
|
-
export declare function navigateFormattedHeading(view: EditorView, requested: Heading, context: OutlineContext): boolean;
|
package/dist/outline.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { parserCtx, remarkCtx } from '@milkdown/kit/core';
|
|
2
|
-
import { TextSelection } from '@milkdown/kit/prose/state';
|
|
3
|
-
import { clipboardText } from './clipboard.js';
|
|
4
|
-
import { revealCalloutAncestors } from './callouts.js';
|
|
5
|
-
import { InkKitError } from './types.js';
|
|
6
|
-
function formattedLocations(doc) {
|
|
7
|
-
const headings = [];
|
|
8
|
-
doc.descendants((node, position) => {
|
|
9
|
-
if (node.type.name === 'heading')
|
|
10
|
-
headings.push({ node, position });
|
|
11
|
-
});
|
|
12
|
-
return headings;
|
|
13
|
-
}
|
|
14
|
-
function sourceLocations(ctx, source) {
|
|
15
|
-
const processor = ctx.get(remarkCtx);
|
|
16
|
-
const tree = processor.runSync(processor.parse(source), {
|
|
17
|
-
value: source,
|
|
18
|
-
});
|
|
19
|
-
const positions = [];
|
|
20
|
-
const bom = source.startsWith('\uFEFF') ? 1 : 0;
|
|
21
|
-
const visit = (node) => {
|
|
22
|
-
if (node.type === 'heading' && node.position?.start.offset != null)
|
|
23
|
-
positions.push({
|
|
24
|
-
position: node.position.start.offset + bom,
|
|
25
|
-
level: node.depth,
|
|
26
|
-
});
|
|
27
|
-
node.children?.forEach(visit);
|
|
28
|
-
};
|
|
29
|
-
visit(tree);
|
|
30
|
-
const parsed = formattedLocations(ctx.get(parserCtx)(source));
|
|
31
|
-
if (positions.length !== parsed.length ||
|
|
32
|
-
positions.some((item, index) => item.level !== parsed[index].node.attrs.level))
|
|
33
|
-
throw new InkKitError('preservation', 'The current Markdown headings cannot be located safely');
|
|
34
|
-
return parsed.map((heading, index) => ({
|
|
35
|
-
...heading,
|
|
36
|
-
position: positions[index].position,
|
|
37
|
-
}));
|
|
38
|
-
}
|
|
39
|
-
function entry(heading, context) {
|
|
40
|
-
return Object.freeze({
|
|
41
|
-
id: `heading:${context.epoch}:${context.mode}:${context.generation}:${context.revision}:${heading.position}`,
|
|
42
|
-
level: Number(heading.node.attrs.level),
|
|
43
|
-
text: clipboardText(heading.node.content),
|
|
44
|
-
documentId: context.documentId,
|
|
45
|
-
generation: context.generation,
|
|
46
|
-
revision: context.revision,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
export function collectFormattedHeadings(doc, context) {
|
|
50
|
-
return Object.freeze(formattedLocations(doc).map((item) => entry(item, context)));
|
|
51
|
-
}
|
|
52
|
-
export function collectSourceHeadings(ctx, source, context) {
|
|
53
|
-
return Object.freeze(sourceLocations(ctx, source).map((item) => entry(item, context)));
|
|
54
|
-
}
|
|
55
|
-
function currentLocation(locations, requested, context) {
|
|
56
|
-
for (const location of locations) {
|
|
57
|
-
const current = entry(location, context);
|
|
58
|
-
if (current.id === requested.id &&
|
|
59
|
-
current.documentId === requested.documentId &&
|
|
60
|
-
current.generation === requested.generation &&
|
|
61
|
-
current.revision === requested.revision &&
|
|
62
|
-
current.level === requested.level &&
|
|
63
|
-
current.text === requested.text)
|
|
64
|
-
return location.position;
|
|
65
|
-
}
|
|
66
|
-
throw new InkKitError('stale-document', 'The heading is no longer current');
|
|
67
|
-
}
|
|
68
|
-
export function sourceHeadingPosition(ctx, source, requested, context) {
|
|
69
|
-
return currentLocation(sourceLocations(ctx, source), requested, context);
|
|
70
|
-
}
|
|
71
|
-
export function navigateFormattedHeading(view, requested, context) {
|
|
72
|
-
const position = currentLocation(formattedLocations(view.state.doc), requested, context);
|
|
73
|
-
revealCalloutAncestors(view, position + 1);
|
|
74
|
-
view.dispatch(view.state.tr
|
|
75
|
-
.setSelection(TextSelection.create(view.state.doc, position + 1))
|
|
76
|
-
.scrollIntoView());
|
|
77
|
-
view.focus();
|
|
78
|
-
return true;
|
|
79
|
-
}
|
package/dist/paste-html.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare function isRichElement(element: Element): boolean;
|
|
2
|
-
/** Whether normalisation enters the element, so its content may be rewritten. */
|
|
3
|
-
export declare function isRewrittenElement(element: Element): boolean;
|
|
4
|
-
/**
|
|
5
|
-
* Whether the element's own inline style makes a browser keep the whitespace
|
|
6
|
-
* of its content, as copied editor code does with `white-space: pre`. The
|
|
7
|
-
* declarations are parsed, so case, spacing, comments, quoted text and
|
|
8
|
-
* `!important` do not matter. Style sheets are not consulted.
|
|
9
|
-
*/
|
|
10
|
-
export declare function keepsWhitespace(element: Element): boolean;
|
|
11
|
-
/**
|
|
12
|
-
* WebKit writes a space beside an inline element to the clipboard as a
|
|
13
|
-
* non-breaking space in a `span.Apple-converted-space`, to protect it from
|
|
14
|
-
* the destination's whitespace collapse. Only such a span is converted back:
|
|
15
|
-
* a non-breaking space anywhere else was authored.
|
|
16
|
-
*/
|
|
17
|
-
export declare function restoreConvertedSpaces(root: ParentNode): void;
|
|
18
|
-
/**
|
|
19
|
-
* Rewrites common web-page elements to ones the importer already understands.
|
|
20
|
-
* Elements that will stay literal source are not entered, so their content is
|
|
21
|
-
* preserved exactly.
|
|
22
|
-
*/
|
|
23
|
-
export declare function normalizeClipboardHTML(root: Node): void;
|
|
24
|
-
export declare function isBlockElement(element: Element): boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Collapses source whitespace the way a browser renders it: runs become one
|
|
27
|
-
* space, and none remains at the edges of a block or beside a line break.
|
|
28
|
-
* `pre`, `code`, elements that stay literal source and elements whose style
|
|
29
|
-
* keeps whitespace are not entered.
|
|
30
|
-
*/
|
|
31
|
-
export declare function collapseClipboardWhitespace(root: Node): void;
|