@restless-dreams/orbit 0.1.0 → 0.2.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/README.md +5 -2
- package/dist/bin/orbit.js +2972 -213
- package/dist/bin/orbit.js.map +25 -13
- package/dist/{chunk-mtrnm0yc.js → chunk-fkdfgtx9.js} +3156 -2854
- package/dist/chunk-fkdfgtx9.js.map +75 -0
- package/dist/{chunk-7n00wx0t.js → chunk-y748hzcr.js} +2 -2
- package/dist/{chunk-0gwh723s.js → chunk-zze4tc9n.js} +2 -2
- package/dist/core/branch-colors.d.ts +57 -0
- package/dist/core/document-source.d.ts +1 -1
- package/dist/core/editing.d.ts +8 -5
- package/dist/core/engine/scene.d.ts +5 -4
- package/dist/core/engine/views.d.ts +10 -2
- package/dist/core/index.d.ts +10 -0
- package/dist/core/input-controller.d.ts +7 -0
- package/dist/core/languages.d.ts +2 -0
- package/dist/core/node-attributes.d.ts +1 -1
- package/dist/core/other-apps/freemind.d.ts +8 -0
- package/dist/core/other-apps/icons.d.ts +16 -0
- package/dist/core/other-apps/ideas.d.ts +76 -0
- package/dist/core/other-apps/import.d.ts +28 -0
- package/dist/core/other-apps/inflate.d.ts +22 -0
- package/dist/core/other-apps/markup.d.ts +51 -0
- package/dist/core/other-apps/notes.d.ts +80 -0
- package/dist/core/other-apps/opml.d.ts +26 -0
- package/dist/core/other-apps/reading.d.ts +36 -0
- package/dist/core/other-apps/xmind.d.ts +14 -0
- package/dist/core/other-apps/zip.d.ts +37 -0
- package/dist/core/print-layout.d.ts +70 -0
- package/dist/core/renderer/content-painter.d.ts +30 -0
- package/dist/core/renderer/dom.d.ts +15 -0
- package/dist/core/renderer/families.d.ts +35 -0
- package/dist/core/renderer/label-painter.d.ts +93 -0
- package/dist/core/renderer/node-painter.d.ts +113 -0
- package/dist/core/renderer/numbers.d.ts +2 -0
- package/dist/core/renderer/options.d.ts +12 -0
- package/dist/core/renderer/rings-and-links.d.ts +23 -0
- package/dist/core/renderer/scene-helpers.d.ts +33 -0
- package/dist/core/renderer/trees.d.ts +22 -0
- package/dist/core/svg-renderer.d.ts +6 -109
- package/dist/core/types.d.ts +9 -1
- package/dist/core/web-page/page.d.ts +2 -1
- package/dist/core/web-page/runtime.d.ts +1 -1
- package/dist/index.js +2935 -26
- package/dist/index.js.map +18 -6
- package/dist/marked/index.js +2 -2
- package/dist/react/index.js +2 -2
- package/dist/web-page.js +79 -79
- package/llms-full.txt +205 -5
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/skills/orbit/SKILL.md +1 -1
- package/skills/orbit/references/editing.md +2 -1
- package/skills/orbit/references/format.md +28 -0
- package/skills/orbit/references/guide.md +25 -1
- package/skills/orbit/references/import-export.md +115 -0
- package/skills/orbit/references/languages.md +1 -0
- package/skills/orbit/references/recipes.md +9 -0
- package/skills/orbit/references/theming.md +15 -0
- package/dist/chunk-mtrnm0yc.js.map +0 -63
- /package/dist/{chunk-7n00wx0t.js.map → chunk-y748hzcr.js.map} +0 -0
- /package/dist/{chunk-0gwh723s.js.map → chunk-zze4tc9n.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,33 +1,44 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderToSVGString2
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-y748hzcr.js";
|
|
4
4
|
import {
|
|
5
5
|
renderOrbitElement2,
|
|
6
6
|
renderOrbitMarkdownBlocks2,
|
|
7
7
|
defineOrbitMapElement2
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-zze4tc9n.js";
|
|
9
9
|
import {
|
|
10
10
|
assignKey,
|
|
11
11
|
emitYaml,
|
|
12
12
|
nodeSlug,
|
|
13
|
+
setParent,
|
|
14
|
+
ensureUniqueNodeIds,
|
|
13
15
|
nodesWithExplicitIds,
|
|
14
16
|
findNodeById2,
|
|
15
17
|
getNodePath2,
|
|
16
18
|
guardUrl,
|
|
19
|
+
resolveUrl,
|
|
20
|
+
videoPageUrl,
|
|
21
|
+
youtubeThumbnailUrl,
|
|
22
|
+
hasMediaSource,
|
|
17
23
|
DIAGNOSTIC_CODES2,
|
|
18
24
|
OrbitParseError2,
|
|
19
25
|
formatDiagnostic2,
|
|
20
26
|
reportParseDiagnostics,
|
|
27
|
+
branchPalette,
|
|
21
28
|
suggest,
|
|
22
29
|
mediaExtras,
|
|
23
30
|
THEME_TOKENS2,
|
|
24
31
|
ORBIT_THEME_PRESETS2,
|
|
25
32
|
isOrbitThemePreset2,
|
|
33
|
+
themeName,
|
|
26
34
|
registerTheme2,
|
|
27
35
|
isThemeName2,
|
|
28
36
|
themeNames2,
|
|
29
37
|
expandTheme,
|
|
38
|
+
mergeThemes,
|
|
30
39
|
themeTokens2,
|
|
40
|
+
resolveThemeTokens,
|
|
41
|
+
resolvePalette,
|
|
31
42
|
documentToRendererOptions2,
|
|
32
43
|
isNodeLink,
|
|
33
44
|
findLinkedNode,
|
|
@@ -45,6 +56,8 @@ import {
|
|
|
45
56
|
updateInTree,
|
|
46
57
|
removeFromTree,
|
|
47
58
|
moveInTree,
|
|
59
|
+
ensureStyles,
|
|
60
|
+
assignNodeColors,
|
|
48
61
|
registerLanguage2,
|
|
49
62
|
languageTags2,
|
|
50
63
|
OrbitWords,
|
|
@@ -53,14 +66,17 @@ import {
|
|
|
53
66
|
toHex,
|
|
54
67
|
toOklch,
|
|
55
68
|
toneColor,
|
|
69
|
+
resolveColor,
|
|
56
70
|
renderNoteMarkdown2,
|
|
57
71
|
statusTone,
|
|
58
72
|
registerNodeType2,
|
|
59
73
|
registerAction2,
|
|
60
74
|
registerStatus2,
|
|
75
|
+
MapExtensions,
|
|
61
76
|
upgradeNodeTypes2,
|
|
77
|
+
guarded,
|
|
62
78
|
OrbitSVGRenderer2
|
|
63
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-fkdfgtx9.js";
|
|
64
80
|
// src/core/serializer-yaml.ts
|
|
65
81
|
var serializeOrbitYAML = (doc) => {
|
|
66
82
|
const out = {};
|
|
@@ -123,6 +139,9 @@ var serializeNode = (n, explicitIds) => {
|
|
|
123
139
|
if (n.color) {
|
|
124
140
|
obj.color = n.color;
|
|
125
141
|
}
|
|
142
|
+
if (n.paletteColor !== undefined) {
|
|
143
|
+
obj.paletteColor = n.paletteColor;
|
|
144
|
+
}
|
|
126
145
|
if (n.type) {
|
|
127
146
|
obj.type = n.typeVersion === undefined ? n.type : `${n.type}@${n.typeVersion}`;
|
|
128
147
|
}
|
|
@@ -182,13 +201,285 @@ var createOrbitMap = (target, source, options = {}) => {
|
|
|
182
201
|
}
|
|
183
202
|
return renderer;
|
|
184
203
|
};
|
|
204
|
+
// src/core/print-layout.ts
|
|
205
|
+
var PRINT_THEMES = {
|
|
206
|
+
auto: "paper",
|
|
207
|
+
night: "paper",
|
|
208
|
+
"glass-night": "glass",
|
|
209
|
+
cosmos: "paper",
|
|
210
|
+
blueprint: "ink",
|
|
211
|
+
"flat-night": "flat",
|
|
212
|
+
"material-night": "material",
|
|
213
|
+
"liquid-night": "liquid"
|
|
214
|
+
};
|
|
215
|
+
var PALETTE_TOKENS = ["color-root", "color-1", "color-2", "color-3", "color-4", "color-5", "color-6", "color-7"];
|
|
216
|
+
var PICTURE_SIZE = 720;
|
|
217
|
+
var printTheme = (theme) => {
|
|
218
|
+
const expanded = expandTheme(theme);
|
|
219
|
+
const preset = themeName(expanded);
|
|
220
|
+
const light = PRINT_THEMES[preset] ?? (preset === "auto" ? "paper" : preset);
|
|
221
|
+
const own = typeof expanded === "object" ? expanded.tokens ?? {} : {};
|
|
222
|
+
const kept = light === preset ? own : Object.fromEntries(Object.entries(own).filter(([token]) => PALETTE_TOKENS.includes(token)));
|
|
223
|
+
return { preset: light, tokens: { ...kept, surface: "transparent", "surface-pattern": "none" } };
|
|
224
|
+
};
|
|
225
|
+
var renderPrintLayout = (container, input, options = {}) => {
|
|
226
|
+
const words = new OrbitWords(options.language, options.strings);
|
|
227
|
+
const map = typeof input === "string" ? parseOrbit2(input, { untitled: words.text("untitledMap") }) : input;
|
|
228
|
+
const fromDocument = documentToRendererOptions2(map.options);
|
|
229
|
+
const theme = printTheme(mergeThemes(fromDocument.theme, options.theme));
|
|
230
|
+
const tokens = resolveThemeTokens(themeName(theme), theme);
|
|
231
|
+
const extensions = new MapExtensions({ nodeTypes: options.nodeTypes, statuses: options.statuses, nodeTypeFor: options.nodeTypeFor });
|
|
232
|
+
const palette = options.palette ?? fromDocument.palette;
|
|
233
|
+
const colors = assignNodeColors(map.root, resolvePalette(palette), (node) => extensions.color(node));
|
|
234
|
+
const nodes = indexNodes(map.root);
|
|
235
|
+
const layout = {
|
|
236
|
+
words,
|
|
237
|
+
tokens,
|
|
238
|
+
extensions,
|
|
239
|
+
colors,
|
|
240
|
+
baseUrl: options.baseUrl ?? fromDocument.baseUrl,
|
|
241
|
+
linked: (href) => findIndexedLinkedNode(nodes, href)
|
|
242
|
+
};
|
|
243
|
+
const title = options.title ?? (map.options.title || map.root.text || words.text("untitledMap"));
|
|
244
|
+
const article = element("article", "orbit-print");
|
|
245
|
+
article.lang = words.locale;
|
|
246
|
+
article.style.cssText = themeProperties(tokens);
|
|
247
|
+
const heading = element("h1", "orbit-print__title");
|
|
248
|
+
appendName(heading, map.root, title, layout);
|
|
249
|
+
article.append(heading, ...entryParts(map.root, layout));
|
|
250
|
+
if (options.picture !== false) {
|
|
251
|
+
const figure = element("figure", "orbit-print__picture");
|
|
252
|
+
const svg = renderToSVGString2(map, {
|
|
253
|
+
width: PICTURE_SIZE,
|
|
254
|
+
height: PICTURE_SIZE,
|
|
255
|
+
viewMode: options.viewMode,
|
|
256
|
+
focus: options.focus,
|
|
257
|
+
theme,
|
|
258
|
+
palette,
|
|
259
|
+
title,
|
|
260
|
+
language: options.language,
|
|
261
|
+
strings: options.strings,
|
|
262
|
+
baseUrl: layout.baseUrl,
|
|
263
|
+
idPrefix: options.idPrefix ?? "orbit-print",
|
|
264
|
+
nodeTypes: options.nodeTypes,
|
|
265
|
+
statuses: options.statuses,
|
|
266
|
+
nodeTypeFor: options.nodeTypeFor
|
|
267
|
+
});
|
|
268
|
+
figure.appendChild(document.importNode(new DOMParser().parseFromString(svg, "image/svg+xml").documentElement, true));
|
|
269
|
+
article.appendChild(figure);
|
|
270
|
+
}
|
|
271
|
+
if (map.root.children.length > 0) {
|
|
272
|
+
const outline = element("div", "orbit-print__outline");
|
|
273
|
+
outline.append(...map.root.children.map((child) => idea(child, 1, layout)));
|
|
274
|
+
article.appendChild(outline);
|
|
275
|
+
}
|
|
276
|
+
container.replaceChildren(article);
|
|
277
|
+
ensureStyles(article);
|
|
278
|
+
return article;
|
|
279
|
+
};
|
|
280
|
+
var idea = (node, depth, layout) => {
|
|
281
|
+
const section = element("section", "orbit-print__idea");
|
|
282
|
+
section.dataset.depth = String(depth);
|
|
283
|
+
const entry = element("div", node.children.length > 0 ? "orbit-print__entry orbit-print__entry--parent" : "orbit-print__entry");
|
|
284
|
+
const level = `h${Math.min(depth + 1, 6)}`;
|
|
285
|
+
const name = element(level, `orbit-print__name orbit-print__name--depth-${Math.min(depth, 3)}`);
|
|
286
|
+
const dot = element("span", "orbit-print__dot");
|
|
287
|
+
dot.setAttribute("aria-hidden", "true");
|
|
288
|
+
dot.style.setProperty("--orbit-print-dot", resolveColor(layout.colors.get(node.id) ?? layout.tokens["color-1"] ?? "#888888", layout.tokens));
|
|
289
|
+
name.appendChild(dot);
|
|
290
|
+
appendName(name, node, node.text || layout.words.text("untitled"), layout);
|
|
291
|
+
entry.append(name, ...entryParts(node, layout));
|
|
292
|
+
section.appendChild(entry);
|
|
293
|
+
if (node.children.length > 0) {
|
|
294
|
+
const children = element("div", "orbit-print__children");
|
|
295
|
+
children.append(...node.children.map((child) => idea(child, depth + 1, layout)));
|
|
296
|
+
section.appendChild(children);
|
|
297
|
+
}
|
|
298
|
+
return section;
|
|
299
|
+
};
|
|
300
|
+
var appendName = (heading, node, text, layout) => {
|
|
301
|
+
const icon = layout.extensions.icon(node);
|
|
302
|
+
if (icon) {
|
|
303
|
+
const glyph = element("span", "orbit-print__icon");
|
|
304
|
+
glyph.textContent = icon;
|
|
305
|
+
heading.append(glyph, document.createTextNode(" "));
|
|
306
|
+
}
|
|
307
|
+
heading.appendChild(document.createTextNode(text));
|
|
308
|
+
};
|
|
309
|
+
var entryParts = (node, layout) => {
|
|
310
|
+
const parts = [];
|
|
311
|
+
const chips = chipsOf(node, layout);
|
|
312
|
+
if (chips) {
|
|
313
|
+
parts.push(chips);
|
|
314
|
+
}
|
|
315
|
+
const link = linkOf(node, layout);
|
|
316
|
+
if (link) {
|
|
317
|
+
parts.push(link);
|
|
318
|
+
}
|
|
319
|
+
const details = layout.extensions.nodeType(node)?.details;
|
|
320
|
+
if (details) {
|
|
321
|
+
const section = element("div", "orbit-print__details");
|
|
322
|
+
guarded("A node type’s details", undefined, () => details(node, section));
|
|
323
|
+
if (section.childNodes.length > 0) {
|
|
324
|
+
parts.push(section);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (node.note?.trim()) {
|
|
328
|
+
const note = element("div", "orbit-print__note");
|
|
329
|
+
if (node.noteFormat === "plain") {
|
|
330
|
+
note.classList.add("orbit-print__note--plain");
|
|
331
|
+
note.textContent = node.note;
|
|
332
|
+
} else {
|
|
333
|
+
note.appendChild(renderNoteMarkdown2(node.note, { baseUrl: layout.baseUrl, resolveLink: layout.linked }));
|
|
334
|
+
}
|
|
335
|
+
parts.push(note);
|
|
336
|
+
}
|
|
337
|
+
parts.push(...mediaOf(node, layout));
|
|
338
|
+
return parts;
|
|
339
|
+
};
|
|
340
|
+
var chipsOf = (node, layout) => {
|
|
341
|
+
const chips = [];
|
|
342
|
+
const typeLabel = layout.extensions.nodeType(node)?.label;
|
|
343
|
+
if (typeLabel) {
|
|
344
|
+
chips.push(chip(typeLabel));
|
|
345
|
+
}
|
|
346
|
+
if (node.status) {
|
|
347
|
+
const status = chip(node.status, "orbit-print__chip--status");
|
|
348
|
+
const look = layout.extensions.status(node.status);
|
|
349
|
+
status.style.setProperty("--orbit-print-status", resolveColor(look.color ?? `var(--_orbit-status-${look.tone})`, layout.tokens));
|
|
350
|
+
chips.push(status);
|
|
351
|
+
}
|
|
352
|
+
if (node.priority !== undefined) {
|
|
353
|
+
chips.push(chip(layout.words.text("priority", { priority: node.priority })));
|
|
354
|
+
}
|
|
355
|
+
for (const tag of node.tags ?? []) {
|
|
356
|
+
chips.push(chip(`#${tag}`));
|
|
357
|
+
}
|
|
358
|
+
if (chips.length === 0) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
const container = element("p", "orbit-print__chips");
|
|
362
|
+
container.append(...chips);
|
|
363
|
+
return container;
|
|
364
|
+
};
|
|
365
|
+
var chip = (text, modifier) => {
|
|
366
|
+
const span = element("span", modifier ? `orbit-print__chip ${modifier}` : "orbit-print__chip");
|
|
367
|
+
span.textContent = text;
|
|
368
|
+
return span;
|
|
369
|
+
};
|
|
370
|
+
var linkOf = (node, layout) => {
|
|
371
|
+
if (!node.link) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const paragraph = element("p", "orbit-print__link");
|
|
375
|
+
if (isNodeLink(node.link)) {
|
|
376
|
+
const target = layout.linked(node.link);
|
|
377
|
+
if (!target) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
paragraph.textContent = `→ ${layout.words.text("seeIdea", { name: target.text || layout.words.text("theIdea") })}`;
|
|
381
|
+
return paragraph;
|
|
382
|
+
}
|
|
383
|
+
const address = guardUrl(node.link, "link");
|
|
384
|
+
if (!address) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
paragraph.appendChild(addressLink(resolveUrl(address, layout.baseUrl)));
|
|
388
|
+
return paragraph;
|
|
389
|
+
};
|
|
390
|
+
var addressLink = (href) => {
|
|
391
|
+
const anchor = element("a", "orbit-print__address");
|
|
392
|
+
anchor.href = href;
|
|
393
|
+
anchor.textContent = href;
|
|
394
|
+
return anchor;
|
|
395
|
+
};
|
|
396
|
+
var mediaOf = (node, layout) => {
|
|
397
|
+
const figures = [];
|
|
398
|
+
if (hasMediaSource(node.image)) {
|
|
399
|
+
const source = guardUrl(node.image.src, "image");
|
|
400
|
+
if (source) {
|
|
401
|
+
const figure = element("figure", "orbit-print__media");
|
|
402
|
+
const image = element("img", "orbit-print__image");
|
|
403
|
+
image.src = resolveUrl(source, layout.baseUrl);
|
|
404
|
+
image.alt = node.image.alt ?? "";
|
|
405
|
+
figure.appendChild(image);
|
|
406
|
+
appendCaption(figure, node.image.caption ?? node.image.alt);
|
|
407
|
+
figures.push(figure);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (hasMediaSource(node.video)) {
|
|
411
|
+
const source = guardUrl(node.video.src, "video");
|
|
412
|
+
if (source) {
|
|
413
|
+
const resolved = resolveUrl(source, layout.baseUrl);
|
|
414
|
+
const figure = element("figure", "orbit-print__media orbit-print__media--video");
|
|
415
|
+
const thumbnail = node.video.thumbnail ? guardUrl(node.video.thumbnail, "image") : youtubeThumbnailUrl(resolved);
|
|
416
|
+
if (thumbnail) {
|
|
417
|
+
const image = element("img", "orbit-print__image");
|
|
418
|
+
image.src = resolveUrl(thumbnail, layout.baseUrl);
|
|
419
|
+
image.alt = "";
|
|
420
|
+
figure.appendChild(image);
|
|
421
|
+
}
|
|
422
|
+
const caption = element("figcaption", "orbit-print__caption");
|
|
423
|
+
caption.append(document.createTextNode(`${node.video.caption ?? layout.words.text("video")}: `), addressLink(videoPageUrl(resolved, node.video.provider)));
|
|
424
|
+
figure.appendChild(caption);
|
|
425
|
+
figures.push(figure);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (hasMediaSource(node.embed) && !hasMediaSource(node.video)) {
|
|
429
|
+
const source = guardUrl(node.embed.src, "embed");
|
|
430
|
+
if (source) {
|
|
431
|
+
const figure = element("figure", "orbit-print__media orbit-print__media--embed");
|
|
432
|
+
const caption = element("figcaption", "orbit-print__caption");
|
|
433
|
+
if (node.embed.caption) {
|
|
434
|
+
caption.append(document.createTextNode(`${node.embed.caption}: `));
|
|
435
|
+
}
|
|
436
|
+
caption.appendChild(addressLink(resolveUrl(source, layout.baseUrl)));
|
|
437
|
+
figure.appendChild(caption);
|
|
438
|
+
figures.push(figure);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return figures;
|
|
442
|
+
};
|
|
443
|
+
var appendCaption = (figure, text) => {
|
|
444
|
+
if (text?.trim()) {
|
|
445
|
+
const caption = element("figcaption", "orbit-print__caption");
|
|
446
|
+
caption.textContent = text;
|
|
447
|
+
figure.appendChild(caption);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
var themeProperties = (tokens) => {
|
|
451
|
+
const value = (token, fallback) => resolveColor(tokens[token] ?? fallback, tokens);
|
|
452
|
+
const font = (token, fallback) => resolveFont(tokens[token] ?? fallback, tokens);
|
|
453
|
+
return [
|
|
454
|
+
`--_orbit-print-text: ${value("text", "#1F2733")};`,
|
|
455
|
+
`--_orbit-print-muted-text: ${value("muted-text", "#5B6474")};`,
|
|
456
|
+
`--_orbit-print-link-text: ${value("link-text", value("text", "#1F2733"))};`,
|
|
457
|
+
`--_orbit-print-chip-background: ${value("chip-background", "#EEF0F4")};`,
|
|
458
|
+
`--_orbit-print-chip-text: ${value("chip-text", value("text", "#1F2733"))};`,
|
|
459
|
+
`--_orbit-print-border: ${value("sheet-border", "#D9DDE3")};`,
|
|
460
|
+
`--_orbit-print-font: ${font("font-family", "sans-serif")};`,
|
|
461
|
+
`--_orbit-print-display-font: ${font("display-font", font("font-family", "sans-serif"))};`
|
|
462
|
+
].join(" ");
|
|
463
|
+
};
|
|
464
|
+
var resolveFont = (value, tokens) => {
|
|
465
|
+
let font = value;
|
|
466
|
+
for (let depth = 0;depth < 4 && font.includes("var("); depth += 1) {
|
|
467
|
+
font = font.replace(/var\(--_?orbit-([a-z0-9-]+)(?:\s*,\s*([^()]+))?\)/gi, (whole, token, fallback) => tokens[token] ?? fallback?.trim() ?? whole);
|
|
468
|
+
}
|
|
469
|
+
return font.includes("var(") || /[;{}]/.test(font) ? "sans-serif" : font;
|
|
470
|
+
};
|
|
471
|
+
var element = (name, className) => {
|
|
472
|
+
const created = document.createElement(name);
|
|
473
|
+
created.className = className;
|
|
474
|
+
return created;
|
|
475
|
+
};
|
|
185
476
|
// src/core/web-page/page.ts
|
|
186
477
|
var ORBIT_ICON = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='18 18 72 72'%3E%3Cdefs%3E%3CradialGradient id='d' gradientUnits='userSpaceOnUse' cx='32.4' cy='23.76' r='102.6'%3E%3Cstop offset='0' stop-color='%234A35A8'/%3E%3Cstop offset='1' stop-color='%2318134A'/%3E%3C/radialGradient%3E%3C/defs%3E%3Crect x='18' y='18' width='72' height='72' rx='16' fill='url(%23d)'/%3E%3Cpath d='M67.53,35.36 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M69.84,37.38 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M71.86,39.69 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M76.52,57.07 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M75.92,60.08 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M74.93,62.99 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M62.21,75.71 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M59.3,76.7 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M56.29,77.3 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M38.91,72.64 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M36.6,70.62 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M34.58,68.31 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M29.92,50.93 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M30.52,47.92 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M31.51,45.01 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M44.23,32.29 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M47.14,31.3 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z M50.15,30.7 a0.78,0.78 0 1,0 1.56,0 a0.78,0.78 0 1,0 -1.56,0 Z' fill='%23E2E6FF' fill-opacity='.5'/%3E%3Ccircle cx='54' cy='54' r='11' fill='%23FAD86F'/%3E%3Ccircle cx='60.08' cy='31.3' r='6.4' fill='%23FF9E96'/%3E%3Ccircle cx='76.7' cy='47.92' r='6.4' fill='%23F8B478'/%3E%3Ccircle cx='70.62' cy='70.62' r='6.4' fill='%237FDAAB'/%3E%3Ccircle cx='47.92' cy='76.7' r='6.4' fill='%2386CDFF'/%3E%3Ccircle cx='31.3' cy='60.08' r='6.4' fill='%23C9B1FE'/%3E%3Ccircle cx='37.38' cy='37.38' r='6.4' fill='%23F7A3D4'/%3E%3C/svg%3E";
|
|
187
478
|
var exportWebPage = (input, options) => {
|
|
188
|
-
const
|
|
479
|
+
const document2 = typeof input === "string" ? parseOrbit2(input) : input;
|
|
189
480
|
const source = typeof input === "string" ? input : serializeOrbitMarkdown2(input);
|
|
190
481
|
const words = new OrbitWords(options.language, options.strings);
|
|
191
|
-
const title = options.title ?? (
|
|
482
|
+
const title = options.title ?? (document2.options.title || document2.root.text || "Orbit");
|
|
192
483
|
const data = {
|
|
193
484
|
source,
|
|
194
485
|
theme: options.theme,
|
|
@@ -198,7 +489,7 @@ var exportWebPage = (input, options) => {
|
|
|
198
489
|
strings: options.strings,
|
|
199
490
|
media: options.media ?? {}
|
|
200
491
|
};
|
|
201
|
-
const theme = options.theme ??
|
|
492
|
+
const theme = options.theme ?? document2.options.theme;
|
|
202
493
|
return `<!doctype html>
|
|
203
494
|
<html lang="${escapeAttribute(words.locale)}">
|
|
204
495
|
<head>
|
|
@@ -226,6 +517,7 @@ ${pageStyles(theme)}
|
|
|
226
517
|
<button class="page__button" type="button" data-close aria-label="${escapeAttribute(words.text("close"))}">✕</button>
|
|
227
518
|
</nav>
|
|
228
519
|
</div>
|
|
520
|
+
<div class="page__print" id="page-print"></div>
|
|
229
521
|
<script type="application/json" id="orbit-page">${scriptJson(data)}</script>
|
|
230
522
|
<script type="application/json" id="orbit-script">${scriptJson(options.script)}</script>
|
|
231
523
|
<script>
|
|
@@ -288,7 +580,14 @@ body { font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
|
288
580
|
.page--presenting .page__present { display: none; }
|
|
289
581
|
.page--presenting:not(.page--controls) .page__controls { opacity: 0; }
|
|
290
582
|
.page--presenting:not(.page--controls) { cursor: none; }
|
|
291
|
-
[hidden] { display: none !important; }
|
|
583
|
+
[hidden] { display: none !important; }
|
|
584
|
+
.page__print { display: none; }
|
|
585
|
+
@media print {
|
|
586
|
+
@page { margin: 16mm 14mm; }
|
|
587
|
+
html, body { height: auto; background: #ffffff; }
|
|
588
|
+
.page { display: none; }
|
|
589
|
+
.page__print { display: block; }
|
|
590
|
+
}`;
|
|
292
591
|
};
|
|
293
592
|
var scriptJson = (value) => {
|
|
294
593
|
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
@@ -891,13 +1190,13 @@ var createMapTools = (map, options = {}) => {
|
|
|
891
1190
|
throw new CallError(`No node has the id "${id}".${nearNode ? ` Did you mean ${nameOf(nearNode)}?` : ""} read_map and find_nodes give every node's id.`);
|
|
892
1191
|
};
|
|
893
1192
|
const calls = {
|
|
894
|
-
read_map: (values, root,
|
|
1193
|
+
read_map: (values, root, document2) => {
|
|
895
1194
|
const start = typeof values.node_id === "string" ? resolve(root, values.node_id, "node_id") : root;
|
|
896
1195
|
const levels = typeof values.depth === "number" ? values.depth : Infinity;
|
|
897
1196
|
const { lines, complete } = readOutline(start, levels, readLimit);
|
|
898
1197
|
const header = [];
|
|
899
1198
|
if (start === root) {
|
|
900
|
-
const title =
|
|
1199
|
+
const title = document2.options.title ?? root.text;
|
|
901
1200
|
header.push(`The map "${title}" has ${count(1 + descendants(root), "node")}.`);
|
|
902
1201
|
} else {
|
|
903
1202
|
const path = getNodePath2(root, start.id);
|
|
@@ -1070,8 +1369,8 @@ var createMapTools = (map, options = {}) => {
|
|
|
1070
1369
|
const near = suggest(String(name), specs.map((each) => each.name));
|
|
1071
1370
|
return failure(`There's no tool "${String(name)}".${near === undefined ? "" : ` Did you mean ${near}?`} The tools are ${list(specs.map((each) => each.name))}.`);
|
|
1072
1371
|
}
|
|
1073
|
-
const
|
|
1074
|
-
if (!
|
|
1372
|
+
const document2 = target.document();
|
|
1373
|
+
if (!document2) {
|
|
1075
1374
|
return failure("There's no map to read yet: it hasn't been drawn.");
|
|
1076
1375
|
}
|
|
1077
1376
|
if (!offered.readOnly && target.newer) {
|
|
@@ -1079,7 +1378,7 @@ var createMapTools = (map, options = {}) => {
|
|
|
1079
1378
|
}
|
|
1080
1379
|
try {
|
|
1081
1380
|
const { values, ignored } = readInput(offered.name, offered.fields, input, offered.synonyms);
|
|
1082
|
-
const outcome = calls[offered.name](values,
|
|
1381
|
+
const outcome = calls[offered.name](values, document2.root, document2);
|
|
1083
1382
|
if (ignored.length > 0) {
|
|
1084
1383
|
notes.push(`${offered.name} doesn't take ${list(ignored.map((key) => `"${key}"`))}, so ${ignored.length === 1 ? "it was" : "they were"} left out. It takes ${list(Object.keys(offered.fields))}.`);
|
|
1085
1384
|
}
|
|
@@ -1117,18 +1416,20 @@ var targetOf = (map) => {
|
|
|
1117
1416
|
newer: false
|
|
1118
1417
|
};
|
|
1119
1418
|
}
|
|
1120
|
-
const
|
|
1121
|
-
if (!
|
|
1419
|
+
const document2 = typeof map === "string" ? parseOrbit2(map) : map;
|
|
1420
|
+
if (!document2 || typeof document2 !== "object" || !document2.root || !Array.isArray(document2.root.children)) {
|
|
1122
1421
|
throw new TypeError(`createMapTools() takes a map on screen (createOrbitMap's result, or <orbit-map>'s renderer), a parsed document, or a map's markdown or YAML, but got ${map === null ? "null" : typeof map}.`);
|
|
1123
1422
|
}
|
|
1423
|
+
const extensions = new MapExtensions;
|
|
1424
|
+
const colors = () => ({ count: branchPalette(resolvePalette(document2.options?.palette)).length, ownColor: (node) => extensions.color(node) });
|
|
1124
1425
|
return {
|
|
1125
|
-
document: () =>
|
|
1126
|
-
add: (parentId, input, index) => addToTree(
|
|
1127
|
-
update: (id, changes) => updateInTree(
|
|
1128
|
-
move: (id, parentId, index) => moveInTree(
|
|
1129
|
-
remove: (id) => removeFromTree(
|
|
1426
|
+
document: () => document2,
|
|
1427
|
+
add: (parentId, input, index) => addToTree(document2.root, parentId, input, index, colors())?.node ?? null,
|
|
1428
|
+
update: (id, changes) => updateInTree(document2.root, id, changes, colors())?.node ?? null,
|
|
1429
|
+
move: (id, parentId, index) => moveInTree(document2.root, id, parentId, index, colors())?.node ?? null,
|
|
1430
|
+
remove: (id) => removeFromTree(document2.root, id, colors())?.node ?? null,
|
|
1130
1431
|
live: null,
|
|
1131
|
-
newer: (
|
|
1432
|
+
newer: (document2.diagnostics ?? []).some((diagnostic) => diagnostic.code === "unsupported-schema-version")
|
|
1132
1433
|
};
|
|
1133
1434
|
};
|
|
1134
1435
|
var isLiveMap = (map) => {
|
|
@@ -1183,19 +1484,19 @@ var readNewNodes = (outline) => {
|
|
|
1183
1484
|
if (/^\s*---\s*\n/.test(source)) {
|
|
1184
1485
|
throw new CallError("outline takes the nodes to add as a markdown list, without frontmatter.");
|
|
1185
1486
|
}
|
|
1186
|
-
let
|
|
1187
|
-
if (
|
|
1487
|
+
let document2 = parseOrbitMarkdown2(source);
|
|
1488
|
+
if (document2.diagnostics.some((diagnostic) => diagnostic.code === "no-structure")) {
|
|
1188
1489
|
source = source.split(`
|
|
1189
1490
|
`).map((line) => line.trim()).filter((line) => line !== "").map((line) => `- ${line}`).join(`
|
|
1190
1491
|
`);
|
|
1191
|
-
|
|
1492
|
+
document2 = parseOrbitMarkdown2(source);
|
|
1192
1493
|
}
|
|
1193
|
-
const roots =
|
|
1494
|
+
const roots = document2.diagnostics.some((diagnostic) => diagnostic.code === "synthetic-root") ? document2.root.children : [document2.root];
|
|
1194
1495
|
const untitled = roots.flatMap(allNodes).find((node) => node.text.trim() === "");
|
|
1195
1496
|
if (roots.length === 0 || untitled) {
|
|
1196
1497
|
throw new CallError(untitled?.source ? `Line ${untitled.source.line} of outline has an item without a title.` : 'outline has no nodes: write them as a markdown list, like "- Social media\\n- Newsletter".');
|
|
1197
1498
|
}
|
|
1198
|
-
const problems =
|
|
1499
|
+
const problems = document2.diagnostics.filter((diagnostic) => !OUTLINE_CODES_LEFT_OUT.has(diagnostic.code)).map((diagnostic) => formatDiagnostic2(diagnostic).replace(/[,;] kept as (?:a custom attribute|written)(?:, but not used)?/, ""));
|
|
1199
1500
|
if (problems.length > 0) {
|
|
1200
1501
|
throw new CallError(`outline doesn't read cleanly. Fix ${problems.length === 1 ? "this" : "these"}, and send it again:
|
|
1201
1502
|
${problems.map((problem) => `- ${problem}`).join(`
|
|
@@ -1326,6 +1627,2602 @@ var readChanges = (values, root, node) => {
|
|
|
1326
1627
|
}
|
|
1327
1628
|
return { changes, set, cleared, absent };
|
|
1328
1629
|
};
|
|
1630
|
+
// src/core/other-apps/icons.ts
|
|
1631
|
+
var FREEMIND_ICONS = new Map(Object.entries({
|
|
1632
|
+
idea: "\uD83D\uDCA1",
|
|
1633
|
+
help: "❓",
|
|
1634
|
+
yes: "❗",
|
|
1635
|
+
messagebox_warning: "⚠️",
|
|
1636
|
+
info: "ℹ️",
|
|
1637
|
+
"stop-sign": "\uD83D\uDED1",
|
|
1638
|
+
closed: "⛔",
|
|
1639
|
+
button_ok: "✅",
|
|
1640
|
+
button_cancel: "❌",
|
|
1641
|
+
stop: "\uD83D\uDD34",
|
|
1642
|
+
prepare: "\uD83D\uDFE1",
|
|
1643
|
+
go: "\uD83D\uDFE2",
|
|
1644
|
+
back: "⬅️",
|
|
1645
|
+
forward: "➡️",
|
|
1646
|
+
up: "⬆️",
|
|
1647
|
+
down: "⬇️",
|
|
1648
|
+
attach: "\uD83D\uDCCE",
|
|
1649
|
+
ksmiletris: "\uD83D\uDE42",
|
|
1650
|
+
"smiley-neutral": "\uD83D\uDE10",
|
|
1651
|
+
"smiley-oh": "\uD83D\uDE2E",
|
|
1652
|
+
"smiley-angry": "\uD83D\uDE20",
|
|
1653
|
+
smily_bad: "\uD83D\uDE1E",
|
|
1654
|
+
clanbomber: "\uD83D\uDCA3",
|
|
1655
|
+
desktop_new: "\uD83D\uDCCC",
|
|
1656
|
+
flag: "\uD83D\uDEA9",
|
|
1657
|
+
"flag-black": "\uD83C\uDFF4",
|
|
1658
|
+
"flag-blue": "\uD83D\uDEA9",
|
|
1659
|
+
"flag-green": "\uD83D\uDEA9",
|
|
1660
|
+
"flag-orange": "\uD83D\uDEA9",
|
|
1661
|
+
"flag-pink": "\uD83D\uDEA9",
|
|
1662
|
+
"flag-yellow": "\uD83D\uDEA9",
|
|
1663
|
+
gohome: "\uD83C\uDFE0",
|
|
1664
|
+
folder: "\uD83D\uDCC1",
|
|
1665
|
+
korn: "\uD83D\uDCEB",
|
|
1666
|
+
Mail: "✉️",
|
|
1667
|
+
kmail: "\uD83D\uDCE7",
|
|
1668
|
+
list: "\uD83D\uDCCB",
|
|
1669
|
+
edit: "✏️",
|
|
1670
|
+
pencil: "✏️",
|
|
1671
|
+
kaddressbook: "☎️",
|
|
1672
|
+
knotify: "\uD83C\uDFB5",
|
|
1673
|
+
password: "\uD83D\uDD11",
|
|
1674
|
+
encrypted: "\uD83D\uDD12",
|
|
1675
|
+
decrypted: "\uD83D\uDD13",
|
|
1676
|
+
wizard: "\uD83E\uDE84",
|
|
1677
|
+
xmag: "\uD83D\uDD0D",
|
|
1678
|
+
bell: "\uD83D\uDD14",
|
|
1679
|
+
bookmark: "\uD83D\uDD16",
|
|
1680
|
+
penguin: "\uD83D\uDC27",
|
|
1681
|
+
licq: "\uD83C\uDF38",
|
|
1682
|
+
freemind_butterfly: "\uD83E\uDD8B",
|
|
1683
|
+
"broken-line": "\uD83D\uDC94",
|
|
1684
|
+
calendar: "\uD83D\uDCC5",
|
|
1685
|
+
clock: "⏰",
|
|
1686
|
+
hourglass: "⏳",
|
|
1687
|
+
launch: "\uD83D\uDE80",
|
|
1688
|
+
family: "\uD83D\uDC6A",
|
|
1689
|
+
female1: "\uD83D\uDC69",
|
|
1690
|
+
female2: "\uD83D\uDC69",
|
|
1691
|
+
male1: "\uD83D\uDC68",
|
|
1692
|
+
male2: "\uD83D\uDC68",
|
|
1693
|
+
fema: "\uD83D\uDC6B",
|
|
1694
|
+
group: "\uD83D\uDC65",
|
|
1695
|
+
bee: "\uD83D\uDC1D",
|
|
1696
|
+
mindmap: "\uD83D\uDDFA️",
|
|
1697
|
+
unchecked: "⬜",
|
|
1698
|
+
checked: "✅"
|
|
1699
|
+
}));
|
|
1700
|
+
var FREEMIND_PROGRESS = new Map(Object.entries({ "0%": "todo", "25%": "active", "50%": "active", "75%": "active", "100%": "done", checked: "done", unchecked: "todo" }));
|
|
1701
|
+
var freeMindIcon = (name) => {
|
|
1702
|
+
const priority = /^full-(\d)$/.exec(name);
|
|
1703
|
+
if (priority) {
|
|
1704
|
+
return { priority: Number(priority[1]) };
|
|
1705
|
+
}
|
|
1706
|
+
const progress = FREEMIND_PROGRESS.get(name);
|
|
1707
|
+
if (progress) {
|
|
1708
|
+
return { status: progress };
|
|
1709
|
+
}
|
|
1710
|
+
const emoji = /^emoji-([0-9A-Fa-f]{2,6}(?:-[0-9A-Fa-f]{2,6})*)$/.exec(name);
|
|
1711
|
+
if (emoji) {
|
|
1712
|
+
const codes = emoji[1].split("-").map((code) => parseInt(code, 16));
|
|
1713
|
+
if (codes.every((code) => code > 0 && code <= 1114111)) {
|
|
1714
|
+
return { icon: String.fromCodePoint(...codes) };
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const icon = FREEMIND_ICONS.get(name);
|
|
1718
|
+
return icon ? { icon } : null;
|
|
1719
|
+
};
|
|
1720
|
+
var XMIND_MARKERS = new Map(Object.entries({
|
|
1721
|
+
"smiley-smile": "\uD83D\uDE42",
|
|
1722
|
+
"smiley-laugh": "\uD83D\uDE04",
|
|
1723
|
+
"smiley-angry": "\uD83D\uDE20",
|
|
1724
|
+
"smiley-cry": "\uD83D\uDE22",
|
|
1725
|
+
"smiley-surprise": "\uD83D\uDE2E",
|
|
1726
|
+
"smiley-boring": "\uD83D\uDE10",
|
|
1727
|
+
"arrow-up": "⬆️",
|
|
1728
|
+
"arrow-down": "⬇️",
|
|
1729
|
+
"arrow-left": "⬅️",
|
|
1730
|
+
"arrow-right": "➡️",
|
|
1731
|
+
"arrow-up-right": "↗️",
|
|
1732
|
+
"arrow-down-right": "↘️",
|
|
1733
|
+
"arrow-down-left": "↙️",
|
|
1734
|
+
"arrow-up-left": "↖️",
|
|
1735
|
+
"arrow-refresh": "\uD83D\uDD04",
|
|
1736
|
+
"symbol-plus": "➕",
|
|
1737
|
+
"symbol-minus": "➖",
|
|
1738
|
+
"symbol-question": "❓",
|
|
1739
|
+
"symbol-exclam": "❗",
|
|
1740
|
+
"symbol-info": "ℹ️",
|
|
1741
|
+
"symbol-wrong": "❌",
|
|
1742
|
+
"symbol-right": "✅",
|
|
1743
|
+
"symbol-attention": "⚠️",
|
|
1744
|
+
"symbol-code": "\uD83D\uDCBB",
|
|
1745
|
+
"symbol-pin": "\uD83D\uDCCC",
|
|
1746
|
+
"symbol-heart": "❤️",
|
|
1747
|
+
"symbol-star": "⭐",
|
|
1748
|
+
"other-calendar": "\uD83D\uDCC5",
|
|
1749
|
+
"other-email": "✉️",
|
|
1750
|
+
"other-phone": "☎️",
|
|
1751
|
+
"other-question": "❓",
|
|
1752
|
+
"other-exclam": "❗",
|
|
1753
|
+
"other-lightbulb": "\uD83D\uDCA1",
|
|
1754
|
+
"other-clock": "⏰",
|
|
1755
|
+
"other-people": "\uD83D\uDC65",
|
|
1756
|
+
"other-money": "\uD83D\uDCB0",
|
|
1757
|
+
"other-books": "\uD83D\uDCDA",
|
|
1758
|
+
"other-music": "\uD83C\uDFB5",
|
|
1759
|
+
"other-yes": "\uD83D\uDC4D",
|
|
1760
|
+
"other-no": "\uD83D\uDC4E",
|
|
1761
|
+
"other-hourglass": "⏳",
|
|
1762
|
+
"other-flag": "\uD83D\uDEA9",
|
|
1763
|
+
"other-star": "⭐",
|
|
1764
|
+
"other-coffee-cup": "☕",
|
|
1765
|
+
"other-bomb": "\uD83D\uDCA3",
|
|
1766
|
+
"other-mic": "\uD83C\uDFA4",
|
|
1767
|
+
"other-phone-call": "\uD83D\uDCDE",
|
|
1768
|
+
"other-map": "\uD83D\uDDFA️",
|
|
1769
|
+
"other-code": "\uD83D\uDCBB",
|
|
1770
|
+
"other-search": "\uD83D\uDD0D",
|
|
1771
|
+
"other-dollar": "\uD83D\uDCB5",
|
|
1772
|
+
"other-heart": "❤️",
|
|
1773
|
+
c_symbol_like: "\uD83D\uDC4D",
|
|
1774
|
+
c_symbol_dislike: "\uD83D\uDC4E",
|
|
1775
|
+
c_symbol_heart: "❤️",
|
|
1776
|
+
c_symbol_star: "⭐",
|
|
1777
|
+
c_symbol_question: "❓",
|
|
1778
|
+
c_symbol_exclam: "❗",
|
|
1779
|
+
c_symbol_pin: "\uD83D\uDCCC",
|
|
1780
|
+
c_symbol_idea: "\uD83D\uDCA1",
|
|
1781
|
+
c_symbol_check: "✅",
|
|
1782
|
+
c_symbol_cross: "❌",
|
|
1783
|
+
c_symbol_fire: "\uD83D\uDD25",
|
|
1784
|
+
c_symbol_trophy: "\uD83C\uDFC6"
|
|
1785
|
+
}));
|
|
1786
|
+
var XMIND_FAMILIES = new Map(Object.entries({
|
|
1787
|
+
flag: "\uD83D\uDEA9",
|
|
1788
|
+
star: "⭐",
|
|
1789
|
+
people: "\uD83D\uDC64",
|
|
1790
|
+
tag: "\uD83C\uDFF7️",
|
|
1791
|
+
month: "\uD83D\uDCC5",
|
|
1792
|
+
week: "\uD83D\uDCC5",
|
|
1793
|
+
"half-star": "⭐"
|
|
1794
|
+
}));
|
|
1795
|
+
var XMIND_TASKS = new Map(Object.entries({
|
|
1796
|
+
"task-start": "todo",
|
|
1797
|
+
"task-oct": "active",
|
|
1798
|
+
"task-quarter": "active",
|
|
1799
|
+
"task-3oct": "active",
|
|
1800
|
+
"task-half": "active",
|
|
1801
|
+
"task-5oct": "active",
|
|
1802
|
+
"task-3quar": "active",
|
|
1803
|
+
"task-7oct": "active",
|
|
1804
|
+
"task-done": "done",
|
|
1805
|
+
"task-pause": "paused"
|
|
1806
|
+
}));
|
|
1807
|
+
var xmindMarker = (markerId) => {
|
|
1808
|
+
const priority = /^priority-(\d)$/.exec(markerId);
|
|
1809
|
+
if (priority) {
|
|
1810
|
+
return { priority: Number(priority[1]) };
|
|
1811
|
+
}
|
|
1812
|
+
const status = XMIND_TASKS.get(markerId);
|
|
1813
|
+
if (status) {
|
|
1814
|
+
return { status };
|
|
1815
|
+
}
|
|
1816
|
+
const icon = XMIND_MARKERS.get(markerId);
|
|
1817
|
+
if (icon) {
|
|
1818
|
+
return { icon };
|
|
1819
|
+
}
|
|
1820
|
+
const family = [...XMIND_FAMILIES.keys()].sort((first, second) => second.length - first.length).find((name) => markerId.startsWith(`${name}-`));
|
|
1821
|
+
return family ? { icon: XMIND_FAMILIES.get(family) } : null;
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
// src/core/other-apps/markup.ts
|
|
1825
|
+
class MarkupError extends Error {
|
|
1826
|
+
line;
|
|
1827
|
+
constructor(message, line) {
|
|
1828
|
+
super(`${message} (line ${line})`);
|
|
1829
|
+
this.name = "MarkupError";
|
|
1830
|
+
this.line = line;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
var MAX_MARKUP_LENGTH = 64 * 1024 * 1024;
|
|
1834
|
+
var MAX_MARKUP_DEPTH = 1000;
|
|
1835
|
+
var XML_ENTITIES = { lt: "<", gt: ">", amp: "&", quot: '"', apos: "'" };
|
|
1836
|
+
var HTML_ENTITIES = {
|
|
1837
|
+
...XML_ENTITIES,
|
|
1838
|
+
nbsp: " ",
|
|
1839
|
+
copy: "©",
|
|
1840
|
+
reg: "®",
|
|
1841
|
+
trade: "™",
|
|
1842
|
+
hellip: "…",
|
|
1843
|
+
mdash: "—",
|
|
1844
|
+
ndash: "–",
|
|
1845
|
+
lsquo: "‘",
|
|
1846
|
+
rsquo: "’",
|
|
1847
|
+
ldquo: "“",
|
|
1848
|
+
rdquo: "”",
|
|
1849
|
+
laquo: "«",
|
|
1850
|
+
raquo: "»",
|
|
1851
|
+
bull: "•",
|
|
1852
|
+
middot: "·",
|
|
1853
|
+
deg: "°",
|
|
1854
|
+
plusmn: "±",
|
|
1855
|
+
times: "×",
|
|
1856
|
+
divide: "÷",
|
|
1857
|
+
euro: "€",
|
|
1858
|
+
pound: "£",
|
|
1859
|
+
yen: "¥",
|
|
1860
|
+
cent: "¢",
|
|
1861
|
+
sect: "§",
|
|
1862
|
+
para: "¶",
|
|
1863
|
+
iexcl: "¡",
|
|
1864
|
+
iquest: "¿",
|
|
1865
|
+
ordf: "ª",
|
|
1866
|
+
ordm: "º",
|
|
1867
|
+
acute: "´",
|
|
1868
|
+
uml: "¨",
|
|
1869
|
+
shy: "",
|
|
1870
|
+
frac12: "½",
|
|
1871
|
+
frac14: "¼",
|
|
1872
|
+
frac34: "¾",
|
|
1873
|
+
sup1: "¹",
|
|
1874
|
+
sup2: "²",
|
|
1875
|
+
sup3: "³",
|
|
1876
|
+
micro: "µ",
|
|
1877
|
+
larr: "←",
|
|
1878
|
+
rarr: "→",
|
|
1879
|
+
uarr: "↑",
|
|
1880
|
+
darr: "↓",
|
|
1881
|
+
harr: "↔",
|
|
1882
|
+
rArr: "⇒",
|
|
1883
|
+
lArr: "⇐",
|
|
1884
|
+
hArr: "⇔",
|
|
1885
|
+
aacute: "á",
|
|
1886
|
+
eacute: "é",
|
|
1887
|
+
iacute: "í",
|
|
1888
|
+
oacute: "ó",
|
|
1889
|
+
uacute: "ú",
|
|
1890
|
+
yacute: "ý",
|
|
1891
|
+
Aacute: "Á",
|
|
1892
|
+
Eacute: "É",
|
|
1893
|
+
Iacute: "Í",
|
|
1894
|
+
Oacute: "Ó",
|
|
1895
|
+
Uacute: "Ú",
|
|
1896
|
+
Yacute: "Ý",
|
|
1897
|
+
agrave: "à",
|
|
1898
|
+
egrave: "è",
|
|
1899
|
+
igrave: "ì",
|
|
1900
|
+
ograve: "ò",
|
|
1901
|
+
ugrave: "ù",
|
|
1902
|
+
Agrave: "À",
|
|
1903
|
+
Egrave: "È",
|
|
1904
|
+
Igrave: "Ì",
|
|
1905
|
+
Ograve: "Ò",
|
|
1906
|
+
Ugrave: "Ù",
|
|
1907
|
+
acirc: "â",
|
|
1908
|
+
ecirc: "ê",
|
|
1909
|
+
icirc: "î",
|
|
1910
|
+
ocirc: "ô",
|
|
1911
|
+
ucirc: "û",
|
|
1912
|
+
Acirc: "Â",
|
|
1913
|
+
Ecirc: "Ê",
|
|
1914
|
+
Icirc: "Î",
|
|
1915
|
+
Ocirc: "Ô",
|
|
1916
|
+
Ucirc: "Û",
|
|
1917
|
+
auml: "ä",
|
|
1918
|
+
euml: "ë",
|
|
1919
|
+
iuml: "ï",
|
|
1920
|
+
ouml: "ö",
|
|
1921
|
+
uuml: "ü",
|
|
1922
|
+
yuml: "ÿ",
|
|
1923
|
+
Auml: "Ä",
|
|
1924
|
+
Euml: "Ë",
|
|
1925
|
+
Iuml: "Ï",
|
|
1926
|
+
Ouml: "Ö",
|
|
1927
|
+
Uuml: "Ü",
|
|
1928
|
+
atilde: "ã",
|
|
1929
|
+
otilde: "õ",
|
|
1930
|
+
ntilde: "ñ",
|
|
1931
|
+
Atilde: "Ã",
|
|
1932
|
+
Otilde: "Õ",
|
|
1933
|
+
Ntilde: "Ñ",
|
|
1934
|
+
ccedil: "ç",
|
|
1935
|
+
Ccedil: "Ç",
|
|
1936
|
+
aring: "å",
|
|
1937
|
+
Aring: "Å",
|
|
1938
|
+
aelig: "æ",
|
|
1939
|
+
AElig: "Æ",
|
|
1940
|
+
oslash: "ø",
|
|
1941
|
+
Oslash: "Ø",
|
|
1942
|
+
szlig: "ß",
|
|
1943
|
+
thorn: "þ",
|
|
1944
|
+
THORN: "Þ",
|
|
1945
|
+
eth: "ð",
|
|
1946
|
+
ETH: "Ð",
|
|
1947
|
+
oelig: "œ",
|
|
1948
|
+
OElig: "Œ"
|
|
1949
|
+
};
|
|
1950
|
+
var VOID_ELEMENTS = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
1951
|
+
var RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
|
|
1952
|
+
var NAME = /^[^\s/>=]+/;
|
|
1953
|
+
var ATTRIBUTE = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/y;
|
|
1954
|
+
var decodeMarkup = (bytes) => {
|
|
1955
|
+
if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
1956
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
1957
|
+
}
|
|
1958
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
1959
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
1960
|
+
}
|
|
1961
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
1962
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
1963
|
+
}
|
|
1964
|
+
if (bytes[0] === 60 && bytes[1] === 0) {
|
|
1965
|
+
return new TextDecoder("utf-16le").decode(bytes);
|
|
1966
|
+
}
|
|
1967
|
+
if (bytes[0] === 0 && bytes[1] === 60) {
|
|
1968
|
+
return new TextDecoder("utf-16be").decode(bytes);
|
|
1969
|
+
}
|
|
1970
|
+
const start = String.fromCharCode(...bytes.subarray(0, 200));
|
|
1971
|
+
const declared = /^\s*<\?xml[^>]*\bencoding\s*=\s*["']([A-Za-z0-9._:-]+)["']/.exec(start)?.[1];
|
|
1972
|
+
if (declared && !/^utf-?8$/i.test(declared)) {
|
|
1973
|
+
try {
|
|
1974
|
+
return new TextDecoder(declared).decode(bytes);
|
|
1975
|
+
} catch {}
|
|
1976
|
+
}
|
|
1977
|
+
try {
|
|
1978
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
1979
|
+
} catch {
|
|
1980
|
+
return new TextDecoder(declared ? "utf-8" : "windows-1252").decode(bytes);
|
|
1981
|
+
}
|
|
1982
|
+
};
|
|
1983
|
+
var parseXml = (text) => {
|
|
1984
|
+
const root = readMarkup(text, { html: false });
|
|
1985
|
+
const elements = root.children.filter(isElement);
|
|
1986
|
+
if (elements.length !== 1) {
|
|
1987
|
+
throw new MarkupError(elements.length === 0 ? "No element in the file" : "More than one top element", 1);
|
|
1988
|
+
}
|
|
1989
|
+
return elements[0];
|
|
1990
|
+
};
|
|
1991
|
+
var parseHtml = (text) => {
|
|
1992
|
+
return readMarkup(text, { html: true });
|
|
1993
|
+
};
|
|
1994
|
+
var isElement = (node) => {
|
|
1995
|
+
return typeof node !== "string";
|
|
1996
|
+
};
|
|
1997
|
+
var childElements = (element, localName) => {
|
|
1998
|
+
return element.children.filter((child) => isElement(child) && (localName === undefined || child.localName === localName));
|
|
1999
|
+
};
|
|
2000
|
+
var childElement = (element, localName) => {
|
|
2001
|
+
return element.children.find((child) => isElement(child) && child.localName === localName);
|
|
2002
|
+
};
|
|
2003
|
+
var attributeOf = (element, localName) => {
|
|
2004
|
+
if (Object.prototype.hasOwnProperty.call(element.attributes, localName)) {
|
|
2005
|
+
return element.attributes[localName];
|
|
2006
|
+
}
|
|
2007
|
+
for (const [name, value] of Object.entries(element.attributes)) {
|
|
2008
|
+
if (localPart(name) === localName) {
|
|
2009
|
+
return value;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return;
|
|
2013
|
+
};
|
|
2014
|
+
var textContent = (node) => {
|
|
2015
|
+
if (!isElement(node)) {
|
|
2016
|
+
return node;
|
|
2017
|
+
}
|
|
2018
|
+
let text = "";
|
|
2019
|
+
const pending = [node];
|
|
2020
|
+
while (pending.length > 0) {
|
|
2021
|
+
const next = pending.pop();
|
|
2022
|
+
if (isElement(next)) {
|
|
2023
|
+
for (let index = next.children.length - 1;index >= 0; index--) {
|
|
2024
|
+
pending.push(next.children[index]);
|
|
2025
|
+
}
|
|
2026
|
+
} else {
|
|
2027
|
+
text += next;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
return text;
|
|
2031
|
+
};
|
|
2032
|
+
var localPart = (name) => {
|
|
2033
|
+
const colon = name.indexOf(":");
|
|
2034
|
+
return colon === -1 ? name : name.slice(colon + 1);
|
|
2035
|
+
};
|
|
2036
|
+
var lineAt = (text, index) => {
|
|
2037
|
+
let line = 1;
|
|
2038
|
+
for (let position = text.indexOf(`
|
|
2039
|
+
`);position !== -1 && position < index; position = text.indexOf(`
|
|
2040
|
+
`, position + 1)) {
|
|
2041
|
+
line += 1;
|
|
2042
|
+
}
|
|
2043
|
+
return line;
|
|
2044
|
+
};
|
|
2045
|
+
var readMarkup = (input, mode) => {
|
|
2046
|
+
if (input.length > MAX_MARKUP_LENGTH) {
|
|
2047
|
+
throw new MarkupError(`The file is larger than ${MAX_MARKUP_LENGTH / 1024 / 1024} MB of text`, 1);
|
|
2048
|
+
}
|
|
2049
|
+
const text = input.replace(/^\ufeff/, "").replace(/\r\n?/g, `
|
|
2050
|
+
`);
|
|
2051
|
+
const fragment = { name: "#fragment", localName: "#fragment", attributes: {}, children: [] };
|
|
2052
|
+
const open = [fragment];
|
|
2053
|
+
const entities = mode.html ? HTML_ENTITIES : XML_ENTITIES;
|
|
2054
|
+
let index = 0;
|
|
2055
|
+
const fail = (message, at) => {
|
|
2056
|
+
throw new MarkupError(message, lineAt(text, at));
|
|
2057
|
+
};
|
|
2058
|
+
const current = () => open[open.length - 1];
|
|
2059
|
+
const addText = (value) => {
|
|
2060
|
+
if (value === "") {
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const children = current().children;
|
|
2064
|
+
const last = children.length - 1;
|
|
2065
|
+
if (last >= 0 && typeof children[last] === "string") {
|
|
2066
|
+
children[last] = children[last] + value;
|
|
2067
|
+
} else {
|
|
2068
|
+
children.push(value);
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
while (index < text.length) {
|
|
2072
|
+
const tag = text.indexOf("<", index);
|
|
2073
|
+
if (tag === -1) {
|
|
2074
|
+
addText(decodeEntities(text.slice(index), entities));
|
|
2075
|
+
break;
|
|
2076
|
+
}
|
|
2077
|
+
if (tag > index) {
|
|
2078
|
+
addText(decodeEntities(text.slice(index, tag), entities));
|
|
2079
|
+
}
|
|
2080
|
+
index = tag;
|
|
2081
|
+
if (text.startsWith("<!--", index)) {
|
|
2082
|
+
const end = text.indexOf("-->", index + 4);
|
|
2083
|
+
if (end === -1) {
|
|
2084
|
+
if (mode.html) {
|
|
2085
|
+
break;
|
|
2086
|
+
}
|
|
2087
|
+
fail("A comment that never ends", index);
|
|
2088
|
+
}
|
|
2089
|
+
index = end + 3;
|
|
2090
|
+
continue;
|
|
2091
|
+
}
|
|
2092
|
+
if (text.startsWith("<![CDATA[", index)) {
|
|
2093
|
+
const end = text.indexOf("]]>", index + 9);
|
|
2094
|
+
if (end === -1) {
|
|
2095
|
+
fail("A CDATA section that never ends", index);
|
|
2096
|
+
}
|
|
2097
|
+
addText(text.slice(index + 9, end));
|
|
2098
|
+
index = end + 3;
|
|
2099
|
+
continue;
|
|
2100
|
+
}
|
|
2101
|
+
if (text.startsWith("<?", index)) {
|
|
2102
|
+
const end = text.indexOf("?>", index + 2);
|
|
2103
|
+
if (end === -1) {
|
|
2104
|
+
fail("A processing instruction that never ends", index);
|
|
2105
|
+
}
|
|
2106
|
+
index = end + 2;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (text.startsWith("<!", index)) {
|
|
2110
|
+
index = skipDeclaration(text, index, fail);
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
if (text.startsWith("</", index)) {
|
|
2114
|
+
const end = text.indexOf(">", index + 2);
|
|
2115
|
+
if (end === -1) {
|
|
2116
|
+
fail("An end tag that never closes", index);
|
|
2117
|
+
}
|
|
2118
|
+
const name = text.slice(index + 2, end).trim();
|
|
2119
|
+
closeElement(open, name, mode, () => fail(`</${name}> doesn't match <${current().name}>`, index));
|
|
2120
|
+
index = end + 1;
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const nameMatch = NAME.exec(text.slice(index + 1, index + 257));
|
|
2124
|
+
if (!nameMatch || !/^[\p{L}_:]/u.test(nameMatch[0])) {
|
|
2125
|
+
if (mode.html) {
|
|
2126
|
+
addText("<");
|
|
2127
|
+
index += 1;
|
|
2128
|
+
continue;
|
|
2129
|
+
}
|
|
2130
|
+
fail('A "<" that starts no tag', index);
|
|
2131
|
+
}
|
|
2132
|
+
const name = nameMatch[0];
|
|
2133
|
+
const [attributes, afterAttributes, selfClosing] = readAttributes(text, index + 1 + name.length, entities, mode, fail);
|
|
2134
|
+
const localName = mode.html ? localPart(name).toLowerCase() : localPart(name);
|
|
2135
|
+
const element = { name, localName, attributes, children: [] };
|
|
2136
|
+
current().children.push(element);
|
|
2137
|
+
index = afterAttributes;
|
|
2138
|
+
if (mode.html && RAW_TEXT_ELEMENTS.has(localName)) {
|
|
2139
|
+
const end = text.toLowerCase().indexOf(`</${localName}`, index);
|
|
2140
|
+
index = end === -1 ? text.length : text.indexOf(">", end) + 1 || text.length;
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
if (selfClosing || mode.html && VOID_ELEMENTS.has(localName)) {
|
|
2144
|
+
continue;
|
|
2145
|
+
}
|
|
2146
|
+
if (open.length > MAX_MARKUP_DEPTH) {
|
|
2147
|
+
fail(`Elements nested more than ${MAX_MARKUP_DEPTH} deep`, index);
|
|
2148
|
+
}
|
|
2149
|
+
open.push(element);
|
|
2150
|
+
}
|
|
2151
|
+
if (!mode.html && open.length > 1) {
|
|
2152
|
+
fail(`<${current().name}> is never closed`, text.length);
|
|
2153
|
+
}
|
|
2154
|
+
return fragment;
|
|
2155
|
+
};
|
|
2156
|
+
var readAttributes = (text, start, entities, mode, fail) => {
|
|
2157
|
+
const attributes = {};
|
|
2158
|
+
let index = start;
|
|
2159
|
+
while (index < text.length) {
|
|
2160
|
+
while (index < text.length && /\s/.test(text[index])) {
|
|
2161
|
+
index += 1;
|
|
2162
|
+
}
|
|
2163
|
+
if (text[index] === ">") {
|
|
2164
|
+
return [attributes, index + 1, false];
|
|
2165
|
+
}
|
|
2166
|
+
if (text.startsWith("/>", index)) {
|
|
2167
|
+
return [attributes, index + 2, true];
|
|
2168
|
+
}
|
|
2169
|
+
if (text[index] === "/") {
|
|
2170
|
+
index += 1;
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
ATTRIBUTE.lastIndex = index;
|
|
2174
|
+
const match = ATTRIBUTE.exec(text);
|
|
2175
|
+
if (!match || match[0].length === 0) {
|
|
2176
|
+
fail("A tag that never closes", start);
|
|
2177
|
+
}
|
|
2178
|
+
const [whole, name, doubleQuoted, singleQuoted, unquoted] = match;
|
|
2179
|
+
if (unquoted !== undefined && !mode.html) {
|
|
2180
|
+
fail(`The value of ${name} has no quotes`, index);
|
|
2181
|
+
}
|
|
2182
|
+
const raw = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
|
|
2183
|
+
attributes[name] = decodeEntities(mode.html ? raw : raw.replace(/[\t\n]/g, " "), entities);
|
|
2184
|
+
index += whole.length;
|
|
2185
|
+
}
|
|
2186
|
+
return fail("A tag that never closes", start);
|
|
2187
|
+
};
|
|
2188
|
+
var skipDeclaration = (text, start, fail) => {
|
|
2189
|
+
let depth = 0;
|
|
2190
|
+
let quote = null;
|
|
2191
|
+
for (let index = start + 2;index < text.length; index++) {
|
|
2192
|
+
const character = text[index];
|
|
2193
|
+
if (quote) {
|
|
2194
|
+
if (character === quote) {
|
|
2195
|
+
quote = null;
|
|
2196
|
+
}
|
|
2197
|
+
} else if (character === '"' || character === "'") {
|
|
2198
|
+
quote = character;
|
|
2199
|
+
} else if (character === "[") {
|
|
2200
|
+
depth += 1;
|
|
2201
|
+
} else if (character === "]") {
|
|
2202
|
+
depth -= 1;
|
|
2203
|
+
} else if (character === ">" && depth <= 0) {
|
|
2204
|
+
return index + 1;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return fail("A declaration that never ends", start);
|
|
2208
|
+
};
|
|
2209
|
+
var closeElement = (open, name, mode, mismatch) => {
|
|
2210
|
+
const top = open[open.length - 1];
|
|
2211
|
+
if (open.length > 1 && (top.name === name || mode.html && top.localName === localPart(name).toLowerCase())) {
|
|
2212
|
+
open.pop();
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
if (!mode.html) {
|
|
2216
|
+
mismatch();
|
|
2217
|
+
}
|
|
2218
|
+
const wanted = localPart(name).toLowerCase();
|
|
2219
|
+
for (let depth = open.length - 1;depth > 0; depth--) {
|
|
2220
|
+
if (open[depth].localName === wanted) {
|
|
2221
|
+
open.length = depth;
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
var decodeEntities = (text, entities) => {
|
|
2227
|
+
if (!text.includes("&")) {
|
|
2228
|
+
return text;
|
|
2229
|
+
}
|
|
2230
|
+
return text.replace(/&(#[xX][0-9a-fA-F]{1,6}|#[0-9]{1,7}|[A-Za-z][A-Za-z0-9]{1,31});/g, (whole, reference) => {
|
|
2231
|
+
if (reference.startsWith("#")) {
|
|
2232
|
+
const code = reference[1] === "x" || reference[1] === "X" ? parseInt(reference.slice(2), 16) : parseInt(reference.slice(1), 10);
|
|
2233
|
+
return code > 0 && code <= 1114111 && !(code >= 55296 && code <= 57343) ? String.fromCodePoint(code) : whole;
|
|
2234
|
+
}
|
|
2235
|
+
return Object.prototype.hasOwnProperty.call(entities, reference) ? entities[reference] : whole;
|
|
2236
|
+
});
|
|
2237
|
+
};
|
|
2238
|
+
|
|
2239
|
+
// src/core/other-apps/notes.ts
|
|
2240
|
+
var BLOCK_CONTAINERS = new Set([
|
|
2241
|
+
"html",
|
|
2242
|
+
"body",
|
|
2243
|
+
"div",
|
|
2244
|
+
"p",
|
|
2245
|
+
"section",
|
|
2246
|
+
"article",
|
|
2247
|
+
"header",
|
|
2248
|
+
"footer",
|
|
2249
|
+
"main",
|
|
2250
|
+
"aside",
|
|
2251
|
+
"nav",
|
|
2252
|
+
"center",
|
|
2253
|
+
"figure",
|
|
2254
|
+
"figcaption",
|
|
2255
|
+
"address",
|
|
2256
|
+
"details",
|
|
2257
|
+
"summary",
|
|
2258
|
+
"dl",
|
|
2259
|
+
"dt",
|
|
2260
|
+
"dd",
|
|
2261
|
+
"form",
|
|
2262
|
+
"fieldset",
|
|
2263
|
+
"tbody",
|
|
2264
|
+
"thead",
|
|
2265
|
+
"tfoot",
|
|
2266
|
+
"caption",
|
|
2267
|
+
"#fragment"
|
|
2268
|
+
]);
|
|
2269
|
+
var SKIPPED = new Set(["head", "title", "script", "style", "meta", "link", "template", "object", "iframe", "svg", "math"]);
|
|
2270
|
+
var HEADINGS = new Map([["h1", 1], ["h2", 2], ["h3", 3], ["h4", 4], ["h5", 5], ["h6", 6]]);
|
|
2271
|
+
var INLINE_KINDS = new Map([
|
|
2272
|
+
["strong", "strong"],
|
|
2273
|
+
["b", "strong"],
|
|
2274
|
+
["em", "emphasis"],
|
|
2275
|
+
["i", "emphasis"],
|
|
2276
|
+
["cite", "emphasis"],
|
|
2277
|
+
["s", "strike"],
|
|
2278
|
+
["strike", "strike"],
|
|
2279
|
+
["del", "strike"]
|
|
2280
|
+
]);
|
|
2281
|
+
var CODE_ELEMENTS = new Set(["code", "tt", "kbd", "samp", "var"]);
|
|
2282
|
+
var isKeptLink = (href) => {
|
|
2283
|
+
return /^(https?:\/\/|mailto:)\S+$/i.test(href) || /^#\S+$/.test(href);
|
|
2284
|
+
};
|
|
2285
|
+
var keptAddress = (href) => {
|
|
2286
|
+
const address = href.trim();
|
|
2287
|
+
if (/^www\.[^\s/]+\.\S+$/i.test(address)) {
|
|
2288
|
+
return `https://${address}`;
|
|
2289
|
+
}
|
|
2290
|
+
return isKeptLink(address) ? address : null;
|
|
2291
|
+
};
|
|
2292
|
+
var isWebAddress = (src) => {
|
|
2293
|
+
return /^https?:\/\/\S+$/i.test(src);
|
|
2294
|
+
};
|
|
2295
|
+
var noteFromHtml = (html, addresses = {}) => {
|
|
2296
|
+
const element = typeof html === "string" ? parseHtml(html) : html;
|
|
2297
|
+
return noteFromBlocks(readBlocks(element.children, addresses), addresses);
|
|
2298
|
+
};
|
|
2299
|
+
var noteFromPlainText = (text) => {
|
|
2300
|
+
const cleaned = (text ?? "").replace(/\r\n?/g, `
|
|
2301
|
+
`).split(`
|
|
2302
|
+
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
2303
|
+
`).replace(/^\n+|\n+$/g, "");
|
|
2304
|
+
if (cleaned.trim() === "") {
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
const paragraphs = cleaned.split(/\n{2,}/);
|
|
2308
|
+
const readsTheSame = paragraphs.every((paragraph) => !paragraph.includes(`
|
|
2309
|
+
`) && isInertText(paragraph) && isInertLineStart(paragraph));
|
|
2310
|
+
return { text: cleaned, format: readsTheSame ? "markdown" : "plain" };
|
|
2311
|
+
};
|
|
2312
|
+
var noteFromBlocks = (blocks, addresses = {}) => {
|
|
2313
|
+
const kept = blocks.filter((block) => !isEmptyBlock(block));
|
|
2314
|
+
if (kept.length === 0) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const writer = new MarkdownWriter(addresses);
|
|
2318
|
+
const markdown = writer.blocks(kept);
|
|
2319
|
+
if (writer.inert && markdown.trim() !== "") {
|
|
2320
|
+
return { text: markdown, format: "markdown" };
|
|
2321
|
+
}
|
|
2322
|
+
const plain = plainBlocks(kept).trim();
|
|
2323
|
+
return plain === "" ? undefined : { text: plain, format: "plain" };
|
|
2324
|
+
};
|
|
2325
|
+
var joinNotes = (notes) => {
|
|
2326
|
+
const present = notes.filter((note) => note !== undefined && note.text.trim() !== "");
|
|
2327
|
+
if (present.length <= 1) {
|
|
2328
|
+
return present[0];
|
|
2329
|
+
}
|
|
2330
|
+
return {
|
|
2331
|
+
text: present.map((note) => note.text).join(`
|
|
2332
|
+
|
|
2333
|
+
`),
|
|
2334
|
+
format: present.every((note) => note.format === "markdown") ? "markdown" : "plain"
|
|
2335
|
+
};
|
|
2336
|
+
};
|
|
2337
|
+
var titleFromHtml = (html) => {
|
|
2338
|
+
const element = typeof html === "string" ? parseHtml(html) : html;
|
|
2339
|
+
const pieces = inlineFrom(element.children, {});
|
|
2340
|
+
const link = firstLink(pieces);
|
|
2341
|
+
return { text: plainInline(pieces, false, false).replace(/\s+/g, " ").trim(), ...link ? { link } : {} };
|
|
2342
|
+
};
|
|
2343
|
+
var firstLink = (pieces) => {
|
|
2344
|
+
for (const piece of pieces) {
|
|
2345
|
+
if (piece.kind === "link") {
|
|
2346
|
+
return piece.href;
|
|
2347
|
+
}
|
|
2348
|
+
if (piece.kind === "strong" || piece.kind === "emphasis" || piece.kind === "strike") {
|
|
2349
|
+
const inner = firstLink(piece.children);
|
|
2350
|
+
if (inner) {
|
|
2351
|
+
return inner;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
return;
|
|
2356
|
+
};
|
|
2357
|
+
var readBlocks = (nodes, addresses) => {
|
|
2358
|
+
const blocks = [];
|
|
2359
|
+
let inline = [];
|
|
2360
|
+
const flush = () => {
|
|
2361
|
+
const trimmed = trimInline(inline);
|
|
2362
|
+
if (trimmed.length > 0) {
|
|
2363
|
+
blocks.push({ kind: "paragraph", inline: trimmed });
|
|
2364
|
+
}
|
|
2365
|
+
inline = [];
|
|
2366
|
+
};
|
|
2367
|
+
for (const node of nodes) {
|
|
2368
|
+
if (!isElement(node)) {
|
|
2369
|
+
inline.push(...inlineFrom([node], addresses));
|
|
2370
|
+
continue;
|
|
2371
|
+
}
|
|
2372
|
+
const name = node.localName.toLowerCase();
|
|
2373
|
+
if (SKIPPED.has(name)) {
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
const headingLevel = HEADINGS.get(name);
|
|
2377
|
+
if (headingLevel !== undefined) {
|
|
2378
|
+
flush();
|
|
2379
|
+
const content = trimInline(inlineFrom(node.children, addresses).map((piece) => piece.kind === "break" ? { kind: "text", text: " " } : piece));
|
|
2380
|
+
if (content.length > 0) {
|
|
2381
|
+
blocks.push({ kind: "heading", level: headingLevel, inline: content });
|
|
2382
|
+
}
|
|
2383
|
+
} else if (name === "ul" || name === "ol" || name === "menu" || name === "dir") {
|
|
2384
|
+
flush();
|
|
2385
|
+
const items = node.children.filter((child) => isElement(child) || child.trim() !== "").map((child) => readBlocks(isElement(child) && child.localName.toLowerCase() === "li" ? child.children : [child], addresses)).filter((item) => item.some((block) => !isEmptyBlock(block)));
|
|
2386
|
+
if (items.length > 0) {
|
|
2387
|
+
blocks.push({ kind: "list", ordered: name === "ol", items });
|
|
2388
|
+
}
|
|
2389
|
+
} else if (name === "li") {
|
|
2390
|
+
flush();
|
|
2391
|
+
blocks.push({ kind: "list", ordered: false, items: [readBlocks(node.children, addresses)] });
|
|
2392
|
+
} else if (name === "blockquote") {
|
|
2393
|
+
flush();
|
|
2394
|
+
const quoted = readBlocks(node.children, addresses);
|
|
2395
|
+
if (quoted.length > 0) {
|
|
2396
|
+
blocks.push({ kind: "quote", blocks: quoted });
|
|
2397
|
+
}
|
|
2398
|
+
} else if (name === "pre") {
|
|
2399
|
+
flush();
|
|
2400
|
+
const code = textContent(node).replace(/^\n/, "").replace(/\s+$/, "");
|
|
2401
|
+
if (code !== "") {
|
|
2402
|
+
blocks.push({ kind: "code", text: code });
|
|
2403
|
+
}
|
|
2404
|
+
} else if (name === "table") {
|
|
2405
|
+
flush();
|
|
2406
|
+
blocks.push(...tableRows(node, addresses));
|
|
2407
|
+
} else if (name === "hr") {
|
|
2408
|
+
flush();
|
|
2409
|
+
} else if (BLOCK_CONTAINERS.has(name)) {
|
|
2410
|
+
flush();
|
|
2411
|
+
blocks.push(...readBlocks(node.children, addresses));
|
|
2412
|
+
} else {
|
|
2413
|
+
inline.push(...inlineFrom([node], addresses));
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
flush();
|
|
2417
|
+
return blocks;
|
|
2418
|
+
};
|
|
2419
|
+
var tableRows = (table, addresses) => {
|
|
2420
|
+
const rows = [];
|
|
2421
|
+
const pending = [table];
|
|
2422
|
+
while (pending.length > 0) {
|
|
2423
|
+
const element = pending.shift();
|
|
2424
|
+
for (const child of childElements(element)) {
|
|
2425
|
+
const name = child.localName.toLowerCase();
|
|
2426
|
+
if (name === "tr") {
|
|
2427
|
+
const cells = childElements(child).filter((cell) => /^t[dh]$/.test(cell.localName.toLowerCase()));
|
|
2428
|
+
const inline = [];
|
|
2429
|
+
for (const cell of cells) {
|
|
2430
|
+
const content = trimInline(inlineFrom(cell.children, addresses).map((piece) => piece.kind === "break" ? { kind: "text", text: " " } : piece));
|
|
2431
|
+
if (content.length > 0) {
|
|
2432
|
+
if (inline.length > 0) {
|
|
2433
|
+
inline.push({ kind: "text", text: " · " });
|
|
2434
|
+
}
|
|
2435
|
+
inline.push(...content);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
if (inline.length > 0) {
|
|
2439
|
+
rows.push({ kind: "paragraph", inline });
|
|
2440
|
+
}
|
|
2441
|
+
} else if (name !== "td" && name !== "th") {
|
|
2442
|
+
pending.push(child);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
return rows;
|
|
2447
|
+
};
|
|
2448
|
+
var inlineFrom = (nodes, addresses) => {
|
|
2449
|
+
const pieces = [];
|
|
2450
|
+
for (const node of nodes) {
|
|
2451
|
+
if (!isElement(node)) {
|
|
2452
|
+
const text = node.replace(/[ \t\n\r\f]+/g, " ");
|
|
2453
|
+
if (text !== "") {
|
|
2454
|
+
pieces.push({ kind: "text", text });
|
|
2455
|
+
}
|
|
2456
|
+
continue;
|
|
2457
|
+
}
|
|
2458
|
+
const name = node.localName.toLowerCase();
|
|
2459
|
+
if (SKIPPED.has(name)) {
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
if (name === "br") {
|
|
2463
|
+
pieces.push({ kind: "break" });
|
|
2464
|
+
} else if (name === "img") {
|
|
2465
|
+
const src = node.attributes.src ?? Object.entries(node.attributes).find(([key]) => key.endsWith(":src"))?.[1] ?? "";
|
|
2466
|
+
pieces.push({ kind: "image", src, alt: (node.attributes.alt ?? node.attributes.title ?? "").trim() });
|
|
2467
|
+
} else if (INLINE_KINDS.has(name)) {
|
|
2468
|
+
pieces.push({ kind: INLINE_KINDS.get(name), children: inlineFrom(node.children, addresses) });
|
|
2469
|
+
} else if (CODE_ELEMENTS.has(name)) {
|
|
2470
|
+
pieces.push({ kind: "code", text: textContent(node).replace(/\s+/g, " ") });
|
|
2471
|
+
} else if (name === "a") {
|
|
2472
|
+
const href = (node.attributes.href ?? Object.entries(node.attributes).find(([key]) => key.endsWith(":href"))?.[1] ?? "").trim();
|
|
2473
|
+
const children = inlineFrom(node.children, addresses);
|
|
2474
|
+
if (href) {
|
|
2475
|
+
pieces.push({ kind: "link", href, children });
|
|
2476
|
+
} else {
|
|
2477
|
+
pieces.push(...children);
|
|
2478
|
+
}
|
|
2479
|
+
} else {
|
|
2480
|
+
const block = BLOCK_CONTAINERS.has(name) || HEADINGS.has(name) || name === "li" || name === "tr";
|
|
2481
|
+
const content = inlineFrom(node.children, addresses);
|
|
2482
|
+
if (block && pieces.length > 0) {
|
|
2483
|
+
pieces.push({ kind: "break" });
|
|
2484
|
+
}
|
|
2485
|
+
pieces.push(...content);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
return pieces;
|
|
2489
|
+
};
|
|
2490
|
+
var trimInline = (pieces) => {
|
|
2491
|
+
const result = [...pieces];
|
|
2492
|
+
while (result.length > 0 && isBlankPiece(result[0])) {
|
|
2493
|
+
result.shift();
|
|
2494
|
+
}
|
|
2495
|
+
while (result.length > 0 && isBlankPiece(result[result.length - 1])) {
|
|
2496
|
+
result.pop();
|
|
2497
|
+
}
|
|
2498
|
+
if (result.length > 0 && result[0].kind === "text") {
|
|
2499
|
+
result[0] = { kind: "text", text: result[0].text.replace(/^ +/, "") };
|
|
2500
|
+
}
|
|
2501
|
+
const last = result.length - 1;
|
|
2502
|
+
if (last >= 0 && result[last].kind === "text") {
|
|
2503
|
+
result[last] = { kind: "text", text: result[last].text.replace(/ +$/, "") };
|
|
2504
|
+
}
|
|
2505
|
+
return result;
|
|
2506
|
+
};
|
|
2507
|
+
var isBlankPiece = (piece) => {
|
|
2508
|
+
return piece.kind === "break" || piece.kind === "text" && piece.text.trim() === "";
|
|
2509
|
+
};
|
|
2510
|
+
var isEmptyBlock = (block) => {
|
|
2511
|
+
switch (block.kind) {
|
|
2512
|
+
case "paragraph":
|
|
2513
|
+
case "heading":
|
|
2514
|
+
return plainInline(block.inline, false).trim() === "" && !block.inline.some((piece) => piece.kind === "image" && piece.src !== "");
|
|
2515
|
+
case "list":
|
|
2516
|
+
return block.items.every((item) => item.every(isEmptyBlock));
|
|
2517
|
+
case "quote":
|
|
2518
|
+
return block.blocks.every(isEmptyBlock);
|
|
2519
|
+
case "code":
|
|
2520
|
+
return block.text.trim() === "";
|
|
2521
|
+
}
|
|
2522
|
+
};
|
|
2523
|
+
var ACTIVE_CHARACTERS = /[`*_~[\]<\\]|&#?[A-Za-z0-9]+;/;
|
|
2524
|
+
var ACTIVE_LINE_START = /^(\s{4,}|\s*(#{1,6}(\s|$)|>|[-*+](\s|$)|\d{1,9}[.)](\s|$)|```|~~~|[-=_]{3,}\s*$))/;
|
|
2525
|
+
var isInertText = (text) => {
|
|
2526
|
+
return !ACTIVE_CHARACTERS.test(text);
|
|
2527
|
+
};
|
|
2528
|
+
var isInertLineStart = (line) => {
|
|
2529
|
+
return !ACTIVE_LINE_START.test(line);
|
|
2530
|
+
};
|
|
2531
|
+
|
|
2532
|
+
class MarkdownWriter {
|
|
2533
|
+
inert = true;
|
|
2534
|
+
addresses;
|
|
2535
|
+
constructor(addresses) {
|
|
2536
|
+
this.addresses = addresses;
|
|
2537
|
+
}
|
|
2538
|
+
blocks(blocks) {
|
|
2539
|
+
return blocks.map((block) => this.block(block)).filter((text) => text !== "").join(`
|
|
2540
|
+
|
|
2541
|
+
`);
|
|
2542
|
+
}
|
|
2543
|
+
block(block) {
|
|
2544
|
+
switch (block.kind) {
|
|
2545
|
+
case "paragraph": {
|
|
2546
|
+
const lines = this.inlineLines(block.inline);
|
|
2547
|
+
return lines.map((line) => this.lineStart(line)).filter((line) => line !== "").join(`
|
|
2548
|
+
|
|
2549
|
+
`);
|
|
2550
|
+
}
|
|
2551
|
+
case "heading": {
|
|
2552
|
+
const text = this.inlineLines(block.inline).join(" ").trim();
|
|
2553
|
+
return text === "" ? "" : `${"#".repeat(block.level)} ${text}`;
|
|
2554
|
+
}
|
|
2555
|
+
case "list":
|
|
2556
|
+
return this.list(block, 0);
|
|
2557
|
+
case "quote": {
|
|
2558
|
+
const inner = this.blocks(block.blocks);
|
|
2559
|
+
return inner.split(`
|
|
2560
|
+
`).map((line) => line === "" ? ">" : `> ${line}`).join(`
|
|
2561
|
+
`);
|
|
2562
|
+
}
|
|
2563
|
+
case "code": {
|
|
2564
|
+
if (/^\s*(```|~~~)/m.test(block.text)) {
|
|
2565
|
+
this.inert = false;
|
|
2566
|
+
}
|
|
2567
|
+
return `\`\`\`
|
|
2568
|
+
${block.text}
|
|
2569
|
+
\`\`\``;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
list(list, indent) {
|
|
2574
|
+
const lines = [];
|
|
2575
|
+
list.items.forEach((item, index) => {
|
|
2576
|
+
const marker = list.ordered ? `${index + 1}.` : "-";
|
|
2577
|
+
const nested = [];
|
|
2578
|
+
const words = [];
|
|
2579
|
+
for (const block of item) {
|
|
2580
|
+
if (block.kind === "list") {
|
|
2581
|
+
nested.push(this.list(block, indent + marker.length + 1));
|
|
2582
|
+
} else {
|
|
2583
|
+
words.push(this.flatText(block));
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
const text = words.filter((word) => word !== "").join(" ");
|
|
2587
|
+
lines.push(`${" ".repeat(indent)}${marker} ${text}`.trimEnd());
|
|
2588
|
+
lines.push(...nested);
|
|
2589
|
+
});
|
|
2590
|
+
return lines.join(`
|
|
2591
|
+
`);
|
|
2592
|
+
}
|
|
2593
|
+
flatText(block) {
|
|
2594
|
+
switch (block.kind) {
|
|
2595
|
+
case "paragraph":
|
|
2596
|
+
case "heading":
|
|
2597
|
+
return this.inlineLines(block.inline).join(" ").trim();
|
|
2598
|
+
case "quote":
|
|
2599
|
+
return block.blocks.map((inner) => this.flatText(inner)).join(" ");
|
|
2600
|
+
case "code":
|
|
2601
|
+
return this.code(block.text.replace(/\s+/g, " "));
|
|
2602
|
+
case "list":
|
|
2603
|
+
return this.list(block, 0);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
lineStart(line) {
|
|
2607
|
+
if (!isInertLineStart(line)) {
|
|
2608
|
+
this.inert = false;
|
|
2609
|
+
}
|
|
2610
|
+
return line.trim();
|
|
2611
|
+
}
|
|
2612
|
+
inlineLines(pieces) {
|
|
2613
|
+
return this.inlineText(pieces).split(`
|
|
2614
|
+
`).map((line) => line.replace(/ {2,}/g, " ").trim());
|
|
2615
|
+
}
|
|
2616
|
+
inlineText(pieces) {
|
|
2617
|
+
return mergeNeighbors(pieces).map((piece) => piece.kind === "break" ? `
|
|
2618
|
+
` : this.inline(piece)).join("");
|
|
2619
|
+
}
|
|
2620
|
+
inline(piece) {
|
|
2621
|
+
switch (piece.kind) {
|
|
2622
|
+
case "text":
|
|
2623
|
+
if (!isInertText(piece.text)) {
|
|
2624
|
+
this.inert = false;
|
|
2625
|
+
}
|
|
2626
|
+
return piece.text;
|
|
2627
|
+
case "break":
|
|
2628
|
+
return " ";
|
|
2629
|
+
case "strong":
|
|
2630
|
+
case "emphasis":
|
|
2631
|
+
case "strike": {
|
|
2632
|
+
const inner = this.inlineText(piece.children).replace(/\n/g, " ").replace(/ {2,}/g, " ");
|
|
2633
|
+
const [, before, middle, after] = /^(\s*)([\s\S]*?)(\s*)$/.exec(inner);
|
|
2634
|
+
if (middle === "") {
|
|
2635
|
+
return inner;
|
|
2636
|
+
}
|
|
2637
|
+
const mark = piece.kind === "strong" ? "**" : piece.kind === "emphasis" ? "*" : "~~";
|
|
2638
|
+
return `${before}${mark}${middle}${mark}${after}`;
|
|
2639
|
+
}
|
|
2640
|
+
case "code":
|
|
2641
|
+
return this.code(piece.text);
|
|
2642
|
+
case "link": {
|
|
2643
|
+
const label = this.inlineLines(piece.children).join(" ").trim();
|
|
2644
|
+
const href = (this.addresses.link ?? defaultLink)(piece.href);
|
|
2645
|
+
if (!href) {
|
|
2646
|
+
return label;
|
|
2647
|
+
}
|
|
2648
|
+
return label === "" || label === href ? `<${encodeAddress(href)}>` : `[${label}](${encodeAddress(href)})`;
|
|
2649
|
+
}
|
|
2650
|
+
case "image": {
|
|
2651
|
+
const src = piece.src ? (this.addresses.picture ?? defaultPicture)(piece.src) : null;
|
|
2652
|
+
if (!src) {
|
|
2653
|
+
if (!isInertText(piece.alt)) {
|
|
2654
|
+
this.inert = false;
|
|
2655
|
+
}
|
|
2656
|
+
return piece.alt;
|
|
2657
|
+
}
|
|
2658
|
+
if (!isInertText(piece.alt)) {
|
|
2659
|
+
this.inert = false;
|
|
2660
|
+
}
|
|
2661
|
+
return `})`;
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
code(text) {
|
|
2666
|
+
const trimmed = text.trim();
|
|
2667
|
+
if (trimmed === "") {
|
|
2668
|
+
return "";
|
|
2669
|
+
}
|
|
2670
|
+
if (trimmed.includes("`")) {
|
|
2671
|
+
this.inert = false;
|
|
2672
|
+
}
|
|
2673
|
+
return `\`${trimmed}\``;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
var mergeNeighbors = (pieces) => {
|
|
2677
|
+
const merged = [];
|
|
2678
|
+
for (const piece of pieces) {
|
|
2679
|
+
const last = merged[merged.length - 1];
|
|
2680
|
+
if (last && last.kind === piece.kind && (piece.kind === "strong" || piece.kind === "emphasis" || piece.kind === "strike")) {
|
|
2681
|
+
merged[merged.length - 1] = { kind: piece.kind, children: [...last.children, ...piece.children] };
|
|
2682
|
+
} else {
|
|
2683
|
+
merged.push(piece);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
return merged;
|
|
2687
|
+
};
|
|
2688
|
+
var defaultLink = (href) => {
|
|
2689
|
+
return keptAddress(href);
|
|
2690
|
+
};
|
|
2691
|
+
var defaultPicture = (src) => {
|
|
2692
|
+
return isWebAddress(src) ? src : null;
|
|
2693
|
+
};
|
|
2694
|
+
var encodeAddress = (address) => {
|
|
2695
|
+
return address.replace(/ /g, "%20").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
2696
|
+
};
|
|
2697
|
+
var plainBlocks = (blocks, indent = "") => {
|
|
2698
|
+
return blocks.map((block) => plainBlock(block, indent)).filter((text) => text.trim() !== "").join(`
|
|
2699
|
+
|
|
2700
|
+
`);
|
|
2701
|
+
};
|
|
2702
|
+
var plainBlock = (block, indent) => {
|
|
2703
|
+
switch (block.kind) {
|
|
2704
|
+
case "paragraph":
|
|
2705
|
+
return prefixLines(plainInline(block.inline, true), indent);
|
|
2706
|
+
case "heading":
|
|
2707
|
+
return prefixLines(plainInline(block.inline, false).trim(), indent);
|
|
2708
|
+
case "list":
|
|
2709
|
+
return block.items.map((item, index) => {
|
|
2710
|
+
const marker = block.ordered ? `${index + 1}. ` : "- ";
|
|
2711
|
+
const text = plainBlocks(item.filter((inner) => inner.kind !== "list"), "").replace(/\n+/g, " ").trim();
|
|
2712
|
+
const nested = item.filter((inner) => inner.kind === "list").map((inner) => plainBlock(inner, `${indent}${" ".repeat(marker.length)}`));
|
|
2713
|
+
return [`${indent}${marker}${text}`.trimEnd(), ...nested].join(`
|
|
2714
|
+
`);
|
|
2715
|
+
}).join(`
|
|
2716
|
+
`);
|
|
2717
|
+
case "quote":
|
|
2718
|
+
return prefixLines(plainBlocks(block.blocks), `${indent}> `);
|
|
2719
|
+
case "code":
|
|
2720
|
+
return prefixLines(block.text, indent);
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
var prefixLines = (text, prefix) => {
|
|
2724
|
+
return prefix === "" ? text : text.split(`
|
|
2725
|
+
`).map((line) => `${prefix}${line}`).join(`
|
|
2726
|
+
`);
|
|
2727
|
+
};
|
|
2728
|
+
var plainInline = (pieces, breaks, addresses = true) => {
|
|
2729
|
+
let text = "";
|
|
2730
|
+
for (const piece of pieces) {
|
|
2731
|
+
switch (piece.kind) {
|
|
2732
|
+
case "text":
|
|
2733
|
+
text += piece.text;
|
|
2734
|
+
break;
|
|
2735
|
+
case "break":
|
|
2736
|
+
text += breaks ? `
|
|
2737
|
+
` : " ";
|
|
2738
|
+
break;
|
|
2739
|
+
case "strong":
|
|
2740
|
+
case "emphasis":
|
|
2741
|
+
case "strike":
|
|
2742
|
+
text += plainInline(piece.children, breaks, addresses);
|
|
2743
|
+
break;
|
|
2744
|
+
case "code":
|
|
2745
|
+
text += piece.text;
|
|
2746
|
+
break;
|
|
2747
|
+
case "link": {
|
|
2748
|
+
const label = plainInline(piece.children, breaks, addresses).trim();
|
|
2749
|
+
const href = addresses && isKeptLink(piece.href) && !piece.href.startsWith("#") ? piece.href : "";
|
|
2750
|
+
text += href && label && label !== href ? `${label} (${href})` : label || href;
|
|
2751
|
+
break;
|
|
2752
|
+
}
|
|
2753
|
+
case "image":
|
|
2754
|
+
text += piece.alt;
|
|
2755
|
+
break;
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
return text.split(`
|
|
2759
|
+
`).map((line) => line.replace(/ {2,}/g, " ").trim()).join(`
|
|
2760
|
+
`);
|
|
2761
|
+
};
|
|
2762
|
+
|
|
2763
|
+
// src/core/other-apps/ideas.ts
|
|
2764
|
+
var IMPORT_WARNING_CODES = [
|
|
2765
|
+
"floating-ideas-moved",
|
|
2766
|
+
"summaries-moved",
|
|
2767
|
+
"callouts-moved",
|
|
2768
|
+
"boundaries-in-notes",
|
|
2769
|
+
"links-in-notes",
|
|
2770
|
+
"missing-link-targets",
|
|
2771
|
+
"icons-not-kept",
|
|
2772
|
+
"pictures-not-kept",
|
|
2773
|
+
"attachments-not-kept",
|
|
2774
|
+
"local-links-not-kept",
|
|
2775
|
+
"encrypted-branches-not-read",
|
|
2776
|
+
"formatting-not-kept"
|
|
2777
|
+
];
|
|
2778
|
+
var IMPORT_ERROR_CODES = ["unknown-format", "unreadable", "empty", "password-protected", "too-large"];
|
|
2779
|
+
|
|
2780
|
+
class OrbitImportError extends Error {
|
|
2781
|
+
code;
|
|
2782
|
+
constructor(code, message) {
|
|
2783
|
+
super(message);
|
|
2784
|
+
this.name = "OrbitImportError";
|
|
2785
|
+
this.code = code;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
var MAX_IMPORTED_IDEAS = 1e5;
|
|
2789
|
+
var MAX_IMPORTED_DEPTH = 500;
|
|
2790
|
+
var newIdea = (text) => {
|
|
2791
|
+
return { text, notes: [], ideaLinks: [], tags: [], icons: [], children: [] };
|
|
2792
|
+
};
|
|
2793
|
+
var MESSAGES = {
|
|
2794
|
+
"floating-ideas-moved": (count, examples) => `${plural(count, "floating topic")}${examples} had no place in the tree: ${count === 1 ? "it's" : "they're"} now under the center.`,
|
|
2795
|
+
"summaries-moved": (count, examples) => `${plural(count, "summary")}${examples} became ${count === 1 ? "an idea" : "ideas"} beside the ideas ${count === 1 ? "it sums" : "they sum"} up.`,
|
|
2796
|
+
"callouts-moved": (count, examples) => `${plural(count, "callout")}${examples} became ${count === 1 ? "an idea" : "ideas"} under the topic ${count === 1 ? "it points" : "they point"} at.`,
|
|
2797
|
+
"boundaries-in-notes": (count, examples) => `${plural(count, "boundary title")}${examples} ${count === 1 ? "is" : "are"} now in the note of the idea whose ideas ${count === 1 ? "it surrounds" : "they surround"}.`,
|
|
2798
|
+
"links-in-notes": (count, examples) => `${plural(count, "link")}${examples} beyond an idea's first ${count === 1 ? "is" : "are"} now in its note.`,
|
|
2799
|
+
"missing-link-targets": (count, examples) => `${plural(count, "link")}${examples} ${count === 1 ? "points" : "point"} at an idea that isn't in this map, so ${count === 1 ? "it was" : "they were"} left out.`,
|
|
2800
|
+
"icons-not-kept": (count, examples) => `${plural(count, "icon")}${examples} ${count === 1 ? "was" : "were"} left out: an idea in Orbit has one icon, an emoji.`,
|
|
2801
|
+
"pictures-not-kept": (count, examples) => `${plural(count, "picture")}${examples} ${count === 1 ? "isn't" : "aren't"} in the file, so ${count === 1 ? "it was" : "they were"} left out.`,
|
|
2802
|
+
"attachments-not-kept": (count, examples) => `${plural(count, "attached file")}${examples} ${count === 1 ? "was" : "were"} left out; the ideas keep their names.`,
|
|
2803
|
+
"local-links-not-kept": (count, examples) => `${plural(count, "link")}${examples} to files on the computer the map was made on ${count === 1 ? "was" : "were"} left out.`,
|
|
2804
|
+
"encrypted-branches-not-read": (count, examples) => `${plural(count, "encrypted branch")}${examples} couldn't be read: the ideas in ${count === 1 ? "it" : "them"} were left out.`,
|
|
2805
|
+
"formatting-not-kept": (count, examples) => `${plural(count, "note")}${examples} ${count === 1 ? "is" : "are"} kept as plain text, without bold, italics or lists: markdown would have shown ${count === 1 ? "it" : "them"} differently.`
|
|
2806
|
+
};
|
|
2807
|
+
var plural = (count, noun) => {
|
|
2808
|
+
const word = count === 1 ? noun : noun.endsWith("y") ? `${noun.slice(0, -1)}ies` : /(ch|sh|s|x)$/.test(noun) ? `${noun}es` : `${noun}s`;
|
|
2809
|
+
return `${count === 1 ? "One" : count} ${word}`;
|
|
2810
|
+
};
|
|
2811
|
+
|
|
2812
|
+
class ImportReport {
|
|
2813
|
+
found = new Map;
|
|
2814
|
+
add(code, example) {
|
|
2815
|
+
const entry = this.found.get(code) ?? { count: 0, examples: [] };
|
|
2816
|
+
const shown = example === undefined ? "" : oneLine(example);
|
|
2817
|
+
entry.count += 1;
|
|
2818
|
+
if (shown !== "" && entry.examples.length < 3 && !entry.examples.includes(shown)) {
|
|
2819
|
+
entry.examples.push(shown);
|
|
2820
|
+
}
|
|
2821
|
+
this.found.set(code, entry);
|
|
2822
|
+
}
|
|
2823
|
+
get warnings() {
|
|
2824
|
+
const order = (code) => IMPORT_WARNING_CODES.indexOf(code);
|
|
2825
|
+
return [...this.found.entries()].sort(([first], [second]) => order(first) - order(second)).map(([code, { count, examples }]) => {
|
|
2826
|
+
const shown = examples.map((example) => `“${example.length > 40 ? `${example.slice(0, 39)}…` : example}”`);
|
|
2827
|
+
const more = count > examples.length && examples.length > 0 ? ", …" : "";
|
|
2828
|
+
return { code, count, message: MESSAGES[code](count, shown.length > 0 ? ` (${shown.join(", ")}${more})` : "") };
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
var oneLine = (text) => {
|
|
2833
|
+
return text.replace(/[\t\n\v\f\r\u2028\u2029 ]+/g, " ").replace(/^ | $/g, "");
|
|
2834
|
+
};
|
|
2835
|
+
var SOURCE_LINK = "#orbit-source:";
|
|
2836
|
+
var sourceLink = (sourceId) => {
|
|
2837
|
+
return `${SOURCE_LINK}${encodeURIComponent(sourceId)}`;
|
|
2838
|
+
};
|
|
2839
|
+
var buildDocument = (rootIdea, report) => {
|
|
2840
|
+
let count = 0;
|
|
2841
|
+
const bySource = new Map;
|
|
2842
|
+
const pending = [];
|
|
2843
|
+
const build = (idea, level) => {
|
|
2844
|
+
count += 1;
|
|
2845
|
+
if (count > MAX_IMPORTED_IDEAS) {
|
|
2846
|
+
throw new OrbitImportError("too-large", `The map has more than ${MAX_IMPORTED_IDEAS.toLocaleString("en")} ideas.`);
|
|
2847
|
+
}
|
|
2848
|
+
if (level > MAX_IMPORTED_DEPTH) {
|
|
2849
|
+
throw new OrbitImportError("too-large", `The map goes more than ${MAX_IMPORTED_DEPTH} levels deep.`);
|
|
2850
|
+
}
|
|
2851
|
+
let text = oneLine(idea.text);
|
|
2852
|
+
const icons = idea.icons.filter((icon) => icon !== "");
|
|
2853
|
+
let icon = icons[0];
|
|
2854
|
+
const emoji = icon === undefined ? leadingEmoji(text) : null;
|
|
2855
|
+
if (emoji) {
|
|
2856
|
+
icon = emoji.icon;
|
|
2857
|
+
text = emoji.text;
|
|
2858
|
+
}
|
|
2859
|
+
for (const extra of icons.slice(1)) {
|
|
2860
|
+
report.add("icons-not-kept", extra);
|
|
2861
|
+
}
|
|
2862
|
+
const node = { id: nodeSlug(text), text, level, children: [] };
|
|
2863
|
+
if (icon) {
|
|
2864
|
+
node.icon = icon;
|
|
2865
|
+
}
|
|
2866
|
+
if (idea.collapsed && idea.children.length > 0) {
|
|
2867
|
+
node.collapsed = true;
|
|
2868
|
+
}
|
|
2869
|
+
if (idea.status) {
|
|
2870
|
+
node.status = idea.status;
|
|
2871
|
+
}
|
|
2872
|
+
if (idea.priority !== undefined && Number.isFinite(idea.priority)) {
|
|
2873
|
+
node.priority = idea.priority;
|
|
2874
|
+
}
|
|
2875
|
+
const tags = [...new Set(idea.tags.map((tag) => oneLine(tag)).filter((tag) => tag !== ""))];
|
|
2876
|
+
if (tags.length > 0) {
|
|
2877
|
+
node.tags = tags;
|
|
2878
|
+
}
|
|
2879
|
+
if (idea.color) {
|
|
2880
|
+
node.color = idea.color;
|
|
2881
|
+
}
|
|
2882
|
+
if (idea.link) {
|
|
2883
|
+
node.link = idea.link;
|
|
2884
|
+
}
|
|
2885
|
+
if (idea.image) {
|
|
2886
|
+
node.image = { src: idea.image };
|
|
2887
|
+
}
|
|
2888
|
+
if (idea.sourceId !== undefined && !bySource.has(idea.sourceId)) {
|
|
2889
|
+
bySource.set(idea.sourceId, node);
|
|
2890
|
+
}
|
|
2891
|
+
pending.push([idea, node]);
|
|
2892
|
+
for (const child of idea.children) {
|
|
2893
|
+
const childNode = build(child, level + 1);
|
|
2894
|
+
setParent(childNode, node);
|
|
2895
|
+
node.children.push(childNode);
|
|
2896
|
+
}
|
|
2897
|
+
return node;
|
|
2898
|
+
};
|
|
2899
|
+
const root = build(rootIdea, 0);
|
|
2900
|
+
ensureUniqueNodeIds(root);
|
|
2901
|
+
for (const [idea, node] of pending) {
|
|
2902
|
+
const extraLines = [];
|
|
2903
|
+
for (const link of idea.ideaLinks) {
|
|
2904
|
+
const target = bySource.get(link.target);
|
|
2905
|
+
if (!target) {
|
|
2906
|
+
report.add("missing-link-targets", link.label ?? node.text);
|
|
2907
|
+
continue;
|
|
2908
|
+
}
|
|
2909
|
+
if (!node.link && !link.label) {
|
|
2910
|
+
node.link = `#${target.id}`;
|
|
2911
|
+
} else {
|
|
2912
|
+
report.add("links-in-notes", link.label ?? target.text);
|
|
2913
|
+
extraLines.push({ label: link.label ?? target.text, target });
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
const note = joinNotes([...idea.notes, linkNote(extraLines, idea.notes)]);
|
|
2917
|
+
if (note) {
|
|
2918
|
+
node.note = resolveSourceLinks(note.text, bySource, note.format);
|
|
2919
|
+
if (note.format === "plain") {
|
|
2920
|
+
node.noteFormat = "plain";
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
const nodes = indexNodes(root);
|
|
2925
|
+
for (const [, node] of pending) {
|
|
2926
|
+
if (isNodeLink(node.link) && !findIndexedLinkedNode(nodes, node.link)) {
|
|
2927
|
+
report.add("missing-link-targets", node.link);
|
|
2928
|
+
delete node.link;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return { root, options: {}, warnings: [], diagnostics: [] };
|
|
2932
|
+
};
|
|
2933
|
+
var linkNote = (lines, notes) => {
|
|
2934
|
+
if (lines.length === 0) {
|
|
2935
|
+
return;
|
|
2936
|
+
}
|
|
2937
|
+
const plain = notes.some((note) => note.format === "plain") || lines.some(({ label }) => /[[\]`*_~<\\]/.test(label));
|
|
2938
|
+
if (plain) {
|
|
2939
|
+
return { text: lines.map(({ label, target }) => label === target.text ? `→ ${label}` : `→ ${label}: ${target.text}`).join(`
|
|
2940
|
+
`), format: "plain" };
|
|
2941
|
+
}
|
|
2942
|
+
return { text: lines.map(({ label, target }) => `→ [${label || target.text}](#${target.id})`).join(`
|
|
2943
|
+
|
|
2944
|
+
`), format: "markdown" };
|
|
2945
|
+
};
|
|
2946
|
+
var resolveSourceLinks = (text, bySource, format) => {
|
|
2947
|
+
if (format !== "markdown" || !text.includes(SOURCE_LINK)) {
|
|
2948
|
+
return text;
|
|
2949
|
+
}
|
|
2950
|
+
const target = (encoded) => {
|
|
2951
|
+
try {
|
|
2952
|
+
return bySource.get(decodeURIComponent(encoded));
|
|
2953
|
+
} catch {
|
|
2954
|
+
return;
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
return text.replace(/\[([^\]]*)\]\(#orbit-source:([^)\s]+)\)/g, (_, label, encoded) => {
|
|
2958
|
+
const node = target(encoded);
|
|
2959
|
+
return node ? `[${label || node.text}](#${node.id})` : label;
|
|
2960
|
+
}).replace(/<#orbit-source:([^>\s]+)>/g, (_, encoded) => {
|
|
2961
|
+
const node = target(encoded);
|
|
2962
|
+
return node ? `[${node.text}](#${node.id})` : "";
|
|
2963
|
+
});
|
|
2964
|
+
};
|
|
2965
|
+
|
|
2966
|
+
// src/core/other-apps/reading.ts
|
|
2967
|
+
var readXmlFile = (data, rootName, what) => {
|
|
2968
|
+
const text = typeof data === "string" ? data : decodeMarkup(data);
|
|
2969
|
+
let root;
|
|
2970
|
+
try {
|
|
2971
|
+
root = parseXml(text);
|
|
2972
|
+
} catch (error) {
|
|
2973
|
+
if (error instanceof MarkupError) {
|
|
2974
|
+
throw new OrbitImportError("unreadable", `The file isn't ${what} Orbit can read: ${error.message}.`);
|
|
2975
|
+
}
|
|
2976
|
+
throw error;
|
|
2977
|
+
}
|
|
2978
|
+
if (root.localName !== rootName) {
|
|
2979
|
+
throw new OrbitImportError("unreadable", `The file isn't ${what}: it starts with <${root.name}>, not <${rootName}>.`);
|
|
2980
|
+
}
|
|
2981
|
+
return root;
|
|
2982
|
+
};
|
|
2983
|
+
var baseName = (name) => {
|
|
2984
|
+
return (name ?? "").replace(/^.*[/\\]/, "").replace(/\.(opml|xml|mm|xmind|txt|md)$/i, "").trim();
|
|
2985
|
+
};
|
|
2986
|
+
|
|
2987
|
+
// src/core/other-apps/freemind.ts
|
|
2988
|
+
var importFreeMind = (data, options = {}) => {
|
|
2989
|
+
const map = readXmlFile(data, "map", "a FreeMind or Freeplane map");
|
|
2990
|
+
const nodes = childElements(map, "node");
|
|
2991
|
+
if (nodes.length === 0) {
|
|
2992
|
+
throw new OrbitImportError("empty", "The map has no nodes.");
|
|
2993
|
+
}
|
|
2994
|
+
const report = new ImportReport;
|
|
2995
|
+
let root;
|
|
2996
|
+
if (nodes.length === 1) {
|
|
2997
|
+
root = readNode(nodes[0], report);
|
|
2998
|
+
} else {
|
|
2999
|
+
root = newIdea(baseName(options.name) || options.untitled || "Untitled map");
|
|
3000
|
+
root.children = nodes.map((node) => readNode(node, report));
|
|
3001
|
+
}
|
|
3002
|
+
return { format: "freemind", maps: [{ document: buildDocument(root, report), media: {}, warnings: report.warnings }] };
|
|
3003
|
+
};
|
|
3004
|
+
var readNode = (node, report) => {
|
|
3005
|
+
const idea = newIdea("");
|
|
3006
|
+
const id = node.attributes.ID;
|
|
3007
|
+
if (id) {
|
|
3008
|
+
idea.sourceId = id;
|
|
3009
|
+
}
|
|
3010
|
+
readTitle(node, idea, report);
|
|
3011
|
+
const addresses = noteAddresses(report);
|
|
3012
|
+
const details = [];
|
|
3013
|
+
const notes = [];
|
|
3014
|
+
for (const content of childElements(node, "richcontent")) {
|
|
3015
|
+
const type = (content.attributes.TYPE ?? "").toUpperCase();
|
|
3016
|
+
if (type === "NOTE" || type === "DETAILS") {
|
|
3017
|
+
const note = richContentNote(content, addresses, report, idea.text);
|
|
3018
|
+
if (note) {
|
|
3019
|
+
(type === "DETAILS" ? details : notes).push(note);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
for (const hook of childElements(node, "hook")) {
|
|
3024
|
+
if (/NodeNote/.test(hook.attributes.NAME ?? "")) {
|
|
3025
|
+
const text = childElement(hook, "text");
|
|
3026
|
+
const note = noteFromPlainText(text ? textContent(text) : undefined);
|
|
3027
|
+
if (note) {
|
|
3028
|
+
notes.push(note);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
idea.notes.push(...details, ...notes);
|
|
3033
|
+
const attributes = attributeNote(node);
|
|
3034
|
+
if (attributes) {
|
|
3035
|
+
idea.notes.push(attributes);
|
|
3036
|
+
}
|
|
3037
|
+
readLink(node.attributes.LINK, idea, report);
|
|
3038
|
+
for (const arrow of childElements(node, "arrowlink")) {
|
|
3039
|
+
const target = arrow.attributes.DESTINATION;
|
|
3040
|
+
if (target) {
|
|
3041
|
+
const label = oneLine(arrow.attributes.MIDDLE_LABEL || arrow.attributes.SOURCE_LABEL || arrow.attributes.TARGET_LABEL || "");
|
|
3042
|
+
idea.ideaLinks.push(label ? { target, label } : { target });
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
if (node.attributes.FOLDED === "true") {
|
|
3046
|
+
idea.collapsed = true;
|
|
3047
|
+
}
|
|
3048
|
+
const background = node.attributes.BACKGROUND_COLOR;
|
|
3049
|
+
if (background && /^#[0-9A-Fa-f]{6}$/.test(background)) {
|
|
3050
|
+
idea.color = background.toUpperCase();
|
|
3051
|
+
}
|
|
3052
|
+
for (const icon of childElements(node, "icon")) {
|
|
3053
|
+
const name = icon.attributes.BUILTIN ?? "";
|
|
3054
|
+
const meaning = freeMindIcon(name);
|
|
3055
|
+
if (!meaning) {
|
|
3056
|
+
report.add("icons-not-kept", name.replace(/^.*\//, ""));
|
|
3057
|
+
} else if ("icon" in meaning) {
|
|
3058
|
+
idea.icons.push(meaning.icon);
|
|
3059
|
+
} else if ("priority" in meaning) {
|
|
3060
|
+
idea.priority ??= meaning.priority;
|
|
3061
|
+
} else {
|
|
3062
|
+
idea.status ??= meaning.status;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
for (const hook of childElements(node, "hook")) {
|
|
3066
|
+
if (hook.attributes.NAME === "ExternalObject" && hook.attributes.URI) {
|
|
3067
|
+
readPicture(hook.attributes.URI, idea, report);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
if (node.attributes.ENCRYPTED_CONTENT) {
|
|
3071
|
+
report.add("encrypted-branches-not-read", idea.text || undefined);
|
|
3072
|
+
}
|
|
3073
|
+
idea.children = childElements(node, "node").map((child) => readNode(child, report));
|
|
3074
|
+
return idea;
|
|
3075
|
+
};
|
|
3076
|
+
var readTitle = (node, idea, report) => {
|
|
3077
|
+
const rich = childElements(node, "richcontent").find((content) => (content.attributes.TYPE ?? "").toUpperCase() === "NODE");
|
|
3078
|
+
const text = node.attributes.TEXT ?? node.attributes.LOCALIZED_TEXT;
|
|
3079
|
+
if (rich) {
|
|
3080
|
+
const html = childElement(rich, "html");
|
|
3081
|
+
const plain = childElement(rich, "text");
|
|
3082
|
+
if (html) {
|
|
3083
|
+
const title = titleFromHtml(html);
|
|
3084
|
+
idea.text = title.text;
|
|
3085
|
+
readLink(title.link, idea, report);
|
|
3086
|
+
for (const image of findElements(html, "img")) {
|
|
3087
|
+
readPicture(image.attributes.src ?? "", idea, report);
|
|
3088
|
+
}
|
|
3089
|
+
} else {
|
|
3090
|
+
idea.text = plain ? textContent(plain) : textContent(rich);
|
|
3091
|
+
}
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
if (text !== undefined && /^\s*<html>/i.test(text)) {
|
|
3095
|
+
const title = titleFromHtml(text);
|
|
3096
|
+
idea.text = title.text;
|
|
3097
|
+
readLink(title.link, idea, report);
|
|
3098
|
+
return;
|
|
3099
|
+
}
|
|
3100
|
+
idea.text = text ?? "";
|
|
3101
|
+
};
|
|
3102
|
+
var readLink = (link, idea, report) => {
|
|
3103
|
+
const address = link?.trim();
|
|
3104
|
+
if (!address || idea.link) {
|
|
3105
|
+
return;
|
|
3106
|
+
}
|
|
3107
|
+
const kept = keptAddress(address);
|
|
3108
|
+
if (address.startsWith("#") || /^(ID_\d+|_?Freemind_Link_\d+)$/.test(address)) {
|
|
3109
|
+
idea.ideaLinks.push({ target: address.replace(/^#/, "") });
|
|
3110
|
+
} else if (kept) {
|
|
3111
|
+
idea.link = kept;
|
|
3112
|
+
} else {
|
|
3113
|
+
report.add("local-links-not-kept", address);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
var readPicture = (address, idea, report) => {
|
|
3117
|
+
if (/^https?:\/\/\S+$/i.test(address)) {
|
|
3118
|
+
idea.image ??= address;
|
|
3119
|
+
} else if (address !== "") {
|
|
3120
|
+
report.add("pictures-not-kept", address.replace(/^.*[/\\]/, ""));
|
|
3121
|
+
}
|
|
3122
|
+
};
|
|
3123
|
+
var noteAddresses = (report) => {
|
|
3124
|
+
return {
|
|
3125
|
+
link: (href) => {
|
|
3126
|
+
if (href.startsWith("#")) {
|
|
3127
|
+
return sourceLink(href.slice(1));
|
|
3128
|
+
}
|
|
3129
|
+
const kept = keptAddress(href);
|
|
3130
|
+
if (kept) {
|
|
3131
|
+
return kept;
|
|
3132
|
+
}
|
|
3133
|
+
report.add("local-links-not-kept", href);
|
|
3134
|
+
return null;
|
|
3135
|
+
},
|
|
3136
|
+
picture: (src) => {
|
|
3137
|
+
if (/^https?:\/\/\S+$/i.test(src)) {
|
|
3138
|
+
return src;
|
|
3139
|
+
}
|
|
3140
|
+
report.add("pictures-not-kept", src.replace(/^.*[/\\]/, ""));
|
|
3141
|
+
return null;
|
|
3142
|
+
}
|
|
3143
|
+
};
|
|
3144
|
+
};
|
|
3145
|
+
var richContentNote = (content, addresses, report, title) => {
|
|
3146
|
+
const html = childElement(content, "html");
|
|
3147
|
+
if (html) {
|
|
3148
|
+
const note = noteFromHtml(html, addresses);
|
|
3149
|
+
if (note?.format === "plain") {
|
|
3150
|
+
report.add("formatting-not-kept", title || undefined);
|
|
3151
|
+
}
|
|
3152
|
+
return note;
|
|
3153
|
+
}
|
|
3154
|
+
const plain = childElement(content, "text");
|
|
3155
|
+
const text = plain ? textContent(plain) : textContent(content);
|
|
3156
|
+
if (/\/markdown$/i.test(content.attributes["CONTENT-TYPE"] ?? "") && text.trim() !== "") {
|
|
3157
|
+
return { text: text.replace(/\r\n?/g, `
|
|
3158
|
+
`).trim(), format: "markdown" };
|
|
3159
|
+
}
|
|
3160
|
+
return noteFromPlainText(text);
|
|
3161
|
+
};
|
|
3162
|
+
var attributeNote = (node) => {
|
|
3163
|
+
const items = [];
|
|
3164
|
+
for (const attribute of childElements(node, "attribute")) {
|
|
3165
|
+
const name = (attributeOf(attribute, "NAME") ?? "").trim();
|
|
3166
|
+
const value = (attributeOf(attribute, "VALUE") ?? "").trim();
|
|
3167
|
+
if (name !== "" || value !== "") {
|
|
3168
|
+
items.push([{ kind: "paragraph", inline: name === "" ? [{ kind: "text", text: value }] : [{ kind: "strong", children: [{ kind: "text", text: `${name}:` }] }, { kind: "text", text: ` ${value}` }] }]);
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
return items.length === 0 ? undefined : noteFromBlocks([{ kind: "list", ordered: false, items }]);
|
|
3172
|
+
};
|
|
3173
|
+
var findElements = (element, localName) => {
|
|
3174
|
+
const found = [];
|
|
3175
|
+
const pending = [element];
|
|
3176
|
+
while (pending.length > 0) {
|
|
3177
|
+
const next = pending.pop();
|
|
3178
|
+
for (const child of childElements(next)) {
|
|
3179
|
+
if (child.localName.toLowerCase() === localName) {
|
|
3180
|
+
found.push(child);
|
|
3181
|
+
}
|
|
3182
|
+
pending.push(child);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return found;
|
|
3186
|
+
};
|
|
3187
|
+
|
|
3188
|
+
// src/core/other-apps/opml.ts
|
|
3189
|
+
var HTML_TAGS = /<\/?(b|i|u|s|em|strong|a|span|br|code|mark|strike|del|sup|sub|font|p|div|ul|ol|li)\b[^>]*>/i;
|
|
3190
|
+
var importOPML = (data, options = {}) => {
|
|
3191
|
+
const opml = readXmlFile(data, "opml", "an OPML file");
|
|
3192
|
+
const head = childElement(opml, "head");
|
|
3193
|
+
const titleElement = head ? childElement(head, "title") : undefined;
|
|
3194
|
+
const title = titleElement ? baseName(textContent(titleElement).replace(/\s+/g, " ").trim()) : "";
|
|
3195
|
+
const body = childElement(opml, "body");
|
|
3196
|
+
const outlines = body ? childElements(body, "outline") : [];
|
|
3197
|
+
if (outlines.length === 0) {
|
|
3198
|
+
throw new OrbitImportError("empty", "The OPML file has no outlines.");
|
|
3199
|
+
}
|
|
3200
|
+
const report = new ImportReport;
|
|
3201
|
+
let root;
|
|
3202
|
+
if (outlines.length === 1) {
|
|
3203
|
+
root = readOutline2(outlines[0], report);
|
|
3204
|
+
} else {
|
|
3205
|
+
root = newIdea(title || baseName(options.name) || options.untitled || "Untitled map");
|
|
3206
|
+
root.children = outlines.map((outline) => readOutline2(outline, report));
|
|
3207
|
+
}
|
|
3208
|
+
return { format: "opml", maps: [{ document: buildDocument(root, report), media: {}, warnings: report.warnings }] };
|
|
3209
|
+
};
|
|
3210
|
+
var readOutline2 = (outline, report) => {
|
|
3211
|
+
const text = attributeOf(outline, "text") ?? attributeOf(outline, "title") ?? "";
|
|
3212
|
+
const idea = newIdea(text);
|
|
3213
|
+
if (HTML_TAGS.test(text) && outline.attributes._textFormat !== "plain") {
|
|
3214
|
+
const title = titleFromHtml(text);
|
|
3215
|
+
idea.text = title.text;
|
|
3216
|
+
if (title.link && isKeptLink(title.link) && !title.link.startsWith("#")) {
|
|
3217
|
+
idea.link = title.link;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
const note = readNote(outline, idea, report);
|
|
3221
|
+
if (note) {
|
|
3222
|
+
idea.notes.push(note);
|
|
3223
|
+
}
|
|
3224
|
+
idea.status = readStatus(outline);
|
|
3225
|
+
const link = readLink2(outline);
|
|
3226
|
+
const kept = link ? keptAddress(link) : null;
|
|
3227
|
+
if (kept) {
|
|
3228
|
+
idea.link = kept;
|
|
3229
|
+
} else if (link) {
|
|
3230
|
+
report.add("local-links-not-kept", link);
|
|
3231
|
+
}
|
|
3232
|
+
const categories = attributeOf(outline, "category");
|
|
3233
|
+
if (categories) {
|
|
3234
|
+
idea.tags.push(...categories.split(",").map((category) => category.trim().replace(/^\/+/, "")).filter((category) => category !== ""));
|
|
3235
|
+
}
|
|
3236
|
+
idea.children = childElements(outline, "outline").map((child) => readOutline2(child, report));
|
|
3237
|
+
return idea;
|
|
3238
|
+
};
|
|
3239
|
+
var readNote = (outline, idea, report) => {
|
|
3240
|
+
const text = attributeOf(outline, "_note") ?? outline.attributes.note ?? attributeOf(outline, "description");
|
|
3241
|
+
if (text === undefined || text.trim() === "") {
|
|
3242
|
+
return;
|
|
3243
|
+
}
|
|
3244
|
+
const format = outline.attributes._noteFormat;
|
|
3245
|
+
if (format === "markdown" || format === "plain") {
|
|
3246
|
+
return { text: text.replace(/\r\n?/g, `
|
|
3247
|
+
`).trim(), format };
|
|
3248
|
+
}
|
|
3249
|
+
if (HTML_TAGS.test(text)) {
|
|
3250
|
+
const note = noteFromHtml(text.replace(/\r\n?|\n/g, "<br>"));
|
|
3251
|
+
if (note?.format === "plain") {
|
|
3252
|
+
report.add("formatting-not-kept", idea.text || undefined);
|
|
3253
|
+
}
|
|
3254
|
+
return note;
|
|
3255
|
+
}
|
|
3256
|
+
return noteFromPlainText(text);
|
|
3257
|
+
};
|
|
3258
|
+
var readStatus = (outline) => {
|
|
3259
|
+
const status = attributeOf(outline, "_status")?.trim();
|
|
3260
|
+
if (status) {
|
|
3261
|
+
return CHECKBOX_STATUSES.get(status.toLowerCase()) ?? status;
|
|
3262
|
+
}
|
|
3263
|
+
const complete = (outline.attributes._complete ?? outline.attributes.complete ?? outline.attributes.checked ?? outline.attributes._checked)?.trim().toLowerCase();
|
|
3264
|
+
if (complete === "true") {
|
|
3265
|
+
return "done";
|
|
3266
|
+
}
|
|
3267
|
+
return outline.attributes.checkbox === "true" ? "todo" : undefined;
|
|
3268
|
+
};
|
|
3269
|
+
var readLink2 = (outline) => {
|
|
3270
|
+
const url = outline.attributes.url ?? outline.attributes.htmlUrl ?? outline.attributes.xmlUrl;
|
|
3271
|
+
return url?.trim() || undefined;
|
|
3272
|
+
};
|
|
3273
|
+
var STATUS_CHECKBOXES = new Map([["done", "checked"], ["todo", "unchecked"], ["active", "indeterminate"]]);
|
|
3274
|
+
var CHECKBOX_STATUSES = new Map([...STATUS_CHECKBOXES].map(([status, checkbox]) => [checkbox, status]));
|
|
3275
|
+
var DONE_WORDS = new Set(["done", "complete", "completed", "shipped"]);
|
|
3276
|
+
var exportOPML = (input, options = {}) => {
|
|
3277
|
+
const document2 = typeof input === "string" ? parseOrbit2(input) : input;
|
|
3278
|
+
const title = options.title ?? (document2.options.title || document2.root.text);
|
|
3279
|
+
const lines = [
|
|
3280
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
3281
|
+
'<opml version="2.0">',
|
|
3282
|
+
" <head>",
|
|
3283
|
+
` <title>${escapeXml(title)}</title>`,
|
|
3284
|
+
" </head>",
|
|
3285
|
+
" <body>"
|
|
3286
|
+
];
|
|
3287
|
+
const write = (node, depth) => {
|
|
3288
|
+
const indent = " ".repeat(depth + 2);
|
|
3289
|
+
const attributes = outlineAttributes(node).map(([name, value]) => ` ${name}="${escapeXml(value, true)}"`).join("");
|
|
3290
|
+
if (node.children.length === 0) {
|
|
3291
|
+
lines.push(`${indent}<outline${attributes}/>`);
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
lines.push(`${indent}<outline${attributes}>`);
|
|
3295
|
+
for (const child of node.children) {
|
|
3296
|
+
write(child, depth + 1);
|
|
3297
|
+
}
|
|
3298
|
+
lines.push(`${indent}</outline>`);
|
|
3299
|
+
};
|
|
3300
|
+
write(document2.root, 0);
|
|
3301
|
+
lines.push(" </body>", "</opml>", "");
|
|
3302
|
+
return lines.join(`
|
|
3303
|
+
`);
|
|
3304
|
+
};
|
|
3305
|
+
var outlineAttributes = (node) => {
|
|
3306
|
+
const text = node.icon ? `${node.icon} ${node.text}` : node.text;
|
|
3307
|
+
const attributes = [["text", text]];
|
|
3308
|
+
if (HTML_TAGS.test(text)) {
|
|
3309
|
+
attributes.push(["_textFormat", "plain"]);
|
|
3310
|
+
}
|
|
3311
|
+
if (node.note) {
|
|
3312
|
+
attributes.push(["_note", node.note]);
|
|
3313
|
+
const format = node.noteFormat ?? "markdown";
|
|
3314
|
+
if (noteFromPlainText(node.note)?.format !== format) {
|
|
3315
|
+
attributes.push(["_noteFormat", format]);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const status = node.status?.trim();
|
|
3319
|
+
if (status) {
|
|
3320
|
+
attributes.push(["_status", STATUS_CHECKBOXES.get(status) ?? status]);
|
|
3321
|
+
if (DONE_WORDS.has(status.toLowerCase())) {
|
|
3322
|
+
attributes.push(["_complete", "true"]);
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
if (node.link && (isKeptLink(node.link) || isNodeLink(node.link))) {
|
|
3326
|
+
attributes.push(["type", "link"], ["url", node.link]);
|
|
3327
|
+
}
|
|
3328
|
+
if (node.tags && node.tags.length > 0) {
|
|
3329
|
+
attributes.push(["category", node.tags.map((tag) => tag.replace(/,/g, " ")).join(",")]);
|
|
3330
|
+
}
|
|
3331
|
+
return attributes;
|
|
3332
|
+
};
|
|
3333
|
+
var escapeXml = (text, attribute = false) => {
|
|
3334
|
+
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3335
|
+
return attribute ? escaped.replace(/"/g, """).replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, "	") : escaped;
|
|
3336
|
+
};
|
|
3337
|
+
|
|
3338
|
+
// src/core/other-apps/inflate.ts
|
|
3339
|
+
class InflateError extends Error {
|
|
3340
|
+
constructor(message) {
|
|
3341
|
+
super(message);
|
|
3342
|
+
this.name = "InflateError";
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
var LENGTH_BASES = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
|
3346
|
+
var LENGTH_EXTRA_BITS = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
3347
|
+
var DISTANCE_BASES = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
|
3348
|
+
var DISTANCE_EXTRA_BITS = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
|
3349
|
+
var CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
3350
|
+
var MAX_BITS = 15;
|
|
3351
|
+
var buildCode = (lengths) => {
|
|
3352
|
+
const counts = new Uint16Array(MAX_BITS + 1);
|
|
3353
|
+
for (const length of lengths) {
|
|
3354
|
+
counts[length] += 1;
|
|
3355
|
+
}
|
|
3356
|
+
counts[0] = 0;
|
|
3357
|
+
let left = 1;
|
|
3358
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
3359
|
+
left = left * 2 - counts[length];
|
|
3360
|
+
if (left < 0) {
|
|
3361
|
+
throw new InflateError("A Huffman code with more codes than its lengths allow");
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
const offsets = new Uint16Array(MAX_BITS + 2);
|
|
3365
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
3366
|
+
offsets[length + 1] = offsets[length] + counts[length];
|
|
3367
|
+
}
|
|
3368
|
+
const symbols = new Uint16Array(lengths.length);
|
|
3369
|
+
for (let symbol = 0;symbol < lengths.length; symbol++) {
|
|
3370
|
+
if (lengths[symbol] !== 0) {
|
|
3371
|
+
symbols[offsets[lengths[symbol]]++] = symbol;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
return { counts, symbols };
|
|
3375
|
+
};
|
|
3376
|
+
var fixedLengths = new Uint8Array(288);
|
|
3377
|
+
fixedLengths.fill(8, 0, 144);
|
|
3378
|
+
fixedLengths.fill(9, 144, 256);
|
|
3379
|
+
fixedLengths.fill(7, 256, 280);
|
|
3380
|
+
fixedLengths.fill(8, 280, 288);
|
|
3381
|
+
var FIXED_LITERALS = buildCode(fixedLengths);
|
|
3382
|
+
var FIXED_DISTANCES = buildCode(new Uint8Array(30).fill(5));
|
|
3383
|
+
|
|
3384
|
+
class BitReader {
|
|
3385
|
+
data;
|
|
3386
|
+
position;
|
|
3387
|
+
buffer = 0;
|
|
3388
|
+
count = 0;
|
|
3389
|
+
constructor(data, start) {
|
|
3390
|
+
this.data = data;
|
|
3391
|
+
this.position = start;
|
|
3392
|
+
}
|
|
3393
|
+
bits(needed) {
|
|
3394
|
+
while (this.count < needed) {
|
|
3395
|
+
if (this.position >= this.data.length) {
|
|
3396
|
+
throw new InflateError("The data ends in the middle of a block");
|
|
3397
|
+
}
|
|
3398
|
+
this.buffer |= this.data[this.position++] << this.count;
|
|
3399
|
+
this.count += 8;
|
|
3400
|
+
}
|
|
3401
|
+
const value = this.buffer & (1 << needed) - 1;
|
|
3402
|
+
this.buffer >>>= needed;
|
|
3403
|
+
this.count -= needed;
|
|
3404
|
+
return value;
|
|
3405
|
+
}
|
|
3406
|
+
bytes(length) {
|
|
3407
|
+
this.buffer = 0;
|
|
3408
|
+
this.count = 0;
|
|
3409
|
+
if (this.position + length > this.data.length) {
|
|
3410
|
+
throw new InflateError("The data ends in the middle of a stored block");
|
|
3411
|
+
}
|
|
3412
|
+
const bytes = this.data.subarray(this.position, this.position + length);
|
|
3413
|
+
this.position += length;
|
|
3414
|
+
return bytes;
|
|
3415
|
+
}
|
|
3416
|
+
word() {
|
|
3417
|
+
const [low, high] = this.bytes(2);
|
|
3418
|
+
return low | high << 8;
|
|
3419
|
+
}
|
|
3420
|
+
decode(code) {
|
|
3421
|
+
let value = 0;
|
|
3422
|
+
let first = 0;
|
|
3423
|
+
let index = 0;
|
|
3424
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
3425
|
+
value |= this.bits(1);
|
|
3426
|
+
const count = code.counts[length];
|
|
3427
|
+
if (value - first < count) {
|
|
3428
|
+
return code.symbols[index + value - first];
|
|
3429
|
+
}
|
|
3430
|
+
index += count;
|
|
3431
|
+
first = first + count << 1;
|
|
3432
|
+
value <<= 1;
|
|
3433
|
+
}
|
|
3434
|
+
throw new InflateError("A code that no Huffman table has");
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
class Output {
|
|
3439
|
+
bytes;
|
|
3440
|
+
length = 0;
|
|
3441
|
+
limit;
|
|
3442
|
+
constructor(expected, limit) {
|
|
3443
|
+
this.limit = limit;
|
|
3444
|
+
this.bytes = new Uint8Array(Math.max(16, Math.min(expected ?? 65536, limit)));
|
|
3445
|
+
}
|
|
3446
|
+
room(extra) {
|
|
3447
|
+
const needed = this.length + extra;
|
|
3448
|
+
if (needed > this.limit) {
|
|
3449
|
+
throw new InflateError(`The data holds more than the ${this.limit} bytes it may`);
|
|
3450
|
+
}
|
|
3451
|
+
if (needed > this.bytes.length) {
|
|
3452
|
+
const grown = new Uint8Array(Math.min(this.limit, Math.max(needed, this.bytes.length * 2)));
|
|
3453
|
+
grown.set(this.bytes.subarray(0, this.length));
|
|
3454
|
+
this.bytes = grown;
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
push(byte) {
|
|
3458
|
+
this.room(1);
|
|
3459
|
+
this.bytes[this.length++] = byte;
|
|
3460
|
+
}
|
|
3461
|
+
append(bytes) {
|
|
3462
|
+
this.room(bytes.length);
|
|
3463
|
+
this.bytes.set(bytes, this.length);
|
|
3464
|
+
this.length += bytes.length;
|
|
3465
|
+
}
|
|
3466
|
+
copy(distance, length) {
|
|
3467
|
+
if (distance > this.length) {
|
|
3468
|
+
throw new InflateError("A back reference to before the start of the data");
|
|
3469
|
+
}
|
|
3470
|
+
this.room(length);
|
|
3471
|
+
for (let index = 0;index < length; index++) {
|
|
3472
|
+
this.bytes[this.length] = this.bytes[this.length - distance];
|
|
3473
|
+
this.length += 1;
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
var inflate = (data, options = {}) => {
|
|
3478
|
+
const limit = options.limit ?? Math.max(options.expected ?? 0, 64 * 1024 * 1024);
|
|
3479
|
+
const reader = new BitReader(data, 0);
|
|
3480
|
+
const output = new Output(options.expected, limit);
|
|
3481
|
+
let last = 0;
|
|
3482
|
+
while (last === 0) {
|
|
3483
|
+
last = reader.bits(1);
|
|
3484
|
+
const type = reader.bits(2);
|
|
3485
|
+
if (type === 0) {
|
|
3486
|
+
const length = reader.word();
|
|
3487
|
+
const complement = reader.word();
|
|
3488
|
+
if ((length ^ 65535) !== complement) {
|
|
3489
|
+
throw new InflateError("A stored block whose length doesn't match its complement");
|
|
3490
|
+
}
|
|
3491
|
+
output.append(reader.bytes(length));
|
|
3492
|
+
} else if (type === 1) {
|
|
3493
|
+
inflateBlock(reader, output, FIXED_LITERALS, FIXED_DISTANCES);
|
|
3494
|
+
} else if (type === 2) {
|
|
3495
|
+
const [literals, distances] = readDynamicCodes(reader);
|
|
3496
|
+
inflateBlock(reader, output, literals, distances);
|
|
3497
|
+
} else {
|
|
3498
|
+
throw new InflateError("A block of an unknown type");
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
return output.bytes.subarray(0, output.length);
|
|
3502
|
+
};
|
|
3503
|
+
var readDynamicCodes = (reader) => {
|
|
3504
|
+
const literalCount = reader.bits(5) + 257;
|
|
3505
|
+
const distanceCount = reader.bits(5) + 1;
|
|
3506
|
+
const codeLengthCount = reader.bits(4) + 4;
|
|
3507
|
+
if (literalCount > 286 || distanceCount > 30) {
|
|
3508
|
+
throw new InflateError("A block with more codes than deflate has");
|
|
3509
|
+
}
|
|
3510
|
+
const codeLengths = new Uint8Array(19);
|
|
3511
|
+
for (let index = 0;index < codeLengthCount; index++) {
|
|
3512
|
+
codeLengths[CODE_LENGTH_ORDER[index]] = reader.bits(3);
|
|
3513
|
+
}
|
|
3514
|
+
const codeLengthCode = buildCode(codeLengths);
|
|
3515
|
+
const lengths = new Uint8Array(literalCount + distanceCount);
|
|
3516
|
+
let index = 0;
|
|
3517
|
+
while (index < lengths.length) {
|
|
3518
|
+
const symbol = reader.decode(codeLengthCode);
|
|
3519
|
+
if (symbol < 16) {
|
|
3520
|
+
lengths[index++] = symbol;
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3523
|
+
let repeat;
|
|
3524
|
+
let value = 0;
|
|
3525
|
+
if (symbol === 16) {
|
|
3526
|
+
if (index === 0) {
|
|
3527
|
+
throw new InflateError("A repeat with nothing before it");
|
|
3528
|
+
}
|
|
3529
|
+
value = lengths[index - 1];
|
|
3530
|
+
repeat = 3 + reader.bits(2);
|
|
3531
|
+
} else if (symbol === 17) {
|
|
3532
|
+
repeat = 3 + reader.bits(3);
|
|
3533
|
+
} else {
|
|
3534
|
+
repeat = 11 + reader.bits(7);
|
|
3535
|
+
}
|
|
3536
|
+
if (index + repeat > lengths.length) {
|
|
3537
|
+
throw new InflateError("Code lengths that run past their table");
|
|
3538
|
+
}
|
|
3539
|
+
lengths.fill(value, index, index + repeat);
|
|
3540
|
+
index += repeat;
|
|
3541
|
+
}
|
|
3542
|
+
if (lengths[256] === 0) {
|
|
3543
|
+
throw new InflateError("A block with no code for its end");
|
|
3544
|
+
}
|
|
3545
|
+
return [buildCode(lengths.subarray(0, literalCount)), buildCode(lengths.subarray(literalCount))];
|
|
3546
|
+
};
|
|
3547
|
+
var inflateBlock = (reader, output, literals, distances) => {
|
|
3548
|
+
for (;; ) {
|
|
3549
|
+
const symbol = reader.decode(literals);
|
|
3550
|
+
if (symbol < 256) {
|
|
3551
|
+
output.push(symbol);
|
|
3552
|
+
} else if (symbol === 256) {
|
|
3553
|
+
return;
|
|
3554
|
+
} else {
|
|
3555
|
+
const lengthIndex = symbol - 257;
|
|
3556
|
+
if (lengthIndex >= LENGTH_BASES.length) {
|
|
3557
|
+
throw new InflateError("A length code deflate has no length for");
|
|
3558
|
+
}
|
|
3559
|
+
const length = LENGTH_BASES[lengthIndex] + reader.bits(LENGTH_EXTRA_BITS[lengthIndex]);
|
|
3560
|
+
const distanceSymbol = reader.decode(distances);
|
|
3561
|
+
if (distanceSymbol >= DISTANCE_BASES.length) {
|
|
3562
|
+
throw new InflateError("A distance code deflate has no distance for");
|
|
3563
|
+
}
|
|
3564
|
+
output.copy(DISTANCE_BASES[distanceSymbol] + reader.bits(DISTANCE_EXTRA_BITS[distanceSymbol]), length);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
var crcTable = null;
|
|
3569
|
+
var crc32 = (bytes) => {
|
|
3570
|
+
if (!crcTable) {
|
|
3571
|
+
crcTable = new Uint32Array(256);
|
|
3572
|
+
for (let index = 0;index < 256; index++) {
|
|
3573
|
+
let value = index;
|
|
3574
|
+
for (let bit = 0;bit < 8; bit++) {
|
|
3575
|
+
value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
3576
|
+
}
|
|
3577
|
+
crcTable[index] = value >>> 0;
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
let crc = 4294967295;
|
|
3581
|
+
for (const byte of bytes) {
|
|
3582
|
+
crc = crcTable[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
3583
|
+
}
|
|
3584
|
+
return (crc ^ 4294967295) >>> 0;
|
|
3585
|
+
};
|
|
3586
|
+
|
|
3587
|
+
// src/core/other-apps/zip.ts
|
|
3588
|
+
class ZipError extends Error {
|
|
3589
|
+
constructor(message) {
|
|
3590
|
+
super(message);
|
|
3591
|
+
this.name = "ZipError";
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
var DEFAULT_ZIP_LIMITS = {
|
|
3595
|
+
entries: 20000,
|
|
3596
|
+
entrySize: 64 * 1024 * 1024,
|
|
3597
|
+
totalSize: 256 * 1024 * 1024
|
|
3598
|
+
};
|
|
3599
|
+
var END_OF_DIRECTORY = 101010256;
|
|
3600
|
+
var DIRECTORY_ENTRY = 33639248;
|
|
3601
|
+
var LOCAL_HEADER = 67324752;
|
|
3602
|
+
var isZip = (bytes) => {
|
|
3603
|
+
return bytes.length >= 4 && bytes[0] === 80 && bytes[1] === 75 && bytes[2] === 3 && bytes[3] === 4;
|
|
3604
|
+
};
|
|
3605
|
+
|
|
3606
|
+
class ZipArchive {
|
|
3607
|
+
bytes;
|
|
3608
|
+
view;
|
|
3609
|
+
limits;
|
|
3610
|
+
entries = new Map;
|
|
3611
|
+
unpacked = 0;
|
|
3612
|
+
constructor(bytes, limits = {}) {
|
|
3613
|
+
this.bytes = bytes;
|
|
3614
|
+
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
3615
|
+
this.limits = { ...DEFAULT_ZIP_LIMITS, ...limits };
|
|
3616
|
+
this.readDirectory();
|
|
3617
|
+
}
|
|
3618
|
+
get names() {
|
|
3619
|
+
return [...this.entries.keys()].filter((name) => !name.endsWith("/"));
|
|
3620
|
+
}
|
|
3621
|
+
has(name) {
|
|
3622
|
+
return this.entries.has(name);
|
|
3623
|
+
}
|
|
3624
|
+
read(name) {
|
|
3625
|
+
const entry = this.entries.get(name);
|
|
3626
|
+
if (!entry) {
|
|
3627
|
+
return null;
|
|
3628
|
+
}
|
|
3629
|
+
if (entry.flags & 1) {
|
|
3630
|
+
throw new ZipError(`${name} is encrypted`);
|
|
3631
|
+
}
|
|
3632
|
+
if (entry.size > this.limits.entrySize || this.unpacked + entry.size > this.limits.totalSize) {
|
|
3633
|
+
throw new ZipError(`${name} is too large to unpack`);
|
|
3634
|
+
}
|
|
3635
|
+
const offset = entry.headerOffset;
|
|
3636
|
+
if (offset + 30 > this.bytes.length || this.view.getUint32(offset, true) !== LOCAL_HEADER) {
|
|
3637
|
+
throw new ZipError(`The archive has no data where ${name} should start`);
|
|
3638
|
+
}
|
|
3639
|
+
const start = offset + 30 + this.view.getUint16(offset + 26, true) + this.view.getUint16(offset + 28, true);
|
|
3640
|
+
const end = start + entry.compressedSize;
|
|
3641
|
+
if (end > this.bytes.length) {
|
|
3642
|
+
throw new ZipError(`${name} runs past the end of the archive`);
|
|
3643
|
+
}
|
|
3644
|
+
const stored = this.bytes.subarray(start, end);
|
|
3645
|
+
let data;
|
|
3646
|
+
if (entry.method === 0) {
|
|
3647
|
+
data = stored;
|
|
3648
|
+
} else if (entry.method === 8) {
|
|
3649
|
+
try {
|
|
3650
|
+
data = inflate(stored, { expected: entry.size, limit: entry.size });
|
|
3651
|
+
} catch (error) {
|
|
3652
|
+
throw new ZipError(`${name} can't be unpacked: ${error instanceof InflateError ? error.message : String(error)}`);
|
|
3653
|
+
}
|
|
3654
|
+
} else {
|
|
3655
|
+
throw new ZipError(`${name} is compressed a way Orbit can't read (method ${entry.method})`);
|
|
3656
|
+
}
|
|
3657
|
+
if (data.length !== entry.size || crc32(data) !== entry.crc) {
|
|
3658
|
+
throw new ZipError(`${name} is damaged: its contents don't match its checksum`);
|
|
3659
|
+
}
|
|
3660
|
+
this.unpacked += data.length;
|
|
3661
|
+
return data;
|
|
3662
|
+
}
|
|
3663
|
+
text(name) {
|
|
3664
|
+
const data = this.read(name);
|
|
3665
|
+
return data === null ? null : new TextDecoder("utf-8").decode(data);
|
|
3666
|
+
}
|
|
3667
|
+
readDirectory() {
|
|
3668
|
+
const bytes = this.bytes;
|
|
3669
|
+
const view = this.view;
|
|
3670
|
+
let end = -1;
|
|
3671
|
+
for (let index = bytes.length - 22;index >= Math.max(0, bytes.length - 22 - 65535); index--) {
|
|
3672
|
+
if (view.getUint32(index, true) === END_OF_DIRECTORY) {
|
|
3673
|
+
end = index;
|
|
3674
|
+
break;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
if (end === -1) {
|
|
3678
|
+
throw new ZipError("The file is not a zip archive, or its end is missing");
|
|
3679
|
+
}
|
|
3680
|
+
const disk = view.getUint16(end + 4, true);
|
|
3681
|
+
const count = view.getUint16(end + 10, true);
|
|
3682
|
+
const size = view.getUint32(end + 12, true);
|
|
3683
|
+
const offset = view.getUint32(end + 16, true);
|
|
3684
|
+
if (disk !== 0) {
|
|
3685
|
+
throw new ZipError("The archive spans several disks");
|
|
3686
|
+
}
|
|
3687
|
+
if (count === 65535 || size === 4294967295 || offset === 4294967295) {
|
|
3688
|
+
throw new ZipError("The archive needs ZIP64, which Orbit reads no map from");
|
|
3689
|
+
}
|
|
3690
|
+
if (count > this.limits.entries) {
|
|
3691
|
+
throw new ZipError(`The archive lists more than ${this.limits.entries} files`);
|
|
3692
|
+
}
|
|
3693
|
+
if (offset + size > end) {
|
|
3694
|
+
throw new ZipError("The archive's directory runs past its end");
|
|
3695
|
+
}
|
|
3696
|
+
const decoder = new TextDecoder("utf-8");
|
|
3697
|
+
let position = offset;
|
|
3698
|
+
for (let index = 0;index < count; index++) {
|
|
3699
|
+
if (position + 46 > end || view.getUint32(position, true) !== DIRECTORY_ENTRY) {
|
|
3700
|
+
throw new ZipError("The archive's directory is damaged");
|
|
3701
|
+
}
|
|
3702
|
+
const nameLength = view.getUint16(position + 28, true);
|
|
3703
|
+
const extraLength = view.getUint16(position + 30, true);
|
|
3704
|
+
const commentLength = view.getUint16(position + 32, true);
|
|
3705
|
+
if (position + 46 + nameLength > end) {
|
|
3706
|
+
throw new ZipError("The archive's directory is damaged");
|
|
3707
|
+
}
|
|
3708
|
+
const name = decoder.decode(bytes.subarray(position + 46, position + 46 + nameLength));
|
|
3709
|
+
const entry = {
|
|
3710
|
+
name,
|
|
3711
|
+
flags: view.getUint16(position + 8, true),
|
|
3712
|
+
method: view.getUint16(position + 10, true),
|
|
3713
|
+
crc: view.getUint32(position + 16, true),
|
|
3714
|
+
compressedSize: view.getUint32(position + 20, true),
|
|
3715
|
+
size: view.getUint32(position + 24, true),
|
|
3716
|
+
headerOffset: view.getUint32(position + 42, true)
|
|
3717
|
+
};
|
|
3718
|
+
if (!this.entries.has(name)) {
|
|
3719
|
+
this.entries.set(name, entry);
|
|
3720
|
+
}
|
|
3721
|
+
position += 46 + nameLength + extraLength + commentLength;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
// src/core/other-apps/xmind.ts
|
|
3727
|
+
var PICTURE_EXTENSIONS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "heic", "heif"]);
|
|
3728
|
+
var importXMind = (data, options = {}) => {
|
|
3729
|
+
const workbook = openWorkbook(data);
|
|
3730
|
+
const maps = [];
|
|
3731
|
+
if (workbook.json !== undefined) {
|
|
3732
|
+
for (const sheet of workbook.json) {
|
|
3733
|
+
if (isObject(sheet) && isObject(sheet.rootTopic)) {
|
|
3734
|
+
maps.push(new JsonSheetReader(workbook.archive).read(sheet));
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
} else if (workbook.xml) {
|
|
3738
|
+
const styles = workbook.archive ? readStyles(workbook.archive) : new Map;
|
|
3739
|
+
const comments = workbook.archive ? readComments(workbook.archive) : new Map;
|
|
3740
|
+
for (const sheet of childElements(workbook.xml, "sheet")) {
|
|
3741
|
+
const topic = childElement(sheet, "topic");
|
|
3742
|
+
if (topic) {
|
|
3743
|
+
maps.push(new XmlSheetReader(workbook.archive, styles, comments).read(sheet, topic));
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
if (maps.length === 0) {
|
|
3748
|
+
throw new OrbitImportError("empty", `The workbook${options.name ? ` ${options.name}` : ""} has no sheet with a topic.`);
|
|
3749
|
+
}
|
|
3750
|
+
return { format: "xmind", maps };
|
|
3751
|
+
};
|
|
3752
|
+
var openWorkbook = (data) => {
|
|
3753
|
+
if (typeof data === "string") {
|
|
3754
|
+
return readContent(data, null, data.trimStart().startsWith("[") ? "json" : "xml");
|
|
3755
|
+
}
|
|
3756
|
+
if (!isZip(data)) {
|
|
3757
|
+
return readContent(new TextDecoder("utf-8").decode(data), null, "xml");
|
|
3758
|
+
}
|
|
3759
|
+
let archive;
|
|
3760
|
+
try {
|
|
3761
|
+
archive = new ZipArchive(data);
|
|
3762
|
+
} catch (error) {
|
|
3763
|
+
throw unreadable(error);
|
|
3764
|
+
}
|
|
3765
|
+
if (isEncrypted(archive)) {
|
|
3766
|
+
throw new OrbitImportError("password-protected", "The workbook is protected with a password: open it in XMind and save it without one first.");
|
|
3767
|
+
}
|
|
3768
|
+
try {
|
|
3769
|
+
if (archive.has("content.json")) {
|
|
3770
|
+
return readContent(archive.text("content.json") ?? "", archive, "json");
|
|
3771
|
+
}
|
|
3772
|
+
if (archive.has("content.xml")) {
|
|
3773
|
+
return readContent(archive.text("content.xml") ?? "", archive, "xml");
|
|
3774
|
+
}
|
|
3775
|
+
} catch (error) {
|
|
3776
|
+
throw unreadable(error);
|
|
3777
|
+
}
|
|
3778
|
+
throw new OrbitImportError("unreadable", "The file is a zip archive, but not an XMind workbook: it has no content.json or content.xml.");
|
|
3779
|
+
};
|
|
3780
|
+
var readContent = (text, archive, kind) => {
|
|
3781
|
+
if (kind === "json") {
|
|
3782
|
+
let json;
|
|
3783
|
+
try {
|
|
3784
|
+
json = JSON.parse(text);
|
|
3785
|
+
} catch (error) {
|
|
3786
|
+
throw new OrbitImportError("unreadable", `The workbook's content.json can't be read: ${error.message}.`);
|
|
3787
|
+
}
|
|
3788
|
+
if (!Array.isArray(json)) {
|
|
3789
|
+
throw new OrbitImportError("unreadable", "The workbook's content.json isn't a list of sheets.");
|
|
3790
|
+
}
|
|
3791
|
+
return { archive, json };
|
|
3792
|
+
}
|
|
3793
|
+
let xml;
|
|
3794
|
+
try {
|
|
3795
|
+
xml = parseXml(text);
|
|
3796
|
+
} catch (error) {
|
|
3797
|
+
throw unreadable(error);
|
|
3798
|
+
}
|
|
3799
|
+
if (xml.localName !== "xmap-content") {
|
|
3800
|
+
throw new OrbitImportError("unreadable", `The file isn't an XMind workbook: it starts with <${xml.name}>, not <xmap-content>.`);
|
|
3801
|
+
}
|
|
3802
|
+
return { archive, xml };
|
|
3803
|
+
};
|
|
3804
|
+
var unreadable = (error) => {
|
|
3805
|
+
if (error instanceof ZipError && /encrypted/.test(error.message)) {
|
|
3806
|
+
return new OrbitImportError("password-protected", "The workbook is protected with a password: open it in XMind and save it without one first.");
|
|
3807
|
+
}
|
|
3808
|
+
if (error instanceof ZipError || error instanceof MarkupError) {
|
|
3809
|
+
return new OrbitImportError("unreadable", `The workbook can't be read: ${error.message}.`);
|
|
3810
|
+
}
|
|
3811
|
+
return error instanceof OrbitImportError ? error : new OrbitImportError("unreadable", `The workbook can't be read: ${error.message}.`);
|
|
3812
|
+
};
|
|
3813
|
+
var isEncrypted = (archive) => {
|
|
3814
|
+
try {
|
|
3815
|
+
const manifest = archive.has("META-INF/manifest.xml") ? archive.text("META-INF/manifest.xml") ?? "" : "";
|
|
3816
|
+
const json = archive.has("manifest.json") ? archive.text("manifest.json") ?? "" : "";
|
|
3817
|
+
return /<encryption-data/.test(manifest) || /"encryption/.test(json);
|
|
3818
|
+
} catch {
|
|
3819
|
+
return false;
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
var isObject = (value) => {
|
|
3823
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3824
|
+
};
|
|
3825
|
+
var asString = (value) => {
|
|
3826
|
+
return typeof value === "string" ? value : undefined;
|
|
3827
|
+
};
|
|
3828
|
+
|
|
3829
|
+
class SheetReader {
|
|
3830
|
+
archive;
|
|
3831
|
+
report = new ImportReport;
|
|
3832
|
+
media = {};
|
|
3833
|
+
topics = new Map;
|
|
3834
|
+
pictureNames = new Map;
|
|
3835
|
+
constructor(archive) {
|
|
3836
|
+
this.archive = archive;
|
|
3837
|
+
}
|
|
3838
|
+
finish(root) {
|
|
3839
|
+
return { document: buildDocument(root, this.report), media: this.media, warnings: this.report.warnings };
|
|
3840
|
+
}
|
|
3841
|
+
readLink(href, idea) {
|
|
3842
|
+
const address = href?.trim();
|
|
3843
|
+
if (!address) {
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3846
|
+
const topic = /^xmind:#(.+)$/.exec(address);
|
|
3847
|
+
const kept = address.startsWith("#") ? null : keptAddress(address);
|
|
3848
|
+
if (topic) {
|
|
3849
|
+
idea.ideaLinks.push({ target: topic[1] });
|
|
3850
|
+
} else if (kept) {
|
|
3851
|
+
idea.link = kept;
|
|
3852
|
+
} else if (/^xap:/.test(address)) {
|
|
3853
|
+
if (PICTURE_EXTENSIONS.has(extensionOf(address)) && !idea.image) {
|
|
3854
|
+
idea.image = this.picture(address, idea.text) ?? undefined;
|
|
3855
|
+
} else {
|
|
3856
|
+
this.report.add("attachments-not-kept", idea.text || address.replace(/^.*\//, ""));
|
|
3857
|
+
}
|
|
3858
|
+
} else {
|
|
3859
|
+
this.report.add("local-links-not-kept", address);
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
picture(src, example) {
|
|
3863
|
+
if (/^https?:\/\/\S+$/i.test(src)) {
|
|
3864
|
+
return src;
|
|
3865
|
+
}
|
|
3866
|
+
const inside = /^xap:(.+)$/.exec(src)?.[1];
|
|
3867
|
+
if (!inside || !this.archive) {
|
|
3868
|
+
this.report.add("pictures-not-kept", example || src.replace(/^.*[/\\]/, ""));
|
|
3869
|
+
return null;
|
|
3870
|
+
}
|
|
3871
|
+
if (!this.pictureNames.has(inside)) {
|
|
3872
|
+
this.pictureNames.set(inside, this.unpackPicture(inside));
|
|
3873
|
+
}
|
|
3874
|
+
const name = this.pictureNames.get(inside) ?? null;
|
|
3875
|
+
if (!name) {
|
|
3876
|
+
this.report.add("pictures-not-kept", example || inside.replace(/^.*\//, ""));
|
|
3877
|
+
}
|
|
3878
|
+
return name;
|
|
3879
|
+
}
|
|
3880
|
+
unpackPicture(path) {
|
|
3881
|
+
const extension = extensionOf(path);
|
|
3882
|
+
if (!PICTURE_EXTENSIONS.has(extension)) {
|
|
3883
|
+
return null;
|
|
3884
|
+
}
|
|
3885
|
+
let bytes;
|
|
3886
|
+
try {
|
|
3887
|
+
bytes = this.archive?.read(path) ?? null;
|
|
3888
|
+
} catch {
|
|
3889
|
+
bytes = null;
|
|
3890
|
+
}
|
|
3891
|
+
if (!bytes) {
|
|
3892
|
+
return null;
|
|
3893
|
+
}
|
|
3894
|
+
const base = path.replace(/^.*\//, "").replace(/\.[^.]*$/, "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[^A-Za-z0-9]+/, "") || "picture";
|
|
3895
|
+
let name = `media/${base}.${extension}`;
|
|
3896
|
+
for (let suffix = 2;this.media[name] !== undefined; suffix++) {
|
|
3897
|
+
name = `media/${base}-${suffix}.${extension}`;
|
|
3898
|
+
}
|
|
3899
|
+
this.media[name] = bytes;
|
|
3900
|
+
return name;
|
|
3901
|
+
}
|
|
3902
|
+
noteAddresses(example) {
|
|
3903
|
+
return {
|
|
3904
|
+
link: (href) => {
|
|
3905
|
+
const topic = /^xmind:#(.+)$/.exec(href);
|
|
3906
|
+
if (topic) {
|
|
3907
|
+
return sourceLink(topic[1]);
|
|
3908
|
+
}
|
|
3909
|
+
const kept = href.startsWith("#") ? null : keptAddress(href);
|
|
3910
|
+
if (kept) {
|
|
3911
|
+
return kept;
|
|
3912
|
+
}
|
|
3913
|
+
this.report.add("local-links-not-kept", href);
|
|
3914
|
+
return null;
|
|
3915
|
+
},
|
|
3916
|
+
picture: (src) => this.picture(src, example)
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
htmlNote(html, idea) {
|
|
3920
|
+
const note = noteFromHtml(html, this.noteAddresses(idea.text));
|
|
3921
|
+
if (note?.format === "plain") {
|
|
3922
|
+
this.report.add("formatting-not-kept", idea.text || undefined);
|
|
3923
|
+
}
|
|
3924
|
+
return note;
|
|
3925
|
+
}
|
|
3926
|
+
readMarker(markerId, idea) {
|
|
3927
|
+
const meaning = xmindMarker(markerId);
|
|
3928
|
+
if (!meaning) {
|
|
3929
|
+
this.report.add("icons-not-kept", markerId);
|
|
3930
|
+
} else if ("icon" in meaning) {
|
|
3931
|
+
idea.icons.push(meaning.icon);
|
|
3932
|
+
} else if ("priority" in meaning) {
|
|
3933
|
+
idea.priority ??= meaning.priority;
|
|
3934
|
+
} else {
|
|
3935
|
+
idea.status ??= meaning.status;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
relate(fromId, toId, title) {
|
|
3939
|
+
const from = fromId ? this.topics.get(fromId) : undefined;
|
|
3940
|
+
if (!from || !toId) {
|
|
3941
|
+
this.report.add("missing-link-targets", title || undefined);
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
const label = title?.trim();
|
|
3945
|
+
from.ideaLinks.push(label ? { target: toId, label } : { target: toId });
|
|
3946
|
+
}
|
|
3947
|
+
adopt(idea, children, code) {
|
|
3948
|
+
for (const child of children) {
|
|
3949
|
+
this.report.add(code, child.text || undefined);
|
|
3950
|
+
idea.children.push(child);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
boundaryNote(titles, idea) {
|
|
3954
|
+
for (const title of titles) {
|
|
3955
|
+
const text = title.trim();
|
|
3956
|
+
if (text !== "") {
|
|
3957
|
+
this.report.add("boundaries-in-notes", text);
|
|
3958
|
+
const note = noteFromPlainText(text);
|
|
3959
|
+
if (note) {
|
|
3960
|
+
idea.notes.push(note);
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
var extensionOf = (path) => {
|
|
3967
|
+
return /\.([A-Za-z0-9]+)$/.exec(path)?.[1].toLowerCase() ?? "";
|
|
3968
|
+
};
|
|
3969
|
+
|
|
3970
|
+
class JsonSheetReader extends SheetReader {
|
|
3971
|
+
read(sheet) {
|
|
3972
|
+
const root = this.topic(sheet.rootTopic, 0);
|
|
3973
|
+
for (const relationship of Array.isArray(sheet.relationships) ? sheet.relationships : []) {
|
|
3974
|
+
if (isObject(relationship)) {
|
|
3975
|
+
this.relate(asString(relationship.end1Id), asString(relationship.end2Id), asString(relationship.title));
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
return this.finish(root);
|
|
3979
|
+
}
|
|
3980
|
+
topic(topic, depth) {
|
|
3981
|
+
if (depth > 500) {
|
|
3982
|
+
throw new OrbitImportError("too-large", "The sheet goes more than 500 levels deep.");
|
|
3983
|
+
}
|
|
3984
|
+
const idea = newIdea(asString(topic.title) ?? "");
|
|
3985
|
+
const id = asString(topic.id);
|
|
3986
|
+
if (id) {
|
|
3987
|
+
idea.sourceId = id;
|
|
3988
|
+
this.topics.set(id, idea);
|
|
3989
|
+
}
|
|
3990
|
+
const note = this.note(topic.notes, idea);
|
|
3991
|
+
if (note) {
|
|
3992
|
+
idea.notes.push(note);
|
|
3993
|
+
}
|
|
3994
|
+
if (Array.isArray(topic.labels)) {
|
|
3995
|
+
idea.tags.push(...topic.labels.filter((label) => typeof label === "string"));
|
|
3996
|
+
}
|
|
3997
|
+
for (const marker of Array.isArray(topic.markers) ? topic.markers : []) {
|
|
3998
|
+
const markerId = isObject(marker) ? asString(marker.markerId) : undefined;
|
|
3999
|
+
if (markerId) {
|
|
4000
|
+
this.readMarker(markerId, idea);
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
this.readLink(asString(topic.href), idea);
|
|
4004
|
+
if (isObject(topic.image)) {
|
|
4005
|
+
const src = asString(topic.image.src);
|
|
4006
|
+
if (src) {
|
|
4007
|
+
idea.image ??= this.picture(src, idea.text) ?? undefined;
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
if (topic.branch === "folded") {
|
|
4011
|
+
idea.collapsed = true;
|
|
4012
|
+
}
|
|
4013
|
+
const style = isObject(topic.style) && isObject(topic.style.properties) ? topic.style.properties : {};
|
|
4014
|
+
const fill = asString(style["svg:fill"]);
|
|
4015
|
+
if (fill && /^#[0-9A-Fa-f]{6}$/.test(fill)) {
|
|
4016
|
+
idea.color = fill.toUpperCase();
|
|
4017
|
+
}
|
|
4018
|
+
const children = isObject(topic.children) ? topic.children : {};
|
|
4019
|
+
const kind = (name) => (Array.isArray(children[name]) ? children[name] : []).filter(isObject).map((child) => this.topic(child, depth + 1));
|
|
4020
|
+
idea.children.push(...kind("attached"));
|
|
4021
|
+
this.adopt(idea, kind("summary"), "summaries-moved");
|
|
4022
|
+
this.adopt(idea, kind("callout"), "callouts-moved");
|
|
4023
|
+
this.adopt(idea, kind("detached"), "floating-ideas-moved");
|
|
4024
|
+
const boundaries = Array.isArray(topic.boundaries) ? topic.boundaries.filter(isObject).map((boundary) => asString(boundary.title) ?? "") : [];
|
|
4025
|
+
this.boundaryNote(boundaries, idea);
|
|
4026
|
+
return idea;
|
|
4027
|
+
}
|
|
4028
|
+
note(notes, idea) {
|
|
4029
|
+
if (!isObject(notes)) {
|
|
4030
|
+
return;
|
|
4031
|
+
}
|
|
4032
|
+
const html = isObject(notes.realHTML) ? asString(notes.realHTML.content) : undefined;
|
|
4033
|
+
if (html) {
|
|
4034
|
+
return this.htmlNote(html, idea);
|
|
4035
|
+
}
|
|
4036
|
+
const paragraphs = isObject(notes.html) && isObject(notes.html.content) && Array.isArray(notes.html.content.paragraphs) ? notes.html.content.paragraphs : null;
|
|
4037
|
+
if (paragraphs) {
|
|
4038
|
+
const blocks = paragraphs.filter(isObject).map((paragraph) => ({
|
|
4039
|
+
kind: "paragraph",
|
|
4040
|
+
inline: (Array.isArray(paragraph.spans) ? paragraph.spans : []).filter(isObject).map((span) => styledSpan(span))
|
|
4041
|
+
}));
|
|
4042
|
+
const note = noteFromBlocks(blocks);
|
|
4043
|
+
if (note?.format === "plain") {
|
|
4044
|
+
this.report.add("formatting-not-kept", idea.text || undefined);
|
|
4045
|
+
}
|
|
4046
|
+
return note;
|
|
4047
|
+
}
|
|
4048
|
+
return noteFromPlainText(isObject(notes.plain) ? asString(notes.plain.content) : undefined);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
var styledSpan = (span) => {
|
|
4052
|
+
const properties = isObject(span.style) && isObject(span.style.properties) ? span.style.properties : {};
|
|
4053
|
+
let piece = { kind: "text", text: (asString(span.text) ?? "").replace(/\s+/g, " ") };
|
|
4054
|
+
if (/line-through/.test(asString(properties["fo:text-decoration"]) ?? "")) {
|
|
4055
|
+
piece = { kind: "strike", children: [piece] };
|
|
4056
|
+
}
|
|
4057
|
+
if (asString(properties["fo:font-style"]) === "italic") {
|
|
4058
|
+
piece = { kind: "emphasis", children: [piece] };
|
|
4059
|
+
}
|
|
4060
|
+
if (/^(bold|[6-9]00)$/.test(asString(properties["fo:font-weight"]) ?? "")) {
|
|
4061
|
+
piece = { kind: "strong", children: [piece] };
|
|
4062
|
+
}
|
|
4063
|
+
return piece;
|
|
4064
|
+
};
|
|
4065
|
+
|
|
4066
|
+
class XmlSheetReader extends SheetReader {
|
|
4067
|
+
styles;
|
|
4068
|
+
comments;
|
|
4069
|
+
constructor(archive, styles, comments) {
|
|
4070
|
+
super(archive);
|
|
4071
|
+
this.styles = styles;
|
|
4072
|
+
this.comments = comments;
|
|
4073
|
+
}
|
|
4074
|
+
read(sheet, topic) {
|
|
4075
|
+
const root = this.topic(topic);
|
|
4076
|
+
for (const relationships of childElements(sheet, "relationships")) {
|
|
4077
|
+
for (const relationship of childElements(relationships, "relationship")) {
|
|
4078
|
+
const title = childElement(relationship, "title");
|
|
4079
|
+
this.relate(relationship.attributes.end1, relationship.attributes.end2, title ? textContent(title) : undefined);
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
return this.finish(root);
|
|
4083
|
+
}
|
|
4084
|
+
topic(topic) {
|
|
4085
|
+
const title = childElement(topic, "title");
|
|
4086
|
+
const idea = newIdea(title ? textContent(title) : "");
|
|
4087
|
+
const id = topic.attributes.id;
|
|
4088
|
+
if (id) {
|
|
4089
|
+
idea.sourceId = id;
|
|
4090
|
+
this.topics.set(id, idea);
|
|
4091
|
+
}
|
|
4092
|
+
const notes = childElement(topic, "notes");
|
|
4093
|
+
if (notes) {
|
|
4094
|
+
const html = childElement(notes, "html");
|
|
4095
|
+
const plain = childElement(notes, "plain");
|
|
4096
|
+
const note = html ? this.htmlNote(html, idea) : noteFromPlainText(plain ? textContent(plain) : undefined);
|
|
4097
|
+
if (note) {
|
|
4098
|
+
idea.notes.push(note);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
for (const comment of id ? this.comments.get(id) ?? [] : []) {
|
|
4102
|
+
const note = noteFromPlainText(comment);
|
|
4103
|
+
if (note) {
|
|
4104
|
+
idea.notes.push(note);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
for (const labels of childElements(topic, "labels")) {
|
|
4108
|
+
idea.tags.push(...childElements(labels, "label").map((label) => textContent(label)));
|
|
4109
|
+
}
|
|
4110
|
+
for (const markers of childElements(topic, "marker-refs")) {
|
|
4111
|
+
for (const marker of childElements(markers, "marker-ref")) {
|
|
4112
|
+
const markerId = marker.attributes["marker-id"];
|
|
4113
|
+
if (markerId) {
|
|
4114
|
+
this.readMarker(markerId, idea);
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
this.readLink(attributeOf(topic, "href"), idea);
|
|
4119
|
+
const image = childElement(topic, "img");
|
|
4120
|
+
const src = image ? attributeOf(image, "src") : undefined;
|
|
4121
|
+
if (src) {
|
|
4122
|
+
idea.image ??= this.picture(src, idea.text) ?? undefined;
|
|
4123
|
+
}
|
|
4124
|
+
if (topic.attributes.branch === "folded") {
|
|
4125
|
+
idea.collapsed = true;
|
|
4126
|
+
}
|
|
4127
|
+
const fill = this.styles.get(topic.attributes["style-id"] ?? "");
|
|
4128
|
+
if (fill) {
|
|
4129
|
+
idea.color = fill;
|
|
4130
|
+
}
|
|
4131
|
+
const children = childElement(topic, "children");
|
|
4132
|
+
const kind = (type) => (children ? childElements(children, "topics") : []).filter((topics) => (topics.attributes.type ?? "attached") === type).flatMap((topics) => childElements(topics, "topic").map((child) => this.topic(child)));
|
|
4133
|
+
idea.children.push(...kind("attached"));
|
|
4134
|
+
this.adopt(idea, kind("summary"), "summaries-moved");
|
|
4135
|
+
this.adopt(idea, kind("callout"), "callouts-moved");
|
|
4136
|
+
this.adopt(idea, kind("detached"), "floating-ideas-moved");
|
|
4137
|
+
const boundaries = childElements(topic, "boundaries").flatMap((list) => childElements(list, "boundary")).map((boundary) => {
|
|
4138
|
+
const boundaryTitle = childElement(boundary, "title");
|
|
4139
|
+
return boundaryTitle ? textContent(boundaryTitle) : "";
|
|
4140
|
+
});
|
|
4141
|
+
this.boundaryNote(boundaries, idea);
|
|
4142
|
+
return idea;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
var readStyles = (archive) => {
|
|
4146
|
+
const fills = new Map;
|
|
4147
|
+
try {
|
|
4148
|
+
const text = archive.has("styles.xml") ? archive.text("styles.xml") : null;
|
|
4149
|
+
if (!text) {
|
|
4150
|
+
return fills;
|
|
4151
|
+
}
|
|
4152
|
+
const pending = [parseXml(text)];
|
|
4153
|
+
while (pending.length > 0) {
|
|
4154
|
+
const element = pending.pop();
|
|
4155
|
+
for (const child of element.children) {
|
|
4156
|
+
if (!isElement(child)) {
|
|
4157
|
+
continue;
|
|
4158
|
+
}
|
|
4159
|
+
if (child.localName === "style" && child.attributes.type === "topic" && child.attributes.id) {
|
|
4160
|
+
const properties = childElement(child, "topic-properties");
|
|
4161
|
+
const fill = properties ? attributeOf(properties, "fill") : undefined;
|
|
4162
|
+
if (fill && /^#[0-9A-Fa-f]{6}$/.test(fill)) {
|
|
4163
|
+
fills.set(child.attributes.id, fill.toUpperCase());
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
pending.push(child);
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
} catch {}
|
|
4170
|
+
return fills;
|
|
4171
|
+
};
|
|
4172
|
+
var readComments = (archive) => {
|
|
4173
|
+
const comments = new Map;
|
|
4174
|
+
try {
|
|
4175
|
+
const text = archive.has("comments.xml") ? archive.text("comments.xml") : null;
|
|
4176
|
+
if (!text) {
|
|
4177
|
+
return comments;
|
|
4178
|
+
}
|
|
4179
|
+
for (const comment of childElements(parseXml(text), "comment")) {
|
|
4180
|
+
const topic = comment.attributes["object-id"];
|
|
4181
|
+
const content = childElement(comment, "content");
|
|
4182
|
+
const words = content ? textContent(content).replace(/\r\n?/g, `
|
|
4183
|
+
`).trim() : "";
|
|
4184
|
+
if (topic && words !== "") {
|
|
4185
|
+
const author = (comment.attributes.author ?? "").trim();
|
|
4186
|
+
comments.set(topic, [...comments.get(topic) ?? [], author ? `${author}: ${words}` : words]);
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
} catch {}
|
|
4190
|
+
return comments;
|
|
4191
|
+
};
|
|
4192
|
+
|
|
4193
|
+
// src/core/other-apps/import.ts
|
|
4194
|
+
var EXTENSIONS = { opml: "opml", mm: "freemind", xmind: "xmind" };
|
|
4195
|
+
var detectImportFormat = (data, name) => {
|
|
4196
|
+
const extension = /\.([A-Za-z0-9]+)$/.exec(name ?? "")?.[1].toLowerCase() ?? "";
|
|
4197
|
+
return formatInside(data) ?? (Object.prototype.hasOwnProperty.call(EXTENSIONS, extension) ? EXTENSIONS[extension] : null);
|
|
4198
|
+
};
|
|
4199
|
+
var formatInside = (data) => {
|
|
4200
|
+
if (typeof data !== "string" && isZip(data)) {
|
|
4201
|
+
try {
|
|
4202
|
+
const archive = new ZipArchive(data);
|
|
4203
|
+
return archive.has("content.json") || archive.has("content.xml") ? "xmind" : null;
|
|
4204
|
+
} catch {
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
const text = typeof data === "string" ? data.slice(0, 4096) : decodeMarkup(data.subarray(0, 4096));
|
|
4209
|
+
const root = /^(?:\s|<\?[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE[^>]*>)*<([A-Za-z_][\w.:-]*)/.exec(text.replace(/^\ufeff/, ""))?.[1];
|
|
4210
|
+
const formats = { opml: "opml", map: "freemind", "xmap-content": "xmind" };
|
|
4211
|
+
return root && Object.prototype.hasOwnProperty.call(formats, root) ? formats[root] : null;
|
|
4212
|
+
};
|
|
4213
|
+
var importMap = (data, options = {}) => {
|
|
4214
|
+
const format = options.format ?? detectImportFormat(data, options.name);
|
|
4215
|
+
switch (format) {
|
|
4216
|
+
case "opml":
|
|
4217
|
+
return importOPML(data, options);
|
|
4218
|
+
case "freemind":
|
|
4219
|
+
return importFreeMind(data, options);
|
|
4220
|
+
case "xmind":
|
|
4221
|
+
return importXMind(data, options);
|
|
4222
|
+
default:
|
|
4223
|
+
throw new OrbitImportError("unknown-format", `${options.name ? `${options.name} isn't` : "The file isn't"} a map Orbit can bring in from another app: it reads OPML, FreeMind and Freeplane (.mm), and XMind (.xmind).`);
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
1329
4226
|
// src/core/material.ts
|
|
1330
4227
|
var MATERIAL_SEED = "#6750A4";
|
|
1331
4228
|
var BRANCH_HUES = [0, 3, 6, 2, 5, 1, 4].map((step) => step * 360 / 7);
|
|
@@ -1407,20 +4304,30 @@ var materialColors = (seed, dark) => {
|
|
|
1407
4304
|
};
|
|
1408
4305
|
export {
|
|
1409
4306
|
DIAGNOSTIC_CODES2 as DIAGNOSTIC_CODES,
|
|
4307
|
+
IMPORT_ERROR_CODES,
|
|
4308
|
+
IMPORT_WARNING_CODES,
|
|
1410
4309
|
MATERIAL_SEED,
|
|
1411
4310
|
ORBIT_THEME_PRESETS2 as ORBIT_THEME_PRESETS,
|
|
4311
|
+
OrbitImportError,
|
|
1412
4312
|
OrbitParseError2 as OrbitParseError,
|
|
1413
4313
|
OrbitSVGRenderer2 as OrbitSVGRenderer,
|
|
4314
|
+
PRINT_THEMES,
|
|
1414
4315
|
SUPPORTED_SCHEMA_VERSION2 as SUPPORTED_SCHEMA_VERSION,
|
|
1415
4316
|
THEME_TOKENS2 as THEME_TOKENS,
|
|
1416
4317
|
createMapTools,
|
|
1417
4318
|
createOrbitMap,
|
|
1418
4319
|
defineOrbitMapElement2 as defineOrbitMapElement,
|
|
4320
|
+
detectImportFormat,
|
|
1419
4321
|
documentToRendererOptions2 as documentToRendererOptions,
|
|
4322
|
+
exportOPML,
|
|
1420
4323
|
exportWebPage,
|
|
1421
4324
|
findNodeById2 as findNodeById,
|
|
1422
4325
|
formatDiagnostic2 as formatDiagnostic,
|
|
1423
4326
|
getNodePath2 as getNodePath,
|
|
4327
|
+
importFreeMind,
|
|
4328
|
+
importMap,
|
|
4329
|
+
importOPML,
|
|
4330
|
+
importXMind,
|
|
1424
4331
|
isOrbitThemePreset2 as isOrbitThemePreset,
|
|
1425
4332
|
isThemeName2 as isThemeName,
|
|
1426
4333
|
languageTags2 as languageTags,
|
|
@@ -1428,6 +4335,7 @@ export {
|
|
|
1428
4335
|
parseOrbit2 as parseOrbit,
|
|
1429
4336
|
parseOrbitMarkdown2 as parseOrbitMarkdown,
|
|
1430
4337
|
parseOrbitYAML2 as parseOrbitYAML,
|
|
4338
|
+
printTheme,
|
|
1431
4339
|
registerAction2 as registerAction,
|
|
1432
4340
|
registerLanguage2 as registerLanguage,
|
|
1433
4341
|
registerNodeType2 as registerNodeType,
|
|
@@ -1436,6 +4344,7 @@ export {
|
|
|
1436
4344
|
renderNoteMarkdown2 as renderNoteMarkdown,
|
|
1437
4345
|
renderOrbitElement2 as renderOrbitElement,
|
|
1438
4346
|
renderOrbitMarkdownBlocks2 as renderOrbitMarkdownBlocks,
|
|
4347
|
+
renderPrintLayout,
|
|
1439
4348
|
renderToSVGString2 as renderToSVGString,
|
|
1440
4349
|
serializeOrbitMarkdown2 as serializeOrbitMarkdown,
|
|
1441
4350
|
serializeOrbitYAML,
|
|
@@ -1444,5 +4353,5 @@ export {
|
|
|
1444
4353
|
upgradeNodeTypes2 as upgradeNodeTypes
|
|
1445
4354
|
};
|
|
1446
4355
|
|
|
1447
|
-
//# debugId=
|
|
4356
|
+
//# debugId=0EF60DBE164EC11364756E2164756E21
|
|
1448
4357
|
//# sourceMappingURL=index.js.map
|