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