@polycode-projects/the-mechanical-code-talker 4.0.0 → 4.0.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 +176 -8
- package/corpus/reference/index.json.gz +0 -0
- package/corpus/reference/manifest.json +8 -8
- package/corpus/reference/shards/ref-00.jsonl.gz +0 -0
- package/corpus/sprites/src/sprite-facts.jsonl +34 -0
- package/corpus/worlds/index.json.gz +0 -0
- package/corpus/worlds/manifest.json +49 -9
- package/corpus/worlds/shards/greyvale-museum.jsonl.gz +0 -0
- package/corpus/worlds/shards/lantern-cottage.jsonl.gz +0 -0
- package/corpus/worlds/shards/mud-hollow.jsonl.gz +0 -0
- package/corpus/worlds/shards/mud-warren.jsonl.gz +0 -0
- package/corpus/worlds/shards/spider-fly.jsonl.gz +0 -0
- package/corpus/worlds/src/greyvale-museum.jsonl +136 -0
- package/corpus/worlds/src/lantern-cottage.jsonl +60 -0
- package/corpus/worlds/src/mud-hollow.jsonl +82 -0
- package/corpus/worlds/src/mud-warren.jsonl +124 -0
- package/corpus/worlds/src/spider-fly.jsonl +1 -1
- package/data/sprites/animal-icon.toml +11 -5
- package/data/sprites/book-icon.toml +9 -5
- package/data/sprites/cabinet-icon.toml +10 -5
- package/data/sprites/cellar-icon.toml +16 -6
- package/data/sprites/container-icon.toml +7 -3
- package/data/sprites/desk-icon.toml +8 -5
- package/data/sprites/dog-icon.toml +8 -5
- package/data/sprites/dog-with-colour-icon.toml +13 -12
- package/data/sprites/drawing-room-icon.toml +14 -5
- package/data/sprites/egg-icon.toml +6 -3
- package/data/sprites/fly-icon.toml +10 -5
- package/data/sprites/furniture-icon.toml +5 -3
- package/data/sprites/garden-icon.toml +10 -3
- package/data/sprites/key-icon.toml +3 -1
- package/data/sprites/kitchen-icon.toml +15 -6
- package/data/sprites/lamp-icon.toml +9 -4
- package/data/sprites/letter-icon.toml +7 -4
- package/data/sprites/library-icon.toml +14 -3
- package/data/sprites/pan-icon.toml +7 -3
- package/data/sprites/person-icon.toml +6 -2
- package/data/sprites/poodle-icon.toml +1 -1
- package/data/sprites/portable-icon.toml +6 -4
- package/data/sprites/portrait-icon.toml +7 -4
- package/data/sprites/room-icon.toml +11 -2
- package/data/sprites/spider-icon.toml +9 -3
- package/data/sprites/study-icon.toml +10 -2
- package/package.json +5 -4
- package/src/domain/grammar/ace.mjs +40 -4
- package/src/domain/grammar/lexicon-core.json +2 -1
- package/src/domain/grammar/lexicon.mjs +18 -0
- package/src/domain/reference-pack.mjs +31 -7
- package/src/domain/spider-fly-world.mjs +1 -1
- package/src/domain/sprite-templates.mjs +8 -6
- package/src/services/adventure-viz.mjs +130 -47
- package/src/services/adventure.mjs +440 -367
- package/src/services/chat-page-viz.mjs +37 -9
- package/src/services/chat.mjs +99 -18
- package/src/services/code-explorer-viz.mjs +52 -14
- package/src/services/extract-facts.mjs +4 -7
- package/src/services/ingest-viz.mjs +54 -26
- package/src/services/ledger-viz.mjs +105 -56
- package/src/services/memory-panel-viz.mjs +24 -0
- package/src/services/mud-viz.mjs +940 -21
- package/src/services/plan-viz.mjs +119 -61
- package/src/services/research-viz.mjs +151 -57
- package/src/services/spider-fly-turn.mjs +1 -1
- package/src/services/spider-fly-viz.mjs +67 -43
- package/src/services/sprite-catalog-viz.mjs +175 -51
- package/src/services/viz-theme.mjs +15 -0
- package/src/surfaces/web/memory-ask-browser.bundle.js +107 -107
- package/src/surfaces/web/mud-browser-entry.mjs +44 -2
|
@@ -288,7 +288,10 @@ export function renderPlanHtml({ plan, rendersAs = {}, sizeOrder = [], title } =
|
|
|
288
288
|
const pageTitle = title || `tmct plan — ${actions.length} move${actions.length === 1 ? "" : "s"}`;
|
|
289
289
|
|
|
290
290
|
return `<!doctype html>
|
|
291
|
-
|
|
291
|
+
<!-- data-theme is pinned to dark on purpose: this page reads as a track/
|
|
292
|
+
timeline console, and that console stays dark under a light OS or site
|
|
293
|
+
theme too, the same way a DAW's own window does. -->
|
|
294
|
+
<html lang="en" data-theme="dark">
|
|
292
295
|
<head>
|
|
293
296
|
<meta charset="utf-8">
|
|
294
297
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -309,97 +312,144 @@ export function renderPlanHtml({ plan, rendersAs = {}, sizeOrder = [], title } =
|
|
|
309
312
|
-->
|
|
310
313
|
<style>
|
|
311
314
|
${THEME_TOKENS_CSS}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
315
|
+
/* This console stays dark under either site theme (the html tag pins
|
|
316
|
+
data-theme="dark" above), so these extra surfaces are plain literals
|
|
317
|
+
rather than theme-reactive tokens — a track console has one lighting
|
|
318
|
+
rig, not two. --taught/--corpus/--entail/--alert still come from
|
|
319
|
+
THEME_TOKENS_CSS above and still carry the site's own provenance
|
|
320
|
+
language: taught (green) marks the actual current position, entail
|
|
321
|
+
(amber) marks tmct's own inferred step goal, corpus (blue) marks a
|
|
322
|
+
session parameter, alert (red) marks a failed solve. */
|
|
323
|
+
:root {
|
|
324
|
+
--rail: #101317; --well: #0A0C0E; --strip: #1A1F25; --strip2: #20262D;
|
|
325
|
+
--taught-glow: rgba(95, 190, 139, .45); --entail-glow: rgba(217, 165, 84, .4);
|
|
326
|
+
}
|
|
327
|
+
html { background: var(--rail); }
|
|
328
|
+
body { margin: 0; background: var(--rail); color: var(--ink); font-family: ${MONO_STACK}; font-size: 14px; }
|
|
329
|
+
main { max-width: 1040px; margin: 0 auto; padding: 1.3rem 1rem 3rem; }
|
|
330
|
+
button { font: inherit; }
|
|
331
|
+
|
|
332
|
+
.rack { border: 1px solid var(--line); background: var(--card); }
|
|
333
|
+
|
|
334
|
+
/* ---- head strip: the nameplate ---- */
|
|
335
|
+
.headStrip { display: flex; justify-content: space-between; align-items: flex-end; gap: .6rem; flex-wrap: wrap; padding: .9rem 1.1rem .75rem; background: linear-gradient(180deg, var(--strip2), var(--strip)); border-bottom: 1px solid var(--line); border-radius: 9px 9px 0 0; }
|
|
336
|
+
.eyebrow { font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin: 0 0 .35rem; }
|
|
337
|
+
h1 { font-family: ${SERIF_STACK}; font-size: 1.2rem; font-weight: 600; margin: 0; color: var(--ink); }
|
|
338
|
+
.chip { font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .02em; color: var(--muted); border: 1px solid var(--line); border-radius: 3px; padding: .22rem .6rem; background: var(--well); text-align: right; }
|
|
339
|
+
|
|
340
|
+
/* ---- session strip: a channel-strip row of solve parameters ---- */
|
|
341
|
+
.sessionStrip { display: flex; align-items: stretch; flex-wrap: wrap; background: var(--strip); border-bottom: 1px solid var(--line); font-family: ${MONO_STACK}; font-size: .74rem; }
|
|
342
|
+
.sessionCell { display: flex; flex-direction: column; justify-content: center; gap: .3rem; padding: .55rem .95rem; border-right: 1px solid var(--line); }
|
|
343
|
+
.paramLabel { font-size: .6rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
|
|
344
|
+
.sessionStrip input[type="number"] { width: 3.6rem; font-family: ${MONO_STACK}; font-size: .78rem; background: var(--well); color: var(--taught); border: 1px solid var(--line); border-radius: 4px; padding: .22rem .4rem; font-variant-numeric: tabular-nums; }
|
|
345
|
+
.sessionAction { justify-content: center; }
|
|
346
|
+
.sessionAction button { font-family: ${MONO_STACK}; font-size: .74rem; padding: .4rem .85rem; border-radius: 5px; border: 1px solid var(--line); background: var(--well); color: var(--ink); cursor: pointer; }
|
|
347
|
+
.sessionAction button:hover:not(:disabled) { border-color: var(--corpus); color: var(--corpus); }
|
|
348
|
+
.sessionAction button:focus-visible { outline: 2px solid var(--corpus); outline-offset: 2px; }
|
|
349
|
+
.sessionAction button:disabled { opacity: .4; cursor: default; }
|
|
350
|
+
.sessionStatus { display: flex; align-items: center; margin-left: auto; padding: 0 .95rem; color: var(--muted); }
|
|
351
|
+
.sessionStatus.isError { color: var(--alert); }
|
|
352
|
+
|
|
353
|
+
/* ---- transport bar ---- */
|
|
354
|
+
.transport { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; padding: .7rem 1.1rem; background: var(--rail); border-bottom: 1px solid var(--line); }
|
|
355
|
+
.transportButtons { display: flex; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
|
|
356
|
+
.transportButtons button { font-family: ${MONO_STACK}; font-size: .78rem; padding: .48rem .8rem; border: none; border-right: 1px solid var(--line); background: var(--strip); color: var(--ink); cursor: pointer; }
|
|
357
|
+
.transportButtons button:last-child { border-right: none; }
|
|
358
|
+
.transportButtons button:hover:not(:disabled) { background: var(--strip2); }
|
|
359
|
+
.transportButtons button:focus-visible { outline: 2px solid var(--taught); outline-offset: -2px; }
|
|
360
|
+
.transportButtons button[disabled] { opacity: .35; cursor: default; }
|
|
361
|
+
.transportButtons #play.isPlaying { background: var(--taught-soft); color: var(--taught); }
|
|
362
|
+
.lcd { margin-left: auto; background: var(--well); border: 1px solid var(--line); border-radius: 6px; padding: .45rem .75rem; min-width: 10rem; }
|
|
363
|
+
#stepLabel { display: block; font-family: ${MONO_STACK}; font-size: 1rem; letter-spacing: .03em; color: var(--taught); text-shadow: 0 0 8px var(--taught-glow); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
364
|
+
.lcdMeter { margin-top: .3rem; height: 3px; background: var(--line); border-radius: 2px; overflow: hidden; }
|
|
365
|
+
.lcdMeterFill { height: 100%; width: 0%; background: var(--taught); box-shadow: 0 0 6px var(--taught-glow); transition: width .18s ease; }
|
|
366
|
+
|
|
367
|
+
/* ---- the deck: board on the left, arrangement view on the right ---- */
|
|
368
|
+
.stage { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 0; }
|
|
319
369
|
/* grid items refuse to shrink below their content's min-width by default, so
|
|
320
370
|
the 640px board would stretch the track and the whole page with it on a
|
|
321
371
|
phone — let the items shrink and the boardwrap scroll instead. */
|
|
322
372
|
.stage > * { min-width: 0; }
|
|
323
|
-
|
|
373
|
+
.deck { padding: 1rem 1.1rem 1.1rem; border-right: 1px solid var(--line); }
|
|
374
|
+
@media (max-width: 660px) { .stage { grid-template-columns: 1fr; } .deck { border-right: none; border-bottom: 1px solid var(--line); } }
|
|
324
375
|
.boardwrap { overflow-x: auto; }
|
|
325
|
-
.board { position: relative; width: ${BOARD_W}px; height: ${BOARD_H}px; background: var(--
|
|
376
|
+
.board { position: relative; width: ${BOARD_W}px; height: ${BOARD_H}px; background: var(--well); border: 1px solid var(--line); border-radius: 6px; }
|
|
326
377
|
.base { position: absolute; left: 4%; right: 4%; bottom: 22px; height: 6px; background: var(--line); border-radius: 3px; }
|
|
327
378
|
.post { position: absolute; bottom: 28px; width: 6px; height: 150px; background: var(--line); border-radius: 3px 3px 0 0; }
|
|
328
|
-
.anchorlabel { position: absolute; bottom: 2px; transform: translateX(-50%); font-family: ${MONO_STACK}; font-size: .
|
|
329
|
-
.block { position: absolute; height: ${BLOCK_H}px; border-radius:
|
|
330
|
-
.block.moving { z-index: 3; box-shadow: 0
|
|
379
|
+
.anchorlabel { position: absolute; bottom: 2px; transform: translateX(-50%); font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
|
|
380
|
+
.block { position: absolute; height: ${BLOCK_H}px; border-radius: 4px; color: #fff; display: flex; align-items: center; justify-content: center; font-family: ${MONO_STACK}; font-size: .64rem; font-weight: 600; box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 2px 4px rgba(0,0,0,.4); }
|
|
381
|
+
.block.moving { z-index: 3; box-shadow: 0 0 0 2px var(--taught), 0 6px 14px rgba(0,0,0,.5); }
|
|
331
382
|
.circle { position: absolute; width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--muted); transform: translate(-50%, -100%); }
|
|
332
383
|
.circlelabel { position: absolute; transform: translateX(-50%); font-family: ${MONO_STACK}; font-size: .6rem; color: var(--muted); }
|
|
333
|
-
.
|
|
334
|
-
.
|
|
335
|
-
.controls button:hover { border-color: var(--taught); }
|
|
336
|
-
.controls button:focus-visible { outline: 2px solid var(--taught); outline-offset: 2px; }
|
|
337
|
-
.controls button[disabled] { opacity: .4; cursor: default; }
|
|
338
|
-
.controls .step { margin-left: auto; font-family: ${MONO_STACK}; font-size: .74rem; color: var(--muted); font-variant-numeric: tabular-nums; }
|
|
339
|
-
.goalline { margin-top: .7rem; font-family: ${MONO_STACK}; font-size: .76rem; color: var(--taught); background: var(--taught-soft); border-radius: 6px; padding: .45rem .65rem; }
|
|
340
|
-
.facts { margin-top: .7rem; font-family: ${MONO_STACK}; font-size: .7rem; color: var(--muted); border-top: 1px dashed var(--line); padding-top: .55rem; line-height: 1.7; }
|
|
384
|
+
.goalline { margin-top: .8rem; font-family: ${MONO_STACK}; font-size: .76rem; color: var(--entail); background: var(--entail-soft); border: 1px solid var(--entail); border-radius: 6px; padding: .5rem .75rem; }
|
|
385
|
+
.facts { margin-top: .7rem; font-family: ${MONO_STACK}; font-size: .68rem; color: var(--muted); border-top: 1px dashed var(--line); padding-top: .55rem; line-height: 1.7; }
|
|
341
386
|
.facts b { color: var(--ink); }
|
|
342
|
-
.movelist { list-style: none; margin: 0; padding:
|
|
343
|
-
.movelist
|
|
344
|
-
.movelist li:hover { color: var(--ink); }
|
|
345
|
-
.movelist li.done { color: var(--ink); }
|
|
346
|
-
.movelist li.current { border-left-color: var(--taught); color: var(--ink); background: var(--taught-soft); font-weight: 700; }
|
|
347
|
-
.movelist .phasehead { font-size: .64rem; letter-spacing: .06em; text-transform: uppercase; border-left: 3px solid var(--taught); margin-top: .5rem; }
|
|
387
|
+
.movelist { list-style: none; margin: 0; padding: .5rem; font-family: ${MONO_STACK}; font-size: .72rem; align-self: start; }
|
|
388
|
+
.movelist .phasehead { display: block; background: var(--strip); border: 1px solid var(--line); border-left: 3px solid var(--corpus); border-radius: 4px; padding: .35rem .55rem; margin: .55rem 0 .3rem; font-size: .62rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); cursor: pointer; }
|
|
348
389
|
.movelist .phasehead:first-child { margin-top: 0; }
|
|
349
|
-
.movelist .phasehead:hover { color: var(--
|
|
390
|
+
.movelist .phasehead:hover { color: var(--ink); border-left-color: var(--taught); }
|
|
350
391
|
.movelist .phasehead:focus-visible { outline: 2px solid var(--taught); outline-offset: 2px; }
|
|
351
|
-
|
|
352
|
-
.
|
|
353
|
-
.
|
|
354
|
-
.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
.
|
|
359
|
-
.liveControls .livestatus { color: var(--muted); margin-left: auto; }
|
|
360
|
-
.lower { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem; margin-top: 1.2rem; }
|
|
392
|
+
.movelist li:not(.phasehead) { padding: .38rem .55rem; margin: .12rem 0; border: 1px solid transparent; border-radius: 4px; color: var(--muted); cursor: pointer; font-variant-numeric: tabular-nums; opacity: .55; }
|
|
393
|
+
.movelist li:not(.phasehead):hover { color: var(--ink); border-color: var(--line); opacity: 1; }
|
|
394
|
+
.movelist li.done { color: var(--ink); background: var(--strip); opacity: 1; }
|
|
395
|
+
.movelist li.current { color: var(--ink); background: var(--taught-soft); border-color: var(--taught); box-shadow: inset 3px 0 0 var(--taught); font-weight: 700; opacity: 1; }
|
|
396
|
+
@media (prefers-reduced-motion: reduce) { .block, .lcdMeterFill { transition: none !important; } }
|
|
397
|
+
|
|
398
|
+
/* ---- lower rack: two channel strips, teach dock and PDDL artifact ---- */
|
|
399
|
+
.lower { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0; border-top: 1px solid var(--line); }
|
|
361
400
|
@media (max-width: 780px) { .lower { grid-template-columns: 1fr; } }
|
|
362
|
-
.chat, .pddlpanel { background: var(--card);
|
|
363
|
-
.chat
|
|
401
|
+
.chat, .pddlpanel { background: var(--card); padding: .65rem .8rem; min-width: 0; }
|
|
402
|
+
.chat { border-right: 1px solid var(--line); }
|
|
403
|
+
@media (max-width: 780px) { .chat { border-right: none; border-bottom: 1px solid var(--line); } }
|
|
404
|
+
.chat h2, .pddlpanel h2 { font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); font-weight: 400; margin: 0 0 .5rem; }
|
|
364
405
|
.chatlog { display: flex; flex-direction: column; gap: .4rem; max-height: 180px; overflow-y: auto; margin-bottom: .5rem; }
|
|
365
406
|
.chatlog:empty { display: none; margin-bottom: 0; }
|
|
366
407
|
.chatlog .u { font-family: ${MONO_STACK}; font-size: .74rem; color: var(--muted); }
|
|
367
408
|
.chatlog .u::before { content: "tmct> "; color: var(--taught); }
|
|
368
|
-
.chatlog .a { font-size: .
|
|
409
|
+
.chatlog .a { font-family: ${SERIF_STACK}; font-size: .87rem; line-height: 1.45; white-space: pre-wrap; color: var(--ink); }
|
|
369
410
|
.chatask { display: flex; align-items: center; gap: .5rem; border-top: 1px solid var(--line); padding-top: .5rem; }
|
|
370
411
|
.chatlog:empty + .chatask { border-top: none; padding-top: 0; }
|
|
371
412
|
.chatask .prompt { color: var(--taught); font-size: .78rem; font-family: ${MONO_STACK}; }
|
|
372
|
-
.chatask input { flex: 1; font-family: ${MONO_STACK}; font-size: .78rem; background: var(--
|
|
413
|
+
.chatask input { flex: 1; font-family: ${MONO_STACK}; font-size: .78rem; background: var(--well); color: var(--ink); border: 1px solid var(--line); border-radius: 4px; padding: .34rem .55rem; min-width: 0; }
|
|
373
414
|
.chatask input:focus-visible { outline: 2px solid var(--taught); outline-offset: 2px; }
|
|
374
415
|
.chatpills { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .5rem; }
|
|
375
|
-
.pill { font-family: ${MONO_STACK}; font-size: .
|
|
376
|
-
.pill:hover { border-color: var(--taught); }
|
|
416
|
+
.pill { font-family: ${MONO_STACK}; font-size: .66rem; padding: .22rem .6rem; border: 1px solid var(--line); border-radius: 99px; background: var(--well); color: var(--ink); cursor: pointer; white-space: nowrap; }
|
|
417
|
+
.pill:hover { border-color: var(--taught); color: var(--taught); }
|
|
377
418
|
.pill:focus-visible { outline: 2px solid var(--taught); outline-offset: 2px; }
|
|
378
|
-
.pddlpanel pre { margin: 0; font-family: ${MONO_STACK}; font-size: .
|
|
419
|
+
.pddlpanel pre { margin: 0; font-family: ${MONO_STACK}; font-size: .67rem; line-height: 1.55; white-space: pre-wrap; word-break: break-word; max-height: 420px; overflow-y: auto; color: var(--ink); background: var(--well); border: 1px solid var(--line); border-radius: 4px; padding: .6rem .7rem; }
|
|
379
420
|
</style>
|
|
380
421
|
</head>
|
|
381
422
|
<body>
|
|
382
423
|
<main>
|
|
383
|
-
|
|
384
|
-
|
|
424
|
+
<div class="rack">
|
|
425
|
+
<div class="headStrip">
|
|
426
|
+
<div>
|
|
427
|
+
<div class="eyebrow">tmct · plan</div>
|
|
428
|
+
<h1 id="pageTitle">${escapeHtml(pageTitle)}</h1>
|
|
429
|
+
</div>
|
|
385
430
|
<span class="chip">blocks archetype · ${pageData.layouts.length} snapshots · plan: findActionPath</span>
|
|
386
431
|
</div>
|
|
387
|
-
<div class="
|
|
388
|
-
<label for="diskCount">disks
|
|
389
|
-
<label for="maxDepth">max
|
|
390
|
-
<button id="resolveBtn" type="button">solve a fresh puzzle</button>
|
|
391
|
-
<span class="
|
|
432
|
+
<div class="sessionStrip" id="liveControls">
|
|
433
|
+
<label class="sessionCell" for="diskCount"><span class="paramLabel">disks</span><input id="diskCount" type="number" min="1" max="7" value="3"></label>
|
|
434
|
+
<label class="sessionCell" for="maxDepth"><span class="paramLabel">max depth</span><input id="maxDepth" type="number" min="1" max="999" value="300"></label>
|
|
435
|
+
<div class="sessionCell sessionAction"><button id="resolveBtn" type="button">solve a fresh puzzle</button></div>
|
|
436
|
+
<span class="sessionStatus" id="liveStatus"></span>
|
|
437
|
+
</div>
|
|
438
|
+
<div class="transport">
|
|
439
|
+
<div class="transportButtons">
|
|
440
|
+
<button id="reset" aria-label="Reset to start">⏮ reset</button>
|
|
441
|
+
<button id="back" aria-label="Step back">◀ back</button>
|
|
442
|
+
<button id="play" aria-label="Play">▶ play</button>
|
|
443
|
+
<button id="next" aria-label="Step forward">step ▶</button>
|
|
444
|
+
</div>
|
|
445
|
+
<div class="lcd">
|
|
446
|
+
<span id="stepLabel"></span>
|
|
447
|
+
<div class="lcdMeter"><div class="lcdMeterFill" id="stepMeter"></div></div>
|
|
448
|
+
</div>
|
|
392
449
|
</div>
|
|
393
450
|
<div class="stage">
|
|
394
|
-
<div>
|
|
451
|
+
<div class="deck">
|
|
395
452
|
<div class="boardwrap"><div class="board" id="board" aria-label="plan board"></div></div>
|
|
396
|
-
<div class="controls">
|
|
397
|
-
<button id="reset" aria-label="Reset to start">⏮ reset</button>
|
|
398
|
-
<button id="back" aria-label="Step back">◀ back</button>
|
|
399
|
-
<button id="play" aria-label="Play">▶ play</button>
|
|
400
|
-
<button id="next" aria-label="Step forward">step ▶</button>
|
|
401
|
-
<span class="step" id="stepLabel"></span>
|
|
402
|
-
</div>
|
|
403
453
|
<div class="goalline" id="goalline" hidden></div>
|
|
404
454
|
<div class="facts" id="facts"></div>
|
|
405
455
|
</div>
|
|
@@ -423,6 +473,7 @@ h1 { font-size: 1.15rem; margin: 0 0 .8rem; }
|
|
|
423
473
|
<pre id="pddlOut">${escapeHtml(pddlText)}</pre>
|
|
424
474
|
</div>
|
|
425
475
|
</div>
|
|
476
|
+
</div>
|
|
426
477
|
</main>
|
|
427
478
|
<script>
|
|
428
479
|
const PLAN = ${embedded};
|
|
@@ -431,6 +482,7 @@ const PLAN = ${embedded};
|
|
|
431
482
|
<script>
|
|
432
483
|
(function () {
|
|
433
484
|
"use strict";
|
|
485
|
+
const escapeHtml = ${escapeHtml.toString()};
|
|
434
486
|
// Best-effort: a copy of this page opened without the sibling worker file
|
|
435
487
|
// (a tmct --render plan --output file, a file:// open) just swallows the
|
|
436
488
|
// registration failure and works exactly as before.
|
|
@@ -438,6 +490,7 @@ const PLAN = ${embedded};
|
|
|
438
490
|
const pageTitleEl = document.getElementById("pageTitle");
|
|
439
491
|
const board = document.getElementById("board");
|
|
440
492
|
const stepLabel = document.getElementById("stepLabel");
|
|
493
|
+
const stepMeterEl = document.getElementById("stepMeter");
|
|
441
494
|
const goalline = document.getElementById("goalline");
|
|
442
495
|
const factsEl = document.getElementById("facts");
|
|
443
496
|
const movelist = document.getElementById("movelist");
|
|
@@ -511,6 +564,7 @@ const PLAN = ${embedded};
|
|
|
511
564
|
function render() {
|
|
512
565
|
const phase = phaseFor(step);
|
|
513
566
|
stepLabel.textContent = "step " + step + " / " + N + (phase ? " · " + phase : "");
|
|
567
|
+
if (stepMeterEl) stepMeterEl.style.width = (N ? Math.min(step, N) / N * 100 : 0) + "%";
|
|
514
568
|
if (plan.stepGoals) {
|
|
515
569
|
goalline.hidden = false;
|
|
516
570
|
goalline.textContent = step < N
|
|
@@ -520,7 +574,7 @@ const PLAN = ${embedded};
|
|
|
520
574
|
goalline.hidden = true;
|
|
521
575
|
}
|
|
522
576
|
factsEl.innerHTML = "<b>board@step" + step + "</b> — " +
|
|
523
|
-
plan.facts[step].map((f) => f
|
|
577
|
+
plan.facts[step].map((f) => escapeHtml(f)).join(" · ") +
|
|
524
578
|
' <span style="opacity:.7">(plan: findActionPath)</span>';
|
|
525
579
|
[...movelist.querySelectorAll("li:not(.phasehead)")].forEach((li, i) => {
|
|
526
580
|
li.classList.toggle("done", i < step);
|
|
@@ -529,6 +583,7 @@ const PLAN = ${embedded};
|
|
|
529
583
|
btn.back.disabled = step === 0 || animating;
|
|
530
584
|
btn.next.disabled = step === N || animating;
|
|
531
585
|
btn.play.textContent = playing ? "⏸ pause" : (step === N ? "▶ replay" : "▶ play");
|
|
586
|
+
btn.play.classList.toggle("isPlaying", playing);
|
|
532
587
|
}
|
|
533
588
|
async function forward() {
|
|
534
589
|
if (animating || step >= N) return;
|
|
@@ -625,6 +680,7 @@ const PLAN = ${embedded};
|
|
|
625
680
|
const liveAvailable = typeof tmctPlan !== "undefined" && typeof tmctPlan.createPlanSession === "function";
|
|
626
681
|
if (!liveAvailable) {
|
|
627
682
|
liveStatusEl.textContent = "live re-solve unavailable here — showing the baked-in replay only.";
|
|
683
|
+
liveStatusEl.classList.add("isError");
|
|
628
684
|
resolveBtn.disabled = true;
|
|
629
685
|
} else {
|
|
630
686
|
let session = null;
|
|
@@ -698,6 +754,7 @@ const PLAN = ${embedded};
|
|
|
698
754
|
const d = Math.max(1, parseInt(maxDepthEl.value, 10) || 300);
|
|
699
755
|
diskCountEl.value = n; maxDepthEl.value = d;
|
|
700
756
|
liveStatusEl.textContent = "solving a " + n + "-disk puzzle…";
|
|
757
|
+
liveStatusEl.classList.remove("isError");
|
|
701
758
|
await tryLoadWink();
|
|
702
759
|
session = await tmctPlan.createPlanSession({ diskCount: n, maxDepth: d });
|
|
703
760
|
if (session.plan) {
|
|
@@ -705,6 +762,7 @@ const PLAN = ${embedded};
|
|
|
705
762
|
liveStatusEl.textContent = "live — " + n + " disk" + (n === 1 ? "" : "s") + ", max depth " + d + ".";
|
|
706
763
|
} else {
|
|
707
764
|
liveStatusEl.textContent = "no plan found within " + d + " moves — raise max search depth and try again.";
|
|
765
|
+
liveStatusEl.classList.add("isError");
|
|
708
766
|
}
|
|
709
767
|
resolveBtn.disabled = false;
|
|
710
768
|
}));
|