@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/bin/orbit.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// bin/orbit.ts
|
|
4
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { basename, dirname, extname, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
|
|
8
8
|
// src/core/yaml-mini.ts
|
|
@@ -1258,6 +1258,42 @@ var lineOfKey = (lines, key, fromIndex = 0, toIndex = lines.length) => {
|
|
|
1258
1258
|
return;
|
|
1259
1259
|
};
|
|
1260
1260
|
|
|
1261
|
+
// src/core/branch-colors.ts
|
|
1262
|
+
var branchPalette = (palette) => {
|
|
1263
|
+
return palette.length > 1 ? palette.slice(1) : [...palette];
|
|
1264
|
+
};
|
|
1265
|
+
var isPaletteNumber = (value) => {
|
|
1266
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
1267
|
+
};
|
|
1268
|
+
var paletteColorOf = (colors, paletteNumber) => {
|
|
1269
|
+
return colors[(paletteNumber - 1) % colors.length];
|
|
1270
|
+
};
|
|
1271
|
+
var wrapped = (paletteNumber, count) => {
|
|
1272
|
+
return (paletteNumber - 1) % count + 1;
|
|
1273
|
+
};
|
|
1274
|
+
var branchNumbers = (branches, count) => {
|
|
1275
|
+
const numbers = new Map;
|
|
1276
|
+
const taken = new Set;
|
|
1277
|
+
for (const branch of branches) {
|
|
1278
|
+
if (isPaletteNumber(branch.paletteColor)) {
|
|
1279
|
+
taken.add(wrapped(branch.paletteColor, count));
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
const all = Array.from({ length: count }, (_, index) => index + 1);
|
|
1283
|
+
const free = all.filter((paletteNumber) => !taken.has(paletteNumber));
|
|
1284
|
+
const turns = free.length > 0 ? free : all;
|
|
1285
|
+
let turn = 0;
|
|
1286
|
+
for (const branch of branches) {
|
|
1287
|
+
if (isPaletteNumber(branch.paletteColor)) {
|
|
1288
|
+
numbers.set(branch, wrapped(branch.paletteColor, count));
|
|
1289
|
+
} else {
|
|
1290
|
+
numbers.set(branch, turns[turn % turns.length]);
|
|
1291
|
+
turn += 1;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
return numbers;
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1261
1297
|
// src/core/suggest.ts
|
|
1262
1298
|
var suggest = (input, candidates, synonyms = {}) => {
|
|
1263
1299
|
const lowered = input.toLowerCase();
|
|
@@ -1387,6 +1423,7 @@ var readEmbed = (value) => {
|
|
|
1387
1423
|
};
|
|
1388
1424
|
var NODE_ATTRIBUTE_KEYS = [
|
|
1389
1425
|
"color",
|
|
1426
|
+
"paletteColor",
|
|
1390
1427
|
"icon",
|
|
1391
1428
|
"status",
|
|
1392
1429
|
"priority",
|
|
@@ -1444,6 +1481,15 @@ var assignNodeAttribute = (node, key, value, diagnostics, line, vocabulary = { k
|
|
|
1444
1481
|
}
|
|
1445
1482
|
return;
|
|
1446
1483
|
}
|
|
1484
|
+
case "paletteColor": {
|
|
1485
|
+
const paletteNumber = typeof value === "number" || typeof value === "string" && value.trim() !== "" ? Number(value) : Number.NaN;
|
|
1486
|
+
if (isPaletteNumber(paletteNumber)) {
|
|
1487
|
+
node.paletteColor = paletteNumber;
|
|
1488
|
+
} else {
|
|
1489
|
+
keepUnread(node, key, value, diagnostics, line, "a whole number from 1");
|
|
1490
|
+
}
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1447
1493
|
case "priority": {
|
|
1448
1494
|
const priority = typeof value === "number" || typeof value === "string" && value.trim() !== "" ? Number(value) : Number.NaN;
|
|
1449
1495
|
if (Number.isFinite(priority)) {
|
|
@@ -3095,6 +3141,7 @@ var nodeFields = (node) => {
|
|
|
3095
3141
|
status: node.status,
|
|
3096
3142
|
tags: copy(node.tags),
|
|
3097
3143
|
color: node.color,
|
|
3144
|
+
paletteColor: node.paletteColor,
|
|
3098
3145
|
priority: node.priority,
|
|
3099
3146
|
collapsed: node.collapsed,
|
|
3100
3147
|
type: node.type,
|
|
@@ -3289,6 +3336,10 @@ var ONLY_EMOJI = new RegExp(String.raw`^(?:${EMOJI})+$`, "u");
|
|
|
3289
3336
|
var isTitleIcon = (icon) => {
|
|
3290
3337
|
return ONLY_EMOJI.test(icon);
|
|
3291
3338
|
};
|
|
3339
|
+
var leadingEmoji = (title) => {
|
|
3340
|
+
const match = LEADING_EMOJI.exec(title);
|
|
3341
|
+
return match ? { icon: match[1], text: match[2].trim() } : null;
|
|
3342
|
+
};
|
|
3292
3343
|
var TASK_MARKER = /^\[( |x|X|\/)\]\s+(.*)$/;
|
|
3293
3344
|
var TASK_STATUSES = { " ": "todo", x: "done", X: "done", "/": "active" };
|
|
3294
3345
|
var ESCAPE = /\\([!-/:-@[-`{-~])/g;
|
|
@@ -4742,6 +4793,9 @@ var collectAttrs = (node, explicitIds) => {
|
|
|
4742
4793
|
if (node.color) {
|
|
4743
4794
|
r.color = node.color;
|
|
4744
4795
|
}
|
|
4796
|
+
if (node.paletteColor !== undefined) {
|
|
4797
|
+
r.paletteColor = node.paletteColor;
|
|
4798
|
+
}
|
|
4745
4799
|
if (node.status) {
|
|
4746
4800
|
r.status = node.status;
|
|
4747
4801
|
}
|
|
@@ -5508,6 +5562,9 @@ var serializeNode = (n, explicitIds) => {
|
|
|
5508
5562
|
if (n.color) {
|
|
5509
5563
|
obj.color = n.color;
|
|
5510
5564
|
}
|
|
5565
|
+
if (n.paletteColor !== undefined) {
|
|
5566
|
+
obj.paletteColor = n.paletteColor;
|
|
5567
|
+
}
|
|
5511
5568
|
if (n.type) {
|
|
5512
5569
|
obj.type = n.typeVersion === undefined ? n.type : `${n.type}@${n.typeVersion}`;
|
|
5513
5570
|
}
|
|
@@ -5613,6 +5670,7 @@ var ENGLISH = {
|
|
|
5613
5670
|
readNote: "Read note",
|
|
5614
5671
|
openLink: "Open link",
|
|
5615
5672
|
goTo: "Go to {name}",
|
|
5673
|
+
seeIdea: "See {name}",
|
|
5616
5674
|
details: "Details",
|
|
5617
5675
|
close: "Close",
|
|
5618
5676
|
previous: "Previous",
|
|
@@ -5689,6 +5747,7 @@ var SPANISH = {
|
|
|
5689
5747
|
readNote: "Leer nota",
|
|
5690
5748
|
openLink: "Abrir enlace",
|
|
5691
5749
|
goTo: "Ir a {name}",
|
|
5750
|
+
seeIdea: "Ver {name}",
|
|
5692
5751
|
details: "Detalles",
|
|
5693
5752
|
close: "Cerrar",
|
|
5694
5753
|
previous: "Anterior",
|
|
@@ -5953,10 +6012,10 @@ var MIN_SUN_RADIUS = 34;
|
|
|
5953
6012
|
var MIN_PLANET_RADIUS = 18;
|
|
5954
6013
|
var MAX_SUN_RADIUS = 76;
|
|
5955
6014
|
var MAX_PLANET_RADIUS = 48;
|
|
5956
|
-
var
|
|
6015
|
+
var ROOMY_RING_LIMIT = 7;
|
|
5957
6016
|
var FOCUS_RING_GAP_FACTOR = 2.15;
|
|
5958
6017
|
var COMPACT_RING_GAP = 8;
|
|
5959
|
-
var
|
|
6018
|
+
var COMPACT_RING_LIMIT = 12;
|
|
5960
6019
|
var CROWDED_RING_COUNT = 4;
|
|
5961
6020
|
var MOON_LIMIT = 6;
|
|
5962
6021
|
var MOON_CLEARANCE = 2;
|
|
@@ -6128,23 +6187,28 @@ var calculateFocusRingPlan = (childCount, sunRadius, planetRadius, baseDim, comp
|
|
|
6128
6187
|
if (childCount === 0) {
|
|
6129
6188
|
return { rings: [], counts: [], ringGap: 0 };
|
|
6130
6189
|
}
|
|
6131
|
-
const roomy = ringPlan(childCount, planetRadius, Math.max(baseDim * 0.26, sunRadius + planetRadius + NODE_GAP * 1.3), planetRadius * FOCUS_RING_GAP_FACTOR + NODE_GAP,
|
|
6190
|
+
const roomy = ringPlan(childCount, planetRadius, Math.max(baseDim * 0.26, sunRadius + planetRadius + NODE_GAP * 1.3), planetRadius * FOCUS_RING_GAP_FACTOR + NODE_GAP, ROOMY_RING_LIMIT);
|
|
6132
6191
|
if (!compact || roomy.rings.length <= 1) {
|
|
6133
6192
|
return roomy;
|
|
6134
6193
|
}
|
|
6135
|
-
const close = ringPlan(childCount, planetRadius, sunRadius + planetRadius + NODE_GAP, planetRadius * 2 + COMPACT_RING_GAP,
|
|
6194
|
+
const close = ringPlan(childCount, planetRadius, sunRadius + planetRadius + NODE_GAP, planetRadius * 2 + COMPACT_RING_GAP, COMPACT_RING_LIMIT);
|
|
6136
6195
|
return (close.rings.at(-1) ?? 0) < (roomy.rings.at(-1) ?? 0) ? close : roomy;
|
|
6137
6196
|
};
|
|
6138
|
-
var ringPlan = (childCount, planetRadius, innerRingRadius, ringGap,
|
|
6197
|
+
var ringPlan = (childCount, planetRadius, innerRingRadius, ringGap, ringLimit) => {
|
|
6139
6198
|
const rings = [];
|
|
6140
6199
|
let totalCapacity = 0;
|
|
6141
|
-
while (totalCapacity < childCount
|
|
6200
|
+
while (totalCapacity < childCount) {
|
|
6142
6201
|
const radius = innerRingRadius + ringGap * rings.length;
|
|
6143
6202
|
rings.push(radius);
|
|
6144
6203
|
totalCapacity += focusRingCapacity(radius, planetRadius);
|
|
6145
6204
|
}
|
|
6146
|
-
const
|
|
6147
|
-
|
|
6205
|
+
const fewer = rings.slice(0, ringLimit);
|
|
6206
|
+
const stretched = { rings: fewer, counts: distributeAcrossRings(childCount, fewer, planetRadius), ringGap };
|
|
6207
|
+
if (rings.length <= ringLimit) {
|
|
6208
|
+
return stretched;
|
|
6209
|
+
}
|
|
6210
|
+
const all = { rings, counts: distributeAcrossRings(childCount, rings, planetRadius), ringGap };
|
|
6211
|
+
return (all.rings.at(-1) ?? 0) < (stretched.rings.at(-1) ?? 0) ? all : stretched;
|
|
6148
6212
|
};
|
|
6149
6213
|
var ringStartAngle = (ringIndex, count) => {
|
|
6150
6214
|
return ringIndex % 2 === 0 ? -90 : -90 + 360 / count / 2;
|
|
@@ -6355,20 +6419,22 @@ var fitExtent = (kind, extent, viewport, insets, below = 0) => {
|
|
|
6355
6419
|
var RING_GAP_PADDING = 7;
|
|
6356
6420
|
var assignNodeColors = (tree, palette, ownColor = (node) => node.color) => {
|
|
6357
6421
|
const colors = new Map;
|
|
6358
|
-
const branchColors = palette
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6422
|
+
const branchColors = branchPalette(palette);
|
|
6423
|
+
const numbers = branchNumbers(tree.children, branchColors.length);
|
|
6424
|
+
const chosen = (node) => ownColor(node) ?? (isPaletteNumber(node.paletteColor) ? paletteColorOf(branchColors, node.paletteColor) : undefined);
|
|
6425
|
+
colors.set(tree.id, chosen(tree) ?? palette[0]);
|
|
6426
|
+
for (const branch of tree.children) {
|
|
6427
|
+
const branchColor = ownColor(branch) ?? paletteColorOf(branchColors, numbers.get(branch));
|
|
6362
6428
|
colors.set(branch.id, branchColor);
|
|
6363
6429
|
const visit = (node, inherited) => {
|
|
6364
6430
|
node.children.forEach((child, siblingIndex) => {
|
|
6365
|
-
const own =
|
|
6431
|
+
const own = chosen(child);
|
|
6366
6432
|
colors.set(child.id, own ?? shade(inherited, siblingIndex));
|
|
6367
6433
|
visit(child, own ?? inherited);
|
|
6368
6434
|
});
|
|
6369
6435
|
};
|
|
6370
6436
|
visit(branch, branchColor);
|
|
6371
|
-
}
|
|
6437
|
+
}
|
|
6372
6438
|
return colors;
|
|
6373
6439
|
};
|
|
6374
6440
|
var SHADES = [
|
|
@@ -8332,6 +8398,7 @@ ${pageStyles(theme)}
|
|
|
8332
8398
|
<button class="page__button" type="button" data-close aria-label="${escapeAttribute(words.text("close"))}">✕</button>
|
|
8333
8399
|
</nav>
|
|
8334
8400
|
</div>
|
|
8401
|
+
<div class="page__print" id="page-print"></div>
|
|
8335
8402
|
<script type="application/json" id="orbit-page">${scriptJson(data)}</script>
|
|
8336
8403
|
<script type="application/json" id="orbit-script">${scriptJson(options.script)}</script>
|
|
8337
8404
|
<script>
|
|
@@ -8394,7 +8461,14 @@ body { font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
|
8394
8461
|
.page--presenting .page__present { display: none; }
|
|
8395
8462
|
.page--presenting:not(.page--controls) .page__controls { opacity: 0; }
|
|
8396
8463
|
.page--presenting:not(.page--controls) { cursor: none; }
|
|
8397
|
-
[hidden] { display: none !important; }
|
|
8464
|
+
[hidden] { display: none !important; }
|
|
8465
|
+
.page__print { display: none; }
|
|
8466
|
+
@media print {
|
|
8467
|
+
@page { margin: 16mm 14mm; }
|
|
8468
|
+
html, body { height: auto; background: #ffffff; }
|
|
8469
|
+
.page { display: none; }
|
|
8470
|
+
.page__print { display: block; }
|
|
8471
|
+
}`;
|
|
8398
8472
|
};
|
|
8399
8473
|
var scriptJson = (value) => {
|
|
8400
8474
|
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
@@ -8406,222 +8480,2804 @@ var escapeAttribute = (text) => {
|
|
|
8406
8480
|
return escapeText(text).replace(/"/g, """);
|
|
8407
8481
|
};
|
|
8408
8482
|
|
|
8409
|
-
//
|
|
8410
|
-
var
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
-
|
|
8431
|
-
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
-
|
|
8438
|
-
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8483
|
+
// src/core/other-apps/icons.ts
|
|
8484
|
+
var FREEMIND_ICONS = new Map(Object.entries({
|
|
8485
|
+
idea: "\uD83D\uDCA1",
|
|
8486
|
+
help: "❓",
|
|
8487
|
+
yes: "❗",
|
|
8488
|
+
messagebox_warning: "⚠️",
|
|
8489
|
+
info: "ℹ️",
|
|
8490
|
+
"stop-sign": "\uD83D\uDED1",
|
|
8491
|
+
closed: "⛔",
|
|
8492
|
+
button_ok: "✅",
|
|
8493
|
+
button_cancel: "❌",
|
|
8494
|
+
stop: "\uD83D\uDD34",
|
|
8495
|
+
prepare: "\uD83D\uDFE1",
|
|
8496
|
+
go: "\uD83D\uDFE2",
|
|
8497
|
+
back: "⬅️",
|
|
8498
|
+
forward: "➡️",
|
|
8499
|
+
up: "⬆️",
|
|
8500
|
+
down: "⬇️",
|
|
8501
|
+
attach: "\uD83D\uDCCE",
|
|
8502
|
+
ksmiletris: "\uD83D\uDE42",
|
|
8503
|
+
"smiley-neutral": "\uD83D\uDE10",
|
|
8504
|
+
"smiley-oh": "\uD83D\uDE2E",
|
|
8505
|
+
"smiley-angry": "\uD83D\uDE20",
|
|
8506
|
+
smily_bad: "\uD83D\uDE1E",
|
|
8507
|
+
clanbomber: "\uD83D\uDCA3",
|
|
8508
|
+
desktop_new: "\uD83D\uDCCC",
|
|
8509
|
+
flag: "\uD83D\uDEA9",
|
|
8510
|
+
"flag-black": "\uD83C\uDFF4",
|
|
8511
|
+
"flag-blue": "\uD83D\uDEA9",
|
|
8512
|
+
"flag-green": "\uD83D\uDEA9",
|
|
8513
|
+
"flag-orange": "\uD83D\uDEA9",
|
|
8514
|
+
"flag-pink": "\uD83D\uDEA9",
|
|
8515
|
+
"flag-yellow": "\uD83D\uDEA9",
|
|
8516
|
+
gohome: "\uD83C\uDFE0",
|
|
8517
|
+
folder: "\uD83D\uDCC1",
|
|
8518
|
+
korn: "\uD83D\uDCEB",
|
|
8519
|
+
Mail: "✉️",
|
|
8520
|
+
kmail: "\uD83D\uDCE7",
|
|
8521
|
+
list: "\uD83D\uDCCB",
|
|
8522
|
+
edit: "✏️",
|
|
8523
|
+
pencil: "✏️",
|
|
8524
|
+
kaddressbook: "☎️",
|
|
8525
|
+
knotify: "\uD83C\uDFB5",
|
|
8526
|
+
password: "\uD83D\uDD11",
|
|
8527
|
+
encrypted: "\uD83D\uDD12",
|
|
8528
|
+
decrypted: "\uD83D\uDD13",
|
|
8529
|
+
wizard: "\uD83E\uDE84",
|
|
8530
|
+
xmag: "\uD83D\uDD0D",
|
|
8531
|
+
bell: "\uD83D\uDD14",
|
|
8532
|
+
bookmark: "\uD83D\uDD16",
|
|
8533
|
+
penguin: "\uD83D\uDC27",
|
|
8534
|
+
licq: "\uD83C\uDF38",
|
|
8535
|
+
freemind_butterfly: "\uD83E\uDD8B",
|
|
8536
|
+
"broken-line": "\uD83D\uDC94",
|
|
8537
|
+
calendar: "\uD83D\uDCC5",
|
|
8538
|
+
clock: "⏰",
|
|
8539
|
+
hourglass: "⏳",
|
|
8540
|
+
launch: "\uD83D\uDE80",
|
|
8541
|
+
family: "\uD83D\uDC6A",
|
|
8542
|
+
female1: "\uD83D\uDC69",
|
|
8543
|
+
female2: "\uD83D\uDC69",
|
|
8544
|
+
male1: "\uD83D\uDC68",
|
|
8545
|
+
male2: "\uD83D\uDC68",
|
|
8546
|
+
fema: "\uD83D\uDC6B",
|
|
8547
|
+
group: "\uD83D\uDC65",
|
|
8548
|
+
bee: "\uD83D\uDC1D",
|
|
8549
|
+
mindmap: "\uD83D\uDDFA️",
|
|
8550
|
+
unchecked: "⬜",
|
|
8551
|
+
checked: "✅"
|
|
8552
|
+
}));
|
|
8553
|
+
var FREEMIND_PROGRESS = new Map(Object.entries({ "0%": "todo", "25%": "active", "50%": "active", "75%": "active", "100%": "done", checked: "done", unchecked: "todo" }));
|
|
8554
|
+
var freeMindIcon = (name) => {
|
|
8555
|
+
const priority = /^full-(\d)$/.exec(name);
|
|
8556
|
+
if (priority) {
|
|
8557
|
+
return { priority: Number(priority[1]) };
|
|
8558
|
+
}
|
|
8559
|
+
const progress = FREEMIND_PROGRESS.get(name);
|
|
8560
|
+
if (progress) {
|
|
8561
|
+
return { status: progress };
|
|
8562
|
+
}
|
|
8563
|
+
const emoji = /^emoji-([0-9A-Fa-f]{2,6}(?:-[0-9A-Fa-f]{2,6})*)$/.exec(name);
|
|
8564
|
+
if (emoji) {
|
|
8565
|
+
const codes = emoji[1].split("-").map((code) => parseInt(code, 16));
|
|
8566
|
+
if (codes.every((code) => code > 0 && code <= 1114111)) {
|
|
8567
|
+
return { icon: String.fromCodePoint(...codes) };
|
|
8568
|
+
}
|
|
8569
|
+
}
|
|
8570
|
+
const icon = FREEMIND_ICONS.get(name);
|
|
8571
|
+
return icon ? { icon } : null;
|
|
8572
|
+
};
|
|
8573
|
+
var XMIND_MARKERS = new Map(Object.entries({
|
|
8574
|
+
"smiley-smile": "\uD83D\uDE42",
|
|
8575
|
+
"smiley-laugh": "\uD83D\uDE04",
|
|
8576
|
+
"smiley-angry": "\uD83D\uDE20",
|
|
8577
|
+
"smiley-cry": "\uD83D\uDE22",
|
|
8578
|
+
"smiley-surprise": "\uD83D\uDE2E",
|
|
8579
|
+
"smiley-boring": "\uD83D\uDE10",
|
|
8580
|
+
"arrow-up": "⬆️",
|
|
8581
|
+
"arrow-down": "⬇️",
|
|
8582
|
+
"arrow-left": "⬅️",
|
|
8583
|
+
"arrow-right": "➡️",
|
|
8584
|
+
"arrow-up-right": "↗️",
|
|
8585
|
+
"arrow-down-right": "↘️",
|
|
8586
|
+
"arrow-down-left": "↙️",
|
|
8587
|
+
"arrow-up-left": "↖️",
|
|
8588
|
+
"arrow-refresh": "\uD83D\uDD04",
|
|
8589
|
+
"symbol-plus": "➕",
|
|
8590
|
+
"symbol-minus": "➖",
|
|
8591
|
+
"symbol-question": "❓",
|
|
8592
|
+
"symbol-exclam": "❗",
|
|
8593
|
+
"symbol-info": "ℹ️",
|
|
8594
|
+
"symbol-wrong": "❌",
|
|
8595
|
+
"symbol-right": "✅",
|
|
8596
|
+
"symbol-attention": "⚠️",
|
|
8597
|
+
"symbol-code": "\uD83D\uDCBB",
|
|
8598
|
+
"symbol-pin": "\uD83D\uDCCC",
|
|
8599
|
+
"symbol-heart": "❤️",
|
|
8600
|
+
"symbol-star": "⭐",
|
|
8601
|
+
"other-calendar": "\uD83D\uDCC5",
|
|
8602
|
+
"other-email": "✉️",
|
|
8603
|
+
"other-phone": "☎️",
|
|
8604
|
+
"other-question": "❓",
|
|
8605
|
+
"other-exclam": "❗",
|
|
8606
|
+
"other-lightbulb": "\uD83D\uDCA1",
|
|
8607
|
+
"other-clock": "⏰",
|
|
8608
|
+
"other-people": "\uD83D\uDC65",
|
|
8609
|
+
"other-money": "\uD83D\uDCB0",
|
|
8610
|
+
"other-books": "\uD83D\uDCDA",
|
|
8611
|
+
"other-music": "\uD83C\uDFB5",
|
|
8612
|
+
"other-yes": "\uD83D\uDC4D",
|
|
8613
|
+
"other-no": "\uD83D\uDC4E",
|
|
8614
|
+
"other-hourglass": "⏳",
|
|
8615
|
+
"other-flag": "\uD83D\uDEA9",
|
|
8616
|
+
"other-star": "⭐",
|
|
8617
|
+
"other-coffee-cup": "☕",
|
|
8618
|
+
"other-bomb": "\uD83D\uDCA3",
|
|
8619
|
+
"other-mic": "\uD83C\uDFA4",
|
|
8620
|
+
"other-phone-call": "\uD83D\uDCDE",
|
|
8621
|
+
"other-map": "\uD83D\uDDFA️",
|
|
8622
|
+
"other-code": "\uD83D\uDCBB",
|
|
8623
|
+
"other-search": "\uD83D\uDD0D",
|
|
8624
|
+
"other-dollar": "\uD83D\uDCB5",
|
|
8625
|
+
"other-heart": "❤️",
|
|
8626
|
+
c_symbol_like: "\uD83D\uDC4D",
|
|
8627
|
+
c_symbol_dislike: "\uD83D\uDC4E",
|
|
8628
|
+
c_symbol_heart: "❤️",
|
|
8629
|
+
c_symbol_star: "⭐",
|
|
8630
|
+
c_symbol_question: "❓",
|
|
8631
|
+
c_symbol_exclam: "❗",
|
|
8632
|
+
c_symbol_pin: "\uD83D\uDCCC",
|
|
8633
|
+
c_symbol_idea: "\uD83D\uDCA1",
|
|
8634
|
+
c_symbol_check: "✅",
|
|
8635
|
+
c_symbol_cross: "❌",
|
|
8636
|
+
c_symbol_fire: "\uD83D\uDD25",
|
|
8637
|
+
c_symbol_trophy: "\uD83C\uDFC6"
|
|
8638
|
+
}));
|
|
8639
|
+
var XMIND_FAMILIES = new Map(Object.entries({
|
|
8640
|
+
flag: "\uD83D\uDEA9",
|
|
8641
|
+
star: "⭐",
|
|
8642
|
+
people: "\uD83D\uDC64",
|
|
8643
|
+
tag: "\uD83C\uDFF7️",
|
|
8644
|
+
month: "\uD83D\uDCC5",
|
|
8645
|
+
week: "\uD83D\uDCC5",
|
|
8646
|
+
"half-star": "⭐"
|
|
8647
|
+
}));
|
|
8648
|
+
var XMIND_TASKS = new Map(Object.entries({
|
|
8649
|
+
"task-start": "todo",
|
|
8650
|
+
"task-oct": "active",
|
|
8651
|
+
"task-quarter": "active",
|
|
8652
|
+
"task-3oct": "active",
|
|
8653
|
+
"task-half": "active",
|
|
8654
|
+
"task-5oct": "active",
|
|
8655
|
+
"task-3quar": "active",
|
|
8656
|
+
"task-7oct": "active",
|
|
8657
|
+
"task-done": "done",
|
|
8658
|
+
"task-pause": "paused"
|
|
8659
|
+
}));
|
|
8660
|
+
var xmindMarker = (markerId) => {
|
|
8661
|
+
const priority = /^priority-(\d)$/.exec(markerId);
|
|
8662
|
+
if (priority) {
|
|
8663
|
+
return { priority: Number(priority[1]) };
|
|
8664
|
+
}
|
|
8665
|
+
const status = XMIND_TASKS.get(markerId);
|
|
8666
|
+
if (status) {
|
|
8667
|
+
return { status };
|
|
8668
|
+
}
|
|
8669
|
+
const icon = XMIND_MARKERS.get(markerId);
|
|
8670
|
+
if (icon) {
|
|
8671
|
+
return { icon };
|
|
8672
|
+
}
|
|
8673
|
+
const family = [...XMIND_FAMILIES.keys()].sort((first, second) => second.length - first.length).find((name) => markerId.startsWith(`${name}-`));
|
|
8674
|
+
return family ? { icon: XMIND_FAMILIES.get(family) } : null;
|
|
8675
|
+
};
|
|
8503
8676
|
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8677
|
+
// src/core/other-apps/markup.ts
|
|
8678
|
+
class MarkupError extends Error {
|
|
8679
|
+
line;
|
|
8680
|
+
constructor(message, line) {
|
|
8681
|
+
super(`${message} (line ${line})`);
|
|
8682
|
+
this.name = "MarkupError";
|
|
8683
|
+
this.line = line;
|
|
8684
|
+
}
|
|
8685
|
+
}
|
|
8686
|
+
var MAX_MARKUP_LENGTH = 64 * 1024 * 1024;
|
|
8687
|
+
var MAX_MARKUP_DEPTH = 1000;
|
|
8688
|
+
var XML_ENTITIES = { lt: "<", gt: ">", amp: "&", quot: '"', apos: "'" };
|
|
8689
|
+
var HTML_ENTITIES = {
|
|
8690
|
+
...XML_ENTITIES,
|
|
8691
|
+
nbsp: " ",
|
|
8692
|
+
copy: "©",
|
|
8693
|
+
reg: "®",
|
|
8694
|
+
trade: "™",
|
|
8695
|
+
hellip: "…",
|
|
8696
|
+
mdash: "—",
|
|
8697
|
+
ndash: "–",
|
|
8698
|
+
lsquo: "‘",
|
|
8699
|
+
rsquo: "’",
|
|
8700
|
+
ldquo: "“",
|
|
8701
|
+
rdquo: "”",
|
|
8702
|
+
laquo: "«",
|
|
8703
|
+
raquo: "»",
|
|
8704
|
+
bull: "•",
|
|
8705
|
+
middot: "·",
|
|
8706
|
+
deg: "°",
|
|
8707
|
+
plusmn: "±",
|
|
8708
|
+
times: "×",
|
|
8709
|
+
divide: "÷",
|
|
8710
|
+
euro: "€",
|
|
8711
|
+
pound: "£",
|
|
8712
|
+
yen: "¥",
|
|
8713
|
+
cent: "¢",
|
|
8714
|
+
sect: "§",
|
|
8715
|
+
para: "¶",
|
|
8716
|
+
iexcl: "¡",
|
|
8717
|
+
iquest: "¿",
|
|
8718
|
+
ordf: "ª",
|
|
8719
|
+
ordm: "º",
|
|
8720
|
+
acute: "´",
|
|
8721
|
+
uml: "¨",
|
|
8722
|
+
shy: "",
|
|
8723
|
+
frac12: "½",
|
|
8724
|
+
frac14: "¼",
|
|
8725
|
+
frac34: "¾",
|
|
8726
|
+
sup1: "¹",
|
|
8727
|
+
sup2: "²",
|
|
8728
|
+
sup3: "³",
|
|
8729
|
+
micro: "µ",
|
|
8730
|
+
larr: "←",
|
|
8731
|
+
rarr: "→",
|
|
8732
|
+
uarr: "↑",
|
|
8733
|
+
darr: "↓",
|
|
8734
|
+
harr: "↔",
|
|
8735
|
+
rArr: "⇒",
|
|
8736
|
+
lArr: "⇐",
|
|
8737
|
+
hArr: "⇔",
|
|
8738
|
+
aacute: "á",
|
|
8739
|
+
eacute: "é",
|
|
8740
|
+
iacute: "í",
|
|
8741
|
+
oacute: "ó",
|
|
8742
|
+
uacute: "ú",
|
|
8743
|
+
yacute: "ý",
|
|
8744
|
+
Aacute: "Á",
|
|
8745
|
+
Eacute: "É",
|
|
8746
|
+
Iacute: "Í",
|
|
8747
|
+
Oacute: "Ó",
|
|
8748
|
+
Uacute: "Ú",
|
|
8749
|
+
Yacute: "Ý",
|
|
8750
|
+
agrave: "à",
|
|
8751
|
+
egrave: "è",
|
|
8752
|
+
igrave: "ì",
|
|
8753
|
+
ograve: "ò",
|
|
8754
|
+
ugrave: "ù",
|
|
8755
|
+
Agrave: "À",
|
|
8756
|
+
Egrave: "È",
|
|
8757
|
+
Igrave: "Ì",
|
|
8758
|
+
Ograve: "Ò",
|
|
8759
|
+
Ugrave: "Ù",
|
|
8760
|
+
acirc: "â",
|
|
8761
|
+
ecirc: "ê",
|
|
8762
|
+
icirc: "î",
|
|
8763
|
+
ocirc: "ô",
|
|
8764
|
+
ucirc: "û",
|
|
8765
|
+
Acirc: "Â",
|
|
8766
|
+
Ecirc: "Ê",
|
|
8767
|
+
Icirc: "Î",
|
|
8768
|
+
Ocirc: "Ô",
|
|
8769
|
+
Ucirc: "Û",
|
|
8770
|
+
auml: "ä",
|
|
8771
|
+
euml: "ë",
|
|
8772
|
+
iuml: "ï",
|
|
8773
|
+
ouml: "ö",
|
|
8774
|
+
uuml: "ü",
|
|
8775
|
+
yuml: "ÿ",
|
|
8776
|
+
Auml: "Ä",
|
|
8777
|
+
Euml: "Ë",
|
|
8778
|
+
Iuml: "Ï",
|
|
8779
|
+
Ouml: "Ö",
|
|
8780
|
+
Uuml: "Ü",
|
|
8781
|
+
atilde: "ã",
|
|
8782
|
+
otilde: "õ",
|
|
8783
|
+
ntilde: "ñ",
|
|
8784
|
+
Atilde: "Ã",
|
|
8785
|
+
Otilde: "Õ",
|
|
8786
|
+
Ntilde: "Ñ",
|
|
8787
|
+
ccedil: "ç",
|
|
8788
|
+
Ccedil: "Ç",
|
|
8789
|
+
aring: "å",
|
|
8790
|
+
Aring: "Å",
|
|
8791
|
+
aelig: "æ",
|
|
8792
|
+
AElig: "Æ",
|
|
8793
|
+
oslash: "ø",
|
|
8794
|
+
Oslash: "Ø",
|
|
8795
|
+
szlig: "ß",
|
|
8796
|
+
thorn: "þ",
|
|
8797
|
+
THORN: "Þ",
|
|
8798
|
+
eth: "ð",
|
|
8799
|
+
ETH: "Ð",
|
|
8800
|
+
oelig: "œ",
|
|
8801
|
+
OElig: "Œ"
|
|
8802
|
+
};
|
|
8803
|
+
var VOID_ELEMENTS = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
8804
|
+
var RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
|
|
8805
|
+
var NAME = /^[^\s/>=]+/;
|
|
8806
|
+
var ATTRIBUTE = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/y;
|
|
8807
|
+
var decodeMarkup = (bytes) => {
|
|
8808
|
+
if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
|
|
8809
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(3));
|
|
8810
|
+
}
|
|
8811
|
+
if (bytes[0] === 255 && bytes[1] === 254) {
|
|
8812
|
+
return new TextDecoder("utf-16le").decode(bytes.subarray(2));
|
|
8813
|
+
}
|
|
8814
|
+
if (bytes[0] === 254 && bytes[1] === 255) {
|
|
8815
|
+
return new TextDecoder("utf-16be").decode(bytes.subarray(2));
|
|
8816
|
+
}
|
|
8817
|
+
if (bytes[0] === 60 && bytes[1] === 0) {
|
|
8818
|
+
return new TextDecoder("utf-16le").decode(bytes);
|
|
8819
|
+
}
|
|
8820
|
+
if (bytes[0] === 0 && bytes[1] === 60) {
|
|
8821
|
+
return new TextDecoder("utf-16be").decode(bytes);
|
|
8822
|
+
}
|
|
8823
|
+
const start = String.fromCharCode(...bytes.subarray(0, 200));
|
|
8824
|
+
const declared = /^\s*<\?xml[^>]*\bencoding\s*=\s*["']([A-Za-z0-9._:-]+)["']/.exec(start)?.[1];
|
|
8825
|
+
if (declared && !/^utf-?8$/i.test(declared)) {
|
|
8509
8826
|
try {
|
|
8510
|
-
|
|
8511
|
-
if (manifest.name === "@restless-dreams/orbit" && manifest.version) {
|
|
8512
|
-
return manifest.version;
|
|
8513
|
-
}
|
|
8827
|
+
return new TextDecoder(declared).decode(bytes);
|
|
8514
8828
|
} catch {}
|
|
8515
8829
|
}
|
|
8516
|
-
|
|
8830
|
+
try {
|
|
8831
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
8832
|
+
} catch {
|
|
8833
|
+
return new TextDecoder(declared ? "utf-8" : "windows-1252").decode(bytes);
|
|
8834
|
+
}
|
|
8517
8835
|
};
|
|
8518
|
-
var
|
|
8519
|
-
|
|
8836
|
+
var parseXml = (text) => {
|
|
8837
|
+
const root = readMarkup(text, { html: false });
|
|
8838
|
+
const elements = root.children.filter(isElement);
|
|
8839
|
+
if (elements.length !== 1) {
|
|
8840
|
+
throw new MarkupError(elements.length === 0 ? "No element in the file" : "More than one top element", 1);
|
|
8841
|
+
}
|
|
8842
|
+
return elements[0];
|
|
8520
8843
|
};
|
|
8521
|
-
var
|
|
8522
|
-
return
|
|
8844
|
+
var parseHtml = (text) => {
|
|
8845
|
+
return readMarkup(text, { html: true });
|
|
8523
8846
|
};
|
|
8524
|
-
var
|
|
8525
|
-
return
|
|
8847
|
+
var isElement = (node) => {
|
|
8848
|
+
return typeof node !== "string";
|
|
8526
8849
|
};
|
|
8527
|
-
var
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8850
|
+
var childElements = (element, localName) => {
|
|
8851
|
+
return element.children.filter((child) => isElement(child) && (localName === undefined || child.localName === localName));
|
|
8852
|
+
};
|
|
8853
|
+
var childElement = (element, localName) => {
|
|
8854
|
+
return element.children.find((child) => isElement(child) && child.localName === localName);
|
|
8855
|
+
};
|
|
8856
|
+
var attributeOf = (element, localName) => {
|
|
8857
|
+
if (Object.prototype.hasOwnProperty.call(element.attributes, localName)) {
|
|
8858
|
+
return element.attributes[localName];
|
|
8532
8859
|
}
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8860
|
+
for (const [name, value] of Object.entries(element.attributes)) {
|
|
8861
|
+
if (localPart(name) === localName) {
|
|
8862
|
+
return value;
|
|
8863
|
+
}
|
|
8537
8864
|
}
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
}
|
|
8865
|
+
return;
|
|
8866
|
+
};
|
|
8867
|
+
var textContent = (node) => {
|
|
8868
|
+
if (!isElement(node)) {
|
|
8869
|
+
return node;
|
|
8870
|
+
}
|
|
8871
|
+
let text = "";
|
|
8872
|
+
const pending = [node];
|
|
8873
|
+
while (pending.length > 0) {
|
|
8874
|
+
const next = pending.pop();
|
|
8875
|
+
if (isElement(next)) {
|
|
8876
|
+
for (let index = next.children.length - 1;index >= 0; index--) {
|
|
8877
|
+
pending.push(next.children[index]);
|
|
8552
8878
|
}
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8879
|
+
} else {
|
|
8880
|
+
text += next;
|
|
8881
|
+
}
|
|
8882
|
+
}
|
|
8883
|
+
return text;
|
|
8884
|
+
};
|
|
8885
|
+
var localPart = (name) => {
|
|
8886
|
+
const colon = name.indexOf(":");
|
|
8887
|
+
return colon === -1 ? name : name.slice(colon + 1);
|
|
8888
|
+
};
|
|
8889
|
+
var lineAt = (text, index) => {
|
|
8890
|
+
let line = 1;
|
|
8891
|
+
for (let position = text.indexOf(`
|
|
8892
|
+
`);position !== -1 && position < index; position = text.indexOf(`
|
|
8893
|
+
`, position + 1)) {
|
|
8894
|
+
line += 1;
|
|
8895
|
+
}
|
|
8896
|
+
return line;
|
|
8897
|
+
};
|
|
8898
|
+
var readMarkup = (input, mode) => {
|
|
8899
|
+
if (input.length > MAX_MARKUP_LENGTH) {
|
|
8900
|
+
throw new MarkupError(`The file is larger than ${MAX_MARKUP_LENGTH / 1024 / 1024} MB of text`, 1);
|
|
8901
|
+
}
|
|
8902
|
+
const text = input.replace(/^\ufeff/, "").replace(/\r\n?/g, `
|
|
8903
|
+
`);
|
|
8904
|
+
const fragment = { name: "#fragment", localName: "#fragment", attributes: {}, children: [] };
|
|
8905
|
+
const open = [fragment];
|
|
8906
|
+
const entities = mode.html ? HTML_ENTITIES : XML_ENTITIES;
|
|
8907
|
+
let index = 0;
|
|
8908
|
+
const fail = (message, at) => {
|
|
8909
|
+
throw new MarkupError(message, lineAt(text, at));
|
|
8910
|
+
};
|
|
8911
|
+
const current = () => open[open.length - 1];
|
|
8912
|
+
const addText = (value) => {
|
|
8913
|
+
if (value === "") {
|
|
8914
|
+
return;
|
|
8915
|
+
}
|
|
8916
|
+
const children = current().children;
|
|
8917
|
+
const last = children.length - 1;
|
|
8918
|
+
if (last >= 0 && typeof children[last] === "string") {
|
|
8919
|
+
children[last] = children[last] + value;
|
|
8920
|
+
} else {
|
|
8921
|
+
children.push(value);
|
|
8922
|
+
}
|
|
8923
|
+
};
|
|
8924
|
+
while (index < text.length) {
|
|
8925
|
+
const tag = text.indexOf("<", index);
|
|
8926
|
+
if (tag === -1) {
|
|
8927
|
+
addText(decodeEntities(text.slice(index), entities));
|
|
8928
|
+
break;
|
|
8929
|
+
}
|
|
8930
|
+
if (tag > index) {
|
|
8931
|
+
addText(decodeEntities(text.slice(index, tag), entities));
|
|
8932
|
+
}
|
|
8933
|
+
index = tag;
|
|
8934
|
+
if (text.startsWith("<!--", index)) {
|
|
8935
|
+
const end = text.indexOf("-->", index + 4);
|
|
8936
|
+
if (end === -1) {
|
|
8937
|
+
if (mode.html) {
|
|
8938
|
+
break;
|
|
8939
|
+
}
|
|
8940
|
+
fail("A comment that never ends", index);
|
|
8557
8941
|
}
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
process.stdout.write(/^(md|markdown)$/.test(to) ? serializeOrbitMarkdown(doc, { keepSource: false }) : serializeOrbitYAML(doc));
|
|
8561
|
-
return 0;
|
|
8942
|
+
index = end + 3;
|
|
8943
|
+
continue;
|
|
8562
8944
|
}
|
|
8563
|
-
if (
|
|
8564
|
-
const
|
|
8565
|
-
if (
|
|
8566
|
-
|
|
8567
|
-
${USAGE}`);
|
|
8568
|
-
return 2;
|
|
8945
|
+
if (text.startsWith("<![CDATA[", index)) {
|
|
8946
|
+
const end = text.indexOf("]]>", index + 9);
|
|
8947
|
+
if (end === -1) {
|
|
8948
|
+
fail("A CDATA section that never ends", index);
|
|
8569
8949
|
}
|
|
8570
|
-
|
|
8571
|
-
|
|
8950
|
+
addText(text.slice(index + 9, end));
|
|
8951
|
+
index = end + 3;
|
|
8952
|
+
continue;
|
|
8572
8953
|
}
|
|
8573
|
-
if (
|
|
8574
|
-
|
|
8954
|
+
if (text.startsWith("<?", index)) {
|
|
8955
|
+
const end = text.indexOf("?>", index + 2);
|
|
8956
|
+
if (end === -1) {
|
|
8957
|
+
fail("A processing instruction that never ends", index);
|
|
8958
|
+
}
|
|
8959
|
+
index = end + 2;
|
|
8960
|
+
continue;
|
|
8575
8961
|
}
|
|
8576
|
-
if (
|
|
8577
|
-
|
|
8962
|
+
if (text.startsWith("<!", index)) {
|
|
8963
|
+
index = skipDeclaration(text, index, fail);
|
|
8964
|
+
continue;
|
|
8578
8965
|
}
|
|
8579
|
-
if (
|
|
8580
|
-
|
|
8966
|
+
if (text.startsWith("</", index)) {
|
|
8967
|
+
const end = text.indexOf(">", index + 2);
|
|
8968
|
+
if (end === -1) {
|
|
8969
|
+
fail("An end tag that never closes", index);
|
|
8970
|
+
}
|
|
8971
|
+
const name = text.slice(index + 2, end).trim();
|
|
8972
|
+
closeElement(open, name, mode, () => fail(`</${name}> doesn't match <${current().name}>`, index));
|
|
8973
|
+
index = end + 1;
|
|
8974
|
+
continue;
|
|
8581
8975
|
}
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
return 1;
|
|
8589
|
-
}
|
|
8590
|
-
};
|
|
8591
|
-
var readOptions = (command, argumentsList, names) => {
|
|
8592
|
-
const values = {};
|
|
8593
|
-
let file = "";
|
|
8594
|
-
for (let index = 0;index < argumentsList.length; index++) {
|
|
8595
|
-
const argument = argumentsList[index];
|
|
8596
|
-
const name = argument.startsWith("--") ? argument.slice(2) : "";
|
|
8597
|
-
if (names.includes(name)) {
|
|
8598
|
-
const value = argumentsList[++index];
|
|
8599
|
-
if (value === undefined) {
|
|
8600
|
-
process.stderr.write(`orbit ${command}: ${argument} needs a value
|
|
8601
|
-
`);
|
|
8602
|
-
return 2;
|
|
8976
|
+
const nameMatch = NAME.exec(text.slice(index + 1, index + 257));
|
|
8977
|
+
if (!nameMatch || !/^[\p{L}_:]/u.test(nameMatch[0])) {
|
|
8978
|
+
if (mode.html) {
|
|
8979
|
+
addText("<");
|
|
8980
|
+
index += 1;
|
|
8981
|
+
continue;
|
|
8603
8982
|
}
|
|
8604
|
-
|
|
8605
|
-
}
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8983
|
+
fail('A "<" that starts no tag', index);
|
|
8984
|
+
}
|
|
8985
|
+
const name = nameMatch[0];
|
|
8986
|
+
const [attributes, afterAttributes, selfClosing] = readAttributes(text, index + 1 + name.length, entities, mode, fail);
|
|
8987
|
+
const localName = mode.html ? localPart(name).toLowerCase() : localPart(name);
|
|
8988
|
+
const element = { name, localName, attributes, children: [] };
|
|
8989
|
+
current().children.push(element);
|
|
8990
|
+
index = afterAttributes;
|
|
8991
|
+
if (mode.html && RAW_TEXT_ELEMENTS.has(localName)) {
|
|
8992
|
+
const end = text.toLowerCase().indexOf(`</${localName}`, index);
|
|
8993
|
+
index = end === -1 ? text.length : text.indexOf(">", end) + 1 || text.length;
|
|
8994
|
+
continue;
|
|
8995
|
+
}
|
|
8996
|
+
if (selfClosing || mode.html && VOID_ELEMENTS.has(localName)) {
|
|
8997
|
+
continue;
|
|
8998
|
+
}
|
|
8999
|
+
if (open.length > MAX_MARKUP_DEPTH) {
|
|
9000
|
+
fail(`Elements nested more than ${MAX_MARKUP_DEPTH} deep`, index);
|
|
8611
9001
|
}
|
|
9002
|
+
open.push(element);
|
|
8612
9003
|
}
|
|
8613
|
-
if (!
|
|
8614
|
-
|
|
8615
|
-
${USAGE}`);
|
|
8616
|
-
return 2;
|
|
9004
|
+
if (!mode.html && open.length > 1) {
|
|
9005
|
+
fail(`<${current().name}> is never closed`, text.length);
|
|
8617
9006
|
}
|
|
8618
|
-
return
|
|
9007
|
+
return fragment;
|
|
8619
9008
|
};
|
|
8620
|
-
var
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
9009
|
+
var readAttributes = (text, start, entities, mode, fail) => {
|
|
9010
|
+
const attributes = {};
|
|
9011
|
+
let index = start;
|
|
9012
|
+
while (index < text.length) {
|
|
9013
|
+
while (index < text.length && /\s/.test(text[index])) {
|
|
9014
|
+
index += 1;
|
|
9015
|
+
}
|
|
9016
|
+
if (text[index] === ">") {
|
|
9017
|
+
return [attributes, index + 1, false];
|
|
9018
|
+
}
|
|
9019
|
+
if (text.startsWith("/>", index)) {
|
|
9020
|
+
return [attributes, index + 2, true];
|
|
9021
|
+
}
|
|
9022
|
+
if (text[index] === "/") {
|
|
9023
|
+
index += 1;
|
|
9024
|
+
continue;
|
|
9025
|
+
}
|
|
9026
|
+
ATTRIBUTE.lastIndex = index;
|
|
9027
|
+
const match = ATTRIBUTE.exec(text);
|
|
9028
|
+
if (!match || match[0].length === 0) {
|
|
9029
|
+
fail("A tag that never closes", start);
|
|
9030
|
+
}
|
|
9031
|
+
const [whole, name, doubleQuoted, singleQuoted, unquoted] = match;
|
|
9032
|
+
if (unquoted !== undefined && !mode.html) {
|
|
9033
|
+
fail(`The value of ${name} has no quotes`, index);
|
|
9034
|
+
}
|
|
9035
|
+
const raw = doubleQuoted ?? singleQuoted ?? unquoted ?? "";
|
|
9036
|
+
attributes[name] = decodeEntities(mode.html ? raw : raw.replace(/[\t\n]/g, " "), entities);
|
|
9037
|
+
index += whole.length;
|
|
9038
|
+
}
|
|
9039
|
+
return fail("A tag that never closes", start);
|
|
9040
|
+
};
|
|
9041
|
+
var skipDeclaration = (text, start, fail) => {
|
|
9042
|
+
let depth = 0;
|
|
9043
|
+
let quote = null;
|
|
9044
|
+
for (let index = start + 2;index < text.length; index++) {
|
|
9045
|
+
const character = text[index];
|
|
9046
|
+
if (quote) {
|
|
9047
|
+
if (character === quote) {
|
|
9048
|
+
quote = null;
|
|
9049
|
+
}
|
|
9050
|
+
} else if (character === '"' || character === "'") {
|
|
9051
|
+
quote = character;
|
|
9052
|
+
} else if (character === "[") {
|
|
9053
|
+
depth += 1;
|
|
9054
|
+
} else if (character === "]") {
|
|
9055
|
+
depth -= 1;
|
|
9056
|
+
} else if (character === ">" && depth <= 0) {
|
|
9057
|
+
return index + 1;
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
return fail("A declaration that never ends", start);
|
|
9061
|
+
};
|
|
9062
|
+
var closeElement = (open, name, mode, mismatch) => {
|
|
9063
|
+
const top = open[open.length - 1];
|
|
9064
|
+
if (open.length > 1 && (top.name === name || mode.html && top.localName === localPart(name).toLowerCase())) {
|
|
9065
|
+
open.pop();
|
|
9066
|
+
return;
|
|
9067
|
+
}
|
|
9068
|
+
if (!mode.html) {
|
|
9069
|
+
mismatch();
|
|
9070
|
+
}
|
|
9071
|
+
const wanted = localPart(name).toLowerCase();
|
|
9072
|
+
for (let depth = open.length - 1;depth > 0; depth--) {
|
|
9073
|
+
if (open[depth].localName === wanted) {
|
|
9074
|
+
open.length = depth;
|
|
9075
|
+
return;
|
|
9076
|
+
}
|
|
9077
|
+
}
|
|
9078
|
+
};
|
|
9079
|
+
var decodeEntities = (text, entities) => {
|
|
9080
|
+
if (!text.includes("&")) {
|
|
9081
|
+
return text;
|
|
9082
|
+
}
|
|
9083
|
+
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) => {
|
|
9084
|
+
if (reference.startsWith("#")) {
|
|
9085
|
+
const code = reference[1] === "x" || reference[1] === "X" ? parseInt(reference.slice(2), 16) : parseInt(reference.slice(1), 10);
|
|
9086
|
+
return code > 0 && code <= 1114111 && !(code >= 55296 && code <= 57343) ? String.fromCodePoint(code) : whole;
|
|
9087
|
+
}
|
|
9088
|
+
return Object.prototype.hasOwnProperty.call(entities, reference) ? entities[reference] : whole;
|
|
9089
|
+
});
|
|
9090
|
+
};
|
|
9091
|
+
|
|
9092
|
+
// src/core/other-apps/notes.ts
|
|
9093
|
+
var BLOCK_CONTAINERS = new Set([
|
|
9094
|
+
"html",
|
|
9095
|
+
"body",
|
|
9096
|
+
"div",
|
|
9097
|
+
"p",
|
|
9098
|
+
"section",
|
|
9099
|
+
"article",
|
|
9100
|
+
"header",
|
|
9101
|
+
"footer",
|
|
9102
|
+
"main",
|
|
9103
|
+
"aside",
|
|
9104
|
+
"nav",
|
|
9105
|
+
"center",
|
|
9106
|
+
"figure",
|
|
9107
|
+
"figcaption",
|
|
9108
|
+
"address",
|
|
9109
|
+
"details",
|
|
9110
|
+
"summary",
|
|
9111
|
+
"dl",
|
|
9112
|
+
"dt",
|
|
9113
|
+
"dd",
|
|
9114
|
+
"form",
|
|
9115
|
+
"fieldset",
|
|
9116
|
+
"tbody",
|
|
9117
|
+
"thead",
|
|
9118
|
+
"tfoot",
|
|
9119
|
+
"caption",
|
|
9120
|
+
"#fragment"
|
|
9121
|
+
]);
|
|
9122
|
+
var SKIPPED = new Set(["head", "title", "script", "style", "meta", "link", "template", "object", "iframe", "svg", "math"]);
|
|
9123
|
+
var HEADINGS = new Map([["h1", 1], ["h2", 2], ["h3", 3], ["h4", 4], ["h5", 5], ["h6", 6]]);
|
|
9124
|
+
var INLINE_KINDS = new Map([
|
|
9125
|
+
["strong", "strong"],
|
|
9126
|
+
["b", "strong"],
|
|
9127
|
+
["em", "emphasis"],
|
|
9128
|
+
["i", "emphasis"],
|
|
9129
|
+
["cite", "emphasis"],
|
|
9130
|
+
["s", "strike"],
|
|
9131
|
+
["strike", "strike"],
|
|
9132
|
+
["del", "strike"]
|
|
9133
|
+
]);
|
|
9134
|
+
var CODE_ELEMENTS = new Set(["code", "tt", "kbd", "samp", "var"]);
|
|
9135
|
+
var isKeptLink = (href) => {
|
|
9136
|
+
return /^(https?:\/\/|mailto:)\S+$/i.test(href) || /^#\S+$/.test(href);
|
|
9137
|
+
};
|
|
9138
|
+
var keptAddress = (href) => {
|
|
9139
|
+
const address = href.trim();
|
|
9140
|
+
if (/^www\.[^\s/]+\.\S+$/i.test(address)) {
|
|
9141
|
+
return `https://${address}`;
|
|
9142
|
+
}
|
|
9143
|
+
return isKeptLink(address) ? address : null;
|
|
9144
|
+
};
|
|
9145
|
+
var isWebAddress = (src) => {
|
|
9146
|
+
return /^https?:\/\/\S+$/i.test(src);
|
|
9147
|
+
};
|
|
9148
|
+
var noteFromHtml = (html, addresses = {}) => {
|
|
9149
|
+
const element = typeof html === "string" ? parseHtml(html) : html;
|
|
9150
|
+
return noteFromBlocks(readBlocks(element.children, addresses), addresses);
|
|
9151
|
+
};
|
|
9152
|
+
var noteFromPlainText = (text) => {
|
|
9153
|
+
const cleaned = (text ?? "").replace(/\r\n?/g, `
|
|
9154
|
+
`).split(`
|
|
9155
|
+
`).map((line) => line.replace(/[ \t]+$/, "")).join(`
|
|
9156
|
+
`).replace(/^\n+|\n+$/g, "");
|
|
9157
|
+
if (cleaned.trim() === "") {
|
|
9158
|
+
return;
|
|
9159
|
+
}
|
|
9160
|
+
const paragraphs = cleaned.split(/\n{2,}/);
|
|
9161
|
+
const readsTheSame = paragraphs.every((paragraph) => !paragraph.includes(`
|
|
9162
|
+
`) && isInertText(paragraph) && isInertLineStart(paragraph));
|
|
9163
|
+
return { text: cleaned, format: readsTheSame ? "markdown" : "plain" };
|
|
9164
|
+
};
|
|
9165
|
+
var noteFromBlocks = (blocks, addresses = {}) => {
|
|
9166
|
+
const kept = blocks.filter((block) => !isEmptyBlock(block));
|
|
9167
|
+
if (kept.length === 0) {
|
|
9168
|
+
return;
|
|
9169
|
+
}
|
|
9170
|
+
const writer = new MarkdownWriter(addresses);
|
|
9171
|
+
const markdown = writer.blocks(kept);
|
|
9172
|
+
if (writer.inert && markdown.trim() !== "") {
|
|
9173
|
+
return { text: markdown, format: "markdown" };
|
|
9174
|
+
}
|
|
9175
|
+
const plain = plainBlocks(kept).trim();
|
|
9176
|
+
return plain === "" ? undefined : { text: plain, format: "plain" };
|
|
9177
|
+
};
|
|
9178
|
+
var joinNotes = (notes) => {
|
|
9179
|
+
const present = notes.filter((note) => note !== undefined && note.text.trim() !== "");
|
|
9180
|
+
if (present.length <= 1) {
|
|
9181
|
+
return present[0];
|
|
9182
|
+
}
|
|
9183
|
+
return {
|
|
9184
|
+
text: present.map((note) => note.text).join(`
|
|
9185
|
+
|
|
9186
|
+
`),
|
|
9187
|
+
format: present.every((note) => note.format === "markdown") ? "markdown" : "plain"
|
|
9188
|
+
};
|
|
9189
|
+
};
|
|
9190
|
+
var titleFromHtml = (html) => {
|
|
9191
|
+
const element = typeof html === "string" ? parseHtml(html) : html;
|
|
9192
|
+
const pieces = inlineFrom(element.children, {});
|
|
9193
|
+
const link = firstLink(pieces);
|
|
9194
|
+
return { text: plainInline(pieces, false, false).replace(/\s+/g, " ").trim(), ...link ? { link } : {} };
|
|
9195
|
+
};
|
|
9196
|
+
var firstLink = (pieces) => {
|
|
9197
|
+
for (const piece of pieces) {
|
|
9198
|
+
if (piece.kind === "link") {
|
|
9199
|
+
return piece.href;
|
|
9200
|
+
}
|
|
9201
|
+
if (piece.kind === "strong" || piece.kind === "emphasis" || piece.kind === "strike") {
|
|
9202
|
+
const inner = firstLink(piece.children);
|
|
9203
|
+
if (inner) {
|
|
9204
|
+
return inner;
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
return;
|
|
9209
|
+
};
|
|
9210
|
+
var readBlocks = (nodes, addresses) => {
|
|
9211
|
+
const blocks = [];
|
|
9212
|
+
let inline = [];
|
|
9213
|
+
const flush = () => {
|
|
9214
|
+
const trimmed = trimInline(inline);
|
|
9215
|
+
if (trimmed.length > 0) {
|
|
9216
|
+
blocks.push({ kind: "paragraph", inline: trimmed });
|
|
9217
|
+
}
|
|
9218
|
+
inline = [];
|
|
9219
|
+
};
|
|
9220
|
+
for (const node of nodes) {
|
|
9221
|
+
if (!isElement(node)) {
|
|
9222
|
+
inline.push(...inlineFrom([node], addresses));
|
|
9223
|
+
continue;
|
|
9224
|
+
}
|
|
9225
|
+
const name = node.localName.toLowerCase();
|
|
9226
|
+
if (SKIPPED.has(name)) {
|
|
9227
|
+
continue;
|
|
9228
|
+
}
|
|
9229
|
+
const headingLevel = HEADINGS.get(name);
|
|
9230
|
+
if (headingLevel !== undefined) {
|
|
9231
|
+
flush();
|
|
9232
|
+
const content = trimInline(inlineFrom(node.children, addresses).map((piece) => piece.kind === "break" ? { kind: "text", text: " " } : piece));
|
|
9233
|
+
if (content.length > 0) {
|
|
9234
|
+
blocks.push({ kind: "heading", level: headingLevel, inline: content });
|
|
9235
|
+
}
|
|
9236
|
+
} else if (name === "ul" || name === "ol" || name === "menu" || name === "dir") {
|
|
9237
|
+
flush();
|
|
9238
|
+
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)));
|
|
9239
|
+
if (items.length > 0) {
|
|
9240
|
+
blocks.push({ kind: "list", ordered: name === "ol", items });
|
|
9241
|
+
}
|
|
9242
|
+
} else if (name === "li") {
|
|
9243
|
+
flush();
|
|
9244
|
+
blocks.push({ kind: "list", ordered: false, items: [readBlocks(node.children, addresses)] });
|
|
9245
|
+
} else if (name === "blockquote") {
|
|
9246
|
+
flush();
|
|
9247
|
+
const quoted = readBlocks(node.children, addresses);
|
|
9248
|
+
if (quoted.length > 0) {
|
|
9249
|
+
blocks.push({ kind: "quote", blocks: quoted });
|
|
9250
|
+
}
|
|
9251
|
+
} else if (name === "pre") {
|
|
9252
|
+
flush();
|
|
9253
|
+
const code = textContent(node).replace(/^\n/, "").replace(/\s+$/, "");
|
|
9254
|
+
if (code !== "") {
|
|
9255
|
+
blocks.push({ kind: "code", text: code });
|
|
9256
|
+
}
|
|
9257
|
+
} else if (name === "table") {
|
|
9258
|
+
flush();
|
|
9259
|
+
blocks.push(...tableRows(node, addresses));
|
|
9260
|
+
} else if (name === "hr") {
|
|
9261
|
+
flush();
|
|
9262
|
+
} else if (BLOCK_CONTAINERS.has(name)) {
|
|
9263
|
+
flush();
|
|
9264
|
+
blocks.push(...readBlocks(node.children, addresses));
|
|
9265
|
+
} else {
|
|
9266
|
+
inline.push(...inlineFrom([node], addresses));
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
flush();
|
|
9270
|
+
return blocks;
|
|
9271
|
+
};
|
|
9272
|
+
var tableRows = (table, addresses) => {
|
|
9273
|
+
const rows = [];
|
|
9274
|
+
const pending = [table];
|
|
9275
|
+
while (pending.length > 0) {
|
|
9276
|
+
const element = pending.shift();
|
|
9277
|
+
for (const child of childElements(element)) {
|
|
9278
|
+
const name = child.localName.toLowerCase();
|
|
9279
|
+
if (name === "tr") {
|
|
9280
|
+
const cells = childElements(child).filter((cell) => /^t[dh]$/.test(cell.localName.toLowerCase()));
|
|
9281
|
+
const inline = [];
|
|
9282
|
+
for (const cell of cells) {
|
|
9283
|
+
const content = trimInline(inlineFrom(cell.children, addresses).map((piece) => piece.kind === "break" ? { kind: "text", text: " " } : piece));
|
|
9284
|
+
if (content.length > 0) {
|
|
9285
|
+
if (inline.length > 0) {
|
|
9286
|
+
inline.push({ kind: "text", text: " · " });
|
|
9287
|
+
}
|
|
9288
|
+
inline.push(...content);
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
if (inline.length > 0) {
|
|
9292
|
+
rows.push({ kind: "paragraph", inline });
|
|
9293
|
+
}
|
|
9294
|
+
} else if (name !== "td" && name !== "th") {
|
|
9295
|
+
pending.push(child);
|
|
9296
|
+
}
|
|
9297
|
+
}
|
|
9298
|
+
}
|
|
9299
|
+
return rows;
|
|
9300
|
+
};
|
|
9301
|
+
var inlineFrom = (nodes, addresses) => {
|
|
9302
|
+
const pieces = [];
|
|
9303
|
+
for (const node of nodes) {
|
|
9304
|
+
if (!isElement(node)) {
|
|
9305
|
+
const text = node.replace(/[ \t\n\r\f]+/g, " ");
|
|
9306
|
+
if (text !== "") {
|
|
9307
|
+
pieces.push({ kind: "text", text });
|
|
9308
|
+
}
|
|
9309
|
+
continue;
|
|
9310
|
+
}
|
|
9311
|
+
const name = node.localName.toLowerCase();
|
|
9312
|
+
if (SKIPPED.has(name)) {
|
|
9313
|
+
continue;
|
|
9314
|
+
}
|
|
9315
|
+
if (name === "br") {
|
|
9316
|
+
pieces.push({ kind: "break" });
|
|
9317
|
+
} else if (name === "img") {
|
|
9318
|
+
const src = node.attributes.src ?? Object.entries(node.attributes).find(([key]) => key.endsWith(":src"))?.[1] ?? "";
|
|
9319
|
+
pieces.push({ kind: "image", src, alt: (node.attributes.alt ?? node.attributes.title ?? "").trim() });
|
|
9320
|
+
} else if (INLINE_KINDS.has(name)) {
|
|
9321
|
+
pieces.push({ kind: INLINE_KINDS.get(name), children: inlineFrom(node.children, addresses) });
|
|
9322
|
+
} else if (CODE_ELEMENTS.has(name)) {
|
|
9323
|
+
pieces.push({ kind: "code", text: textContent(node).replace(/\s+/g, " ") });
|
|
9324
|
+
} else if (name === "a") {
|
|
9325
|
+
const href = (node.attributes.href ?? Object.entries(node.attributes).find(([key]) => key.endsWith(":href"))?.[1] ?? "").trim();
|
|
9326
|
+
const children = inlineFrom(node.children, addresses);
|
|
9327
|
+
if (href) {
|
|
9328
|
+
pieces.push({ kind: "link", href, children });
|
|
9329
|
+
} else {
|
|
9330
|
+
pieces.push(...children);
|
|
9331
|
+
}
|
|
9332
|
+
} else {
|
|
9333
|
+
const block = BLOCK_CONTAINERS.has(name) || HEADINGS.has(name) || name === "li" || name === "tr";
|
|
9334
|
+
const content = inlineFrom(node.children, addresses);
|
|
9335
|
+
if (block && pieces.length > 0) {
|
|
9336
|
+
pieces.push({ kind: "break" });
|
|
9337
|
+
}
|
|
9338
|
+
pieces.push(...content);
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
return pieces;
|
|
9342
|
+
};
|
|
9343
|
+
var trimInline = (pieces) => {
|
|
9344
|
+
const result = [...pieces];
|
|
9345
|
+
while (result.length > 0 && isBlankPiece(result[0])) {
|
|
9346
|
+
result.shift();
|
|
9347
|
+
}
|
|
9348
|
+
while (result.length > 0 && isBlankPiece(result[result.length - 1])) {
|
|
9349
|
+
result.pop();
|
|
9350
|
+
}
|
|
9351
|
+
if (result.length > 0 && result[0].kind === "text") {
|
|
9352
|
+
result[0] = { kind: "text", text: result[0].text.replace(/^ +/, "") };
|
|
9353
|
+
}
|
|
9354
|
+
const last = result.length - 1;
|
|
9355
|
+
if (last >= 0 && result[last].kind === "text") {
|
|
9356
|
+
result[last] = { kind: "text", text: result[last].text.replace(/ +$/, "") };
|
|
9357
|
+
}
|
|
9358
|
+
return result;
|
|
9359
|
+
};
|
|
9360
|
+
var isBlankPiece = (piece) => {
|
|
9361
|
+
return piece.kind === "break" || piece.kind === "text" && piece.text.trim() === "";
|
|
9362
|
+
};
|
|
9363
|
+
var isEmptyBlock = (block) => {
|
|
9364
|
+
switch (block.kind) {
|
|
9365
|
+
case "paragraph":
|
|
9366
|
+
case "heading":
|
|
9367
|
+
return plainInline(block.inline, false).trim() === "" && !block.inline.some((piece) => piece.kind === "image" && piece.src !== "");
|
|
9368
|
+
case "list":
|
|
9369
|
+
return block.items.every((item) => item.every(isEmptyBlock));
|
|
9370
|
+
case "quote":
|
|
9371
|
+
return block.blocks.every(isEmptyBlock);
|
|
9372
|
+
case "code":
|
|
9373
|
+
return block.text.trim() === "";
|
|
9374
|
+
}
|
|
9375
|
+
};
|
|
9376
|
+
var ACTIVE_CHARACTERS = /[`*_~[\]<\\]|&#?[A-Za-z0-9]+;/;
|
|
9377
|
+
var ACTIVE_LINE_START = /^(\s{4,}|\s*(#{1,6}(\s|$)|>|[-*+](\s|$)|\d{1,9}[.)](\s|$)|```|~~~|[-=_]{3,}\s*$))/;
|
|
9378
|
+
var isInertText = (text) => {
|
|
9379
|
+
return !ACTIVE_CHARACTERS.test(text);
|
|
9380
|
+
};
|
|
9381
|
+
var isInertLineStart = (line) => {
|
|
9382
|
+
return !ACTIVE_LINE_START.test(line);
|
|
9383
|
+
};
|
|
9384
|
+
|
|
9385
|
+
class MarkdownWriter {
|
|
9386
|
+
inert = true;
|
|
9387
|
+
addresses;
|
|
9388
|
+
constructor(addresses) {
|
|
9389
|
+
this.addresses = addresses;
|
|
9390
|
+
}
|
|
9391
|
+
blocks(blocks) {
|
|
9392
|
+
return blocks.map((block) => this.block(block)).filter((text) => text !== "").join(`
|
|
9393
|
+
|
|
9394
|
+
`);
|
|
9395
|
+
}
|
|
9396
|
+
block(block) {
|
|
9397
|
+
switch (block.kind) {
|
|
9398
|
+
case "paragraph": {
|
|
9399
|
+
const lines = this.inlineLines(block.inline);
|
|
9400
|
+
return lines.map((line) => this.lineStart(line)).filter((line) => line !== "").join(`
|
|
9401
|
+
|
|
9402
|
+
`);
|
|
9403
|
+
}
|
|
9404
|
+
case "heading": {
|
|
9405
|
+
const text = this.inlineLines(block.inline).join(" ").trim();
|
|
9406
|
+
return text === "" ? "" : `${"#".repeat(block.level)} ${text}`;
|
|
9407
|
+
}
|
|
9408
|
+
case "list":
|
|
9409
|
+
return this.list(block, 0);
|
|
9410
|
+
case "quote": {
|
|
9411
|
+
const inner = this.blocks(block.blocks);
|
|
9412
|
+
return inner.split(`
|
|
9413
|
+
`).map((line) => line === "" ? ">" : `> ${line}`).join(`
|
|
9414
|
+
`);
|
|
9415
|
+
}
|
|
9416
|
+
case "code": {
|
|
9417
|
+
if (/^\s*(```|~~~)/m.test(block.text)) {
|
|
9418
|
+
this.inert = false;
|
|
9419
|
+
}
|
|
9420
|
+
return `\`\`\`
|
|
9421
|
+
${block.text}
|
|
9422
|
+
\`\`\``;
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
list(list, indent) {
|
|
9427
|
+
const lines = [];
|
|
9428
|
+
list.items.forEach((item, index) => {
|
|
9429
|
+
const marker = list.ordered ? `${index + 1}.` : "-";
|
|
9430
|
+
const nested = [];
|
|
9431
|
+
const words = [];
|
|
9432
|
+
for (const block of item) {
|
|
9433
|
+
if (block.kind === "list") {
|
|
9434
|
+
nested.push(this.list(block, indent + marker.length + 1));
|
|
9435
|
+
} else {
|
|
9436
|
+
words.push(this.flatText(block));
|
|
9437
|
+
}
|
|
9438
|
+
}
|
|
9439
|
+
const text = words.filter((word) => word !== "").join(" ");
|
|
9440
|
+
lines.push(`${" ".repeat(indent)}${marker} ${text}`.trimEnd());
|
|
9441
|
+
lines.push(...nested);
|
|
9442
|
+
});
|
|
9443
|
+
return lines.join(`
|
|
9444
|
+
`);
|
|
9445
|
+
}
|
|
9446
|
+
flatText(block) {
|
|
9447
|
+
switch (block.kind) {
|
|
9448
|
+
case "paragraph":
|
|
9449
|
+
case "heading":
|
|
9450
|
+
return this.inlineLines(block.inline).join(" ").trim();
|
|
9451
|
+
case "quote":
|
|
9452
|
+
return block.blocks.map((inner) => this.flatText(inner)).join(" ");
|
|
9453
|
+
case "code":
|
|
9454
|
+
return this.code(block.text.replace(/\s+/g, " "));
|
|
9455
|
+
case "list":
|
|
9456
|
+
return this.list(block, 0);
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
9459
|
+
lineStart(line) {
|
|
9460
|
+
if (!isInertLineStart(line)) {
|
|
9461
|
+
this.inert = false;
|
|
9462
|
+
}
|
|
9463
|
+
return line.trim();
|
|
9464
|
+
}
|
|
9465
|
+
inlineLines(pieces) {
|
|
9466
|
+
return this.inlineText(pieces).split(`
|
|
9467
|
+
`).map((line) => line.replace(/ {2,}/g, " ").trim());
|
|
9468
|
+
}
|
|
9469
|
+
inlineText(pieces) {
|
|
9470
|
+
return mergeNeighbors(pieces).map((piece) => piece.kind === "break" ? `
|
|
9471
|
+
` : this.inline(piece)).join("");
|
|
9472
|
+
}
|
|
9473
|
+
inline(piece) {
|
|
9474
|
+
switch (piece.kind) {
|
|
9475
|
+
case "text":
|
|
9476
|
+
if (!isInertText(piece.text)) {
|
|
9477
|
+
this.inert = false;
|
|
9478
|
+
}
|
|
9479
|
+
return piece.text;
|
|
9480
|
+
case "break":
|
|
9481
|
+
return " ";
|
|
9482
|
+
case "strong":
|
|
9483
|
+
case "emphasis":
|
|
9484
|
+
case "strike": {
|
|
9485
|
+
const inner = this.inlineText(piece.children).replace(/\n/g, " ").replace(/ {2,}/g, " ");
|
|
9486
|
+
const [, before, middle, after] = /^(\s*)([\s\S]*?)(\s*)$/.exec(inner);
|
|
9487
|
+
if (middle === "") {
|
|
9488
|
+
return inner;
|
|
9489
|
+
}
|
|
9490
|
+
const mark = piece.kind === "strong" ? "**" : piece.kind === "emphasis" ? "*" : "~~";
|
|
9491
|
+
return `${before}${mark}${middle}${mark}${after}`;
|
|
9492
|
+
}
|
|
9493
|
+
case "code":
|
|
9494
|
+
return this.code(piece.text);
|
|
9495
|
+
case "link": {
|
|
9496
|
+
const label = this.inlineLines(piece.children).join(" ").trim();
|
|
9497
|
+
const href = (this.addresses.link ?? defaultLink)(piece.href);
|
|
9498
|
+
if (!href) {
|
|
9499
|
+
return label;
|
|
9500
|
+
}
|
|
9501
|
+
return label === "" || label === href ? `<${encodeAddress(href)}>` : `[${label}](${encodeAddress(href)})`;
|
|
9502
|
+
}
|
|
9503
|
+
case "image": {
|
|
9504
|
+
const src = piece.src ? (this.addresses.picture ?? defaultPicture)(piece.src) : null;
|
|
9505
|
+
if (!src) {
|
|
9506
|
+
if (!isInertText(piece.alt)) {
|
|
9507
|
+
this.inert = false;
|
|
9508
|
+
}
|
|
9509
|
+
return piece.alt;
|
|
9510
|
+
}
|
|
9511
|
+
if (!isInertText(piece.alt)) {
|
|
9512
|
+
this.inert = false;
|
|
9513
|
+
}
|
|
9514
|
+
return `})`;
|
|
9515
|
+
}
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9518
|
+
code(text) {
|
|
9519
|
+
const trimmed = text.trim();
|
|
9520
|
+
if (trimmed === "") {
|
|
9521
|
+
return "";
|
|
9522
|
+
}
|
|
9523
|
+
if (trimmed.includes("`")) {
|
|
9524
|
+
this.inert = false;
|
|
9525
|
+
}
|
|
9526
|
+
return `\`${trimmed}\``;
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
var mergeNeighbors = (pieces) => {
|
|
9530
|
+
const merged = [];
|
|
9531
|
+
for (const piece of pieces) {
|
|
9532
|
+
const last = merged[merged.length - 1];
|
|
9533
|
+
if (last && last.kind === piece.kind && (piece.kind === "strong" || piece.kind === "emphasis" || piece.kind === "strike")) {
|
|
9534
|
+
merged[merged.length - 1] = { kind: piece.kind, children: [...last.children, ...piece.children] };
|
|
9535
|
+
} else {
|
|
9536
|
+
merged.push(piece);
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
return merged;
|
|
9540
|
+
};
|
|
9541
|
+
var defaultLink = (href) => {
|
|
9542
|
+
return keptAddress(href);
|
|
9543
|
+
};
|
|
9544
|
+
var defaultPicture = (src) => {
|
|
9545
|
+
return isWebAddress(src) ? src : null;
|
|
9546
|
+
};
|
|
9547
|
+
var encodeAddress = (address) => {
|
|
9548
|
+
return address.replace(/ /g, "%20").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
9549
|
+
};
|
|
9550
|
+
var plainBlocks = (blocks, indent = "") => {
|
|
9551
|
+
return blocks.map((block) => plainBlock(block, indent)).filter((text) => text.trim() !== "").join(`
|
|
9552
|
+
|
|
9553
|
+
`);
|
|
9554
|
+
};
|
|
9555
|
+
var plainBlock = (block, indent) => {
|
|
9556
|
+
switch (block.kind) {
|
|
9557
|
+
case "paragraph":
|
|
9558
|
+
return prefixLines(plainInline(block.inline, true), indent);
|
|
9559
|
+
case "heading":
|
|
9560
|
+
return prefixLines(plainInline(block.inline, false).trim(), indent);
|
|
9561
|
+
case "list":
|
|
9562
|
+
return block.items.map((item, index) => {
|
|
9563
|
+
const marker = block.ordered ? `${index + 1}. ` : "- ";
|
|
9564
|
+
const text = plainBlocks(item.filter((inner) => inner.kind !== "list"), "").replace(/\n+/g, " ").trim();
|
|
9565
|
+
const nested = item.filter((inner) => inner.kind === "list").map((inner) => plainBlock(inner, `${indent}${" ".repeat(marker.length)}`));
|
|
9566
|
+
return [`${indent}${marker}${text}`.trimEnd(), ...nested].join(`
|
|
9567
|
+
`);
|
|
9568
|
+
}).join(`
|
|
9569
|
+
`);
|
|
9570
|
+
case "quote":
|
|
9571
|
+
return prefixLines(plainBlocks(block.blocks), `${indent}> `);
|
|
9572
|
+
case "code":
|
|
9573
|
+
return prefixLines(block.text, indent);
|
|
9574
|
+
}
|
|
9575
|
+
};
|
|
9576
|
+
var prefixLines = (text, prefix) => {
|
|
9577
|
+
return prefix === "" ? text : text.split(`
|
|
9578
|
+
`).map((line) => `${prefix}${line}`).join(`
|
|
9579
|
+
`);
|
|
9580
|
+
};
|
|
9581
|
+
var plainInline = (pieces, breaks, addresses = true) => {
|
|
9582
|
+
let text = "";
|
|
9583
|
+
for (const piece of pieces) {
|
|
9584
|
+
switch (piece.kind) {
|
|
9585
|
+
case "text":
|
|
9586
|
+
text += piece.text;
|
|
9587
|
+
break;
|
|
9588
|
+
case "break":
|
|
9589
|
+
text += breaks ? `
|
|
9590
|
+
` : " ";
|
|
9591
|
+
break;
|
|
9592
|
+
case "strong":
|
|
9593
|
+
case "emphasis":
|
|
9594
|
+
case "strike":
|
|
9595
|
+
text += plainInline(piece.children, breaks, addresses);
|
|
9596
|
+
break;
|
|
9597
|
+
case "code":
|
|
9598
|
+
text += piece.text;
|
|
9599
|
+
break;
|
|
9600
|
+
case "link": {
|
|
9601
|
+
const label = plainInline(piece.children, breaks, addresses).trim();
|
|
9602
|
+
const href = addresses && isKeptLink(piece.href) && !piece.href.startsWith("#") ? piece.href : "";
|
|
9603
|
+
text += href && label && label !== href ? `${label} (${href})` : label || href;
|
|
9604
|
+
break;
|
|
9605
|
+
}
|
|
9606
|
+
case "image":
|
|
9607
|
+
text += piece.alt;
|
|
9608
|
+
break;
|
|
9609
|
+
}
|
|
9610
|
+
}
|
|
9611
|
+
return text.split(`
|
|
9612
|
+
`).map((line) => line.replace(/ {2,}/g, " ").trim()).join(`
|
|
9613
|
+
`);
|
|
9614
|
+
};
|
|
9615
|
+
|
|
9616
|
+
// src/core/other-apps/ideas.ts
|
|
9617
|
+
var IMPORT_WARNING_CODES = [
|
|
9618
|
+
"floating-ideas-moved",
|
|
9619
|
+
"summaries-moved",
|
|
9620
|
+
"callouts-moved",
|
|
9621
|
+
"boundaries-in-notes",
|
|
9622
|
+
"links-in-notes",
|
|
9623
|
+
"missing-link-targets",
|
|
9624
|
+
"icons-not-kept",
|
|
9625
|
+
"pictures-not-kept",
|
|
9626
|
+
"attachments-not-kept",
|
|
9627
|
+
"local-links-not-kept",
|
|
9628
|
+
"encrypted-branches-not-read",
|
|
9629
|
+
"formatting-not-kept"
|
|
9630
|
+
];
|
|
9631
|
+
class OrbitImportError extends Error {
|
|
9632
|
+
code;
|
|
9633
|
+
constructor(code, message) {
|
|
9634
|
+
super(message);
|
|
9635
|
+
this.name = "OrbitImportError";
|
|
9636
|
+
this.code = code;
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
var MAX_IMPORTED_IDEAS = 1e5;
|
|
9640
|
+
var MAX_IMPORTED_DEPTH = 500;
|
|
9641
|
+
var newIdea = (text) => {
|
|
9642
|
+
return { text, notes: [], ideaLinks: [], tags: [], icons: [], children: [] };
|
|
9643
|
+
};
|
|
9644
|
+
var MESSAGES = {
|
|
9645
|
+
"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.`,
|
|
9646
|
+
"summaries-moved": (count, examples) => `${plural(count, "summary")}${examples} became ${count === 1 ? "an idea" : "ideas"} beside the ideas ${count === 1 ? "it sums" : "they sum"} up.`,
|
|
9647
|
+
"callouts-moved": (count, examples) => `${plural(count, "callout")}${examples} became ${count === 1 ? "an idea" : "ideas"} under the topic ${count === 1 ? "it points" : "they point"} at.`,
|
|
9648
|
+
"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"}.`,
|
|
9649
|
+
"links-in-notes": (count, examples) => `${plural(count, "link")}${examples} beyond an idea's first ${count === 1 ? "is" : "are"} now in its note.`,
|
|
9650
|
+
"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.`,
|
|
9651
|
+
"icons-not-kept": (count, examples) => `${plural(count, "icon")}${examples} ${count === 1 ? "was" : "were"} left out: an idea in Orbit has one icon, an emoji.`,
|
|
9652
|
+
"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.`,
|
|
9653
|
+
"attachments-not-kept": (count, examples) => `${plural(count, "attached file")}${examples} ${count === 1 ? "was" : "were"} left out; the ideas keep their names.`,
|
|
9654
|
+
"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.`,
|
|
9655
|
+
"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.`,
|
|
9656
|
+
"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.`
|
|
9657
|
+
};
|
|
9658
|
+
var plural = (count, noun) => {
|
|
9659
|
+
const word = count === 1 ? noun : noun.endsWith("y") ? `${noun.slice(0, -1)}ies` : /(ch|sh|s|x)$/.test(noun) ? `${noun}es` : `${noun}s`;
|
|
9660
|
+
return `${count === 1 ? "One" : count} ${word}`;
|
|
9661
|
+
};
|
|
9662
|
+
|
|
9663
|
+
class ImportReport {
|
|
9664
|
+
found = new Map;
|
|
9665
|
+
add(code, example) {
|
|
9666
|
+
const entry = this.found.get(code) ?? { count: 0, examples: [] };
|
|
9667
|
+
const shown = example === undefined ? "" : oneLine(example);
|
|
9668
|
+
entry.count += 1;
|
|
9669
|
+
if (shown !== "" && entry.examples.length < 3 && !entry.examples.includes(shown)) {
|
|
9670
|
+
entry.examples.push(shown);
|
|
9671
|
+
}
|
|
9672
|
+
this.found.set(code, entry);
|
|
9673
|
+
}
|
|
9674
|
+
get warnings() {
|
|
9675
|
+
const order = (code) => IMPORT_WARNING_CODES.indexOf(code);
|
|
9676
|
+
return [...this.found.entries()].sort(([first], [second]) => order(first) - order(second)).map(([code, { count, examples }]) => {
|
|
9677
|
+
const shown = examples.map((example) => `“${example.length > 40 ? `${example.slice(0, 39)}…` : example}”`);
|
|
9678
|
+
const more = count > examples.length && examples.length > 0 ? ", …" : "";
|
|
9679
|
+
return { code, count, message: MESSAGES[code](count, shown.length > 0 ? ` (${shown.join(", ")}${more})` : "") };
|
|
9680
|
+
});
|
|
9681
|
+
}
|
|
9682
|
+
}
|
|
9683
|
+
var oneLine = (text) => {
|
|
9684
|
+
return text.replace(/[\t\n\v\f\r\u2028\u2029 ]+/g, " ").replace(/^ | $/g, "");
|
|
9685
|
+
};
|
|
9686
|
+
var SOURCE_LINK = "#orbit-source:";
|
|
9687
|
+
var sourceLink = (sourceId) => {
|
|
9688
|
+
return `${SOURCE_LINK}${encodeURIComponent(sourceId)}`;
|
|
9689
|
+
};
|
|
9690
|
+
var buildDocument = (rootIdea, report) => {
|
|
9691
|
+
let count = 0;
|
|
9692
|
+
const bySource = new Map;
|
|
9693
|
+
const pending = [];
|
|
9694
|
+
const build = (idea, level) => {
|
|
9695
|
+
count += 1;
|
|
9696
|
+
if (count > MAX_IMPORTED_IDEAS) {
|
|
9697
|
+
throw new OrbitImportError("too-large", `The map has more than ${MAX_IMPORTED_IDEAS.toLocaleString("en")} ideas.`);
|
|
9698
|
+
}
|
|
9699
|
+
if (level > MAX_IMPORTED_DEPTH) {
|
|
9700
|
+
throw new OrbitImportError("too-large", `The map goes more than ${MAX_IMPORTED_DEPTH} levels deep.`);
|
|
9701
|
+
}
|
|
9702
|
+
let text = oneLine(idea.text);
|
|
9703
|
+
const icons = idea.icons.filter((icon) => icon !== "");
|
|
9704
|
+
let icon = icons[0];
|
|
9705
|
+
const emoji = icon === undefined ? leadingEmoji(text) : null;
|
|
9706
|
+
if (emoji) {
|
|
9707
|
+
icon = emoji.icon;
|
|
9708
|
+
text = emoji.text;
|
|
9709
|
+
}
|
|
9710
|
+
for (const extra of icons.slice(1)) {
|
|
9711
|
+
report.add("icons-not-kept", extra);
|
|
9712
|
+
}
|
|
9713
|
+
const node = { id: nodeSlug(text), text, level, children: [] };
|
|
9714
|
+
if (icon) {
|
|
9715
|
+
node.icon = icon;
|
|
9716
|
+
}
|
|
9717
|
+
if (idea.collapsed && idea.children.length > 0) {
|
|
9718
|
+
node.collapsed = true;
|
|
9719
|
+
}
|
|
9720
|
+
if (idea.status) {
|
|
9721
|
+
node.status = idea.status;
|
|
9722
|
+
}
|
|
9723
|
+
if (idea.priority !== undefined && Number.isFinite(idea.priority)) {
|
|
9724
|
+
node.priority = idea.priority;
|
|
9725
|
+
}
|
|
9726
|
+
const tags = [...new Set(idea.tags.map((tag) => oneLine(tag)).filter((tag) => tag !== ""))];
|
|
9727
|
+
if (tags.length > 0) {
|
|
9728
|
+
node.tags = tags;
|
|
9729
|
+
}
|
|
9730
|
+
if (idea.color) {
|
|
9731
|
+
node.color = idea.color;
|
|
9732
|
+
}
|
|
9733
|
+
if (idea.link) {
|
|
9734
|
+
node.link = idea.link;
|
|
9735
|
+
}
|
|
9736
|
+
if (idea.image) {
|
|
9737
|
+
node.image = { src: idea.image };
|
|
9738
|
+
}
|
|
9739
|
+
if (idea.sourceId !== undefined && !bySource.has(idea.sourceId)) {
|
|
9740
|
+
bySource.set(idea.sourceId, node);
|
|
9741
|
+
}
|
|
9742
|
+
pending.push([idea, node]);
|
|
9743
|
+
for (const child of idea.children) {
|
|
9744
|
+
const childNode = build(child, level + 1);
|
|
9745
|
+
setParent(childNode, node);
|
|
9746
|
+
node.children.push(childNode);
|
|
9747
|
+
}
|
|
9748
|
+
return node;
|
|
9749
|
+
};
|
|
9750
|
+
const root = build(rootIdea, 0);
|
|
9751
|
+
ensureUniqueNodeIds(root);
|
|
9752
|
+
for (const [idea, node] of pending) {
|
|
9753
|
+
const extraLines = [];
|
|
9754
|
+
for (const link of idea.ideaLinks) {
|
|
9755
|
+
const target = bySource.get(link.target);
|
|
9756
|
+
if (!target) {
|
|
9757
|
+
report.add("missing-link-targets", link.label ?? node.text);
|
|
9758
|
+
continue;
|
|
9759
|
+
}
|
|
9760
|
+
if (!node.link && !link.label) {
|
|
9761
|
+
node.link = `#${target.id}`;
|
|
9762
|
+
} else {
|
|
9763
|
+
report.add("links-in-notes", link.label ?? target.text);
|
|
9764
|
+
extraLines.push({ label: link.label ?? target.text, target });
|
|
9765
|
+
}
|
|
9766
|
+
}
|
|
9767
|
+
const note = joinNotes([...idea.notes, linkNote(extraLines, idea.notes)]);
|
|
9768
|
+
if (note) {
|
|
9769
|
+
node.note = resolveSourceLinks(note.text, bySource, note.format);
|
|
9770
|
+
if (note.format === "plain") {
|
|
9771
|
+
node.noteFormat = "plain";
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
}
|
|
9775
|
+
const nodes = indexNodes(root);
|
|
9776
|
+
for (const [, node] of pending) {
|
|
9777
|
+
if (isNodeLink(node.link) && !findIndexedLinkedNode(nodes, node.link)) {
|
|
9778
|
+
report.add("missing-link-targets", node.link);
|
|
9779
|
+
delete node.link;
|
|
9780
|
+
}
|
|
9781
|
+
}
|
|
9782
|
+
return { root, options: {}, warnings: [], diagnostics: [] };
|
|
9783
|
+
};
|
|
9784
|
+
var linkNote = (lines, notes) => {
|
|
9785
|
+
if (lines.length === 0) {
|
|
9786
|
+
return;
|
|
9787
|
+
}
|
|
9788
|
+
const plain = notes.some((note) => note.format === "plain") || lines.some(({ label }) => /[[\]`*_~<\\]/.test(label));
|
|
9789
|
+
if (plain) {
|
|
9790
|
+
return { text: lines.map(({ label, target }) => label === target.text ? `→ ${label}` : `→ ${label}: ${target.text}`).join(`
|
|
9791
|
+
`), format: "plain" };
|
|
9792
|
+
}
|
|
9793
|
+
return { text: lines.map(({ label, target }) => `→ [${label || target.text}](#${target.id})`).join(`
|
|
9794
|
+
|
|
9795
|
+
`), format: "markdown" };
|
|
9796
|
+
};
|
|
9797
|
+
var resolveSourceLinks = (text, bySource, format) => {
|
|
9798
|
+
if (format !== "markdown" || !text.includes(SOURCE_LINK)) {
|
|
9799
|
+
return text;
|
|
9800
|
+
}
|
|
9801
|
+
const target = (encoded) => {
|
|
9802
|
+
try {
|
|
9803
|
+
return bySource.get(decodeURIComponent(encoded));
|
|
9804
|
+
} catch {
|
|
9805
|
+
return;
|
|
9806
|
+
}
|
|
9807
|
+
};
|
|
9808
|
+
return text.replace(/\[([^\]]*)\]\(#orbit-source:([^)\s]+)\)/g, (_, label, encoded) => {
|
|
9809
|
+
const node = target(encoded);
|
|
9810
|
+
return node ? `[${label || node.text}](#${node.id})` : label;
|
|
9811
|
+
}).replace(/<#orbit-source:([^>\s]+)>/g, (_, encoded) => {
|
|
9812
|
+
const node = target(encoded);
|
|
9813
|
+
return node ? `[${node.text}](#${node.id})` : "";
|
|
9814
|
+
});
|
|
9815
|
+
};
|
|
9816
|
+
|
|
9817
|
+
// src/core/other-apps/reading.ts
|
|
9818
|
+
var readXmlFile = (data, rootName, what) => {
|
|
9819
|
+
const text = typeof data === "string" ? data : decodeMarkup(data);
|
|
9820
|
+
let root;
|
|
9821
|
+
try {
|
|
9822
|
+
root = parseXml(text);
|
|
9823
|
+
} catch (error) {
|
|
9824
|
+
if (error instanceof MarkupError) {
|
|
9825
|
+
throw new OrbitImportError("unreadable", `The file isn't ${what} Orbit can read: ${error.message}.`);
|
|
9826
|
+
}
|
|
9827
|
+
throw error;
|
|
9828
|
+
}
|
|
9829
|
+
if (root.localName !== rootName) {
|
|
9830
|
+
throw new OrbitImportError("unreadable", `The file isn't ${what}: it starts with <${root.name}>, not <${rootName}>.`);
|
|
9831
|
+
}
|
|
9832
|
+
return root;
|
|
9833
|
+
};
|
|
9834
|
+
var baseName = (name) => {
|
|
9835
|
+
return (name ?? "").replace(/^.*[/\\]/, "").replace(/\.(opml|xml|mm|xmind|txt|md)$/i, "").trim();
|
|
9836
|
+
};
|
|
9837
|
+
|
|
9838
|
+
// src/core/other-apps/freemind.ts
|
|
9839
|
+
var importFreeMind = (data, options = {}) => {
|
|
9840
|
+
const map = readXmlFile(data, "map", "a FreeMind or Freeplane map");
|
|
9841
|
+
const nodes = childElements(map, "node");
|
|
9842
|
+
if (nodes.length === 0) {
|
|
9843
|
+
throw new OrbitImportError("empty", "The map has no nodes.");
|
|
9844
|
+
}
|
|
9845
|
+
const report = new ImportReport;
|
|
9846
|
+
let root;
|
|
9847
|
+
if (nodes.length === 1) {
|
|
9848
|
+
root = readNode(nodes[0], report);
|
|
9849
|
+
} else {
|
|
9850
|
+
root = newIdea(baseName(options.name) || options.untitled || "Untitled map");
|
|
9851
|
+
root.children = nodes.map((node) => readNode(node, report));
|
|
9852
|
+
}
|
|
9853
|
+
return { format: "freemind", maps: [{ document: buildDocument(root, report), media: {}, warnings: report.warnings }] };
|
|
9854
|
+
};
|
|
9855
|
+
var readNode = (node, report) => {
|
|
9856
|
+
const idea = newIdea("");
|
|
9857
|
+
const id = node.attributes.ID;
|
|
9858
|
+
if (id) {
|
|
9859
|
+
idea.sourceId = id;
|
|
9860
|
+
}
|
|
9861
|
+
readTitle(node, idea, report);
|
|
9862
|
+
const addresses = noteAddresses(report);
|
|
9863
|
+
const details = [];
|
|
9864
|
+
const notes = [];
|
|
9865
|
+
for (const content of childElements(node, "richcontent")) {
|
|
9866
|
+
const type = (content.attributes.TYPE ?? "").toUpperCase();
|
|
9867
|
+
if (type === "NOTE" || type === "DETAILS") {
|
|
9868
|
+
const note = richContentNote(content, addresses, report, idea.text);
|
|
9869
|
+
if (note) {
|
|
9870
|
+
(type === "DETAILS" ? details : notes).push(note);
|
|
9871
|
+
}
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
for (const hook of childElements(node, "hook")) {
|
|
9875
|
+
if (/NodeNote/.test(hook.attributes.NAME ?? "")) {
|
|
9876
|
+
const text = childElement(hook, "text");
|
|
9877
|
+
const note = noteFromPlainText(text ? textContent(text) : undefined);
|
|
9878
|
+
if (note) {
|
|
9879
|
+
notes.push(note);
|
|
9880
|
+
}
|
|
9881
|
+
}
|
|
9882
|
+
}
|
|
9883
|
+
idea.notes.push(...details, ...notes);
|
|
9884
|
+
const attributes = attributeNote(node);
|
|
9885
|
+
if (attributes) {
|
|
9886
|
+
idea.notes.push(attributes);
|
|
9887
|
+
}
|
|
9888
|
+
readLink(node.attributes.LINK, idea, report);
|
|
9889
|
+
for (const arrow of childElements(node, "arrowlink")) {
|
|
9890
|
+
const target = arrow.attributes.DESTINATION;
|
|
9891
|
+
if (target) {
|
|
9892
|
+
const label = oneLine(arrow.attributes.MIDDLE_LABEL || arrow.attributes.SOURCE_LABEL || arrow.attributes.TARGET_LABEL || "");
|
|
9893
|
+
idea.ideaLinks.push(label ? { target, label } : { target });
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
if (node.attributes.FOLDED === "true") {
|
|
9897
|
+
idea.collapsed = true;
|
|
9898
|
+
}
|
|
9899
|
+
const background = node.attributes.BACKGROUND_COLOR;
|
|
9900
|
+
if (background && /^#[0-9A-Fa-f]{6}$/.test(background)) {
|
|
9901
|
+
idea.color = background.toUpperCase();
|
|
9902
|
+
}
|
|
9903
|
+
for (const icon of childElements(node, "icon")) {
|
|
9904
|
+
const name = icon.attributes.BUILTIN ?? "";
|
|
9905
|
+
const meaning = freeMindIcon(name);
|
|
9906
|
+
if (!meaning) {
|
|
9907
|
+
report.add("icons-not-kept", name.replace(/^.*\//, ""));
|
|
9908
|
+
} else if ("icon" in meaning) {
|
|
9909
|
+
idea.icons.push(meaning.icon);
|
|
9910
|
+
} else if ("priority" in meaning) {
|
|
9911
|
+
idea.priority ??= meaning.priority;
|
|
9912
|
+
} else {
|
|
9913
|
+
idea.status ??= meaning.status;
|
|
9914
|
+
}
|
|
9915
|
+
}
|
|
9916
|
+
for (const hook of childElements(node, "hook")) {
|
|
9917
|
+
if (hook.attributes.NAME === "ExternalObject" && hook.attributes.URI) {
|
|
9918
|
+
readPicture(hook.attributes.URI, idea, report);
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
9921
|
+
if (node.attributes.ENCRYPTED_CONTENT) {
|
|
9922
|
+
report.add("encrypted-branches-not-read", idea.text || undefined);
|
|
9923
|
+
}
|
|
9924
|
+
idea.children = childElements(node, "node").map((child) => readNode(child, report));
|
|
9925
|
+
return idea;
|
|
9926
|
+
};
|
|
9927
|
+
var readTitle = (node, idea, report) => {
|
|
9928
|
+
const rich = childElements(node, "richcontent").find((content) => (content.attributes.TYPE ?? "").toUpperCase() === "NODE");
|
|
9929
|
+
const text = node.attributes.TEXT ?? node.attributes.LOCALIZED_TEXT;
|
|
9930
|
+
if (rich) {
|
|
9931
|
+
const html = childElement(rich, "html");
|
|
9932
|
+
const plain = childElement(rich, "text");
|
|
9933
|
+
if (html) {
|
|
9934
|
+
const title = titleFromHtml(html);
|
|
9935
|
+
idea.text = title.text;
|
|
9936
|
+
readLink(title.link, idea, report);
|
|
9937
|
+
for (const image of findElements(html, "img")) {
|
|
9938
|
+
readPicture(image.attributes.src ?? "", idea, report);
|
|
9939
|
+
}
|
|
9940
|
+
} else {
|
|
9941
|
+
idea.text = plain ? textContent(plain) : textContent(rich);
|
|
9942
|
+
}
|
|
9943
|
+
return;
|
|
9944
|
+
}
|
|
9945
|
+
if (text !== undefined && /^\s*<html>/i.test(text)) {
|
|
9946
|
+
const title = titleFromHtml(text);
|
|
9947
|
+
idea.text = title.text;
|
|
9948
|
+
readLink(title.link, idea, report);
|
|
9949
|
+
return;
|
|
9950
|
+
}
|
|
9951
|
+
idea.text = text ?? "";
|
|
9952
|
+
};
|
|
9953
|
+
var readLink = (link, idea, report) => {
|
|
9954
|
+
const address = link?.trim();
|
|
9955
|
+
if (!address || idea.link) {
|
|
9956
|
+
return;
|
|
9957
|
+
}
|
|
9958
|
+
const kept = keptAddress(address);
|
|
9959
|
+
if (address.startsWith("#") || /^(ID_\d+|_?Freemind_Link_\d+)$/.test(address)) {
|
|
9960
|
+
idea.ideaLinks.push({ target: address.replace(/^#/, "") });
|
|
9961
|
+
} else if (kept) {
|
|
9962
|
+
idea.link = kept;
|
|
9963
|
+
} else {
|
|
9964
|
+
report.add("local-links-not-kept", address);
|
|
9965
|
+
}
|
|
9966
|
+
};
|
|
9967
|
+
var readPicture = (address, idea, report) => {
|
|
9968
|
+
if (/^https?:\/\/\S+$/i.test(address)) {
|
|
9969
|
+
idea.image ??= address;
|
|
9970
|
+
} else if (address !== "") {
|
|
9971
|
+
report.add("pictures-not-kept", address.replace(/^.*[/\\]/, ""));
|
|
9972
|
+
}
|
|
9973
|
+
};
|
|
9974
|
+
var noteAddresses = (report) => {
|
|
9975
|
+
return {
|
|
9976
|
+
link: (href) => {
|
|
9977
|
+
if (href.startsWith("#")) {
|
|
9978
|
+
return sourceLink(href.slice(1));
|
|
9979
|
+
}
|
|
9980
|
+
const kept = keptAddress(href);
|
|
9981
|
+
if (kept) {
|
|
9982
|
+
return kept;
|
|
9983
|
+
}
|
|
9984
|
+
report.add("local-links-not-kept", href);
|
|
9985
|
+
return null;
|
|
9986
|
+
},
|
|
9987
|
+
picture: (src) => {
|
|
9988
|
+
if (/^https?:\/\/\S+$/i.test(src)) {
|
|
9989
|
+
return src;
|
|
9990
|
+
}
|
|
9991
|
+
report.add("pictures-not-kept", src.replace(/^.*[/\\]/, ""));
|
|
9992
|
+
return null;
|
|
9993
|
+
}
|
|
9994
|
+
};
|
|
9995
|
+
};
|
|
9996
|
+
var richContentNote = (content, addresses, report, title) => {
|
|
9997
|
+
const html = childElement(content, "html");
|
|
9998
|
+
if (html) {
|
|
9999
|
+
const note = noteFromHtml(html, addresses);
|
|
10000
|
+
if (note?.format === "plain") {
|
|
10001
|
+
report.add("formatting-not-kept", title || undefined);
|
|
10002
|
+
}
|
|
10003
|
+
return note;
|
|
10004
|
+
}
|
|
10005
|
+
const plain = childElement(content, "text");
|
|
10006
|
+
const text = plain ? textContent(plain) : textContent(content);
|
|
10007
|
+
if (/\/markdown$/i.test(content.attributes["CONTENT-TYPE"] ?? "") && text.trim() !== "") {
|
|
10008
|
+
return { text: text.replace(/\r\n?/g, `
|
|
10009
|
+
`).trim(), format: "markdown" };
|
|
10010
|
+
}
|
|
10011
|
+
return noteFromPlainText(text);
|
|
10012
|
+
};
|
|
10013
|
+
var attributeNote = (node) => {
|
|
10014
|
+
const items = [];
|
|
10015
|
+
for (const attribute of childElements(node, "attribute")) {
|
|
10016
|
+
const name = (attributeOf(attribute, "NAME") ?? "").trim();
|
|
10017
|
+
const value = (attributeOf(attribute, "VALUE") ?? "").trim();
|
|
10018
|
+
if (name !== "" || value !== "") {
|
|
10019
|
+
items.push([{ kind: "paragraph", inline: name === "" ? [{ kind: "text", text: value }] : [{ kind: "strong", children: [{ kind: "text", text: `${name}:` }] }, { kind: "text", text: ` ${value}` }] }]);
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
return items.length === 0 ? undefined : noteFromBlocks([{ kind: "list", ordered: false, items }]);
|
|
10023
|
+
};
|
|
10024
|
+
var findElements = (element, localName) => {
|
|
10025
|
+
const found = [];
|
|
10026
|
+
const pending = [element];
|
|
10027
|
+
while (pending.length > 0) {
|
|
10028
|
+
const next = pending.pop();
|
|
10029
|
+
for (const child of childElements(next)) {
|
|
10030
|
+
if (child.localName.toLowerCase() === localName) {
|
|
10031
|
+
found.push(child);
|
|
10032
|
+
}
|
|
10033
|
+
pending.push(child);
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
return found;
|
|
10037
|
+
};
|
|
10038
|
+
|
|
10039
|
+
// src/core/other-apps/opml.ts
|
|
10040
|
+
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;
|
|
10041
|
+
var importOPML = (data, options = {}) => {
|
|
10042
|
+
const opml = readXmlFile(data, "opml", "an OPML file");
|
|
10043
|
+
const head = childElement(opml, "head");
|
|
10044
|
+
const titleElement = head ? childElement(head, "title") : undefined;
|
|
10045
|
+
const title = titleElement ? baseName(textContent(titleElement).replace(/\s+/g, " ").trim()) : "";
|
|
10046
|
+
const body = childElement(opml, "body");
|
|
10047
|
+
const outlines = body ? childElements(body, "outline") : [];
|
|
10048
|
+
if (outlines.length === 0) {
|
|
10049
|
+
throw new OrbitImportError("empty", "The OPML file has no outlines.");
|
|
10050
|
+
}
|
|
10051
|
+
const report = new ImportReport;
|
|
10052
|
+
let root;
|
|
10053
|
+
if (outlines.length === 1) {
|
|
10054
|
+
root = readOutline(outlines[0], report);
|
|
10055
|
+
} else {
|
|
10056
|
+
root = newIdea(title || baseName(options.name) || options.untitled || "Untitled map");
|
|
10057
|
+
root.children = outlines.map((outline) => readOutline(outline, report));
|
|
10058
|
+
}
|
|
10059
|
+
return { format: "opml", maps: [{ document: buildDocument(root, report), media: {}, warnings: report.warnings }] };
|
|
10060
|
+
};
|
|
10061
|
+
var readOutline = (outline, report) => {
|
|
10062
|
+
const text = attributeOf(outline, "text") ?? attributeOf(outline, "title") ?? "";
|
|
10063
|
+
const idea = newIdea(text);
|
|
10064
|
+
if (HTML_TAGS.test(text) && outline.attributes._textFormat !== "plain") {
|
|
10065
|
+
const title = titleFromHtml(text);
|
|
10066
|
+
idea.text = title.text;
|
|
10067
|
+
if (title.link && isKeptLink(title.link) && !title.link.startsWith("#")) {
|
|
10068
|
+
idea.link = title.link;
|
|
10069
|
+
}
|
|
10070
|
+
}
|
|
10071
|
+
const note = readNote(outline, idea, report);
|
|
10072
|
+
if (note) {
|
|
10073
|
+
idea.notes.push(note);
|
|
10074
|
+
}
|
|
10075
|
+
idea.status = readStatus(outline);
|
|
10076
|
+
const link = readLink2(outline);
|
|
10077
|
+
const kept = link ? keptAddress(link) : null;
|
|
10078
|
+
if (kept) {
|
|
10079
|
+
idea.link = kept;
|
|
10080
|
+
} else if (link) {
|
|
10081
|
+
report.add("local-links-not-kept", link);
|
|
10082
|
+
}
|
|
10083
|
+
const categories = attributeOf(outline, "category");
|
|
10084
|
+
if (categories) {
|
|
10085
|
+
idea.tags.push(...categories.split(",").map((category) => category.trim().replace(/^\/+/, "")).filter((category) => category !== ""));
|
|
10086
|
+
}
|
|
10087
|
+
idea.children = childElements(outline, "outline").map((child) => readOutline(child, report));
|
|
10088
|
+
return idea;
|
|
10089
|
+
};
|
|
10090
|
+
var readNote = (outline, idea, report) => {
|
|
10091
|
+
const text = attributeOf(outline, "_note") ?? outline.attributes.note ?? attributeOf(outline, "description");
|
|
10092
|
+
if (text === undefined || text.trim() === "") {
|
|
10093
|
+
return;
|
|
10094
|
+
}
|
|
10095
|
+
const format = outline.attributes._noteFormat;
|
|
10096
|
+
if (format === "markdown" || format === "plain") {
|
|
10097
|
+
return { text: text.replace(/\r\n?/g, `
|
|
10098
|
+
`).trim(), format };
|
|
10099
|
+
}
|
|
10100
|
+
if (HTML_TAGS.test(text)) {
|
|
10101
|
+
const note = noteFromHtml(text.replace(/\r\n?|\n/g, "<br>"));
|
|
10102
|
+
if (note?.format === "plain") {
|
|
10103
|
+
report.add("formatting-not-kept", idea.text || undefined);
|
|
10104
|
+
}
|
|
10105
|
+
return note;
|
|
10106
|
+
}
|
|
10107
|
+
return noteFromPlainText(text);
|
|
10108
|
+
};
|
|
10109
|
+
var readStatus = (outline) => {
|
|
10110
|
+
const status = attributeOf(outline, "_status")?.trim();
|
|
10111
|
+
if (status) {
|
|
10112
|
+
return CHECKBOX_STATUSES.get(status.toLowerCase()) ?? status;
|
|
10113
|
+
}
|
|
10114
|
+
const complete = (outline.attributes._complete ?? outline.attributes.complete ?? outline.attributes.checked ?? outline.attributes._checked)?.trim().toLowerCase();
|
|
10115
|
+
if (complete === "true") {
|
|
10116
|
+
return "done";
|
|
10117
|
+
}
|
|
10118
|
+
return outline.attributes.checkbox === "true" ? "todo" : undefined;
|
|
10119
|
+
};
|
|
10120
|
+
var readLink2 = (outline) => {
|
|
10121
|
+
const url = outline.attributes.url ?? outline.attributes.htmlUrl ?? outline.attributes.xmlUrl;
|
|
10122
|
+
return url?.trim() || undefined;
|
|
10123
|
+
};
|
|
10124
|
+
var STATUS_CHECKBOXES = new Map([["done", "checked"], ["todo", "unchecked"], ["active", "indeterminate"]]);
|
|
10125
|
+
var CHECKBOX_STATUSES = new Map([...STATUS_CHECKBOXES].map(([status, checkbox]) => [checkbox, status]));
|
|
10126
|
+
var DONE_WORDS = new Set(["done", "complete", "completed", "shipped"]);
|
|
10127
|
+
var exportOPML = (input, options = {}) => {
|
|
10128
|
+
const document2 = typeof input === "string" ? parseOrbit(input) : input;
|
|
10129
|
+
const title = options.title ?? (document2.options.title || document2.root.text);
|
|
10130
|
+
const lines = [
|
|
10131
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
10132
|
+
'<opml version="2.0">',
|
|
10133
|
+
" <head>",
|
|
10134
|
+
` <title>${escapeXml3(title)}</title>`,
|
|
10135
|
+
" </head>",
|
|
10136
|
+
" <body>"
|
|
10137
|
+
];
|
|
10138
|
+
const write = (node, depth) => {
|
|
10139
|
+
const indent = " ".repeat(depth + 2);
|
|
10140
|
+
const attributes = outlineAttributes(node).map(([name, value]) => ` ${name}="${escapeXml3(value, true)}"`).join("");
|
|
10141
|
+
if (node.children.length === 0) {
|
|
10142
|
+
lines.push(`${indent}<outline${attributes}/>`);
|
|
10143
|
+
return;
|
|
10144
|
+
}
|
|
10145
|
+
lines.push(`${indent}<outline${attributes}>`);
|
|
10146
|
+
for (const child of node.children) {
|
|
10147
|
+
write(child, depth + 1);
|
|
10148
|
+
}
|
|
10149
|
+
lines.push(`${indent}</outline>`);
|
|
10150
|
+
};
|
|
10151
|
+
write(document2.root, 0);
|
|
10152
|
+
lines.push(" </body>", "</opml>", "");
|
|
10153
|
+
return lines.join(`
|
|
10154
|
+
`);
|
|
10155
|
+
};
|
|
10156
|
+
var outlineAttributes = (node) => {
|
|
10157
|
+
const text = node.icon ? `${node.icon} ${node.text}` : node.text;
|
|
10158
|
+
const attributes = [["text", text]];
|
|
10159
|
+
if (HTML_TAGS.test(text)) {
|
|
10160
|
+
attributes.push(["_textFormat", "plain"]);
|
|
10161
|
+
}
|
|
10162
|
+
if (node.note) {
|
|
10163
|
+
attributes.push(["_note", node.note]);
|
|
10164
|
+
const format = node.noteFormat ?? "markdown";
|
|
10165
|
+
if (noteFromPlainText(node.note)?.format !== format) {
|
|
10166
|
+
attributes.push(["_noteFormat", format]);
|
|
10167
|
+
}
|
|
10168
|
+
}
|
|
10169
|
+
const status = node.status?.trim();
|
|
10170
|
+
if (status) {
|
|
10171
|
+
attributes.push(["_status", STATUS_CHECKBOXES.get(status) ?? status]);
|
|
10172
|
+
if (DONE_WORDS.has(status.toLowerCase())) {
|
|
10173
|
+
attributes.push(["_complete", "true"]);
|
|
10174
|
+
}
|
|
10175
|
+
}
|
|
10176
|
+
if (node.link && (isKeptLink(node.link) || isNodeLink(node.link))) {
|
|
10177
|
+
attributes.push(["type", "link"], ["url", node.link]);
|
|
10178
|
+
}
|
|
10179
|
+
if (node.tags && node.tags.length > 0) {
|
|
10180
|
+
attributes.push(["category", node.tags.map((tag) => tag.replace(/,/g, " ")).join(",")]);
|
|
10181
|
+
}
|
|
10182
|
+
return attributes;
|
|
10183
|
+
};
|
|
10184
|
+
var escapeXml3 = (text, attribute = false) => {
|
|
10185
|
+
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
10186
|
+
return attribute ? escaped.replace(/"/g, """).replace(/\n/g, " ").replace(/\r/g, " ").replace(/\t/g, "	") : escaped;
|
|
10187
|
+
};
|
|
10188
|
+
|
|
10189
|
+
// src/core/other-apps/inflate.ts
|
|
10190
|
+
class InflateError extends Error {
|
|
10191
|
+
constructor(message) {
|
|
10192
|
+
super(message);
|
|
10193
|
+
this.name = "InflateError";
|
|
10194
|
+
}
|
|
10195
|
+
}
|
|
10196
|
+
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];
|
|
10197
|
+
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];
|
|
10198
|
+
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];
|
|
10199
|
+
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];
|
|
10200
|
+
var CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
10201
|
+
var MAX_BITS = 15;
|
|
10202
|
+
var buildCode = (lengths) => {
|
|
10203
|
+
const counts = new Uint16Array(MAX_BITS + 1);
|
|
10204
|
+
for (const length of lengths) {
|
|
10205
|
+
counts[length] += 1;
|
|
10206
|
+
}
|
|
10207
|
+
counts[0] = 0;
|
|
10208
|
+
let left = 1;
|
|
10209
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
10210
|
+
left = left * 2 - counts[length];
|
|
10211
|
+
if (left < 0) {
|
|
10212
|
+
throw new InflateError("A Huffman code with more codes than its lengths allow");
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
const offsets = new Uint16Array(MAX_BITS + 2);
|
|
10216
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
10217
|
+
offsets[length + 1] = offsets[length] + counts[length];
|
|
10218
|
+
}
|
|
10219
|
+
const symbols = new Uint16Array(lengths.length);
|
|
10220
|
+
for (let symbol = 0;symbol < lengths.length; symbol++) {
|
|
10221
|
+
if (lengths[symbol] !== 0) {
|
|
10222
|
+
symbols[offsets[lengths[symbol]]++] = symbol;
|
|
10223
|
+
}
|
|
10224
|
+
}
|
|
10225
|
+
return { counts, symbols };
|
|
10226
|
+
};
|
|
10227
|
+
var fixedLengths = new Uint8Array(288);
|
|
10228
|
+
fixedLengths.fill(8, 0, 144);
|
|
10229
|
+
fixedLengths.fill(9, 144, 256);
|
|
10230
|
+
fixedLengths.fill(7, 256, 280);
|
|
10231
|
+
fixedLengths.fill(8, 280, 288);
|
|
10232
|
+
var FIXED_LITERALS = buildCode(fixedLengths);
|
|
10233
|
+
var FIXED_DISTANCES = buildCode(new Uint8Array(30).fill(5));
|
|
10234
|
+
|
|
10235
|
+
class BitReader {
|
|
10236
|
+
data;
|
|
10237
|
+
position;
|
|
10238
|
+
buffer = 0;
|
|
10239
|
+
count = 0;
|
|
10240
|
+
constructor(data, start) {
|
|
10241
|
+
this.data = data;
|
|
10242
|
+
this.position = start;
|
|
10243
|
+
}
|
|
10244
|
+
bits(needed) {
|
|
10245
|
+
while (this.count < needed) {
|
|
10246
|
+
if (this.position >= this.data.length) {
|
|
10247
|
+
throw new InflateError("The data ends in the middle of a block");
|
|
10248
|
+
}
|
|
10249
|
+
this.buffer |= this.data[this.position++] << this.count;
|
|
10250
|
+
this.count += 8;
|
|
10251
|
+
}
|
|
10252
|
+
const value = this.buffer & (1 << needed) - 1;
|
|
10253
|
+
this.buffer >>>= needed;
|
|
10254
|
+
this.count -= needed;
|
|
10255
|
+
return value;
|
|
10256
|
+
}
|
|
10257
|
+
bytes(length) {
|
|
10258
|
+
this.buffer = 0;
|
|
10259
|
+
this.count = 0;
|
|
10260
|
+
if (this.position + length > this.data.length) {
|
|
10261
|
+
throw new InflateError("The data ends in the middle of a stored block");
|
|
10262
|
+
}
|
|
10263
|
+
const bytes = this.data.subarray(this.position, this.position + length);
|
|
10264
|
+
this.position += length;
|
|
10265
|
+
return bytes;
|
|
10266
|
+
}
|
|
10267
|
+
word() {
|
|
10268
|
+
const [low, high] = this.bytes(2);
|
|
10269
|
+
return low | high << 8;
|
|
10270
|
+
}
|
|
10271
|
+
decode(code) {
|
|
10272
|
+
let value = 0;
|
|
10273
|
+
let first = 0;
|
|
10274
|
+
let index = 0;
|
|
10275
|
+
for (let length = 1;length <= MAX_BITS; length++) {
|
|
10276
|
+
value |= this.bits(1);
|
|
10277
|
+
const count = code.counts[length];
|
|
10278
|
+
if (value - first < count) {
|
|
10279
|
+
return code.symbols[index + value - first];
|
|
10280
|
+
}
|
|
10281
|
+
index += count;
|
|
10282
|
+
first = first + count << 1;
|
|
10283
|
+
value <<= 1;
|
|
10284
|
+
}
|
|
10285
|
+
throw new InflateError("A code that no Huffman table has");
|
|
10286
|
+
}
|
|
10287
|
+
}
|
|
10288
|
+
|
|
10289
|
+
class Output {
|
|
10290
|
+
bytes;
|
|
10291
|
+
length = 0;
|
|
10292
|
+
limit;
|
|
10293
|
+
constructor(expected, limit) {
|
|
10294
|
+
this.limit = limit;
|
|
10295
|
+
this.bytes = new Uint8Array(Math.max(16, Math.min(expected ?? 65536, limit)));
|
|
10296
|
+
}
|
|
10297
|
+
room(extra) {
|
|
10298
|
+
const needed = this.length + extra;
|
|
10299
|
+
if (needed > this.limit) {
|
|
10300
|
+
throw new InflateError(`The data holds more than the ${this.limit} bytes it may`);
|
|
10301
|
+
}
|
|
10302
|
+
if (needed > this.bytes.length) {
|
|
10303
|
+
const grown = new Uint8Array(Math.min(this.limit, Math.max(needed, this.bytes.length * 2)));
|
|
10304
|
+
grown.set(this.bytes.subarray(0, this.length));
|
|
10305
|
+
this.bytes = grown;
|
|
10306
|
+
}
|
|
10307
|
+
}
|
|
10308
|
+
push(byte) {
|
|
10309
|
+
this.room(1);
|
|
10310
|
+
this.bytes[this.length++] = byte;
|
|
10311
|
+
}
|
|
10312
|
+
append(bytes) {
|
|
10313
|
+
this.room(bytes.length);
|
|
10314
|
+
this.bytes.set(bytes, this.length);
|
|
10315
|
+
this.length += bytes.length;
|
|
10316
|
+
}
|
|
10317
|
+
copy(distance, length) {
|
|
10318
|
+
if (distance > this.length) {
|
|
10319
|
+
throw new InflateError("A back reference to before the start of the data");
|
|
10320
|
+
}
|
|
10321
|
+
this.room(length);
|
|
10322
|
+
for (let index = 0;index < length; index++) {
|
|
10323
|
+
this.bytes[this.length] = this.bytes[this.length - distance];
|
|
10324
|
+
this.length += 1;
|
|
10325
|
+
}
|
|
10326
|
+
}
|
|
10327
|
+
}
|
|
10328
|
+
var inflate = (data, options = {}) => {
|
|
10329
|
+
const limit = options.limit ?? Math.max(options.expected ?? 0, 64 * 1024 * 1024);
|
|
10330
|
+
const reader = new BitReader(data, 0);
|
|
10331
|
+
const output = new Output(options.expected, limit);
|
|
10332
|
+
let last = 0;
|
|
10333
|
+
while (last === 0) {
|
|
10334
|
+
last = reader.bits(1);
|
|
10335
|
+
const type = reader.bits(2);
|
|
10336
|
+
if (type === 0) {
|
|
10337
|
+
const length = reader.word();
|
|
10338
|
+
const complement = reader.word();
|
|
10339
|
+
if ((length ^ 65535) !== complement) {
|
|
10340
|
+
throw new InflateError("A stored block whose length doesn't match its complement");
|
|
10341
|
+
}
|
|
10342
|
+
output.append(reader.bytes(length));
|
|
10343
|
+
} else if (type === 1) {
|
|
10344
|
+
inflateBlock(reader, output, FIXED_LITERALS, FIXED_DISTANCES);
|
|
10345
|
+
} else if (type === 2) {
|
|
10346
|
+
const [literals, distances] = readDynamicCodes(reader);
|
|
10347
|
+
inflateBlock(reader, output, literals, distances);
|
|
10348
|
+
} else {
|
|
10349
|
+
throw new InflateError("A block of an unknown type");
|
|
10350
|
+
}
|
|
10351
|
+
}
|
|
10352
|
+
return output.bytes.subarray(0, output.length);
|
|
10353
|
+
};
|
|
10354
|
+
var readDynamicCodes = (reader) => {
|
|
10355
|
+
const literalCount = reader.bits(5) + 257;
|
|
10356
|
+
const distanceCount = reader.bits(5) + 1;
|
|
10357
|
+
const codeLengthCount = reader.bits(4) + 4;
|
|
10358
|
+
if (literalCount > 286 || distanceCount > 30) {
|
|
10359
|
+
throw new InflateError("A block with more codes than deflate has");
|
|
10360
|
+
}
|
|
10361
|
+
const codeLengths = new Uint8Array(19);
|
|
10362
|
+
for (let index = 0;index < codeLengthCount; index++) {
|
|
10363
|
+
codeLengths[CODE_LENGTH_ORDER[index]] = reader.bits(3);
|
|
10364
|
+
}
|
|
10365
|
+
const codeLengthCode = buildCode(codeLengths);
|
|
10366
|
+
const lengths = new Uint8Array(literalCount + distanceCount);
|
|
10367
|
+
let index = 0;
|
|
10368
|
+
while (index < lengths.length) {
|
|
10369
|
+
const symbol = reader.decode(codeLengthCode);
|
|
10370
|
+
if (symbol < 16) {
|
|
10371
|
+
lengths[index++] = symbol;
|
|
10372
|
+
continue;
|
|
10373
|
+
}
|
|
10374
|
+
let repeat;
|
|
10375
|
+
let value = 0;
|
|
10376
|
+
if (symbol === 16) {
|
|
10377
|
+
if (index === 0) {
|
|
10378
|
+
throw new InflateError("A repeat with nothing before it");
|
|
10379
|
+
}
|
|
10380
|
+
value = lengths[index - 1];
|
|
10381
|
+
repeat = 3 + reader.bits(2);
|
|
10382
|
+
} else if (symbol === 17) {
|
|
10383
|
+
repeat = 3 + reader.bits(3);
|
|
10384
|
+
} else {
|
|
10385
|
+
repeat = 11 + reader.bits(7);
|
|
10386
|
+
}
|
|
10387
|
+
if (index + repeat > lengths.length) {
|
|
10388
|
+
throw new InflateError("Code lengths that run past their table");
|
|
10389
|
+
}
|
|
10390
|
+
lengths.fill(value, index, index + repeat);
|
|
10391
|
+
index += repeat;
|
|
10392
|
+
}
|
|
10393
|
+
if (lengths[256] === 0) {
|
|
10394
|
+
throw new InflateError("A block with no code for its end");
|
|
10395
|
+
}
|
|
10396
|
+
return [buildCode(lengths.subarray(0, literalCount)), buildCode(lengths.subarray(literalCount))];
|
|
10397
|
+
};
|
|
10398
|
+
var inflateBlock = (reader, output, literals, distances) => {
|
|
10399
|
+
for (;; ) {
|
|
10400
|
+
const symbol = reader.decode(literals);
|
|
10401
|
+
if (symbol < 256) {
|
|
10402
|
+
output.push(symbol);
|
|
10403
|
+
} else if (symbol === 256) {
|
|
10404
|
+
return;
|
|
10405
|
+
} else {
|
|
10406
|
+
const lengthIndex = symbol - 257;
|
|
10407
|
+
if (lengthIndex >= LENGTH_BASES.length) {
|
|
10408
|
+
throw new InflateError("A length code deflate has no length for");
|
|
10409
|
+
}
|
|
10410
|
+
const length = LENGTH_BASES[lengthIndex] + reader.bits(LENGTH_EXTRA_BITS[lengthIndex]);
|
|
10411
|
+
const distanceSymbol = reader.decode(distances);
|
|
10412
|
+
if (distanceSymbol >= DISTANCE_BASES.length) {
|
|
10413
|
+
throw new InflateError("A distance code deflate has no distance for");
|
|
10414
|
+
}
|
|
10415
|
+
output.copy(DISTANCE_BASES[distanceSymbol] + reader.bits(DISTANCE_EXTRA_BITS[distanceSymbol]), length);
|
|
10416
|
+
}
|
|
10417
|
+
}
|
|
10418
|
+
};
|
|
10419
|
+
var crcTable = null;
|
|
10420
|
+
var crc32 = (bytes) => {
|
|
10421
|
+
if (!crcTable) {
|
|
10422
|
+
crcTable = new Uint32Array(256);
|
|
10423
|
+
for (let index = 0;index < 256; index++) {
|
|
10424
|
+
let value = index;
|
|
10425
|
+
for (let bit = 0;bit < 8; bit++) {
|
|
10426
|
+
value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
10427
|
+
}
|
|
10428
|
+
crcTable[index] = value >>> 0;
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
let crc = 4294967295;
|
|
10432
|
+
for (const byte of bytes) {
|
|
10433
|
+
crc = crcTable[(crc ^ byte) & 255] ^ crc >>> 8;
|
|
10434
|
+
}
|
|
10435
|
+
return (crc ^ 4294967295) >>> 0;
|
|
10436
|
+
};
|
|
10437
|
+
|
|
10438
|
+
// src/core/other-apps/zip.ts
|
|
10439
|
+
class ZipError extends Error {
|
|
10440
|
+
constructor(message) {
|
|
10441
|
+
super(message);
|
|
10442
|
+
this.name = "ZipError";
|
|
10443
|
+
}
|
|
10444
|
+
}
|
|
10445
|
+
var DEFAULT_ZIP_LIMITS = {
|
|
10446
|
+
entries: 20000,
|
|
10447
|
+
entrySize: 64 * 1024 * 1024,
|
|
10448
|
+
totalSize: 256 * 1024 * 1024
|
|
10449
|
+
};
|
|
10450
|
+
var END_OF_DIRECTORY = 101010256;
|
|
10451
|
+
var DIRECTORY_ENTRY = 33639248;
|
|
10452
|
+
var LOCAL_HEADER = 67324752;
|
|
10453
|
+
var isZip = (bytes) => {
|
|
10454
|
+
return bytes.length >= 4 && bytes[0] === 80 && bytes[1] === 75 && bytes[2] === 3 && bytes[3] === 4;
|
|
10455
|
+
};
|
|
10456
|
+
|
|
10457
|
+
class ZipArchive {
|
|
10458
|
+
bytes;
|
|
10459
|
+
view;
|
|
10460
|
+
limits;
|
|
10461
|
+
entries = new Map;
|
|
10462
|
+
unpacked = 0;
|
|
10463
|
+
constructor(bytes, limits = {}) {
|
|
10464
|
+
this.bytes = bytes;
|
|
10465
|
+
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
10466
|
+
this.limits = { ...DEFAULT_ZIP_LIMITS, ...limits };
|
|
10467
|
+
this.readDirectory();
|
|
10468
|
+
}
|
|
10469
|
+
get names() {
|
|
10470
|
+
return [...this.entries.keys()].filter((name) => !name.endsWith("/"));
|
|
10471
|
+
}
|
|
10472
|
+
has(name) {
|
|
10473
|
+
return this.entries.has(name);
|
|
10474
|
+
}
|
|
10475
|
+
read(name) {
|
|
10476
|
+
const entry = this.entries.get(name);
|
|
10477
|
+
if (!entry) {
|
|
10478
|
+
return null;
|
|
10479
|
+
}
|
|
10480
|
+
if (entry.flags & 1) {
|
|
10481
|
+
throw new ZipError(`${name} is encrypted`);
|
|
10482
|
+
}
|
|
10483
|
+
if (entry.size > this.limits.entrySize || this.unpacked + entry.size > this.limits.totalSize) {
|
|
10484
|
+
throw new ZipError(`${name} is too large to unpack`);
|
|
10485
|
+
}
|
|
10486
|
+
const offset = entry.headerOffset;
|
|
10487
|
+
if (offset + 30 > this.bytes.length || this.view.getUint32(offset, true) !== LOCAL_HEADER) {
|
|
10488
|
+
throw new ZipError(`The archive has no data where ${name} should start`);
|
|
10489
|
+
}
|
|
10490
|
+
const start = offset + 30 + this.view.getUint16(offset + 26, true) + this.view.getUint16(offset + 28, true);
|
|
10491
|
+
const end = start + entry.compressedSize;
|
|
10492
|
+
if (end > this.bytes.length) {
|
|
10493
|
+
throw new ZipError(`${name} runs past the end of the archive`);
|
|
10494
|
+
}
|
|
10495
|
+
const stored = this.bytes.subarray(start, end);
|
|
10496
|
+
let data;
|
|
10497
|
+
if (entry.method === 0) {
|
|
10498
|
+
data = stored;
|
|
10499
|
+
} else if (entry.method === 8) {
|
|
10500
|
+
try {
|
|
10501
|
+
data = inflate(stored, { expected: entry.size, limit: entry.size });
|
|
10502
|
+
} catch (error) {
|
|
10503
|
+
throw new ZipError(`${name} can't be unpacked: ${error instanceof InflateError ? error.message : String(error)}`);
|
|
10504
|
+
}
|
|
10505
|
+
} else {
|
|
10506
|
+
throw new ZipError(`${name} is compressed a way Orbit can't read (method ${entry.method})`);
|
|
10507
|
+
}
|
|
10508
|
+
if (data.length !== entry.size || crc32(data) !== entry.crc) {
|
|
10509
|
+
throw new ZipError(`${name} is damaged: its contents don't match its checksum`);
|
|
10510
|
+
}
|
|
10511
|
+
this.unpacked += data.length;
|
|
10512
|
+
return data;
|
|
10513
|
+
}
|
|
10514
|
+
text(name) {
|
|
10515
|
+
const data = this.read(name);
|
|
10516
|
+
return data === null ? null : new TextDecoder("utf-8").decode(data);
|
|
10517
|
+
}
|
|
10518
|
+
readDirectory() {
|
|
10519
|
+
const bytes = this.bytes;
|
|
10520
|
+
const view = this.view;
|
|
10521
|
+
let end = -1;
|
|
10522
|
+
for (let index = bytes.length - 22;index >= Math.max(0, bytes.length - 22 - 65535); index--) {
|
|
10523
|
+
if (view.getUint32(index, true) === END_OF_DIRECTORY) {
|
|
10524
|
+
end = index;
|
|
10525
|
+
break;
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
if (end === -1) {
|
|
10529
|
+
throw new ZipError("The file is not a zip archive, or its end is missing");
|
|
10530
|
+
}
|
|
10531
|
+
const disk = view.getUint16(end + 4, true);
|
|
10532
|
+
const count = view.getUint16(end + 10, true);
|
|
10533
|
+
const size = view.getUint32(end + 12, true);
|
|
10534
|
+
const offset = view.getUint32(end + 16, true);
|
|
10535
|
+
if (disk !== 0) {
|
|
10536
|
+
throw new ZipError("The archive spans several disks");
|
|
10537
|
+
}
|
|
10538
|
+
if (count === 65535 || size === 4294967295 || offset === 4294967295) {
|
|
10539
|
+
throw new ZipError("The archive needs ZIP64, which Orbit reads no map from");
|
|
10540
|
+
}
|
|
10541
|
+
if (count > this.limits.entries) {
|
|
10542
|
+
throw new ZipError(`The archive lists more than ${this.limits.entries} files`);
|
|
10543
|
+
}
|
|
10544
|
+
if (offset + size > end) {
|
|
10545
|
+
throw new ZipError("The archive's directory runs past its end");
|
|
10546
|
+
}
|
|
10547
|
+
const decoder = new TextDecoder("utf-8");
|
|
10548
|
+
let position = offset;
|
|
10549
|
+
for (let index = 0;index < count; index++) {
|
|
10550
|
+
if (position + 46 > end || view.getUint32(position, true) !== DIRECTORY_ENTRY) {
|
|
10551
|
+
throw new ZipError("The archive's directory is damaged");
|
|
10552
|
+
}
|
|
10553
|
+
const nameLength = view.getUint16(position + 28, true);
|
|
10554
|
+
const extraLength = view.getUint16(position + 30, true);
|
|
10555
|
+
const commentLength = view.getUint16(position + 32, true);
|
|
10556
|
+
if (position + 46 + nameLength > end) {
|
|
10557
|
+
throw new ZipError("The archive's directory is damaged");
|
|
10558
|
+
}
|
|
10559
|
+
const name = decoder.decode(bytes.subarray(position + 46, position + 46 + nameLength));
|
|
10560
|
+
const entry = {
|
|
10561
|
+
name,
|
|
10562
|
+
flags: view.getUint16(position + 8, true),
|
|
10563
|
+
method: view.getUint16(position + 10, true),
|
|
10564
|
+
crc: view.getUint32(position + 16, true),
|
|
10565
|
+
compressedSize: view.getUint32(position + 20, true),
|
|
10566
|
+
size: view.getUint32(position + 24, true),
|
|
10567
|
+
headerOffset: view.getUint32(position + 42, true)
|
|
10568
|
+
};
|
|
10569
|
+
if (!this.entries.has(name)) {
|
|
10570
|
+
this.entries.set(name, entry);
|
|
10571
|
+
}
|
|
10572
|
+
position += 46 + nameLength + extraLength + commentLength;
|
|
10573
|
+
}
|
|
10574
|
+
}
|
|
10575
|
+
}
|
|
10576
|
+
|
|
10577
|
+
// src/core/other-apps/xmind.ts
|
|
10578
|
+
var PICTURE_EXTENSIONS = new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "heic", "heif"]);
|
|
10579
|
+
var importXMind = (data, options = {}) => {
|
|
10580
|
+
const workbook = openWorkbook(data);
|
|
10581
|
+
const maps = [];
|
|
10582
|
+
if (workbook.json !== undefined) {
|
|
10583
|
+
for (const sheet of workbook.json) {
|
|
10584
|
+
if (isObject(sheet) && isObject(sheet.rootTopic)) {
|
|
10585
|
+
maps.push(new JsonSheetReader(workbook.archive).read(sheet));
|
|
10586
|
+
}
|
|
10587
|
+
}
|
|
10588
|
+
} else if (workbook.xml) {
|
|
10589
|
+
const styles = workbook.archive ? readStyles(workbook.archive) : new Map;
|
|
10590
|
+
const comments = workbook.archive ? readComments(workbook.archive) : new Map;
|
|
10591
|
+
for (const sheet of childElements(workbook.xml, "sheet")) {
|
|
10592
|
+
const topic = childElement(sheet, "topic");
|
|
10593
|
+
if (topic) {
|
|
10594
|
+
maps.push(new XmlSheetReader(workbook.archive, styles, comments).read(sheet, topic));
|
|
10595
|
+
}
|
|
10596
|
+
}
|
|
10597
|
+
}
|
|
10598
|
+
if (maps.length === 0) {
|
|
10599
|
+
throw new OrbitImportError("empty", `The workbook${options.name ? ` ${options.name}` : ""} has no sheet with a topic.`);
|
|
10600
|
+
}
|
|
10601
|
+
return { format: "xmind", maps };
|
|
10602
|
+
};
|
|
10603
|
+
var openWorkbook = (data) => {
|
|
10604
|
+
if (typeof data === "string") {
|
|
10605
|
+
return readContent(data, null, data.trimStart().startsWith("[") ? "json" : "xml");
|
|
10606
|
+
}
|
|
10607
|
+
if (!isZip(data)) {
|
|
10608
|
+
return readContent(new TextDecoder("utf-8").decode(data), null, "xml");
|
|
10609
|
+
}
|
|
10610
|
+
let archive;
|
|
10611
|
+
try {
|
|
10612
|
+
archive = new ZipArchive(data);
|
|
10613
|
+
} catch (error) {
|
|
10614
|
+
throw unreadable(error);
|
|
10615
|
+
}
|
|
10616
|
+
if (isEncrypted(archive)) {
|
|
10617
|
+
throw new OrbitImportError("password-protected", "The workbook is protected with a password: open it in XMind and save it without one first.");
|
|
10618
|
+
}
|
|
10619
|
+
try {
|
|
10620
|
+
if (archive.has("content.json")) {
|
|
10621
|
+
return readContent(archive.text("content.json") ?? "", archive, "json");
|
|
10622
|
+
}
|
|
10623
|
+
if (archive.has("content.xml")) {
|
|
10624
|
+
return readContent(archive.text("content.xml") ?? "", archive, "xml");
|
|
10625
|
+
}
|
|
10626
|
+
} catch (error) {
|
|
10627
|
+
throw unreadable(error);
|
|
10628
|
+
}
|
|
10629
|
+
throw new OrbitImportError("unreadable", "The file is a zip archive, but not an XMind workbook: it has no content.json or content.xml.");
|
|
10630
|
+
};
|
|
10631
|
+
var readContent = (text, archive, kind) => {
|
|
10632
|
+
if (kind === "json") {
|
|
10633
|
+
let json;
|
|
10634
|
+
try {
|
|
10635
|
+
json = JSON.parse(text);
|
|
10636
|
+
} catch (error) {
|
|
10637
|
+
throw new OrbitImportError("unreadable", `The workbook's content.json can't be read: ${error.message}.`);
|
|
10638
|
+
}
|
|
10639
|
+
if (!Array.isArray(json)) {
|
|
10640
|
+
throw new OrbitImportError("unreadable", "The workbook's content.json isn't a list of sheets.");
|
|
10641
|
+
}
|
|
10642
|
+
return { archive, json };
|
|
10643
|
+
}
|
|
10644
|
+
let xml;
|
|
10645
|
+
try {
|
|
10646
|
+
xml = parseXml(text);
|
|
10647
|
+
} catch (error) {
|
|
10648
|
+
throw unreadable(error);
|
|
10649
|
+
}
|
|
10650
|
+
if (xml.localName !== "xmap-content") {
|
|
10651
|
+
throw new OrbitImportError("unreadable", `The file isn't an XMind workbook: it starts with <${xml.name}>, not <xmap-content>.`);
|
|
10652
|
+
}
|
|
10653
|
+
return { archive, xml };
|
|
10654
|
+
};
|
|
10655
|
+
var unreadable = (error) => {
|
|
10656
|
+
if (error instanceof ZipError && /encrypted/.test(error.message)) {
|
|
10657
|
+
return new OrbitImportError("password-protected", "The workbook is protected with a password: open it in XMind and save it without one first.");
|
|
10658
|
+
}
|
|
10659
|
+
if (error instanceof ZipError || error instanceof MarkupError) {
|
|
10660
|
+
return new OrbitImportError("unreadable", `The workbook can't be read: ${error.message}.`);
|
|
10661
|
+
}
|
|
10662
|
+
return error instanceof OrbitImportError ? error : new OrbitImportError("unreadable", `The workbook can't be read: ${error.message}.`);
|
|
10663
|
+
};
|
|
10664
|
+
var isEncrypted = (archive) => {
|
|
10665
|
+
try {
|
|
10666
|
+
const manifest = archive.has("META-INF/manifest.xml") ? archive.text("META-INF/manifest.xml") ?? "" : "";
|
|
10667
|
+
const json = archive.has("manifest.json") ? archive.text("manifest.json") ?? "" : "";
|
|
10668
|
+
return /<encryption-data/.test(manifest) || /"encryption/.test(json);
|
|
10669
|
+
} catch {
|
|
10670
|
+
return false;
|
|
10671
|
+
}
|
|
10672
|
+
};
|
|
10673
|
+
var isObject = (value) => {
|
|
10674
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10675
|
+
};
|
|
10676
|
+
var asString = (value) => {
|
|
10677
|
+
return typeof value === "string" ? value : undefined;
|
|
10678
|
+
};
|
|
10679
|
+
|
|
10680
|
+
class SheetReader {
|
|
10681
|
+
archive;
|
|
10682
|
+
report = new ImportReport;
|
|
10683
|
+
media = {};
|
|
10684
|
+
topics = new Map;
|
|
10685
|
+
pictureNames = new Map;
|
|
10686
|
+
constructor(archive) {
|
|
10687
|
+
this.archive = archive;
|
|
10688
|
+
}
|
|
10689
|
+
finish(root) {
|
|
10690
|
+
return { document: buildDocument(root, this.report), media: this.media, warnings: this.report.warnings };
|
|
10691
|
+
}
|
|
10692
|
+
readLink(href, idea) {
|
|
10693
|
+
const address = href?.trim();
|
|
10694
|
+
if (!address) {
|
|
10695
|
+
return;
|
|
10696
|
+
}
|
|
10697
|
+
const topic = /^xmind:#(.+)$/.exec(address);
|
|
10698
|
+
const kept = address.startsWith("#") ? null : keptAddress(address);
|
|
10699
|
+
if (topic) {
|
|
10700
|
+
idea.ideaLinks.push({ target: topic[1] });
|
|
10701
|
+
} else if (kept) {
|
|
10702
|
+
idea.link = kept;
|
|
10703
|
+
} else if (/^xap:/.test(address)) {
|
|
10704
|
+
if (PICTURE_EXTENSIONS.has(extensionOf(address)) && !idea.image) {
|
|
10705
|
+
idea.image = this.picture(address, idea.text) ?? undefined;
|
|
10706
|
+
} else {
|
|
10707
|
+
this.report.add("attachments-not-kept", idea.text || address.replace(/^.*\//, ""));
|
|
10708
|
+
}
|
|
10709
|
+
} else {
|
|
10710
|
+
this.report.add("local-links-not-kept", address);
|
|
10711
|
+
}
|
|
10712
|
+
}
|
|
10713
|
+
picture(src, example) {
|
|
10714
|
+
if (/^https?:\/\/\S+$/i.test(src)) {
|
|
10715
|
+
return src;
|
|
10716
|
+
}
|
|
10717
|
+
const inside = /^xap:(.+)$/.exec(src)?.[1];
|
|
10718
|
+
if (!inside || !this.archive) {
|
|
10719
|
+
this.report.add("pictures-not-kept", example || src.replace(/^.*[/\\]/, ""));
|
|
10720
|
+
return null;
|
|
10721
|
+
}
|
|
10722
|
+
if (!this.pictureNames.has(inside)) {
|
|
10723
|
+
this.pictureNames.set(inside, this.unpackPicture(inside));
|
|
10724
|
+
}
|
|
10725
|
+
const name = this.pictureNames.get(inside) ?? null;
|
|
10726
|
+
if (!name) {
|
|
10727
|
+
this.report.add("pictures-not-kept", example || inside.replace(/^.*\//, ""));
|
|
10728
|
+
}
|
|
10729
|
+
return name;
|
|
10730
|
+
}
|
|
10731
|
+
unpackPicture(path) {
|
|
10732
|
+
const extension = extensionOf(path);
|
|
10733
|
+
if (!PICTURE_EXTENSIONS.has(extension)) {
|
|
10734
|
+
return null;
|
|
10735
|
+
}
|
|
10736
|
+
let bytes;
|
|
10737
|
+
try {
|
|
10738
|
+
bytes = this.archive?.read(path) ?? null;
|
|
10739
|
+
} catch {
|
|
10740
|
+
bytes = null;
|
|
10741
|
+
}
|
|
10742
|
+
if (!bytes) {
|
|
10743
|
+
return null;
|
|
10744
|
+
}
|
|
10745
|
+
const base = path.replace(/^.*\//, "").replace(/\.[^.]*$/, "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[^A-Za-z0-9]+/, "") || "picture";
|
|
10746
|
+
let name = `media/${base}.${extension}`;
|
|
10747
|
+
for (let suffix = 2;this.media[name] !== undefined; suffix++) {
|
|
10748
|
+
name = `media/${base}-${suffix}.${extension}`;
|
|
10749
|
+
}
|
|
10750
|
+
this.media[name] = bytes;
|
|
10751
|
+
return name;
|
|
10752
|
+
}
|
|
10753
|
+
noteAddresses(example) {
|
|
10754
|
+
return {
|
|
10755
|
+
link: (href) => {
|
|
10756
|
+
const topic = /^xmind:#(.+)$/.exec(href);
|
|
10757
|
+
if (topic) {
|
|
10758
|
+
return sourceLink(topic[1]);
|
|
10759
|
+
}
|
|
10760
|
+
const kept = href.startsWith("#") ? null : keptAddress(href);
|
|
10761
|
+
if (kept) {
|
|
10762
|
+
return kept;
|
|
10763
|
+
}
|
|
10764
|
+
this.report.add("local-links-not-kept", href);
|
|
10765
|
+
return null;
|
|
10766
|
+
},
|
|
10767
|
+
picture: (src) => this.picture(src, example)
|
|
10768
|
+
};
|
|
10769
|
+
}
|
|
10770
|
+
htmlNote(html, idea) {
|
|
10771
|
+
const note = noteFromHtml(html, this.noteAddresses(idea.text));
|
|
10772
|
+
if (note?.format === "plain") {
|
|
10773
|
+
this.report.add("formatting-not-kept", idea.text || undefined);
|
|
10774
|
+
}
|
|
10775
|
+
return note;
|
|
10776
|
+
}
|
|
10777
|
+
readMarker(markerId, idea) {
|
|
10778
|
+
const meaning = xmindMarker(markerId);
|
|
10779
|
+
if (!meaning) {
|
|
10780
|
+
this.report.add("icons-not-kept", markerId);
|
|
10781
|
+
} else if ("icon" in meaning) {
|
|
10782
|
+
idea.icons.push(meaning.icon);
|
|
10783
|
+
} else if ("priority" in meaning) {
|
|
10784
|
+
idea.priority ??= meaning.priority;
|
|
10785
|
+
} else {
|
|
10786
|
+
idea.status ??= meaning.status;
|
|
10787
|
+
}
|
|
10788
|
+
}
|
|
10789
|
+
relate(fromId, toId, title) {
|
|
10790
|
+
const from = fromId ? this.topics.get(fromId) : undefined;
|
|
10791
|
+
if (!from || !toId) {
|
|
10792
|
+
this.report.add("missing-link-targets", title || undefined);
|
|
10793
|
+
return;
|
|
10794
|
+
}
|
|
10795
|
+
const label = title?.trim();
|
|
10796
|
+
from.ideaLinks.push(label ? { target: toId, label } : { target: toId });
|
|
10797
|
+
}
|
|
10798
|
+
adopt(idea, children, code) {
|
|
10799
|
+
for (const child of children) {
|
|
10800
|
+
this.report.add(code, child.text || undefined);
|
|
10801
|
+
idea.children.push(child);
|
|
10802
|
+
}
|
|
10803
|
+
}
|
|
10804
|
+
boundaryNote(titles, idea) {
|
|
10805
|
+
for (const title of titles) {
|
|
10806
|
+
const text = title.trim();
|
|
10807
|
+
if (text !== "") {
|
|
10808
|
+
this.report.add("boundaries-in-notes", text);
|
|
10809
|
+
const note = noteFromPlainText(text);
|
|
10810
|
+
if (note) {
|
|
10811
|
+
idea.notes.push(note);
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
var extensionOf = (path) => {
|
|
10818
|
+
return /\.([A-Za-z0-9]+)$/.exec(path)?.[1].toLowerCase() ?? "";
|
|
10819
|
+
};
|
|
10820
|
+
|
|
10821
|
+
class JsonSheetReader extends SheetReader {
|
|
10822
|
+
read(sheet) {
|
|
10823
|
+
const root = this.topic(sheet.rootTopic, 0);
|
|
10824
|
+
for (const relationship of Array.isArray(sheet.relationships) ? sheet.relationships : []) {
|
|
10825
|
+
if (isObject(relationship)) {
|
|
10826
|
+
this.relate(asString(relationship.end1Id), asString(relationship.end2Id), asString(relationship.title));
|
|
10827
|
+
}
|
|
10828
|
+
}
|
|
10829
|
+
return this.finish(root);
|
|
10830
|
+
}
|
|
10831
|
+
topic(topic, depth) {
|
|
10832
|
+
if (depth > 500) {
|
|
10833
|
+
throw new OrbitImportError("too-large", "The sheet goes more than 500 levels deep.");
|
|
10834
|
+
}
|
|
10835
|
+
const idea = newIdea(asString(topic.title) ?? "");
|
|
10836
|
+
const id = asString(topic.id);
|
|
10837
|
+
if (id) {
|
|
10838
|
+
idea.sourceId = id;
|
|
10839
|
+
this.topics.set(id, idea);
|
|
10840
|
+
}
|
|
10841
|
+
const note = this.note(topic.notes, idea);
|
|
10842
|
+
if (note) {
|
|
10843
|
+
idea.notes.push(note);
|
|
10844
|
+
}
|
|
10845
|
+
if (Array.isArray(topic.labels)) {
|
|
10846
|
+
idea.tags.push(...topic.labels.filter((label) => typeof label === "string"));
|
|
10847
|
+
}
|
|
10848
|
+
for (const marker of Array.isArray(topic.markers) ? topic.markers : []) {
|
|
10849
|
+
const markerId = isObject(marker) ? asString(marker.markerId) : undefined;
|
|
10850
|
+
if (markerId) {
|
|
10851
|
+
this.readMarker(markerId, idea);
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
this.readLink(asString(topic.href), idea);
|
|
10855
|
+
if (isObject(topic.image)) {
|
|
10856
|
+
const src = asString(topic.image.src);
|
|
10857
|
+
if (src) {
|
|
10858
|
+
idea.image ??= this.picture(src, idea.text) ?? undefined;
|
|
10859
|
+
}
|
|
10860
|
+
}
|
|
10861
|
+
if (topic.branch === "folded") {
|
|
10862
|
+
idea.collapsed = true;
|
|
10863
|
+
}
|
|
10864
|
+
const style = isObject(topic.style) && isObject(topic.style.properties) ? topic.style.properties : {};
|
|
10865
|
+
const fill = asString(style["svg:fill"]);
|
|
10866
|
+
if (fill && /^#[0-9A-Fa-f]{6}$/.test(fill)) {
|
|
10867
|
+
idea.color = fill.toUpperCase();
|
|
10868
|
+
}
|
|
10869
|
+
const children = isObject(topic.children) ? topic.children : {};
|
|
10870
|
+
const kind = (name) => (Array.isArray(children[name]) ? children[name] : []).filter(isObject).map((child) => this.topic(child, depth + 1));
|
|
10871
|
+
idea.children.push(...kind("attached"));
|
|
10872
|
+
this.adopt(idea, kind("summary"), "summaries-moved");
|
|
10873
|
+
this.adopt(idea, kind("callout"), "callouts-moved");
|
|
10874
|
+
this.adopt(idea, kind("detached"), "floating-ideas-moved");
|
|
10875
|
+
const boundaries = Array.isArray(topic.boundaries) ? topic.boundaries.filter(isObject).map((boundary) => asString(boundary.title) ?? "") : [];
|
|
10876
|
+
this.boundaryNote(boundaries, idea);
|
|
10877
|
+
return idea;
|
|
10878
|
+
}
|
|
10879
|
+
note(notes, idea) {
|
|
10880
|
+
if (!isObject(notes)) {
|
|
10881
|
+
return;
|
|
10882
|
+
}
|
|
10883
|
+
const html = isObject(notes.realHTML) ? asString(notes.realHTML.content) : undefined;
|
|
10884
|
+
if (html) {
|
|
10885
|
+
return this.htmlNote(html, idea);
|
|
10886
|
+
}
|
|
10887
|
+
const paragraphs = isObject(notes.html) && isObject(notes.html.content) && Array.isArray(notes.html.content.paragraphs) ? notes.html.content.paragraphs : null;
|
|
10888
|
+
if (paragraphs) {
|
|
10889
|
+
const blocks = paragraphs.filter(isObject).map((paragraph) => ({
|
|
10890
|
+
kind: "paragraph",
|
|
10891
|
+
inline: (Array.isArray(paragraph.spans) ? paragraph.spans : []).filter(isObject).map((span) => styledSpan(span))
|
|
10892
|
+
}));
|
|
10893
|
+
const note = noteFromBlocks(blocks);
|
|
10894
|
+
if (note?.format === "plain") {
|
|
10895
|
+
this.report.add("formatting-not-kept", idea.text || undefined);
|
|
10896
|
+
}
|
|
10897
|
+
return note;
|
|
10898
|
+
}
|
|
10899
|
+
return noteFromPlainText(isObject(notes.plain) ? asString(notes.plain.content) : undefined);
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
var styledSpan = (span) => {
|
|
10903
|
+
const properties = isObject(span.style) && isObject(span.style.properties) ? span.style.properties : {};
|
|
10904
|
+
let piece = { kind: "text", text: (asString(span.text) ?? "").replace(/\s+/g, " ") };
|
|
10905
|
+
if (/line-through/.test(asString(properties["fo:text-decoration"]) ?? "")) {
|
|
10906
|
+
piece = { kind: "strike", children: [piece] };
|
|
10907
|
+
}
|
|
10908
|
+
if (asString(properties["fo:font-style"]) === "italic") {
|
|
10909
|
+
piece = { kind: "emphasis", children: [piece] };
|
|
10910
|
+
}
|
|
10911
|
+
if (/^(bold|[6-9]00)$/.test(asString(properties["fo:font-weight"]) ?? "")) {
|
|
10912
|
+
piece = { kind: "strong", children: [piece] };
|
|
10913
|
+
}
|
|
10914
|
+
return piece;
|
|
10915
|
+
};
|
|
10916
|
+
|
|
10917
|
+
class XmlSheetReader extends SheetReader {
|
|
10918
|
+
styles;
|
|
10919
|
+
comments;
|
|
10920
|
+
constructor(archive, styles, comments) {
|
|
10921
|
+
super(archive);
|
|
10922
|
+
this.styles = styles;
|
|
10923
|
+
this.comments = comments;
|
|
10924
|
+
}
|
|
10925
|
+
read(sheet, topic) {
|
|
10926
|
+
const root = this.topic(topic);
|
|
10927
|
+
for (const relationships of childElements(sheet, "relationships")) {
|
|
10928
|
+
for (const relationship of childElements(relationships, "relationship")) {
|
|
10929
|
+
const title = childElement(relationship, "title");
|
|
10930
|
+
this.relate(relationship.attributes.end1, relationship.attributes.end2, title ? textContent(title) : undefined);
|
|
10931
|
+
}
|
|
10932
|
+
}
|
|
10933
|
+
return this.finish(root);
|
|
10934
|
+
}
|
|
10935
|
+
topic(topic) {
|
|
10936
|
+
const title = childElement(topic, "title");
|
|
10937
|
+
const idea = newIdea(title ? textContent(title) : "");
|
|
10938
|
+
const id = topic.attributes.id;
|
|
10939
|
+
if (id) {
|
|
10940
|
+
idea.sourceId = id;
|
|
10941
|
+
this.topics.set(id, idea);
|
|
10942
|
+
}
|
|
10943
|
+
const notes = childElement(topic, "notes");
|
|
10944
|
+
if (notes) {
|
|
10945
|
+
const html = childElement(notes, "html");
|
|
10946
|
+
const plain = childElement(notes, "plain");
|
|
10947
|
+
const note = html ? this.htmlNote(html, idea) : noteFromPlainText(plain ? textContent(plain) : undefined);
|
|
10948
|
+
if (note) {
|
|
10949
|
+
idea.notes.push(note);
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10952
|
+
for (const comment of id ? this.comments.get(id) ?? [] : []) {
|
|
10953
|
+
const note = noteFromPlainText(comment);
|
|
10954
|
+
if (note) {
|
|
10955
|
+
idea.notes.push(note);
|
|
10956
|
+
}
|
|
10957
|
+
}
|
|
10958
|
+
for (const labels of childElements(topic, "labels")) {
|
|
10959
|
+
idea.tags.push(...childElements(labels, "label").map((label) => textContent(label)));
|
|
10960
|
+
}
|
|
10961
|
+
for (const markers of childElements(topic, "marker-refs")) {
|
|
10962
|
+
for (const marker of childElements(markers, "marker-ref")) {
|
|
10963
|
+
const markerId = marker.attributes["marker-id"];
|
|
10964
|
+
if (markerId) {
|
|
10965
|
+
this.readMarker(markerId, idea);
|
|
10966
|
+
}
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
10969
|
+
this.readLink(attributeOf(topic, "href"), idea);
|
|
10970
|
+
const image = childElement(topic, "img");
|
|
10971
|
+
const src = image ? attributeOf(image, "src") : undefined;
|
|
10972
|
+
if (src) {
|
|
10973
|
+
idea.image ??= this.picture(src, idea.text) ?? undefined;
|
|
10974
|
+
}
|
|
10975
|
+
if (topic.attributes.branch === "folded") {
|
|
10976
|
+
idea.collapsed = true;
|
|
10977
|
+
}
|
|
10978
|
+
const fill = this.styles.get(topic.attributes["style-id"] ?? "");
|
|
10979
|
+
if (fill) {
|
|
10980
|
+
idea.color = fill;
|
|
10981
|
+
}
|
|
10982
|
+
const children = childElement(topic, "children");
|
|
10983
|
+
const kind = (type) => (children ? childElements(children, "topics") : []).filter((topics) => (topics.attributes.type ?? "attached") === type).flatMap((topics) => childElements(topics, "topic").map((child) => this.topic(child)));
|
|
10984
|
+
idea.children.push(...kind("attached"));
|
|
10985
|
+
this.adopt(idea, kind("summary"), "summaries-moved");
|
|
10986
|
+
this.adopt(idea, kind("callout"), "callouts-moved");
|
|
10987
|
+
this.adopt(idea, kind("detached"), "floating-ideas-moved");
|
|
10988
|
+
const boundaries = childElements(topic, "boundaries").flatMap((list) => childElements(list, "boundary")).map((boundary) => {
|
|
10989
|
+
const boundaryTitle = childElement(boundary, "title");
|
|
10990
|
+
return boundaryTitle ? textContent(boundaryTitle) : "";
|
|
10991
|
+
});
|
|
10992
|
+
this.boundaryNote(boundaries, idea);
|
|
10993
|
+
return idea;
|
|
10994
|
+
}
|
|
10995
|
+
}
|
|
10996
|
+
var readStyles = (archive) => {
|
|
10997
|
+
const fills = new Map;
|
|
10998
|
+
try {
|
|
10999
|
+
const text = archive.has("styles.xml") ? archive.text("styles.xml") : null;
|
|
11000
|
+
if (!text) {
|
|
11001
|
+
return fills;
|
|
11002
|
+
}
|
|
11003
|
+
const pending = [parseXml(text)];
|
|
11004
|
+
while (pending.length > 0) {
|
|
11005
|
+
const element = pending.pop();
|
|
11006
|
+
for (const child of element.children) {
|
|
11007
|
+
if (!isElement(child)) {
|
|
11008
|
+
continue;
|
|
11009
|
+
}
|
|
11010
|
+
if (child.localName === "style" && child.attributes.type === "topic" && child.attributes.id) {
|
|
11011
|
+
const properties = childElement(child, "topic-properties");
|
|
11012
|
+
const fill = properties ? attributeOf(properties, "fill") : undefined;
|
|
11013
|
+
if (fill && /^#[0-9A-Fa-f]{6}$/.test(fill)) {
|
|
11014
|
+
fills.set(child.attributes.id, fill.toUpperCase());
|
|
11015
|
+
}
|
|
11016
|
+
}
|
|
11017
|
+
pending.push(child);
|
|
11018
|
+
}
|
|
11019
|
+
}
|
|
11020
|
+
} catch {}
|
|
11021
|
+
return fills;
|
|
11022
|
+
};
|
|
11023
|
+
var readComments = (archive) => {
|
|
11024
|
+
const comments = new Map;
|
|
11025
|
+
try {
|
|
11026
|
+
const text = archive.has("comments.xml") ? archive.text("comments.xml") : null;
|
|
11027
|
+
if (!text) {
|
|
11028
|
+
return comments;
|
|
11029
|
+
}
|
|
11030
|
+
for (const comment of childElements(parseXml(text), "comment")) {
|
|
11031
|
+
const topic = comment.attributes["object-id"];
|
|
11032
|
+
const content = childElement(comment, "content");
|
|
11033
|
+
const words = content ? textContent(content).replace(/\r\n?/g, `
|
|
11034
|
+
`).trim() : "";
|
|
11035
|
+
if (topic && words !== "") {
|
|
11036
|
+
const author = (comment.attributes.author ?? "").trim();
|
|
11037
|
+
comments.set(topic, [...comments.get(topic) ?? [], author ? `${author}: ${words}` : words]);
|
|
11038
|
+
}
|
|
11039
|
+
}
|
|
11040
|
+
} catch {}
|
|
11041
|
+
return comments;
|
|
11042
|
+
};
|
|
11043
|
+
|
|
11044
|
+
// src/core/other-apps/import.ts
|
|
11045
|
+
var EXTENSIONS = { opml: "opml", mm: "freemind", xmind: "xmind" };
|
|
11046
|
+
var detectImportFormat = (data, name) => {
|
|
11047
|
+
const extension = /\.([A-Za-z0-9]+)$/.exec(name ?? "")?.[1].toLowerCase() ?? "";
|
|
11048
|
+
return formatInside(data) ?? (Object.prototype.hasOwnProperty.call(EXTENSIONS, extension) ? EXTENSIONS[extension] : null);
|
|
11049
|
+
};
|
|
11050
|
+
var formatInside = (data) => {
|
|
11051
|
+
if (typeof data !== "string" && isZip(data)) {
|
|
11052
|
+
try {
|
|
11053
|
+
const archive = new ZipArchive(data);
|
|
11054
|
+
return archive.has("content.json") || archive.has("content.xml") ? "xmind" : null;
|
|
11055
|
+
} catch {
|
|
11056
|
+
return null;
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
11059
|
+
const text = typeof data === "string" ? data.slice(0, 4096) : decodeMarkup(data.subarray(0, 4096));
|
|
11060
|
+
const root = /^(?:\s|<\?[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE[^>]*>)*<([A-Za-z_][\w.:-]*)/.exec(text.replace(/^\ufeff/, ""))?.[1];
|
|
11061
|
+
const formats = { opml: "opml", map: "freemind", "xmap-content": "xmind" };
|
|
11062
|
+
return root && Object.prototype.hasOwnProperty.call(formats, root) ? formats[root] : null;
|
|
11063
|
+
};
|
|
11064
|
+
var importMap = (data, options = {}) => {
|
|
11065
|
+
const format = options.format ?? detectImportFormat(data, options.name);
|
|
11066
|
+
switch (format) {
|
|
11067
|
+
case "opml":
|
|
11068
|
+
return importOPML(data, options);
|
|
11069
|
+
case "freemind":
|
|
11070
|
+
return importFreeMind(data, options);
|
|
11071
|
+
case "xmind":
|
|
11072
|
+
return importXMind(data, options);
|
|
11073
|
+
default:
|
|
11074
|
+
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).`);
|
|
11075
|
+
}
|
|
11076
|
+
};
|
|
11077
|
+
|
|
11078
|
+
// bin/example.ts
|
|
11079
|
+
var EXAMPLE_MAP = `---
|
|
11080
|
+
title: Product roadmap
|
|
11081
|
+
theme: paper
|
|
11082
|
+
---
|
|
11083
|
+
# \uD83D\uDE80 Product roadmap
|
|
11084
|
+
What ships this year. Click a planet to fly into it; flick the orbit to spin it.
|
|
11085
|
+
|
|
11086
|
+
## \uD83D\uDD2D Discovery <!-- orbit status: done -->
|
|
11087
|
+
Six weeks of research before we commit to scope.
|
|
11088
|
+
- [x] User interviews
|
|
11089
|
+
- [x] Pricing survey
|
|
11090
|
+
- Competitor teardown #research
|
|
11091
|
+
|
|
11092
|
+
## \uD83C\uDFA8 Design
|
|
11093
|
+
<!-- orbit
|
|
11094
|
+
status: active
|
|
11095
|
+
color: "#C6B4F2"
|
|
11096
|
+
-->
|
|
11097
|
+

|
|
11098
|
+
|
|
11099
|
+
- [/] Design system refresh
|
|
11100
|
+
- Onboarding flow
|
|
11101
|
+
Three screens, one decision each.
|
|
11102
|
+
- Dark mode
|
|
11103
|
+
|
|
11104
|
+
## \uD83D\uDEE0 Build <!-- orbit status: active -->
|
|
11105
|
+
### Frontend
|
|
11106
|
+
- App shell
|
|
11107
|
+
- Offline sync <!-- orbit status: blocked -->
|
|
11108
|
+
Waits on the [search index](#search-index).
|
|
11109
|
+
### Backend
|
|
11110
|
+
- Auth service <!-- orbit priority: 1, status: active -->
|
|
11111
|
+
- Search index
|
|
11112
|
+
|
|
11113
|
+
## \uD83D\uDCC8 [Growth](https://example.com/growth-plan)
|
|
11114
|
+
- SEO content #marketing
|
|
11115
|
+
- Referral program <!-- orbit owner: Ana -->
|
|
11116
|
+
|
|
11117
|
+
## \uD83C\uDFAC Launch
|
|
11118
|
+
[Launch trailer](https://www.youtube.com/watch?v=aqz-KE-bpKQ)
|
|
11119
|
+
|
|
11120
|
+
- [ ] Launch event
|
|
11121
|
+
- [ ] Press kit
|
|
11122
|
+
`;
|
|
11123
|
+
|
|
11124
|
+
// bin/orbit.ts
|
|
11125
|
+
var USAGE = `orbit: check, render and convert Orbit mind maps
|
|
11126
|
+
|
|
11127
|
+
Usage:
|
|
11128
|
+
orbit validate [--json] [--loose] [--tree] <file|->
|
|
11129
|
+
Check a map and list every problem, with its line
|
|
11130
|
+
orbit render [options] <file|-> Render a map to a standalone SVG
|
|
11131
|
+
orbit export --html [options] <file|-> One web page with Orbit, the map and its pictures inside,
|
|
11132
|
+
to explore it or present it in any browser, offline
|
|
11133
|
+
orbit convert --to <md|yaml|opml> [--out <file>] <file|->
|
|
11134
|
+
Convert between the markdown and YAML flavors and OPML, and
|
|
11135
|
+
bring in maps from other apps: OPML, FreeMind and Freeplane
|
|
11136
|
+
(.mm) and XMind (.xmind)
|
|
11137
|
+
orbit example [--yaml] Print a map with the main features, to copy
|
|
11138
|
+
orbit help Show this message (so does --help after any command)
|
|
11139
|
+
orbit --version Print the version of Orbit
|
|
11140
|
+
|
|
11141
|
+
Validate options:
|
|
11142
|
+
--json Print the result as JSON: valid, flavor, root, nodes, depth (the
|
|
11143
|
+
levels below the root) and diagnostics
|
|
11144
|
+
--loose Exit 0 even when there are problems the map can work around
|
|
11145
|
+
--tree Print the map as Orbit reads it, a node a line with its #id
|
|
11146
|
+
(with --json, as "tree"), to see what became a node and find ids
|
|
11147
|
+
|
|
11148
|
+
Render options:
|
|
11149
|
+
--out <file> Write the SVG to a file instead of stdout
|
|
11150
|
+
--width <px> Width of the drawing (default 1200)
|
|
11151
|
+
--height <px> Height of the drawing (default 800)
|
|
11152
|
+
--theme <name> paper, night, glass, glass-night, cosmos, blueprint, ink, contrast,
|
|
11153
|
+
flat, flat-night, material, material-night, liquid, liquid-night,
|
|
11154
|
+
or auto, which draws paper: a picture can't follow the reader's
|
|
11155
|
+
light or dark mode (default: the document's, or paper)
|
|
11156
|
+
--view <focus|all> focus draws the map as it opens, where only the node at the center
|
|
11157
|
+
and its children have labels; all draws the whole map (default:
|
|
11158
|
+
the document's, or focus)
|
|
11159
|
+
--focus <node-id> The node at the center in focus mode (default: the root)
|
|
11160
|
+
--language <tag> The language of the drawing's few words, such as an untitled
|
|
11161
|
+
map's name: en or es (default: en)
|
|
11162
|
+
|
|
11163
|
+
Export options:
|
|
11164
|
+
--out <file> Write the page to a file instead of stdout
|
|
11165
|
+
--title <text> The page's title (default: the map's title, or its root's text)
|
|
11166
|
+
--theme, --view, --focus, --language
|
|
11167
|
+
As for render; the page follows the reader's light or dark mode
|
|
11168
|
+
with --theme auto, and speaks the language's words
|
|
11169
|
+
|
|
11170
|
+
Convert options:
|
|
11171
|
+
--to <md|yaml|opml> The flavor or format to write
|
|
11172
|
+
--out <file> Write to a file instead of stdout. An XMind workbook's pictures go
|
|
11173
|
+
in media/ next to it, and its other sheets in <file>-2, -3 and on
|
|
11174
|
+
|
|
11175
|
+
Use "-" as the file to read from stdin. Output goes to stdout; problems go
|
|
11176
|
+
to stderr as file:line: severity: message [code].
|
|
11177
|
+
|
|
11178
|
+
Exit codes: 0 when it worked (for validate, when the map is valid), 1 when the
|
|
11179
|
+
map has problems or can't be read, and 2 for a mistake in the command itself.
|
|
11180
|
+
|
|
11181
|
+
The whole format and API, for people and coding agents: llms-full.txt in this
|
|
11182
|
+
package, or https://cdn.jsdelivr.net/npm/@restless-dreams/orbit/llms-full.txt
|
|
11183
|
+
`;
|
|
11184
|
+
var packageVersion = () => {
|
|
11185
|
+
for (const path of ["../package.json", "../../package.json"]) {
|
|
11186
|
+
try {
|
|
11187
|
+
const manifest = JSON.parse(readFileSync(new URL(path, import.meta.url), "utf8"));
|
|
11188
|
+
if (manifest.name === "@restless-dreams/orbit" && manifest.version) {
|
|
11189
|
+
return manifest.version;
|
|
11190
|
+
}
|
|
11191
|
+
} catch {}
|
|
11192
|
+
}
|
|
11193
|
+
return "unknown";
|
|
11194
|
+
};
|
|
11195
|
+
var isYamlFile = (file) => {
|
|
11196
|
+
return /\.ya?ml$/i.test(file);
|
|
11197
|
+
};
|
|
11198
|
+
var parseFile = (file, source, options = {}) => {
|
|
11199
|
+
return isYamlFile(file) ? parseOrbitYAML(source, options) : parseOrbit(source, options);
|
|
11200
|
+
};
|
|
11201
|
+
var readInput = (path) => {
|
|
11202
|
+
return readFileSync(path === "-" ? 0 : path, "utf8");
|
|
11203
|
+
};
|
|
11204
|
+
var runCli = (argv) => {
|
|
11205
|
+
const [command, ...rest] = argv;
|
|
11206
|
+
if (command === undefined || command === "help" || command === "-h" || command === "--help" || rest.includes("--help") || rest.includes("-h")) {
|
|
11207
|
+
process.stdout.write(USAGE);
|
|
11208
|
+
return command === undefined ? 2 : 0;
|
|
11209
|
+
}
|
|
11210
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
11211
|
+
process.stdout.write(`${packageVersion()}
|
|
11212
|
+
`);
|
|
11213
|
+
return 0;
|
|
11214
|
+
}
|
|
11215
|
+
try {
|
|
11216
|
+
if (command === "convert") {
|
|
11217
|
+
return convertCommand(rest.map((argument) => argument === "-t" ? "--to" : argument));
|
|
11218
|
+
}
|
|
11219
|
+
if (command === "example") {
|
|
11220
|
+
const yaml = rest.includes("--yaml");
|
|
11221
|
+
if (rest.some((argument) => argument !== "--yaml")) {
|
|
11222
|
+
process.stderr.write(`orbit example: the only option is --yaml
|
|
11223
|
+
${USAGE}`);
|
|
11224
|
+
return 2;
|
|
11225
|
+
}
|
|
11226
|
+
process.stdout.write(yaml ? serializeOrbitYAML(parseOrbit(EXAMPLE_MAP)) : EXAMPLE_MAP);
|
|
11227
|
+
return 0;
|
|
11228
|
+
}
|
|
11229
|
+
if (command === "export") {
|
|
11230
|
+
return exportCommand(rest);
|
|
11231
|
+
}
|
|
11232
|
+
if (command === "render") {
|
|
11233
|
+
return renderCommand(rest);
|
|
11234
|
+
}
|
|
11235
|
+
if (command === "validate") {
|
|
11236
|
+
return validateCommand(rest);
|
|
11237
|
+
}
|
|
11238
|
+
process.stderr.write(`orbit: unknown command "${command}"
|
|
11239
|
+
${USAGE}`);
|
|
11240
|
+
return 2;
|
|
11241
|
+
} catch (err) {
|
|
11242
|
+
process.stderr.write(`error: ${err.message}
|
|
11243
|
+
`);
|
|
11244
|
+
return 1;
|
|
11245
|
+
}
|
|
11246
|
+
};
|
|
11247
|
+
var readOptions = (command, argumentsList, names) => {
|
|
11248
|
+
const values = {};
|
|
11249
|
+
let file = "";
|
|
11250
|
+
for (let index = 0;index < argumentsList.length; index++) {
|
|
11251
|
+
const argument = argumentsList[index];
|
|
11252
|
+
const name = argument.startsWith("--") ? argument.slice(2) : "";
|
|
11253
|
+
if (names.includes(name)) {
|
|
11254
|
+
const value = argumentsList[++index];
|
|
11255
|
+
if (value === undefined) {
|
|
11256
|
+
process.stderr.write(`orbit ${command}: ${argument} needs a value
|
|
11257
|
+
`);
|
|
11258
|
+
return 2;
|
|
11259
|
+
}
|
|
11260
|
+
values[name] = value;
|
|
11261
|
+
} else if (argument === "-" || !argument.startsWith("--")) {
|
|
11262
|
+
file = argument;
|
|
11263
|
+
} else {
|
|
11264
|
+
process.stderr.write(`orbit ${command}: unknown option ${argument}
|
|
11265
|
+
${USAGE}`);
|
|
11266
|
+
return 2;
|
|
11267
|
+
}
|
|
11268
|
+
}
|
|
11269
|
+
if (!file) {
|
|
11270
|
+
process.stderr.write(`orbit ${command}: missing <file>
|
|
11271
|
+
${USAGE}`);
|
|
11272
|
+
return 2;
|
|
11273
|
+
}
|
|
11274
|
+
return { values, file };
|
|
11275
|
+
};
|
|
11276
|
+
var checkLook = (command, values) => {
|
|
11277
|
+
if (values.theme !== undefined && !isOrbitThemePreset(values.theme)) {
|
|
11278
|
+
process.stderr.write(`orbit ${command}: unknown theme "${values.theme}". Themes: ${themeNames().join(", ")}.${didYouMean(values.theme, themeNames(), THEME_SYNONYMS)}
|
|
11279
|
+
`);
|
|
11280
|
+
return 2;
|
|
8625
11281
|
}
|
|
8626
11282
|
if (values.view !== undefined && values.view !== "focus" && values.view !== "all") {
|
|
8627
11283
|
process.stderr.write(`orbit ${command}: --view must be focus or all
|
|
@@ -8641,6 +11297,109 @@ var checkFocus = (command, values, doc) => {
|
|
|
8641
11297
|
}
|
|
8642
11298
|
return null;
|
|
8643
11299
|
};
|
|
11300
|
+
var writeAs = (to, doc) => {
|
|
11301
|
+
if (to === "opml") {
|
|
11302
|
+
return exportOPML(doc);
|
|
11303
|
+
}
|
|
11304
|
+
return /^(md|markdown)$/.test(to) ? serializeOrbitMarkdown(doc, { keepSource: false }) : serializeOrbitYAML(doc);
|
|
11305
|
+
};
|
|
11306
|
+
var convertCommand = (argumentsList) => {
|
|
11307
|
+
const values = {};
|
|
11308
|
+
let file = "";
|
|
11309
|
+
for (let index = 0;index < argumentsList.length; index++) {
|
|
11310
|
+
const argument = argumentsList[index];
|
|
11311
|
+
if (argument === "--to" || argument === "--out") {
|
|
11312
|
+
values[argument.slice(2)] = argumentsList[++index] ?? "";
|
|
11313
|
+
} else if (argument.startsWith("-") && argument !== "-") {
|
|
11314
|
+
process.stderr.write(`orbit convert: unknown option ${argument}
|
|
11315
|
+
${USAGE}`);
|
|
11316
|
+
return 2;
|
|
11317
|
+
} else {
|
|
11318
|
+
file = argument;
|
|
11319
|
+
}
|
|
11320
|
+
}
|
|
11321
|
+
const to = values.to ?? "";
|
|
11322
|
+
if (!file || !/^(md|markdown|yaml|yml|opml)$/.test(to)) {
|
|
11323
|
+
process.stderr.write(`orbit convert: expected --to <md|yaml|opml> <file|->
|
|
11324
|
+
${USAGE}`);
|
|
11325
|
+
return 2;
|
|
11326
|
+
}
|
|
11327
|
+
const bytes = new Uint8Array(readFileSync(file === "-" ? 0 : file));
|
|
11328
|
+
const format = detectImportFormat(bytes, file === "-" ? undefined : basename(file));
|
|
11329
|
+
const name = file === "-" ? "<stdin>" : file;
|
|
11330
|
+
if (format === null) {
|
|
11331
|
+
const doc = parseFile(file, new TextDecoder().decode(bytes));
|
|
11332
|
+
printDiagnostics(file, doc.diagnostics ?? []);
|
|
11333
|
+
writeOutput(values.out, writeAs(to, doc));
|
|
11334
|
+
return 0;
|
|
11335
|
+
}
|
|
11336
|
+
let imported;
|
|
11337
|
+
try {
|
|
11338
|
+
imported = importMap(bytes, { name: file === "-" ? undefined : basename(file), format });
|
|
11339
|
+
} catch (error) {
|
|
11340
|
+
if (error instanceof OrbitImportError) {
|
|
11341
|
+
process.stderr.write(`${name}: error: ${error.message} [${error.code}]
|
|
11342
|
+
`);
|
|
11343
|
+
return 1;
|
|
11344
|
+
}
|
|
11345
|
+
throw error;
|
|
11346
|
+
}
|
|
11347
|
+
const { maps } = imported;
|
|
11348
|
+
maps.forEach((map, index) => {
|
|
11349
|
+
const sheet = maps.length > 1 ? ` (sheet ${index + 1}, ${map.document.root.text || "untitled"})` : "";
|
|
11350
|
+
for (const warning of map.warnings) {
|
|
11351
|
+
process.stderr.write(`${name}${sheet}: warning: ${warning.message} [${warning.code}]
|
|
11352
|
+
`);
|
|
11353
|
+
}
|
|
11354
|
+
});
|
|
11355
|
+
if (!values.out) {
|
|
11356
|
+
const pictures = maps.reduce((count, map) => count + Object.keys(map.media).length, 0);
|
|
11357
|
+
if (maps.length > 1) {
|
|
11358
|
+
process.stderr.write(`${name}: the workbook has ${maps.length} sheets: this is the first. Write them all with --out.
|
|
11359
|
+
`);
|
|
11360
|
+
}
|
|
11361
|
+
if (pictures > 0) {
|
|
11362
|
+
process.stderr.write(`${name}: ${pictures === 1 ? "one picture" : `${pictures} pictures`} inside the file ${pictures === 1 ? "was" : "were"} left out: write the map with --out to save them in media/ next to it.
|
|
11363
|
+
`);
|
|
11364
|
+
}
|
|
11365
|
+
process.stdout.write(writeAs(to, maps[0].document));
|
|
11366
|
+
return 0;
|
|
11367
|
+
}
|
|
11368
|
+
const folder = dirname(values.out);
|
|
11369
|
+
const extension = extname(values.out);
|
|
11370
|
+
const stem = values.out.slice(0, values.out.length - extension.length);
|
|
11371
|
+
maps.forEach((map, index) => {
|
|
11372
|
+
const renamed = new Map;
|
|
11373
|
+
for (const [path, data] of Object.entries(map.media)) {
|
|
11374
|
+
let target = path;
|
|
11375
|
+
for (let suffix = 2;existsSync(join(folder, target)) && !sameBytes(join(folder, target), data); suffix++) {
|
|
11376
|
+
target = path.replace(/(\.[^.]+)$/, `-${index + 1}-${suffix}$1`);
|
|
11377
|
+
}
|
|
11378
|
+
mkdirSync(dirname(join(folder, target)), { recursive: true });
|
|
11379
|
+
writeFileSync(join(folder, target), data);
|
|
11380
|
+
renamed.set(path, target);
|
|
11381
|
+
}
|
|
11382
|
+
const out = index === 0 ? values.out : `${stem}-${index + 1}${extension}`;
|
|
11383
|
+
let text = writeAs(to, map.document);
|
|
11384
|
+
for (const [from, target] of renamed) {
|
|
11385
|
+
if (from !== target) {
|
|
11386
|
+
text = text.split(from).join(target);
|
|
11387
|
+
}
|
|
11388
|
+
}
|
|
11389
|
+
writeFileSync(out, text);
|
|
11390
|
+
});
|
|
11391
|
+
return 0;
|
|
11392
|
+
};
|
|
11393
|
+
var sameBytes = (path, data) => {
|
|
11394
|
+
return Buffer.from(readFileSync(path)).equals(Buffer.from(data));
|
|
11395
|
+
};
|
|
11396
|
+
var writeOutput = (out, text) => {
|
|
11397
|
+
if (out) {
|
|
11398
|
+
writeFileSync(out, text);
|
|
11399
|
+
} else {
|
|
11400
|
+
process.stdout.write(text);
|
|
11401
|
+
}
|
|
11402
|
+
};
|
|
8644
11403
|
var renderCommand = (argumentsList) => {
|
|
8645
11404
|
const read = readOptions("render", argumentsList, ["out", "width", "height", "theme", "view", "focus", "language"]);
|
|
8646
11405
|
if (typeof read === "number") {
|
|
@@ -8875,7 +11634,7 @@ ${USAGE}`);
|
|
|
8875
11634
|
return 1;
|
|
8876
11635
|
}
|
|
8877
11636
|
const warnings = report.diagnostics.length;
|
|
8878
|
-
process.stdout.write(`OK: ${
|
|
11637
|
+
process.stdout.write(`OK: ${plural2(report.nodes, "node")}, ${plural2(report.depth + 1, "level")}, ${plural2(warnings, "warning")}
|
|
8879
11638
|
`);
|
|
8880
11639
|
return 0;
|
|
8881
11640
|
};
|
|
@@ -8887,7 +11646,7 @@ var printDiagnostics = (file, diagnostics) => {
|
|
|
8887
11646
|
`);
|
|
8888
11647
|
}
|
|
8889
11648
|
};
|
|
8890
|
-
var
|
|
11649
|
+
var plural2 = (count, word) => {
|
|
8891
11650
|
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
8892
11651
|
};
|
|
8893
11652
|
var countNodes = (node) => {
|
|
@@ -8903,5 +11662,5 @@ export {
|
|
|
8903
11662
|
runCli
|
|
8904
11663
|
};
|
|
8905
11664
|
|
|
8906
|
-
//# debugId=
|
|
11665
|
+
//# debugId=7AC091CE99A9461164756E2164756E21
|
|
8907
11666
|
//# sourceMappingURL=orbit.js.map
|