@cardenelabs/dragon 0.35.1 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -3
- package/dist/cli.cjs +597 -375
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +597 -375
- package/dist/cli.js.map +1 -1
- package/dist/export.cjs +104 -37
- package/dist/export.cjs.map +1 -1
- package/dist/export.d.cts +1 -1
- package/dist/export.d.ts +1 -1
- package/dist/export.js +104 -37
- package/dist/export.js.map +1 -1
- package/dist/index.cjs +833 -526
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -15
- package/dist/index.d.ts +21 -15
- package/dist/index.js +833 -526
- package/dist/index.js.map +1 -1
- package/dist/player.global.js +2 -2
- package/dist/{schema-BBWfniwH.d.cts → schema-9VLameDG.d.cts} +15 -3
- package/dist/{schema-BBWfniwH.d.ts → schema-9VLameDG.d.ts} +15 -3
- package/package.json +3 -2
- package/src/cli.ts +13 -14
- package/src/figure-text.ts +1 -1
- package/src/index.ts +4 -4
- package/src/look/values.ts +16 -4
- package/src/messages.ts +158 -80
- package/src/parser.ts +402 -245
- package/src/player.ts +80 -9
- package/src/printer.ts +90 -60
- package/src/render/parts.ts +28 -8
- package/src/render/svg.ts +128 -33
- package/src/schema.ts +1 -1
- package/src/tokenize.ts +11 -8
- package/src/view.ts +5 -2
- package/src/words.ts +39 -65
package/dist/cli.js
CHANGED
|
@@ -284,8 +284,16 @@ var LOOK = {
|
|
|
284
284
|
stroke: { hair: 1, edge: 1.7, edgeOn: 2.25, arrow: 1.5, flow: 1.3, halo: 7, on: 1.75, glyph: 2, lifeline: 1.2, envelope: 1.5, badgeRing: 1.5 },
|
|
285
285
|
shadow: { near: 0.8, far: 4, nearY: 1, farY: 3 },
|
|
286
286
|
mini: { w: 168, pad: 8, title: 14, inset: 12, radius: 10 },
|
|
287
|
-
/**
|
|
288
|
-
|
|
287
|
+
/**
|
|
288
|
+
* 寄って追う見せ方の窓の高さの、幅に対する割合と下限。
|
|
289
|
+
* `fitMinH` は見える高さに収める時 (#3047) の窓の高さの下限で、これより低い枠では収めずに枠の中で送る。
|
|
290
|
+
* `narrow` は縦長の全体図を出す枠の幅の下限で、これより狭い枠 (携帯の幅) では全体図を出さずに窓へ幅を渡す (#3055)。
|
|
291
|
+
* `badgeRoom` は番号の丸が窓から外れる段で窓を寄せ直す時に、丸の周りに空ける幅 (#3046)。
|
|
292
|
+
* `headRoom` は時系列図の図の頭の箱の下端から、窓に線を見せ始める所までの幅 (#3118)。
|
|
293
|
+
* `head` は時系列図の窓の上端に貼る頭の列の頭の箱で、アイコンを外した名前と下の字の 2 段 (#3120)。
|
|
294
|
+
* `pad` は頭の列の上下の余白、`h` は箱の高さ、`nameY` と `subY` は箱の上端から名前と下の字の基準線まで
|
|
295
|
+
*/
|
|
296
|
+
follow: { aspect: 0.75, minH: 320, fitMinH: 160, ease: 0.6, narrow: 440, badgeRoom: 8, headRoom: 10, head: { pad: 8, h: 40, nameY: 17, subY: 31 } },
|
|
289
297
|
/** 色を混ぜる割合 (%) */
|
|
290
298
|
mix: { icon: 14, regionIcon: 16, regionIconEdge: 34, cap: 12, capEdge: 42, badgePast: 14, badgePastEdge: 70, mini: 62, miniBand: 16, miniBandEdge: 36, view: 6 },
|
|
291
299
|
/** 字の並びの基準線を、置きたい高さの真ん中からどれだけ下げるか */
|
|
@@ -2940,9 +2948,9 @@ function buildTimeline(diagram) {
|
|
|
2940
2948
|
const hasMain = departures.some((d) => d.who === "main");
|
|
2941
2949
|
const carriers = [];
|
|
2942
2950
|
for (const who of ["main", "coin"]) {
|
|
2943
|
-
const
|
|
2944
|
-
|
|
2945
|
-
const next =
|
|
2951
|
+
const list2 = departures.filter((d) => d.who === who);
|
|
2952
|
+
list2.forEach((d, k) => {
|
|
2953
|
+
const next = list2[k + 1];
|
|
2946
2954
|
const waits = k === 0 && (who === "main" || !hasMain);
|
|
2947
2955
|
carriers.push({
|
|
2948
2956
|
step: d.step,
|
|
@@ -3049,20 +3057,31 @@ function band(region) {
|
|
|
3049
3057
|
const mark = region.icon && ICONS[region.icon] ? `<rect class="dg-bi" x="${r1(x)}" y="${r1(y)}" width="${icon}" height="${icon}" rx="${iconRadius}"/>${glyph(region.icon, x + icon / 2, y + icon / 2, iconScale)}` : "";
|
|
3050
3058
|
return `<g style="${cs(region.color)}"><rect class="dg-band" x="${r1(region.x + 0.5)}" y="${r1(region.y + 0.5)}" width="${r1(region.w - 1)}" height="${r1(region.h - 1)}" rx="${radius}"/>${mark}${lines}${note}</g>`;
|
|
3051
3059
|
}
|
|
3052
|
-
function
|
|
3053
|
-
const { m, sh } = ctx;
|
|
3054
|
-
const { radius, icon, iconRadius, iconScale, iconY, nameY, subY } = LOOK.box;
|
|
3060
|
+
function subLines(box, m, changes, y) {
|
|
3055
3061
|
const seq = [[0, box.sub ?? "", "dg-ss"], ...changes];
|
|
3056
|
-
|
|
3062
|
+
return seq.map(([t, text2, cls], k) => {
|
|
3057
3063
|
const next = seq[k + 1]?.[0];
|
|
3058
3064
|
const anim = k === 0 ? next === void 0 ? "" : m.until(next - MOTION.untilFade) : next === void 0 ? m.rise(t) : m.rise(t, next - MOTION.untilFade);
|
|
3059
3065
|
if (text2 === "") return "";
|
|
3060
|
-
return `<g${anim ? ` class="dg-a" style="${anim}"` : ""}><text class="${cls}" x="${r1(box.cx)}" y="${r1(
|
|
3066
|
+
return `<g${anim ? ` class="dg-a" style="${anim}"` : ""}><text class="${cls}" x="${r1(box.cx)}" y="${r1(y)}" text-anchor="middle">${esc(text2)}</text></g>`;
|
|
3061
3067
|
}).join("");
|
|
3068
|
+
}
|
|
3069
|
+
function cardFrame(box, ctx, act, y, h) {
|
|
3070
|
+
const rect = (cls, extra = "") => `<rect class="${cls}" x="${r1(box.x)}" y="${r1(y)}" width="${r1(box.w)}" height="${h}" rx="${LOOK.box.radius}"${extra}/>`;
|
|
3071
|
+
return `${act ? `<g class="dg-a" style="${act}">${rect("dg-halo")}</g>` : ""}${rect("dg-st", ` ${ctx.sh}`)}${act ? `<g class="dg-a" style="${act}">${rect("dg-on")}</g>` : ""}`;
|
|
3072
|
+
}
|
|
3073
|
+
function card(box, ctx, on, changes) {
|
|
3074
|
+
const { icon, iconRadius, iconScale, iconY, nameY, subY } = LOOK.box;
|
|
3075
|
+
const subs = subLines(box, ctx.m, changes, box.cy + subY);
|
|
3062
3076
|
const act = on.length > 0 ? activeKf(ctx, on) : void 0;
|
|
3063
|
-
const rect = (cls, extra = "") => `<rect class="${cls}" x="${r1(box.x)}" y="${r1(box.y)}" width="${r1(box.w)}" height="${box.h}" rx="${radius}"${extra}/>`;
|
|
3064
3077
|
const mark = box.icon && ICONS[box.icon] ? `<rect class="dg-ic" x="${r1(box.cx - icon / 2)}" y="${r1(box.cy + iconY - icon / 2)}" width="${icon}" height="${icon}" rx="${iconRadius}"/>${glyph(box.icon, box.cx, box.cy + iconY, iconScale)}` : "";
|
|
3065
|
-
return `<g style="${cs(box.color)}">${
|
|
3078
|
+
return `<g style="${cs(box.color)}">${cardFrame(box, ctx, act, box.y, box.h)}${mark}<text class="dg-sn" x="${r1(box.cx)}" y="${r1(box.cy + nameY)}" text-anchor="middle">${esc(box.name)}</text>${subs}</g>`;
|
|
3079
|
+
}
|
|
3080
|
+
function headCard(box, ctx, on, changes) {
|
|
3081
|
+
const { pad: pad2, h, nameY, subY } = LOOK.follow.head;
|
|
3082
|
+
const subs = subLines(box, ctx.m, changes, pad2 + subY);
|
|
3083
|
+
const act = on.length > 0 ? activeKf(ctx, on) : void 0;
|
|
3084
|
+
return `<g style="${cs(box.color)}">${cardFrame(box, ctx, act, pad2, h)}<text class="dg-sn" x="${r1(box.cx)}" y="${r1(pad2 + nameY)}" text-anchor="middle">${esc(box.name)}</text>${subs}</g>`;
|
|
3066
3085
|
}
|
|
3067
3086
|
function lifeline(x, y1, y2) {
|
|
3068
3087
|
return `<line class="dg-ll" x1="${r1(x)}" y1="${r1(y1)}" x2="${r1(x)}" y2="${r1(y2)}"/>`;
|
|
@@ -3187,9 +3206,9 @@ function strip(s, changes, ctx) {
|
|
|
3187
3206
|
parts.push(`<line class="dg-sep" x1="${r1(sx)}" y1="${r1(field.y + S2.sepInset)}" x2="${r1(sx)}" y2="${r1(field.y + S2.h - S2.sepInset)}"/>`);
|
|
3188
3207
|
}
|
|
3189
3208
|
parts.push(`<text class="dg-fl" x="${r1(field.x)}" y="${r1(field.y + S2.nameY)}">${esc(field.name)}</text>`);
|
|
3190
|
-
const
|
|
3191
|
-
const values =
|
|
3192
|
-
const tags =
|
|
3209
|
+
const list2 = changes.get(field.name) ?? [];
|
|
3210
|
+
const values = list2.filter((c) => c.value !== void 0);
|
|
3211
|
+
const tags = list2.filter((c) => c.tag !== void 0);
|
|
3193
3212
|
values.forEach((c, k) => {
|
|
3194
3213
|
const next = values[k + 1]?.at;
|
|
3195
3214
|
const value = c.value;
|
|
@@ -3355,6 +3374,7 @@ var PART_CSS = [
|
|
|
3355
3374
|
`.dg-tg text{${font(TYPE.tag)};fill:var(--on-c)}`,
|
|
3356
3375
|
".dg-vp-bg{fill:var(--bg)}",
|
|
3357
3376
|
".dg-vp-frame{fill:none;stroke:var(--hair)}",
|
|
3377
|
+
".dg-hd-bg{fill:var(--bg)}",
|
|
3358
3378
|
".dg-mm-bg{fill:var(--surf);stroke:var(--hair)}",
|
|
3359
3379
|
`.dg-mm-t{${font(TYPE.miniTitle)};fill:var(--mute)}`,
|
|
3360
3380
|
".dg-mm-world{fill:var(--sunk);stroke:var(--hair)}",
|
|
@@ -3402,7 +3422,7 @@ function subChanges(diagram, timeline, actionsInBox) {
|
|
|
3402
3422
|
let base = [box.sub ?? "", "dg-ss"];
|
|
3403
3423
|
let acting;
|
|
3404
3424
|
let shown = base.join("|");
|
|
3405
|
-
const
|
|
3425
|
+
const list2 = [];
|
|
3406
3426
|
for (const change of raw.filter((r) => r.box === box.id)) {
|
|
3407
3427
|
if (change.set !== void 0) {
|
|
3408
3428
|
base = [change.set, "dg-ss-ok"];
|
|
@@ -3412,10 +3432,10 @@ function subChanges(diagram, timeline, actionsInBox) {
|
|
|
3412
3432
|
const now = acting !== void 0 ? [acting, "dg-ss-act"] : base;
|
|
3413
3433
|
if (now.join("|") === shown) continue;
|
|
3414
3434
|
shown = now.join("|");
|
|
3415
|
-
|
|
3435
|
+
list2.push([change.t, now[0], now[1]]);
|
|
3416
3436
|
}
|
|
3417
|
-
const kept =
|
|
3418
|
-
const next =
|
|
3437
|
+
const kept = list2.filter((change, k) => {
|
|
3438
|
+
const next = list2[k + 1];
|
|
3419
3439
|
return next === void 0 || next[0] - change[0] >= MOTION.minShow;
|
|
3420
3440
|
});
|
|
3421
3441
|
out.set(box.id, kept);
|
|
@@ -3428,9 +3448,9 @@ function fieldChanges(diagram, timeline, layout) {
|
|
|
3428
3448
|
const event = diagram.steps[time.step]?.events[time.event];
|
|
3429
3449
|
if (event?.type !== "strip") continue;
|
|
3430
3450
|
const color = layout.badges[time.step]?.color ?? "slate";
|
|
3431
|
-
const
|
|
3432
|
-
|
|
3433
|
-
out.set(event.field,
|
|
3451
|
+
const list2 = out.get(event.field) ?? [];
|
|
3452
|
+
list2.push({ at: time.at, color, ...event.value !== void 0 ? { value: event.value } : {}, ...event.tag !== void 0 ? { tag: event.tag } : {} });
|
|
3453
|
+
out.set(event.field, list2);
|
|
3434
3454
|
}
|
|
3435
3455
|
return out;
|
|
3436
3456
|
}
|
|
@@ -3468,13 +3488,34 @@ function drawSvg(diagram, layout, timeline, palette, theme, follow) {
|
|
|
3468
3488
|
const content = Object.values(L).map((parts) => parts.join("")).join("");
|
|
3469
3489
|
const { near, far, nearY, farY } = LOOK.shadow;
|
|
3470
3490
|
const shadow = `<filter id="dg-sh-${id}" x="-20%" y="-30%" width="140%" height="180%"><feDropShadow dx="0" dy="${nearY}" stdDeviation="${near}" style="flood-color:var(--sh);flood-opacity:var(--shA)"/><feDropShadow dx="0" dy="${farY}" stdDeviation="${far}" style="flood-color:var(--sh);flood-opacity:var(--shB)"/></filter>`;
|
|
3471
|
-
const
|
|
3491
|
+
const heads = follow && layout.kind === "sequence" ? layout.boxes.map((box) => headCard(box, ctx, on.get(box.id) ?? [], subs.get(box.id) ?? [])).join("") : "";
|
|
3492
|
+
const view = follow ? followView(layout, timeline, diagram, m, id, content, follow, heads) : { body: `<rect class="dg-bg" width="${layout.width}" height="${layout.height}" rx="${LOOK.region.radius}"/>${content}`, w: layout.width, h: layout.height };
|
|
3472
3493
|
const css = `.dg-${id}{${FONT_VARS};--T:${timeline.total}s;${paletteVars(palette, theme)}}${PART_CSS}${m.frames.join("")}`;
|
|
3473
3494
|
return `<svg xmlns="http://www.w3.org/2000/svg" class="dg dg-${id}" viewBox="0 0 ${Math.ceil(view.w)} ${Math.ceil(view.h)}" width="${Math.ceil(view.w)}" height="${Math.ceil(view.h)}" role="img" aria-label="${esc(diagram.title)}" data-dragon="figure" data-view="${follow ? "follow" : "fixed"}"><style>${css}</style><defs>${shadow}</defs>${view.body}</svg>`;
|
|
3474
3495
|
}
|
|
3496
|
+
function union(rects) {
|
|
3497
|
+
const x = Math.min(...rects.map((r) => r.x));
|
|
3498
|
+
const y = Math.min(...rects.map((r) => r.y));
|
|
3499
|
+
return { x, y, w: Math.max(...rects.map((r) => r.x + r.w)) - x, h: Math.max(...rects.map((r) => r.y + r.h)) - y };
|
|
3500
|
+
}
|
|
3475
3501
|
function focusOf(layout, diagram, step) {
|
|
3502
|
+
const rects = focusParts(layout, diagram, step);
|
|
3503
|
+
return rects.length === 0 ? void 0 : union(rects);
|
|
3504
|
+
}
|
|
3505
|
+
function badgeFocus(badge2, parts, vw, vh) {
|
|
3506
|
+
const room = LOOK.badge.r + LOOK.follow.badgeRoom;
|
|
3507
|
+
const { x: bx, y: by } = badge2;
|
|
3508
|
+
const away = (p) => Math.hypot(Math.max(0, p.x - bx, bx - p.x - p.w), Math.max(0, p.y - by, by - p.y - p.h));
|
|
3509
|
+
let held = { x: bx - room, y: by - room, w: room * 2, h: room * 2 };
|
|
3510
|
+
for (const p of [...parts].sort((a, b) => away(a) - away(b))) {
|
|
3511
|
+
const next = union([held, p]);
|
|
3512
|
+
if (next.w <= vw && next.h <= vh) held = next;
|
|
3513
|
+
}
|
|
3514
|
+
return held;
|
|
3515
|
+
}
|
|
3516
|
+
function focusParts(layout, diagram, step) {
|
|
3476
3517
|
const rects = [];
|
|
3477
|
-
for (const e of layout.edges.filter((
|
|
3518
|
+
for (const e of layout.edges.filter((x) => x.step === step)) {
|
|
3478
3519
|
const xs = e.points.map((p) => p[0]);
|
|
3479
3520
|
const ys = e.points.map((p) => p[1]);
|
|
3480
3521
|
rects.push({ x: Math.min(...xs), y: Math.min(...ys), w: Math.max(...xs) - Math.min(...xs), h: Math.max(...ys) - Math.min(...ys) });
|
|
@@ -3487,24 +3528,44 @@ function focusOf(layout, diagram, step) {
|
|
|
3487
3528
|
if (box && (layout.kind === "architecture" || event.type === "swap")) rects.push(box);
|
|
3488
3529
|
}
|
|
3489
3530
|
}
|
|
3490
|
-
|
|
3491
|
-
const x = Math.min(...rects.map((r) => r.x));
|
|
3492
|
-
const y = Math.min(...rects.map((r) => r.y));
|
|
3493
|
-
return { x, y, w: Math.max(...rects.map((r) => r.x + r.w)) - x, h: Math.max(...rects.map((r) => r.y + r.h)) - y };
|
|
3531
|
+
return rects;
|
|
3494
3532
|
}
|
|
3495
|
-
function followView(layout, timeline, diagram, m, id, content, follow) {
|
|
3533
|
+
function followView(layout, timeline, diagram, m, id, content, follow, heads) {
|
|
3496
3534
|
const F = LOOK.follow;
|
|
3497
3535
|
const M = LOOK.mini;
|
|
3498
|
-
const
|
|
3499
|
-
const
|
|
3536
|
+
const frameWidth = Math.max(1, follow.width);
|
|
3537
|
+
const full = Math.min(layout.width, frameWidth);
|
|
3538
|
+
const title = FIGURE_TEXT[follow.lang].overview;
|
|
3539
|
+
const sized = (vh2) => {
|
|
3540
|
+
const mw2 = Math.min(M.w, full * 0.4, vh2 * 0.4 / layout.height * layout.width);
|
|
3541
|
+
const k2 = mw2 / layout.width;
|
|
3542
|
+
const mh2 = layout.height * k2;
|
|
3543
|
+
const bw2 = Math.max(mw2, Math.ceil(textWidth(title, TYPE.miniTitle)));
|
|
3544
|
+
return { vh: vh2, mw: mw2, k: k2, mh: mh2, bw: bw2, bgW: bw2 + M.pad * 2, bgH: mh2 + M.pad * 2 + M.title, beside: mh2 > mw2 };
|
|
3545
|
+
};
|
|
3546
|
+
const natural = sized(Math.min(layout.height, Math.max(F.minH, full * F.aspect)));
|
|
3547
|
+
const bare = natural.beside && frameWidth < F.narrow;
|
|
3548
|
+
const room = follow.height === void 0 ? Number.POSITIVE_INFINITY : natural.beside ? follow.height : follow.height - natural.bgH - M.inset;
|
|
3549
|
+
const { vh, mw, k, mh, bw, bgW, bgH, beside } = room < natural.vh ? sized(Math.max(F.fitMinH, room)) : { ...natural, vh: Number.isFinite(room) ? Math.min(layout.height, room) : natural.vh };
|
|
3550
|
+
const vw = beside && !bare ? Math.min(layout.width, frameWidth - bgW - M.inset) : full;
|
|
3500
3551
|
const clampX = (x) => Math.min(layout.width - vw / 2, Math.max(vw / 2, x));
|
|
3501
|
-
const
|
|
3552
|
+
const top = heads === "" ? 0 : Math.max(...layout.boxes.map((b) => b.y + b.h)) + F.headRoom;
|
|
3553
|
+
const strip2 = heads === "" ? 0 : F.head.pad * 2 + F.head.h;
|
|
3554
|
+
const below = vh - strip2;
|
|
3555
|
+
const clampY = (y) => Math.min(layout.height - below / 2, Math.max(top + below / 2, y));
|
|
3556
|
+
const r = LOOK.badge.r;
|
|
3502
3557
|
let prev = [layout.width / 2, layout.height / 2];
|
|
3503
|
-
const
|
|
3558
|
+
const middles = timeline.steps.map((_, s) => {
|
|
3504
3559
|
const f = focusOf(layout, diagram, s);
|
|
3505
3560
|
prev = f ? [f.x + f.w / 2, f.y + f.h / 2] : prev;
|
|
3561
|
+
const at2 = [clampX(prev[0]), clampY(prev[1])];
|
|
3562
|
+
const b = layout.badges.find((x) => x.step === s);
|
|
3563
|
+
if (!b || Math.abs(b.x - at2[0]) <= vw / 2 - r && Math.abs(b.y - at2[1]) <= below / 2 - r) return at2;
|
|
3564
|
+
const near = badgeFocus(b, focusParts(layout, diagram, s), vw, below);
|
|
3565
|
+
prev = [near.x + near.w / 2, near.y + near.h / 2];
|
|
3506
3566
|
return [clampX(prev[0]), clampY(prev[1])];
|
|
3507
3567
|
});
|
|
3568
|
+
const centers = middles.map(([x, y]) => [x, y - strip2 / 2]);
|
|
3508
3569
|
const c0 = centers[0];
|
|
3509
3570
|
const cam = [
|
|
3510
3571
|
[0, c0[0], c0[1]],
|
|
@@ -3519,22 +3580,26 @@ function followView(layout, timeline, diagram, m, id, content, follow) {
|
|
|
3519
3580
|
const cl = centers[centers.length - 1];
|
|
3520
3581
|
cam.push([timeline.last, cl[0], cl[1], 1, EASE.inOut], [timeline.total, c0[0], c0[1]]);
|
|
3521
3582
|
const camKf = m.move(cam.map(([t, cx, cy, s, e]) => [t, vw / 2 - cx, vh / 2 - cy, s, e]));
|
|
3522
|
-
const
|
|
3523
|
-
const
|
|
3524
|
-
const
|
|
3525
|
-
const title = FIGURE_TEXT[follow.lang].overview;
|
|
3526
|
-
const bw = Math.max(mw, Math.ceil(textWidth(title, TYPE.miniTitle)));
|
|
3527
|
-
const bx = vw - bw - M.inset - M.pad;
|
|
3583
|
+
const bgX = beside ? vw + M.inset : vw - bgW - M.inset;
|
|
3584
|
+
const bgY = beside ? 0 : vh + M.inset;
|
|
3585
|
+
const bx = bgX + M.pad;
|
|
3528
3586
|
const mx = bx + (bw - mw) / 2;
|
|
3529
|
-
const my =
|
|
3530
|
-
const
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3587
|
+
const my = bgY + M.pad + M.title;
|
|
3588
|
+
const overview = () => {
|
|
3589
|
+
const shapes = [
|
|
3590
|
+
...layout.regions.map((r3) => `<rect class="dg-mm-band" style="${cs(r3.color)}" x="${mx + r3.x * k}" y="${my + r3.y * k}" width="${r3.w * k}" height="${r3.h * k}" rx="3"/>`),
|
|
3591
|
+
...layout.boxes.map((b) => `<rect class="dg-mm-card" style="${cs(b.color)}" x="${mx + b.x * k}" y="${my + b.y * k}" width="${b.w * k}" height="${b.h * k}" rx="1.5"/>`)
|
|
3592
|
+
].join("");
|
|
3593
|
+
const vp = m.move(cam.map(([t, cx, cy, , e]) => [t, mx + (cx - vw / 2) * k, my + (cy - vh / 2) * k, 1, e]));
|
|
3594
|
+
return {
|
|
3595
|
+
clip: `<clipPath id="dg-mm-${id}"><rect x="${mx}" y="${my}" width="${mw}" height="${mh}" rx="4"/></clipPath>`,
|
|
3596
|
+
body: `<rect class="dg-mm-bg" x="${bgX}" y="${bgY}" width="${bgW}" height="${bgH}" rx="${M.radius}"/><text class="dg-mm-t" x="${bx}" y="${my - M.pad + 2}">${title}</text><g clip-path="url(#dg-mm-${id})"><rect class="dg-mm-world" x="${mx}" y="${my}" width="${mw}" height="${mh}"/>${shapes}<rect class="dg-mm-vp dg-a"${strip2 === 0 ? "" : ` y="${strip2 * k}"`} width="${vw * k}" height="${(vh - strip2) * k}" rx="2" style="${vp}"/></g>`
|
|
3597
|
+
};
|
|
3598
|
+
};
|
|
3599
|
+
const mini = bare ? { clip: "", body: "" } : overview();
|
|
3600
|
+
const head = strip2 === 0 ? "" : `<g class="dg-hd"><rect class="dg-hd-bg" width="${vw}" height="${strip2}"/><line class="dg-sep" x1="0" y1="${strip2}" x2="${vw}" y2="${strip2}"/><g class="dg-a" style="${m.move(cam.map(([t, cx, , s, e]) => [t, vw / 2 - cx, 0, s, e]))}">${heads}</g></g>`;
|
|
3601
|
+
const body = `<defs><clipPath id="dg-vp-${id}"><rect width="${vw}" height="${vh}" rx="${LOOK.region.radius}"/></clipPath>${mini.clip}</defs><g clip-path="url(#dg-vp-${id})"><rect class="dg-vp-bg" width="${vw}" height="${vh}"/><g class="dg-a" style="${camKf}">${content}</g>${head}</g><rect class="dg-vp-frame" x="0.5" y="0.5" width="${vw - 1}" height="${vh - 1}" rx="${LOOK.region.radius}"/>${mini.body}`;
|
|
3602
|
+
return { body, w: beside && !bare ? vw + M.inset + bgW : full, h: beside ? vh : bgY + bgH };
|
|
3538
3603
|
}
|
|
3539
3604
|
function renderDiagram(input, options = {}) {
|
|
3540
3605
|
const lang = options.lang ?? "ja";
|
|
@@ -3558,8 +3623,10 @@ function renderDiagram(input, options = {}) {
|
|
|
3558
3623
|
}
|
|
3559
3624
|
|
|
3560
3625
|
// src/view.ts
|
|
3561
|
-
function chooseView(rendered, frameWidth) {
|
|
3562
|
-
const
|
|
3626
|
+
function chooseView(rendered, frameWidth, frameHeight) {
|
|
3627
|
+
const byWidth = Math.max(0, frameWidth) / rendered.layout.width;
|
|
3628
|
+
const scale = byWidth ;
|
|
3629
|
+
const textPx = scale * TYPE.boxName.size;
|
|
3563
3630
|
if (rendered.view) return { mode: rendered.view, textPx };
|
|
3564
3631
|
return { mode: textPx >= READABLE_PX ? "fixed" : "follow", textPx };
|
|
3565
3632
|
}
|
|
@@ -3778,8 +3845,8 @@ var PACKAGES = [
|
|
|
3778
3845
|
];
|
|
3779
3846
|
var weights = Object.values(TYPE).map((style) => style.weight);
|
|
3780
3847
|
var WEIGHT = { min: Math.min(...weights), max: Math.max(...weights) };
|
|
3781
|
-
function ranges(
|
|
3782
|
-
return
|
|
3848
|
+
function ranges(list2) {
|
|
3849
|
+
return list2.split(",").map((part) => {
|
|
3783
3850
|
const [from = "", to = from] = part.trim().replace(/^U\+/i, "").split("-");
|
|
3784
3851
|
return [parseInt(from, 16), parseInt(to, 16)];
|
|
3785
3852
|
});
|
|
@@ -3793,8 +3860,8 @@ async function fontFaces(chars) {
|
|
|
3793
3860
|
PACKAGES.flatMap(({ family, name }) => {
|
|
3794
3861
|
const base = dirname(require_2.resolve(`@fontsource-variable/${name}/unicode.json`));
|
|
3795
3862
|
const table = JSON.parse(readFileSync(join(base, "unicode.json"), "utf8"));
|
|
3796
|
-
return Object.entries(table).map(async ([key,
|
|
3797
|
-
const span = ranges(
|
|
3863
|
+
return Object.entries(table).map(async ([key, list2]) => {
|
|
3864
|
+
const span = ranges(list2);
|
|
3798
3865
|
const own = chars.filter((char) => {
|
|
3799
3866
|
const code = char.codePointAt(0) ?? -1;
|
|
3800
3867
|
return span.some(([from, to]) => code >= from && code <= to);
|
|
@@ -3904,145 +3971,174 @@ function writeOutputs(outDir, name, outputs) {
|
|
|
3904
3971
|
}
|
|
3905
3972
|
|
|
3906
3973
|
// src/words.ts
|
|
3907
|
-
var
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
none: { ja: "\u306A\u3057", en: "none" }
|
|
3929
|
-
},
|
|
3930
|
-
/** 見せ方の値 */
|
|
3931
|
-
view: { fixed: { ja: "\u6B62\u3081\u308B", en: "fixed" }, follow: { ja: "\u5BC4\u308B", en: "follow" } },
|
|
3932
|
-
/** 線の名前を置く側 */
|
|
3933
|
-
side: {
|
|
3934
|
-
above: { ja: "\u4E0A", en: "above" },
|
|
3935
|
-
below: { ja: "\u4E0B", en: "below" },
|
|
3936
|
-
left: { ja: "\u5DE6", en: "left" },
|
|
3937
|
-
right: { ja: "\u53F3", en: "right" }
|
|
3938
|
-
}
|
|
3974
|
+
var keys = (table) => Object.keys(table);
|
|
3975
|
+
var KINDS = keys({ sequence: true, architecture: true });
|
|
3976
|
+
var HEADS = ["group", "end", "box", "strip", "step", "say", "code", "act", "note", "set"];
|
|
3977
|
+
var AS = "as";
|
|
3978
|
+
var ATTRS = {
|
|
3979
|
+
diagram: ["view"],
|
|
3980
|
+
group: ["note", "color", "icon"],
|
|
3981
|
+
box: ["note", "color", "icon"],
|
|
3982
|
+
edge: ["carry", "tag", "side"],
|
|
3983
|
+
set: ["tag"]
|
|
3984
|
+
};
|
|
3985
|
+
var CARRIES = keys({ coin: true, none: true });
|
|
3986
|
+
var VIEWS = keys({ fixed: true, follow: true });
|
|
3987
|
+
var SIDES = keys({ above: true, below: true, left: true, right: true });
|
|
3988
|
+
var OLD_HEADS = {
|
|
3989
|
+
\u6642\u7CFB\u5217\u56F3: "sequence",
|
|
3990
|
+
\u69CB\u6210\u56F3: "architecture",
|
|
3991
|
+
\u533A\u753B: "group",
|
|
3992
|
+
\u898B\u305B\u65B9: "view",
|
|
3993
|
+
region: "group",
|
|
3994
|
+
view: "view"
|
|
3939
3995
|
};
|
|
3940
|
-
function wordOf(group, value, lang) {
|
|
3941
|
-
return WORDS[group][value][lang];
|
|
3942
|
-
}
|
|
3943
|
-
function valueOf(group, word, lang) {
|
|
3944
|
-
const found = Object.entries(WORDS[group]).find(([, pair]) => pair[lang] === word);
|
|
3945
|
-
return found?.[0];
|
|
3946
|
-
}
|
|
3947
|
-
var otherLang = (lang) => lang === "ja" ? "en" : "ja";
|
|
3948
|
-
function languageOf(headLine) {
|
|
3949
|
-
const first = headLine.trim().split(/\s+/)[0] ?? "";
|
|
3950
|
-
return ["ja", "en"].find((lang) => valueOf("kind", first, lang) !== void 0);
|
|
3951
|
-
}
|
|
3952
|
-
function notationLanguage(notation) {
|
|
3953
|
-
const head = notation.split(/\r?\n/).map((line) => line.trim()).find((line) => line !== "" && !line.startsWith("//"));
|
|
3954
|
-
return head === void 0 ? void 0 : languageOf(head);
|
|
3955
|
-
}
|
|
3956
3996
|
|
|
3957
3997
|
// src/messages.ts
|
|
3998
|
+
var list = (words) => words.join(" / ");
|
|
3958
3999
|
var ja2 = () => {
|
|
3959
|
-
const
|
|
4000
|
+
const shapes = {
|
|
4001
|
+
sequence: "sequence <\u984C>",
|
|
4002
|
+
architecture: "architecture <\u984C>",
|
|
4003
|
+
group: "group <\u540D\u672D> as <\u540D\u524D> [note: <\u6DFB\u3048\u66F8\u304D>] \u304B\u3089 end \u307E\u3067",
|
|
4004
|
+
box: "box <\u540D\u672D> as <\u540D\u524D> [note: <\u4E0B\u306E\u5C0F\u3055\u306A\u5B57>]",
|
|
4005
|
+
strip: "strip <\u984C>: <\u6B04>, <\u6B04>",
|
|
4006
|
+
view: "sequence <\u984C> [view: follow]",
|
|
4007
|
+
step: "step <\u6BB5\u306E\u984C>",
|
|
4008
|
+
say: "say <\u6BB5\u306E\u672C\u6587>",
|
|
4009
|
+
code: "code <\u547D\u4EE4>",
|
|
4010
|
+
act: "act <\u540D\u672D>: <\u51E6\u7406\u306E\u540D\u524D>",
|
|
4011
|
+
note: "note <\u540D\u672D>: <\u4E0B\u306E\u5C0F\u3055\u306A\u5B57>",
|
|
4012
|
+
set: "set <\u6B04>: <\u5024> [tag: <\u672D>]"
|
|
4013
|
+
};
|
|
3960
4014
|
return {
|
|
3961
|
-
noHead: `\u6700\u521D\u306E\u884C\u306B\u56F3\u306E\u7A2E\u985E (${
|
|
3962
|
-
badHead: `\u6700\u521D\u306E\u884C\u306F ${
|
|
4015
|
+
noHead: `\u6700\u521D\u306E\u884C\u306B\u56F3\u306E\u7A2E\u985E (${list(KINDS)}) \u3068\u984C\u3092\u66F8\u304F`,
|
|
4016
|
+
badHead: `\u6700\u521D\u306E\u884C\u306F ${KINDS.join(" \u304B ")} \u3067\u59CB\u3081\u3001\u7D9A\u3051\u3066\u984C\u3092\u66F8\u304F`,
|
|
4017
|
+
oldForm: (word, shape) => `${word} \u306F\u524D\u306E\u5F62\u306E\u66F8\u304D\u65B9\u3002\u65B0\u3057\u3044\u5F62\u3067\u306F ${shapes[shape]} \u3068\u66F8\u304F`,
|
|
4018
|
+
unknownHead: (word) => `${word} \u306F\u884C\u306E\u982D\u306B\u66F8\u3051\u306A\u3044\u3002\u884C\u306F ${list(HEADS)} \u304B\u3001<\u540D\u672D> -> <\u540D\u672D> \u3067\u59CB\u3081\u308B`,
|
|
3963
4019
|
duplicate: (id) => `${id} \u306F\u65E2\u306B\u5BA3\u8A00\u3057\u305F\u540D\u672D`,
|
|
4020
|
+
needsId: (word) => `${word} \u306E\u5F8C\u308D\u306B\u540D\u672D\u3092\u66F8\u304F`,
|
|
4021
|
+
badId: (id) => `${id} \u306F\u540D\u672D\u306B\u4F7F\u3048\u306A\u3044\u3002\u540D\u672D\u306B\u306F\u7A7A\u767D\u3068 [ ] \u300C \u300D = : \u3092\u542B\u3081\u306A\u3044`,
|
|
4022
|
+
afterId: "\u540D\u672D\u306E\u5F8C\u308D\u306B\u306F as <\u540D\u524D> \u304B\u5C5E\u6027\u3092\u66F8\u304F",
|
|
3964
4023
|
undeclaredBox: (id) => `${id} \u3068\u3044\u3046\u7BB1\u306F\u5BA3\u8A00\u3057\u3066\u3044\u306A\u3044`,
|
|
3965
|
-
noBody: (title) => `\u6BB5\u300C${title}\u300D\u306B\u672C\u6587 (
|
|
3966
|
-
emptyStepTitle: "\
|
|
4024
|
+
noBody: (title) => `\u6BB5\u300C${title}\u300D\u306B\u672C\u6587 (say \u306E\u884C) \u304C\u8981\u308B`,
|
|
4025
|
+
emptyStepTitle: "step \u306E\u5F8C\u308D\u306B\u6BB5\u306E\u984C\u3092\u66F8\u304F",
|
|
3967
4026
|
noBoxes: "\u7BB1\u3092 1 \u3064\u4EE5\u4E0A\u5BA3\u8A00\u3059\u308B",
|
|
3968
|
-
noSteps: "\u6BB5 (
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
4027
|
+
noSteps: "\u6BB5 (step \u306E\u884C) \u3092 1 \u3064\u4EE5\u4E0A\u66F8\u304F",
|
|
4028
|
+
emptySay: "say \u306E\u5F8C\u308D\u304B\u3001\u305D\u306E\u4E0B\u306B\u5B57\u4E0B\u3052\u3057\u305F\u884C\u306B\u6BB5\u306E\u672C\u6587\u3092\u66F8\u304F",
|
|
4029
|
+
sayTwice: "1 \u3064\u306E\u6BB5\u306B say \u306F 1 \u3064\u307E\u3067\u3002\u9577\u3044\u672C\u6587\u306F say \u306E\u4E0B\u306B\u5B57\u4E0B\u3052\u3057\u3066\u7D9A\u3051\u308B",
|
|
4030
|
+
codeTwice: "1 \u3064\u306E\u6BB5\u306B code \u306F 1 \u884C\u307E\u3067",
|
|
4031
|
+
emptyCode: "code \u306E\u5F8C\u308D\u306B\u547D\u4EE4\u3092\u66F8\u304F",
|
|
4032
|
+
declarationInStep: "group \u3068 box \u3068 strip \u306F\u6700\u521D\u306E step \u3088\u308A\u524D\u306B\u66F8\u304F",
|
|
4033
|
+
stepLineBeforeStep: "\u7DDA\u3068 say \u3068 code \u3068 act \u3068 note \u3068 set \u306F step \u306E\u4E0B\u306B\u66F8\u304F",
|
|
4034
|
+
nestedGroup: "group \u306E\u4E2D\u306B group \u306F\u66F8\u3051\u306A\u3044\u3002\u5148\u306B end \u3067\u9589\u3058\u308B",
|
|
4035
|
+
strayEnd: "end \u306E\u524D\u306B\u3001\u9589\u3058\u308B group \u304C\u7121\u3044",
|
|
4036
|
+
endTakesNothing: "end \u306E\u5F8C\u308D\u306B\u306F\u4F55\u3082\u66F8\u304B\u306A\u3044",
|
|
4037
|
+
unclosedGroup: (id) => `group ${id} \u3092 end \u3067\u9589\u3058\u3066\u3044\u306A\u3044`,
|
|
4038
|
+
regionReopened: (id) => `group ${id} \u306F\u5BA3\u8A00\u6E08\u307F\u3002\u3082\u3046\u4E00\u5EA6\u958B\u304F\u6642\u306F group ${id} \u3060\u3051\u3092\u66F8\u304D\u3001\u540D\u524D\u3068\u5C5E\u6027\u306F\u6700\u521D\u306E\u5BA3\u8A00\u306B\u66F8\u304F`,
|
|
3975
4039
|
emptyName: (id) => `${id} \u306E\u540D\u524D\u304C\u7A7A`,
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
4040
|
+
stripTwice: "strip \u306F 1 \u3064\u3060\u3051\u5BA3\u8A00\u3059\u308B",
|
|
4041
|
+
stripShape: "strip \u306F strip <\u984C>: <\u6B04>, <\u6B04> \u306E\u5F62\u3067\u66F8\u304F",
|
|
4042
|
+
emptyStripField: "strip \u306E\u6B04\u306E\u540D\u524D\u304C\u7A7A",
|
|
4043
|
+
stripUndeclared: "set \u3092\u66F8\u304F\u524D\u306B\u3001strip <\u984C>: <\u6B04>, <\u6B04> \u3067\u5E2F\u3092\u5BA3\u8A00\u3059\u308B",
|
|
4044
|
+
noSuchField: (field) => `${field} \u3068\u3044\u3046\u6B04\u306F strip \u306B\u7121\u3044`,
|
|
4045
|
+
stripNeedsValue: "set \u306F set <\u6B04>: <\u5024> \u304B set <\u6B04> [tag: <\u672D>] \u306E\u5F62\u3067\u66F8\u304F",
|
|
4046
|
+
actShape: "act \u306F act <\u540D\u672D>: <\u51E6\u7406\u306E\u540D\u524D> \u306E\u5F62\u3067\u66F8\u304F",
|
|
4047
|
+
noteShape: "note \u306F note <\u540D\u672D>: <\u4E0B\u306E\u5C0F\u3055\u306A\u5B57> \u306E\u5F62\u3067\u66F8\u304F",
|
|
4048
|
+
edgeShape: "\u7DDA\u306F <\u540D\u672D> -> <\u540D\u672D>: <\u7DDA\u306E\u540D\u524D> [<\u5C5E\u6027>] \u306E\u5F62\u3067\u66F8\u304F",
|
|
4049
|
+
emptyLabel: "\u7DDA\u306E : \u306E\u5F8C\u308D\u306B\u7DDA\u306E\u540D\u524D\u3092\u66F8\u304F\u3002\u540D\u524D\u3092\u66F8\u304B\u306A\u3044\u6642\u306F : \u3082\u66F8\u304B\u306A\u3044",
|
|
4050
|
+
quoteUnclosed: '" \u3067\u59CB\u3081\u305F\u5B57\u3092 " \u3067\u9589\u3058\u3066\u3044\u306A\u3044',
|
|
4051
|
+
afterQuote: '" \u3067\u9589\u3058\u305F\u5B57\u306E\u5F8C\u308D\u306B\u306F\u3001\u5C5E\u6027\u304B\u6B21\u306E , \u3060\u3051\u3092\u66F8\u304F',
|
|
4052
|
+
attrUnclosed: "\u5C5E\u6027\u306F [ \u3067\u59CB\u3081\u3066 ] \u3067\u9589\u3058\u3001\u884C\u306E\u7D42\u308F\u308A\u306B\u7F6E\u304F",
|
|
4053
|
+
attrNoColon: (raw) => `[${raw}] \u306F\u66F8\u3051\u306A\u3044\u3002\u5C5E\u6027\u306F [<\u9375>: <\u5024>] \u306E\u5F62\u3067\u66F8\u304F`,
|
|
4054
|
+
attrNoValue: (key) => `${key}: \u306E\u5F8C\u308D\u306B\u5024\u3092\u66F8\u304F`,
|
|
4055
|
+
attrTwice: (key) => `${key} \u306F 1 \u3064\u306E\u884C\u306B 1 \u3064\u307E\u3067`,
|
|
4056
|
+
badAttr: (key, allowed) => `${key} \u306F\u3053\u306E\u884C\u306B\u66F8\u3051\u306A\u3044\u5C5E\u6027\u3002\u66F8\u3051\u308B\u306E\u306F ${list(allowed)}`,
|
|
4057
|
+
badColor: `color \u306F ${list(COLORS)} \u306E\u3069\u308C\u304B`,
|
|
4058
|
+
badView: `view \u306F ${list(VIEWS)} \u306E\u3069\u308C\u304B`,
|
|
4059
|
+
badCarry: `carry \u306F ${list(CARRIES)} \u306E\u3069\u308C\u304B\u3002\u672D\u306F tag: <\u5024> \u3067\u66F8\u304F`,
|
|
4060
|
+
badSide: `side \u306F ${list(SIDES)} \u306E\u3069\u308C\u304B`,
|
|
4061
|
+
carryTwice: "\u904B\u3076\u7269\u306F 1 \u672C\u306E\u7DDA\u306B 1 \u3064\u307E\u3067\u3002carry \u3068 tag \u306F\u4E00\u7DD2\u306B\u66F8\u304B\u306A\u3044"
|
|
3992
4062
|
};
|
|
3993
4063
|
};
|
|
3994
4064
|
var en2 = () => {
|
|
3995
|
-
const
|
|
4065
|
+
const shapes = {
|
|
4066
|
+
sequence: "sequence <title>",
|
|
4067
|
+
architecture: "architecture <title>",
|
|
4068
|
+
group: "group <id> as <name> [note: <note>] through end",
|
|
4069
|
+
box: "box <id> as <name> [note: <caption>]",
|
|
4070
|
+
strip: "strip <title>: <field>, <field>",
|
|
4071
|
+
view: "sequence <title> [view: follow]",
|
|
4072
|
+
step: "step <step title>",
|
|
4073
|
+
say: "say <step body>",
|
|
4074
|
+
code: "code <command>",
|
|
4075
|
+
act: "act <id>: <action>",
|
|
4076
|
+
note: "note <id>: <caption>",
|
|
4077
|
+
set: "set <field>: <value> [tag: <tag>]"
|
|
4078
|
+
};
|
|
3996
4079
|
return {
|
|
3997
|
-
noHead: `Write the diagram kind (${
|
|
3998
|
-
badHead: `Start the first line with ${
|
|
4080
|
+
noHead: `Write the diagram kind (${list(KINDS)}) and a title on the first line`,
|
|
4081
|
+
badHead: `Start the first line with ${KINDS.join(" or ")}, followed by the title`,
|
|
4082
|
+
oldForm: (word, shape) => `${word} is the old notation. Write ${shapes[shape]} instead`,
|
|
4083
|
+
unknownHead: (word) => `A line cannot start with ${word}. Start a line with ${list(HEADS)}, or <id> -> <id>`,
|
|
3999
4084
|
duplicate: (id) => `${id} is already declared`,
|
|
4085
|
+
needsId: (word) => `Write an id after ${word}`,
|
|
4086
|
+
badId: (id) => `${id} cannot be an id. An id has no spaces and none of [ ] \u300C \u300D = :`,
|
|
4087
|
+
afterId: "Write as <name> or attributes after the id",
|
|
4000
4088
|
undeclaredBox: (id) => `No box named ${id} is declared`,
|
|
4001
|
-
noBody: (title) => `Step "${title}" needs a body (a line
|
|
4002
|
-
emptyStepTitle: "Write a title after
|
|
4089
|
+
noBody: (title) => `Step "${title}" needs a body (a say line)`,
|
|
4090
|
+
emptyStepTitle: "Write a title after step",
|
|
4003
4091
|
noBoxes: "Declare at least one box",
|
|
4004
|
-
noSteps: "Write at least one step (a line
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4092
|
+
noSteps: "Write at least one step (a step line)",
|
|
4093
|
+
emptySay: "Write the step body after say, or on indented lines below it",
|
|
4094
|
+
sayTwice: "A step takes one say. Continue a long body on indented lines below it",
|
|
4095
|
+
codeTwice: "A step takes at most one code line",
|
|
4096
|
+
emptyCode: "Write the command after code",
|
|
4097
|
+
declarationInStep: "Write group, box and strip before the first step",
|
|
4098
|
+
stepLineBeforeStep: "Write lines, say, code, act, note and set under a step",
|
|
4099
|
+
nestedGroup: "A group cannot hold another group. Close it with end first",
|
|
4100
|
+
strayEnd: "There is no group for this end to close",
|
|
4101
|
+
endTakesNothing: "Write nothing after end",
|
|
4102
|
+
unclosedGroup: (id) => `group ${id} is not closed with end`,
|
|
4103
|
+
regionReopened: (id) => `group ${id} is already declared. To open it again, write only group ${id}; put the name and attributes on the first one`,
|
|
4011
4104
|
emptyName: (id) => `${id} has an empty name`,
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4105
|
+
stripTwice: "Declare only one strip",
|
|
4106
|
+
stripShape: "Write a strip as strip <title>: <field>, <field>",
|
|
4107
|
+
emptyStripField: "A strip field name is empty",
|
|
4108
|
+
stripUndeclared: "Declare the strip as strip <title>: <field>, <field> before writing set",
|
|
4109
|
+
noSuchField: (field) => `The strip has no field named ${field}`,
|
|
4110
|
+
stripNeedsValue: "Write set as set <field>: <value> or set <field> [tag: <tag>]",
|
|
4111
|
+
actShape: "Write act as act <id>: <action>",
|
|
4112
|
+
noteShape: "Write note as note <id>: <caption>",
|
|
4113
|
+
edgeShape: "Write a line as <id> -> <id>: <label> [<attributes>]",
|
|
4114
|
+
emptyLabel: "Write the label after the : of a line, or leave out the : as well",
|
|
4115
|
+
quoteUnclosed: 'A value that starts with " is not closed with "',
|
|
4116
|
+
afterQuote: 'After a value closed with ", write only attributes or the next ,',
|
|
4117
|
+
attrUnclosed: "Start attributes with [, close them with ], and put them at the end of the line",
|
|
4118
|
+
attrNoColon: (raw) => `[${raw}] cannot be read. Write an attribute as [<key>: <value>]`,
|
|
4119
|
+
attrNoValue: (key) => `Write a value after ${key}:`,
|
|
4120
|
+
attrTwice: (key) => `Write ${key} only once on a line`,
|
|
4121
|
+
badAttr: (key, allowed) => `${key} cannot be written on this line. Write ${list(allowed)}`,
|
|
4122
|
+
badColor: `color is one of ${list(COLORS)}`,
|
|
4123
|
+
badView: `view is one of ${list(VIEWS)}`,
|
|
4124
|
+
badCarry: `carry is one of ${list(CARRIES)}. Write a tag as tag: <value>`,
|
|
4125
|
+
badSide: `side is one of ${list(SIDES)}`,
|
|
4126
|
+
carryTwice: "A line carries one thing. Do not write carry and tag together"
|
|
4028
4127
|
};
|
|
4029
4128
|
};
|
|
4030
4129
|
var MESSAGES = { ja: ja2(), en: en2() };
|
|
4031
4130
|
var messagesFor = (lang) => MESSAGES[lang];
|
|
4032
4131
|
|
|
4033
4132
|
// src/tokenize.ts
|
|
4133
|
+
var isBlankOrComment = (source) => source.text === "" || source.text.startsWith("//");
|
|
4034
4134
|
function columnOf(source, index) {
|
|
4035
4135
|
return source.column + Array.from(source.text.slice(0, index)).length;
|
|
4036
4136
|
}
|
|
4037
4137
|
function tokenize(notation) {
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
if (text2 === "" || text2.startsWith("//")) return;
|
|
4042
|
-
const lead = raw.length - raw.trimStart().length;
|
|
4043
|
-
lines.push({ line: index + 1, column: Array.from(raw.slice(0, lead)).length + 1, text: text2 });
|
|
4138
|
+
return notation.split(/\r?\n/).map((raw, index) => {
|
|
4139
|
+
const indent = Array.from(raw.slice(0, raw.length - raw.trimStart().length)).length;
|
|
4140
|
+
return { line: index + 1, column: indent + 1, indent, text: raw.trim() };
|
|
4044
4141
|
});
|
|
4045
|
-
return lines;
|
|
4046
4142
|
}
|
|
4047
4143
|
|
|
4048
4144
|
// src/parser.ts
|
|
@@ -4056,74 +4152,162 @@ var NotationError = class extends Error {
|
|
|
4056
4152
|
column;
|
|
4057
4153
|
};
|
|
4058
4154
|
var fail = (source, index, message) => new NotationError(source.line, columnOf(source, index), message);
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4155
|
+
var ID = /^[^\s[\]「」=:]+$/u;
|
|
4156
|
+
var EDGE = /^([^\s:[\]]+?)\s*->\s*([^\s:[\]]+)/du;
|
|
4157
|
+
var isOneOf = (list2, word) => list2.includes(word);
|
|
4158
|
+
function skipSpaces(text2, at2) {
|
|
4159
|
+
let i = at2;
|
|
4160
|
+
while (i < text2.length && /\s/u.test(text2[i] ?? "")) i++;
|
|
4161
|
+
return i;
|
|
4162
|
+
}
|
|
4163
|
+
function readQuoted(source, at2, m) {
|
|
4164
|
+
const text2 = source.text;
|
|
4165
|
+
let value = "";
|
|
4166
|
+
for (let i = at2 + 1; i < text2.length; i++) {
|
|
4167
|
+
const c = text2[i] ?? "";
|
|
4168
|
+
const next = text2[i + 1] ?? "";
|
|
4169
|
+
if (c === "\\" && (next === '"' || next === "\\")) {
|
|
4170
|
+
value += next;
|
|
4171
|
+
i++;
|
|
4172
|
+
} else if (c === '"') {
|
|
4173
|
+
return { value, end: i + 1 };
|
|
4174
|
+
} else {
|
|
4175
|
+
value += c;
|
|
4176
|
+
}
|
|
4069
4177
|
}
|
|
4070
|
-
|
|
4178
|
+
throw fail(source, at2, m.quoteUnclosed);
|
|
4071
4179
|
}
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
};
|
|
4080
|
-
function nameAndNote(text2, lang) {
|
|
4081
|
-
for (const [open, close] of QUOTES[lang]) {
|
|
4082
|
-
const found = new RegExp(`^(.*?)\\s*${escape2(open)}(.*)${escape2(close)}$`).exec(text2);
|
|
4083
|
-
if (found) return { name: (found[1] ?? "").trim(), note: found[2] ?? "" };
|
|
4180
|
+
function readName(source, at2, m) {
|
|
4181
|
+
const text2 = source.text;
|
|
4182
|
+
const start = skipSpaces(text2, at2);
|
|
4183
|
+
if (text2[start] === '"') {
|
|
4184
|
+
const quoted = readQuoted(source, start, m);
|
|
4185
|
+
const rest2 = skipSpaces(text2, quoted.end);
|
|
4186
|
+
if (rest2 < text2.length && text2[rest2] !== "[") throw fail(source, rest2, m.afterQuote);
|
|
4187
|
+
return { value: quoted.value, rest: rest2 };
|
|
4084
4188
|
}
|
|
4085
|
-
|
|
4189
|
+
const open = text2.indexOf("[", start);
|
|
4190
|
+
const rest = open < 0 ? text2.length : open;
|
|
4191
|
+
return { value: text2.slice(start, rest).trim(), rest };
|
|
4086
4192
|
}
|
|
4087
|
-
function
|
|
4088
|
-
const
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4193
|
+
function readAttrs(source, at2, allowed, m) {
|
|
4194
|
+
const text2 = source.text;
|
|
4195
|
+
if (!text2.endsWith("]")) throw fail(source, at2, m.attrUnclosed);
|
|
4196
|
+
const close = text2.length - 1;
|
|
4197
|
+
const attrs = /* @__PURE__ */ new Map();
|
|
4198
|
+
for (let i = at2 + 1; ; ) {
|
|
4199
|
+
const start = skipSpaces(text2, i);
|
|
4200
|
+
let colon = start;
|
|
4201
|
+
while (colon < close && text2[colon] !== ":" && text2[colon] !== ",") colon++;
|
|
4202
|
+
if (colon >= close || text2[colon] !== ":") throw fail(source, start, m.attrNoColon(text2.slice(start, colon).trim()));
|
|
4203
|
+
const key = text2.slice(start, colon).trim();
|
|
4204
|
+
if (!allowed.includes(key)) throw fail(source, start, m.badAttr(key, allowed));
|
|
4205
|
+
if (attrs.has(key)) throw fail(source, start, m.attrTwice(key));
|
|
4206
|
+
let end = skipSpaces(text2, colon + 1);
|
|
4207
|
+
let value;
|
|
4208
|
+
if (text2[end] === '"') {
|
|
4209
|
+
const quoted = readQuoted(source, end, m);
|
|
4210
|
+
value = quoted.value;
|
|
4211
|
+
end = skipSpaces(text2, quoted.end);
|
|
4212
|
+
if (end < close && text2[end] !== ",") throw fail(source, end, m.afterQuote);
|
|
4213
|
+
} else {
|
|
4214
|
+
const comma = text2.indexOf(",", end);
|
|
4215
|
+
const stop = comma < 0 || comma > close ? close : comma;
|
|
4216
|
+
value = text2.slice(end, stop).trim();
|
|
4217
|
+
end = stop;
|
|
4218
|
+
}
|
|
4219
|
+
if (value === "") throw fail(source, start, m.attrNoValue(key));
|
|
4220
|
+
attrs.set(key, { value, at: start });
|
|
4221
|
+
if (end >= close) return attrs;
|
|
4222
|
+
i = end + 1;
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
function readList(source, at2, m) {
|
|
4226
|
+
const text2 = source.text;
|
|
4227
|
+
const items = [];
|
|
4228
|
+
for (let i = at2; ; ) {
|
|
4229
|
+
const start = skipSpaces(text2, i);
|
|
4230
|
+
let value;
|
|
4231
|
+
let end;
|
|
4232
|
+
if (text2[start] === '"') {
|
|
4233
|
+
const quoted = readQuoted(source, start, m);
|
|
4234
|
+
value = quoted.value;
|
|
4235
|
+
end = skipSpaces(text2, quoted.end);
|
|
4236
|
+
if (end < text2.length && text2[end] !== ",") throw fail(source, end, m.afterQuote);
|
|
4237
|
+
} else {
|
|
4238
|
+
const comma = text2.indexOf(",", start);
|
|
4239
|
+
end = comma < 0 ? text2.length : comma;
|
|
4240
|
+
value = text2.slice(start, end).trim();
|
|
4241
|
+
}
|
|
4242
|
+
if (value === "") throw fail(source, start, m.emptyStripField);
|
|
4243
|
+
items.push(value);
|
|
4244
|
+
if (end >= text2.length) return items;
|
|
4245
|
+
i = end + 1;
|
|
4092
4246
|
}
|
|
4093
|
-
return null;
|
|
4094
4247
|
}
|
|
4095
|
-
function
|
|
4096
|
-
const
|
|
4097
|
-
const
|
|
4248
|
+
function readDeclared(source, word, allowed, m) {
|
|
4249
|
+
const text2 = source.text;
|
|
4250
|
+
const start = skipSpaces(text2, word.length);
|
|
4251
|
+
const id = /^[^\s[]+/u.exec(text2.slice(start))?.[0];
|
|
4252
|
+
if (id === void 0) throw fail(source, 0, m.needsId(word));
|
|
4253
|
+
if (!ID.test(id)) throw fail(source, start, m.badId(id));
|
|
4254
|
+
let rest = skipSpaces(text2, start + id.length);
|
|
4255
|
+
let name;
|
|
4256
|
+
if (text2.startsWith(AS, rest) && /^(\s|$)/u.test(text2.slice(rest + AS.length, rest + AS.length + 1))) {
|
|
4257
|
+
const named = readName(source, rest + AS.length, m);
|
|
4258
|
+
if (named.value === "") throw fail(source, rest, m.emptyName(id));
|
|
4259
|
+
name = named.value;
|
|
4260
|
+
rest = named.rest;
|
|
4261
|
+
}
|
|
4262
|
+
if (rest < text2.length && text2[rest] !== "[") throw fail(source, rest, m.afterId);
|
|
4263
|
+
const attrs = rest < text2.length ? readAttrs(source, rest, allowed, m) : /* @__PURE__ */ new Map();
|
|
4264
|
+
return { id, idAt: start, ...name !== void 0 ? { name } : {}, attrs };
|
|
4265
|
+
}
|
|
4266
|
+
function lookOf(source, attrs, m) {
|
|
4267
|
+
const color = attrs.get("color");
|
|
4268
|
+
if (color && !isOneOf(COLORS, color.value)) throw fail(source, color.at, m.badColor);
|
|
4269
|
+
const note = attrs.get("note")?.value;
|
|
4270
|
+
const icon = attrs.get("icon")?.value;
|
|
4098
4271
|
return {
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
// 空白を全部読んでから次の字を見る (`\s` を外すと、空白 2 つの `帯 -> 受け手` を帯の値と読む)
|
|
4103
|
-
stripValue: new RegExp(`^${head("strip")}\\s+(?!\\s|->|:|=${opens})`)
|
|
4272
|
+
...note !== void 0 ? { note } : {},
|
|
4273
|
+
...color ? { color: color.value } : {},
|
|
4274
|
+
...icon !== void 0 ? { icon } : {}
|
|
4104
4275
|
};
|
|
4105
4276
|
}
|
|
4106
|
-
function
|
|
4107
|
-
const [
|
|
4108
|
-
if (
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
if (
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
const
|
|
4277
|
+
function oldForm(text2, inStep) {
|
|
4278
|
+
const first = text2.split(/\s/u)[0] ?? "";
|
|
4279
|
+
if (first === "\u5E2F") return { word: first, shape: inStep ? "set" : "strip" };
|
|
4280
|
+
if (Object.hasOwn(OLD_HEADS, first)) return { word: first, shape: OLD_HEADS[first] };
|
|
4281
|
+
if (text2.startsWith("#")) return { word: "#", shape: "step" };
|
|
4282
|
+
if (text2.startsWith(">")) return { word: ">", shape: "say" };
|
|
4283
|
+
if (text2.startsWith("`")) return { word: "`", shape: "code" };
|
|
4284
|
+
if (text2.includes("\u300C")) return { word: "\u300C\u300D", shape: inStep ? "note" : "box" };
|
|
4285
|
+
if (/^[^\s=:]+\s*=(?!>)/u.test(text2)) return { word: "=", shape: "box" };
|
|
4286
|
+
if (inStep && /^[^\s:]+\s*:/u.test(text2)) return { word: ":", shape: "act" };
|
|
4287
|
+
return void 0;
|
|
4288
|
+
}
|
|
4289
|
+
function parseLines(lines, m) {
|
|
4290
|
+
const headAt = lines.findIndex((source) => !isBlankOrComment(source));
|
|
4291
|
+
const head = lines[headAt];
|
|
4292
|
+
if (!head) throw new NotationError(1, 1, m.noHead);
|
|
4293
|
+
const kind = head.text.split(/\s/u)[0] ?? "";
|
|
4294
|
+
if (!isOneOf(KINDS, kind)) {
|
|
4295
|
+
const old = oldForm(head.text, false);
|
|
4296
|
+
throw fail(head, 0, old ? m.oldForm(old.word, old.shape) : m.badHead);
|
|
4297
|
+
}
|
|
4298
|
+
const titled = readName(head, kind.length, m);
|
|
4299
|
+
if (titled.value === "") throw fail(head, 0, m.badHead);
|
|
4300
|
+
const title = titled.value;
|
|
4301
|
+
const shown = titled.rest < head.text.length ? readAttrs(head, titled.rest, ATTRS.diagram, m).get("view") : void 0;
|
|
4302
|
+
if (shown && !isOneOf(VIEWS, shown.value)) throw fail(head, shown.at, m.badView);
|
|
4303
|
+
const view = shown?.value;
|
|
4120
4304
|
const regions = [];
|
|
4121
4305
|
const boxes = [];
|
|
4122
4306
|
const ids = /* @__PURE__ */ new Set();
|
|
4123
4307
|
let strip2;
|
|
4124
|
-
let view;
|
|
4125
4308
|
const steps = [];
|
|
4126
4309
|
let building;
|
|
4310
|
+
let group;
|
|
4127
4311
|
const declare = (source, id, at2) => {
|
|
4128
4312
|
if (ids.has(id)) throw fail(source, at2, m.duplicate(id));
|
|
4129
4313
|
ids.add(id);
|
|
@@ -4132,9 +4316,16 @@ function parseLines(lines, fallback) {
|
|
|
4132
4316
|
if (!boxes.some((box) => box.id === id)) throw fail(source, at2, m.undeclaredBox(id));
|
|
4133
4317
|
return id;
|
|
4134
4318
|
};
|
|
4319
|
+
const inStep = (source) => {
|
|
4320
|
+
if (!building) throw fail(source, 0, m.stepLineBeforeStep);
|
|
4321
|
+
return building;
|
|
4322
|
+
};
|
|
4323
|
+
const beforeSteps = (source) => {
|
|
4324
|
+
if (building) throw fail(source, 0, m.declarationInStep);
|
|
4325
|
+
};
|
|
4135
4326
|
const finish = () => {
|
|
4136
4327
|
if (!building) return;
|
|
4137
|
-
if (building.body
|
|
4328
|
+
if (building.body === void 0) throw fail(building.source, 0, m.noBody(building.title));
|
|
4138
4329
|
steps.push({
|
|
4139
4330
|
title: building.title,
|
|
4140
4331
|
body: building.body.join("\n"),
|
|
@@ -4142,21 +4333,73 @@ function parseLines(lines, fallback) {
|
|
|
4142
4333
|
events: building.events
|
|
4143
4334
|
});
|
|
4144
4335
|
};
|
|
4145
|
-
|
|
4336
|
+
const rest = lines.slice(headAt + 1);
|
|
4337
|
+
for (let i = 0; i < rest.length; i++) {
|
|
4338
|
+
const source = rest[i];
|
|
4339
|
+
if (!source || isBlankOrComment(source)) continue;
|
|
4146
4340
|
const text2 = source.text;
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
if (stepTitle === "") throw fail(source, 0, m.emptyStepTitle);
|
|
4151
|
-
building = { source, title: stepTitle, body: [], events: [] };
|
|
4341
|
+
const edge2 = EDGE.exec(text2);
|
|
4342
|
+
if (edge2) {
|
|
4343
|
+
inStep(source).events.push(readEdge(source, edge2));
|
|
4152
4344
|
continue;
|
|
4153
4345
|
}
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4346
|
+
const word = text2.split(/\s/u)[0] ?? "";
|
|
4347
|
+
if (!isOneOf(HEADS, word)) {
|
|
4348
|
+
const old = oldForm(text2, building !== void 0);
|
|
4349
|
+
throw fail(source, 0, old ? m.oldForm(old.word, old.shape) : m.unknownHead(word));
|
|
4350
|
+
}
|
|
4351
|
+
switch (word) {
|
|
4352
|
+
case "group":
|
|
4353
|
+
readGroup(source);
|
|
4354
|
+
break;
|
|
4355
|
+
case "end":
|
|
4356
|
+
if (text2 !== word) throw fail(source, word.length, m.endTakesNothing);
|
|
4357
|
+
if (!group) throw fail(source, 0, m.strayEnd);
|
|
4358
|
+
group = void 0;
|
|
4359
|
+
break;
|
|
4360
|
+
case "box":
|
|
4361
|
+
readBox(source);
|
|
4362
|
+
break;
|
|
4363
|
+
case "strip":
|
|
4364
|
+
readStrip(source);
|
|
4365
|
+
break;
|
|
4366
|
+
case "step": {
|
|
4367
|
+
if (group) throw fail(group.source, 0, m.unclosedGroup(group.id));
|
|
4368
|
+
finish();
|
|
4369
|
+
const stepTitle = text2.slice(word.length).trim();
|
|
4370
|
+
if (stepTitle === "") throw fail(source, 0, m.emptyStepTitle);
|
|
4371
|
+
building = { source, title: stepTitle, events: [] };
|
|
4372
|
+
break;
|
|
4373
|
+
}
|
|
4374
|
+
case "say":
|
|
4375
|
+
i = readSay(source, i);
|
|
4376
|
+
break;
|
|
4377
|
+
case "code": {
|
|
4378
|
+
const step = inStep(source);
|
|
4379
|
+
if (step.code !== void 0) throw fail(source, 0, m.codeTwice);
|
|
4380
|
+
const command = text2.slice(word.length).trim();
|
|
4381
|
+
if (command === "") throw fail(source, 0, m.emptyCode);
|
|
4382
|
+
step.code = command;
|
|
4383
|
+
break;
|
|
4384
|
+
}
|
|
4385
|
+
case "act": {
|
|
4386
|
+
const step = inStep(source);
|
|
4387
|
+
const on = readOnBox(source, word, m.actShape);
|
|
4388
|
+
step.events.push({ type: "action", box: on.box, label: on.text });
|
|
4389
|
+
break;
|
|
4390
|
+
}
|
|
4391
|
+
case "note": {
|
|
4392
|
+
const step = inStep(source);
|
|
4393
|
+
const on = readOnBox(source, word, m.noteShape);
|
|
4394
|
+
step.events.push({ type: "swap", box: on.box, text: on.text });
|
|
4395
|
+
break;
|
|
4396
|
+
}
|
|
4397
|
+
case "set":
|
|
4398
|
+
inStep(source).events.push(readSet(source));
|
|
4399
|
+
break;
|
|
4157
4400
|
}
|
|
4158
|
-
building.events.push(...parseStepLine(source, building));
|
|
4159
4401
|
}
|
|
4402
|
+
if (group) throw fail(group.source, 0, m.unclosedGroup(group.id));
|
|
4160
4403
|
finish();
|
|
4161
4404
|
if (boxes.length === 0) throw fail(head, 0, m.noBoxes);
|
|
4162
4405
|
if (steps.length === 0) throw fail(head, 0, m.noSteps);
|
|
@@ -4171,168 +4414,148 @@ function parseLines(lines, fallback) {
|
|
|
4171
4414
|
steps,
|
|
4172
4415
|
...kind === "sequence" ? { sequence: { lifelines: boxes.map((box) => box.id) } } : {}
|
|
4173
4416
|
};
|
|
4174
|
-
function
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
if (valueOf("view", word, other) !== void 0) throw foreignWord(source, at2, "view", word);
|
|
4184
|
-
throw fail(source, at2, m.badView);
|
|
4185
|
-
}
|
|
4186
|
-
view = chosen;
|
|
4187
|
-
return;
|
|
4188
|
-
}
|
|
4189
|
-
const stripDecl = own.strip.exec(text2);
|
|
4190
|
-
if (stripDecl) {
|
|
4191
|
-
if (strip2) throw fail(source, 0, m.stripTwice);
|
|
4192
|
-
const fields = (stripDecl[2] ?? "").split(",").map((field) => field.trim());
|
|
4193
|
-
if (fields.some((field) => field === "")) throw fail(source, 0, m.emptyStripField);
|
|
4194
|
-
strip2 = { title: stripDecl[1] ?? "", fields };
|
|
4195
|
-
return;
|
|
4196
|
-
}
|
|
4197
|
-
const regionDecl = own.region.exec(text2);
|
|
4198
|
-
const boxDecl = regionDecl ? void 0 : /^([^\s=]+)\s*=(?!>)\s*/d.exec(text2);
|
|
4199
|
-
const decl = regionDecl ?? boxDecl;
|
|
4200
|
-
if (!decl?.indices?.[1]) {
|
|
4201
|
-
if (foreign.view.test(text2) || foreign.strip.test(text2) || foreign.region.test(text2)) {
|
|
4202
|
-
throw foreignWord(source, 0, "head", text2.split(/\s+/)[0] ?? "");
|
|
4203
|
-
}
|
|
4204
|
-
if (looksLikeStepLine(text2, lang)) throw fail(source, 0, m.stepLineBeforeStep);
|
|
4205
|
-
throw fail(source, 0, m.unreadableDeclaration);
|
|
4206
|
-
}
|
|
4207
|
-
const id = decl[1] ?? "";
|
|
4208
|
-
declare(source, id, decl.indices[1][0]);
|
|
4209
|
-
const { end, attrs } = trailingAttrs(source, decl[0].length);
|
|
4210
|
-
const { name, note } = nameAndNote(text2.slice(decl[0].length, end), lang);
|
|
4211
|
-
if (name === "") throw fail(source, decl[0].length, m.emptyName(id));
|
|
4212
|
-
const look = {};
|
|
4213
|
-
for (const attr of attrs) {
|
|
4214
|
-
const key = valueOf("attr", attr.key, lang);
|
|
4215
|
-
if (key === "color") {
|
|
4216
|
-
if (!COLORS.includes(attr.value)) throw fail(source, attr.at, m.badColor);
|
|
4217
|
-
look.color = attr.value;
|
|
4218
|
-
} else if (key === "icon" && attr.value !== "") {
|
|
4219
|
-
look.icon = attr.value;
|
|
4220
|
-
} else if (key === "region" && !regionDecl) {
|
|
4221
|
-
if (!regions.some((region) => region.id === attr.value)) throw fail(source, attr.at, m.regionFirst(attr.value));
|
|
4222
|
-
look.region = attr.value;
|
|
4223
|
-
} else {
|
|
4224
|
-
if (valueOf("attr", attr.key, other) !== void 0) throw foreignWord(source, attr.at, "attr", attr.key);
|
|
4225
|
-
throw fail(source, attr.at, m.badAttr(attr.raw));
|
|
4226
|
-
}
|
|
4227
|
-
}
|
|
4228
|
-
if (regionDecl) {
|
|
4229
|
-
regions.push({
|
|
4230
|
-
id,
|
|
4231
|
-
name,
|
|
4232
|
-
...note !== void 0 ? { note } : {},
|
|
4233
|
-
...look.color ? { color: look.color } : {},
|
|
4234
|
-
...look.icon ? { icon: look.icon } : {}
|
|
4235
|
-
});
|
|
4236
|
-
return;
|
|
4417
|
+
function readGroup(source) {
|
|
4418
|
+
beforeSteps(source);
|
|
4419
|
+
if (group) throw fail(source, 0, m.nestedGroup);
|
|
4420
|
+
const declared = readDeclared(source, "group", ATTRS.group, m);
|
|
4421
|
+
if (regions.some((region) => region.id === declared.id)) {
|
|
4422
|
+
if (declared.name !== void 0 || declared.attrs.size > 0) throw fail(source, 0, m.regionReopened(declared.id));
|
|
4423
|
+
} else {
|
|
4424
|
+
declare(source, declared.id, declared.idAt);
|
|
4425
|
+
regions.push({ id: declared.id, name: declared.name ?? declared.id, ...lookOf(source, declared.attrs, m) });
|
|
4237
4426
|
}
|
|
4427
|
+
group = { source, id: declared.id };
|
|
4428
|
+
}
|
|
4429
|
+
function readBox(source) {
|
|
4430
|
+
beforeSteps(source);
|
|
4431
|
+
const declared = readDeclared(source, "box", ATTRS.box, m);
|
|
4432
|
+
declare(source, declared.id, declared.idAt);
|
|
4433
|
+
const { note, ...look } = lookOf(source, declared.attrs, m);
|
|
4238
4434
|
boxes.push({
|
|
4239
|
-
id,
|
|
4240
|
-
name,
|
|
4435
|
+
id: declared.id,
|
|
4436
|
+
name: declared.name ?? declared.id,
|
|
4241
4437
|
...note !== void 0 ? { sub: note } : {},
|
|
4242
|
-
...look
|
|
4243
|
-
...
|
|
4244
|
-
...look.region ? { region: look.region } : {}
|
|
4438
|
+
...look,
|
|
4439
|
+
...group ? { region: group.id } : {}
|
|
4245
4440
|
});
|
|
4246
4441
|
}
|
|
4247
|
-
function
|
|
4442
|
+
function readStrip(source) {
|
|
4443
|
+
beforeSteps(source);
|
|
4444
|
+
if (strip2) throw fail(source, 0, m.stripTwice);
|
|
4248
4445
|
const text2 = source.text;
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
if (own.stripValue.test(text2)) return [parseStripValue(source)];
|
|
4260
|
-
if (foreign.stripValue.test(text2)) throw foreignWord(source, 0, "head", text2.split(/\s+/)[0] ?? "");
|
|
4261
|
-
if (text2.includes("->")) return [parseEdge(source)];
|
|
4262
|
-
if (/^[^\s=]+\s*=(?!>)/.test(text2)) throw fail(source, 0, m.declarationInStep);
|
|
4263
|
-
const swap = swapLine(text2, lang);
|
|
4264
|
-
if (swap?.indices?.[1]) {
|
|
4265
|
-
const box = boxAt(source, swap[1] ?? "", swap.indices[1][0]);
|
|
4266
|
-
if ((swap[2] ?? "") === "") throw fail(source, swap.indices[1][1], m.emptySwap);
|
|
4267
|
-
return [{ type: "swap", box, text: swap[2] ?? "" }];
|
|
4446
|
+
const start = skipSpaces(text2, "strip".length);
|
|
4447
|
+
let stripTitle;
|
|
4448
|
+
let colon;
|
|
4449
|
+
if (text2[start] === '"') {
|
|
4450
|
+
const quoted = readQuoted(source, start, m);
|
|
4451
|
+
stripTitle = quoted.value;
|
|
4452
|
+
colon = skipSpaces(text2, quoted.end);
|
|
4453
|
+
} else {
|
|
4454
|
+
colon = text2.indexOf(":", start);
|
|
4455
|
+
stripTitle = colon < 0 ? "" : text2.slice(start, colon).trim();
|
|
4268
4456
|
}
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
throw fail(source, 0, m.unreadableStepLine);
|
|
4457
|
+
if (stripTitle === "" || text2[colon] !== ":") throw fail(source, 0, m.stripShape);
|
|
4458
|
+
strip2 = { title: stripTitle, fields: readList(source, colon + 1, m) };
|
|
4272
4459
|
}
|
|
4273
|
-
function
|
|
4274
|
-
const
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
const
|
|
4278
|
-
|
|
4279
|
-
let
|
|
4280
|
-
let
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
if (kind2 === "tag") {
|
|
4287
|
-
if (attr.value === "") throw fail(source, attr.at, m.tagNeedsValue);
|
|
4288
|
-
carry = { kind: kind2, text: attr.value };
|
|
4289
|
-
} else {
|
|
4290
|
-
if (attr.value !== "") throw fail(source, attr.at, m.carryTakesNoValue(attr.key));
|
|
4291
|
-
carry = { kind: kind2 };
|
|
4292
|
-
}
|
|
4293
|
-
} else if (label2 && valueOf("side", attr.value, lang) !== void 0) {
|
|
4294
|
-
labelSide = valueOf("side", attr.value, lang);
|
|
4295
|
-
} else {
|
|
4296
|
-
if (label2 && valueOf("side", attr.value, other) !== void 0) throw foreignWord(source, attr.at, "side", attr.value);
|
|
4297
|
-
if (valueOf("carry", attr.key, other) !== void 0) throw foreignWord(source, attr.at, "carry", attr.key);
|
|
4298
|
-
if (valueOf("attr", attr.key, other) !== void 0) throw foreignWord(source, attr.at, "attr", attr.key);
|
|
4299
|
-
throw fail(source, attr.at, m.badAttr(attr.raw));
|
|
4460
|
+
function readSay(source, index) {
|
|
4461
|
+
const step = inStep(source);
|
|
4462
|
+
if (step.body !== void 0) throw fail(source, 0, m.sayTwice);
|
|
4463
|
+
const inline = source.text.slice("say".length).trim();
|
|
4464
|
+
const body = inline === "" ? [] : [inline];
|
|
4465
|
+
let blanks = 0;
|
|
4466
|
+
let last = index;
|
|
4467
|
+
for (let j = index + 1; j < rest.length; j++) {
|
|
4468
|
+
const next = rest[j];
|
|
4469
|
+
if (!next) break;
|
|
4470
|
+
if (next.text === "") {
|
|
4471
|
+
blanks++;
|
|
4472
|
+
continue;
|
|
4300
4473
|
}
|
|
4474
|
+
if (next.indent <= source.indent) break;
|
|
4475
|
+
body.push(...Array.from({ length: blanks }, () => ""), next.text);
|
|
4476
|
+
blanks = 0;
|
|
4477
|
+
last = j;
|
|
4478
|
+
}
|
|
4479
|
+
if (body.length === 0) throw fail(source, 0, m.emptySay);
|
|
4480
|
+
step.body = body;
|
|
4481
|
+
return last;
|
|
4482
|
+
}
|
|
4483
|
+
function readOnBox(source, word, shape) {
|
|
4484
|
+
const found = new RegExp(`^${word}\\s+([^\\s:]+)\\s*:\\s*(.*)$`, "du").exec(source.text);
|
|
4485
|
+
const idAt = found?.indices?.[1]?.[0];
|
|
4486
|
+
if (!found || idAt === void 0 || (found[2] ?? "") === "") throw fail(source, 0, shape);
|
|
4487
|
+
return { box: boxAt(source, found[1] ?? "", idAt), text: found[2] ?? "" };
|
|
4488
|
+
}
|
|
4489
|
+
function readEdge(source, edge2) {
|
|
4490
|
+
const text2 = source.text;
|
|
4491
|
+
const from = boxAt(source, edge2[1] ?? "", edge2.indices?.[1]?.[0] ?? 0);
|
|
4492
|
+
const to = boxAt(source, edge2[2] ?? "", edge2.indices?.[2]?.[0] ?? 0);
|
|
4493
|
+
let at2 = skipSpaces(text2, edge2[0].length);
|
|
4494
|
+
let label2;
|
|
4495
|
+
if (text2[at2] === ":") {
|
|
4496
|
+
const named = readName(source, at2 + 1, m);
|
|
4497
|
+
if (named.value === "") throw fail(source, at2, m.emptyLabel);
|
|
4498
|
+
label2 = named.value;
|
|
4499
|
+
at2 = named.rest;
|
|
4301
4500
|
}
|
|
4501
|
+
if (at2 < text2.length && text2[at2] !== "[") throw fail(source, at2, m.edgeShape);
|
|
4502
|
+
const attrs = at2 < text2.length ? readAttrs(source, at2, ATTRS.edge, m) : /* @__PURE__ */ new Map();
|
|
4503
|
+
const tag = attrs.get("tag");
|
|
4504
|
+
const carried = attrs.get("carry");
|
|
4505
|
+
if (tag && carried) throw fail(source, Math.max(tag.at, carried.at), m.carryTwice);
|
|
4506
|
+
if (carried && !isOneOf(CARRIES, carried.value)) throw fail(source, carried.at, m.badCarry);
|
|
4507
|
+
const side = attrs.get("side");
|
|
4508
|
+
if (side && !isOneOf(SIDES, side.value)) throw fail(source, side.at, m.badSide);
|
|
4509
|
+
let carry = { kind: "envelope" };
|
|
4510
|
+
if (tag) carry = { kind: "tag", text: tag.value };
|
|
4511
|
+
else if (carried) carry = carried.value === "coin" ? { kind: "coin" } : { kind: "none" };
|
|
4302
4512
|
return {
|
|
4303
4513
|
type: "edge",
|
|
4304
4514
|
from,
|
|
4305
4515
|
to,
|
|
4306
|
-
...
|
|
4307
|
-
carry
|
|
4308
|
-
...
|
|
4516
|
+
...label2 !== void 0 ? { label: label2 } : {},
|
|
4517
|
+
carry,
|
|
4518
|
+
...side ? { labelSide: side.value } : {}
|
|
4309
4519
|
};
|
|
4310
4520
|
}
|
|
4311
|
-
function
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
const
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4521
|
+
function readSet(source) {
|
|
4522
|
+
const declared = strip2;
|
|
4523
|
+
if (!declared) throw fail(source, 0, m.stripUndeclared);
|
|
4524
|
+
const text2 = source.text;
|
|
4525
|
+
const start = skipSpaces(text2, "set".length);
|
|
4526
|
+
let field;
|
|
4527
|
+
let at2;
|
|
4528
|
+
if (text2[start] === '"') {
|
|
4529
|
+
const quoted = readQuoted(source, start, m);
|
|
4530
|
+
field = quoted.value;
|
|
4531
|
+
at2 = skipSpaces(text2, quoted.end);
|
|
4532
|
+
} else {
|
|
4533
|
+
at2 = start;
|
|
4534
|
+
while (at2 < text2.length && text2[at2] !== ":" && text2[at2] !== "[") at2++;
|
|
4535
|
+
field = text2.slice(start, at2).trim();
|
|
4536
|
+
}
|
|
4537
|
+
if (!declared.fields.includes(field)) throw fail(source, start, m.noSuchField(field));
|
|
4538
|
+
let value;
|
|
4539
|
+
if (text2[at2] === ":") {
|
|
4540
|
+
const named = readName(source, at2 + 1, m);
|
|
4541
|
+
if (named.value === "") throw fail(source, at2, m.stripNeedsValue);
|
|
4542
|
+
value = named.value;
|
|
4543
|
+
at2 = named.rest;
|
|
4544
|
+
}
|
|
4545
|
+
if (at2 < text2.length && text2[at2] !== "[") throw fail(source, at2, m.stripNeedsValue);
|
|
4546
|
+
const tag = at2 < text2.length ? readAttrs(source, at2, ATTRS.set, m).get("tag")?.value : void 0;
|
|
4547
|
+
if (value === void 0 && tag === void 0) throw fail(source, 0, m.stripNeedsValue);
|
|
4322
4548
|
return {
|
|
4323
4549
|
type: "strip",
|
|
4324
4550
|
field,
|
|
4325
|
-
...
|
|
4551
|
+
...value !== void 0 ? { value } : {},
|
|
4326
4552
|
...tag !== void 0 ? { tag } : {}
|
|
4327
4553
|
};
|
|
4328
4554
|
}
|
|
4329
4555
|
}
|
|
4330
|
-
function looksLikeStepLine(text2, lang) {
|
|
4331
|
-
return text2.startsWith(">") || text2.startsWith("`") || text2.includes("->") || swapLine(text2, lang) !== null;
|
|
4332
|
-
}
|
|
4333
4556
|
function parseNotation(notation, options = {}) {
|
|
4334
4557
|
try {
|
|
4335
|
-
return { ok: true, diagram: parseLines(tokenize(notation), options.lang ?? "ja") };
|
|
4558
|
+
return { ok: true, diagram: parseLines(tokenize(notation), messagesFor(options.lang ?? "ja")) };
|
|
4336
4559
|
} catch (error) {
|
|
4337
4560
|
if (!(error instanceof NotationError)) throw error;
|
|
4338
4561
|
return { ok: false, errors: [{ line: error.line, column: error.column, message: error.message }] };
|
|
@@ -4360,7 +4583,7 @@ var CLI_TEXT = {
|
|
|
4360
4583
|
" --width <px> \u67A0\u306E\u5E45\u3002\u898B\u305B\u65B9\u3092\u9078\u3073\u3001\u5BC4\u3063\u3066\u898B\u305B\u308B\u7A93\u306E\u5E45\u306B\u306A\u308B",
|
|
4361
4584
|
` --palette <\u540D\u524D> \u8272\u5473 (${PALETTE_NAMES})`,
|
|
4362
4585
|
" --theme <\u660E\u6697> light \u304B dark",
|
|
4363
|
-
" --lang <\u8A00\u8449> \u56F3\u306E\u4E2D\u306E\u5B57\u3068\u547D\u4EE4\u306E\u6587\u306E\u8A00\u8449 (ja \u304B en)\u3002\u65E2\u5B9A\u306F
|
|
4586
|
+
" --lang <\u8A00\u8449> \u56F3\u306E\u4E2D\u306E\u5B57\u3068\u547D\u4EE4\u306E\u6587\u306E\u8A00\u8449 (ja \u304B en)\u3002\u65E2\u5B9A\u306F ja",
|
|
4364
4587
|
" --fps <\u6570> mp4 \u306E 1 \u79D2\u306E\u30B3\u30DE\u306E\u6570 (\u65E2\u5B9A 30)",
|
|
4365
4588
|
" --gif-fps <\u6570> GIF \u306E 1 \u79D2\u306E\u30B3\u30DE\u306E\u6570 (\u65E2\u5B9A 10)",
|
|
4366
4589
|
" --format <\u5F62> \u66F8\u304D\u51FA\u3059\u5F62\u3092 , \u3067\u533A\u5207\u308B (\u65E2\u5B9A mp4,gif,html,png)"
|
|
@@ -4382,7 +4605,7 @@ var CLI_TEXT = {
|
|
|
4382
4605
|
" --width <px> Frame width. Picks the view and sets the window width when following",
|
|
4383
4606
|
` --palette <name> Colors (${PALETTE_NAMES})`,
|
|
4384
4607
|
" --theme <mode> light or dark",
|
|
4385
|
-
" --lang <lang> Language of the diagram text and command messages (ja or en). Defaults to
|
|
4608
|
+
" --lang <lang> Language of the diagram text and command messages (ja or en). Defaults to ja",
|
|
4386
4609
|
" --fps <n> Frames per second of the mp4 (default 30)",
|
|
4387
4610
|
" --gif-fps <n> Frames per second of the GIF (default 10)",
|
|
4388
4611
|
" --format <list> Formats to write, separated by , (default mp4,gif,html,png)"
|
|
@@ -4435,7 +4658,7 @@ function failedPath(error) {
|
|
|
4435
4658
|
if (!(error instanceof Error) || !("syscall" in error) || !("path" in error)) return void 0;
|
|
4436
4659
|
return typeof error.path === "string" ? error.path : void 0;
|
|
4437
4660
|
}
|
|
4438
|
-
function read(file, text2) {
|
|
4661
|
+
function read(file, text2, lang) {
|
|
4439
4662
|
let source;
|
|
4440
4663
|
try {
|
|
4441
4664
|
source = readFileSync(file, "utf8");
|
|
@@ -4445,15 +4668,14 @@ function read(file, text2) {
|
|
|
4445
4668
|
}
|
|
4446
4669
|
if (extname(file) !== ".dragon") {
|
|
4447
4670
|
try {
|
|
4448
|
-
return
|
|
4671
|
+
return JSON.parse(source);
|
|
4449
4672
|
} catch (error) {
|
|
4450
4673
|
throw new Error(text2.badJson(file, detail(error)));
|
|
4451
4674
|
}
|
|
4452
4675
|
}
|
|
4453
|
-
const parsed = parseNotation(source);
|
|
4676
|
+
const parsed = parseNotation(source, { lang });
|
|
4454
4677
|
if (!parsed.ok) throw new Error(parsed.errors.map((e) => `${file}:${e.line} ${e.message}`).join("\n"));
|
|
4455
|
-
|
|
4456
|
-
return lang === void 0 ? { diagram: parsed.diagram } : { diagram: parsed.diagram, lang };
|
|
4678
|
+
return parsed.diagram;
|
|
4457
4679
|
}
|
|
4458
4680
|
async function main(argv) {
|
|
4459
4681
|
const [command, ...rest] = argv;
|
|
@@ -4489,8 +4711,8 @@ ${early.usage}`);
|
|
|
4489
4711
|
const width = number(values.width);
|
|
4490
4712
|
const fps = number(values.fps);
|
|
4491
4713
|
const gifFps = number(values["gif-fps"]);
|
|
4492
|
-
const
|
|
4493
|
-
const
|
|
4714
|
+
const lang = isLang(chosen) ? chosen : void 0;
|
|
4715
|
+
const diagram = read(file, early, lang ?? "ja");
|
|
4494
4716
|
let result;
|
|
4495
4717
|
try {
|
|
4496
4718
|
result = await exportDiagram(diagram, {
|