@aibyzero/byz 0.1.1 → 0.1.3

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +34 -24
  3. package/dist/cli.js +22 -14
  4. package/dist/core/export-html/template.css +1066 -0
  5. package/dist/core/export-html/template.html +55 -0
  6. package/dist/core/export-html/template.js +1864 -0
  7. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  8. package/dist/core/export-html/vendor/marked.min.js +78 -0
  9. package/dist/fast.js +64 -0
  10. package/dist/modes/interactive/assets/clankolas.png +0 -0
  11. package/dist/modes/interactive/theme/dark.json +90 -0
  12. package/dist/modes/interactive/theme/light.json +89 -0
  13. package/dist/modes/interactive/theme/theme-schema.json +352 -0
  14. package/dist/runtime/bundle/chunks/{chunk-CCRJHU72.js → chunk-QY7DJQRI.js} +2 -2
  15. package/dist/runtime/bundle/chunks/github-copilot.js +1 -1
  16. package/dist/runtime/bundle/cli.js +1 -1
  17. package/dist/runtime/bundle/index.js +1 -1
  18. package/dist/runtime/bundle/rpc-entry.js +1 -1
  19. package/dist/runtime/core/resource-loader.d.ts +1 -0
  20. package/dist/runtime/core/resource-loader.d.ts.map +1 -1
  21. package/dist/runtime/core/resource-loader.js +8 -3
  22. package/dist/runtime/core/resource-loader.js.map +1 -1
  23. package/dist/workflows.js +4 -131
  24. package/package.json +2 -1
  25. package/workflows/cm-plugin/LICENSE +21 -0
  26. package/workflows/cm-plugin/README.md +195 -0
  27. package/workflows/cm-plugin/VERSION +1 -0
  28. package/workflows/cm-plugin/agents/cm-plugin-backend-agent.md +34 -0
  29. package/workflows/cm-plugin/agents/cm-plugin-extension-agent.md +35 -0
  30. package/workflows/cm-plugin/agents/cm-plugin-ui-agent.md +34 -0
  31. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N1-init.md +60 -0
  32. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N2-enter-feature.md +51 -0
  33. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N3-execute-task.md +32 -0
  34. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N4-review.md +58 -0
  35. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N5-mark-done.md +80 -0
  36. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N6-qa-eval.md +69 -0
  37. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N7-context.md +23 -0
  38. package/workflows/cm-plugin/commands/cm-plugin-ai-nodes/N8-finish.md +61 -0
  39. package/workflows/cm-plugin/commands/cm-plugin-prd-modes/brownfield.md +13 -0
  40. package/workflows/cm-plugin/commands/cm-plugin-prd-modes/change-mode.md +130 -0
  41. package/workflows/cm-plugin/commands/cm-plugin-prd-modes/greenfield.md +82 -0
  42. package/workflows/cm-plugin/commands/cm-plugin:ai.md +75 -0
  43. package/workflows/cm-plugin/commands/cm-plugin:check.md +66 -0
  44. package/workflows/cm-plugin/commands/cm-plugin:fix.md +100 -0
  45. package/workflows/cm-plugin/commands/cm-plugin:idea.md +29 -0
  46. package/workflows/cm-plugin/commands/cm-plugin:init.md +145 -0
  47. package/workflows/cm-plugin/commands/cm-plugin:prd.md +403 -0
  48. package/workflows/cm-plugin/commands/cm-plugin:refactor.md +147 -0
  49. package/workflows/cm-plugin/commands/cm-plugin:rewrite.md +90 -0
  50. package/workflows/cm-plugin/commands/cm-plugin:scout.md +202 -0
  51. package/workflows/cm-plugin/docs//346/265/213/350/257/225/346/214/207/345/274/225-/345/277/253/351/200/237/344/270/212/346/211/213.md +189 -0
  52. package/workflows/cm-plugin/docs//351/207/215/346/236/204/346/265/201/347/250/213/350/256/276/350/256/241/README.md +17 -0
  53. package/workflows/cm-plugin/docs//351/207/215/346/236/204/346/265/201/347/250/213/350/256/276/350/256/241/refactor-flow.excalidraw +3650 -0
  54. package/workflows/cm-plugin/docs//351/207/215/346/236/204/346/265/201/347/250/213/350/256/276/350/256/241/refactor-flow.mp4 +0 -0
  55. package/workflows/cm-plugin/docs//351/207/215/346/236/204/346/265/201/347/250/213/350/256/276/350/256/241/refactor-flow.png +0 -0
  56. package/workflows/cm-plugin/docs//351/207/215/346/236/204/346/265/201/347/250/213/350/256/276/350/256/241/refactor-flow.spec.json +223 -0
  57. package/workflows/cm-plugin/package.json +33 -0
  58. package/workflows/cm-plugin/skills/cm-plugin-backend-engineer/SKILL.md +100 -0
  59. package/workflows/cm-plugin/skills/cm-plugin-devops-engineer/NOTICE.md +14 -0
  60. package/workflows/cm-plugin/skills/cm-plugin-devops-engineer/SKILL.md +120 -0
  61. package/workflows/cm-plugin/skills/cm-plugin-devops-engineer/references/cws-ci-cd.md +139 -0
  62. package/workflows/cm-plugin/skills/cm-plugin-devops-engineer/references/cws-submission-checklist.md +87 -0
  63. package/workflows/cm-plugin/skills/cm-plugin-doc-syncer/SKILL.md +149 -0
  64. package/workflows/cm-plugin/skills/cm-plugin-extension-engineer/SKILL.md +105 -0
  65. package/workflows/cm-plugin/skills/cm-plugin-product-manager/SKILL.md +83 -0
  66. package/workflows/cm-plugin/skills/cm-plugin-qa-engineer/NOTICE.md +14 -0
  67. package/workflows/cm-plugin/skills/cm-plugin-qa-engineer/SKILL.md +134 -0
  68. package/workflows/cm-plugin/skills/cm-plugin-qa-engineer/references/cws-scan-checklist.md +136 -0
  69. package/workflows/cm-plugin/skills/cm-plugin-qa-engineer/references/cws-violation-codes.md +68 -0
  70. package/workflows/cm-plugin/skills/cm-plugin-ui-engineer/SKILL.md +94 -0
  71. package/workflows/cm-plugin/skills/codebase-context/SKILL.md +489 -0
  72. package/workflows/cm-plugin/skills/darwin-skill/NOTICE.md +24 -0
  73. package/workflows/cm-plugin/skills/darwin-skill/README.md +272 -0
  74. package/workflows/cm-plugin/skills/darwin-skill/SKILL.md +492 -0
  75. package/workflows/cm-plugin/skills/darwin-skill/references/runtime-neutrality.md +68 -0
  76. package/workflows/cm-plugin/skills/darwin-skill/references/skilllens-evidence.md +142 -0
  77. package/workflows/cm-plugin/skills/darwin-skill/scripts/screenshot.mjs +71 -0
  78. package/workflows/cm-plugin/skills/darwin-skill/templates/result-card-dark.html +698 -0
  79. package/workflows/cm-plugin/skills/darwin-skill/templates/result-card-white.html +444 -0
  80. package/workflows/cm-plugin/skills/darwin-skill/templates/result-card.html +616 -0
  81. package/workflows/cm-plugin/skills/idea-to-prd/SKILL.md +289 -0
  82. package/workflows/cm-plugin/skills/idea-to-prd/references/domains/trading.md +97 -0
  83. package/workflows/cm-plugin/skills/idea-to-prd/references/example-prd.md +92 -0
  84. package/workflows/cm-plugin/templates/arch-reference.md +55 -0
  85. package/workflows/cm-plugin/templates/auto-update/cm-announce.sh +19 -0
  86. package/workflows/cm-plugin/templates/auto-update/cm-update.sh +251 -0
  87. package/workflows/cm-plugin/templates/dashboard/dashboard.html +153 -0
  88. package/workflows/cm-plugin/templates/dashboard/serve.sh +10 -0
  89. package/workflows/cm-plugin/templates/e2e/extension-harness.ts +110 -0
  90. package/workflows/cm-plugin/templates/e2e/smoke.spec.example.ts +51 -0
  91. package/workflows/cm-plugin/templates/hooks/pre-commit-cm-task-check +33 -0
  92. package/workflows/cm-plugin/templates/pixel/cm-pixel.html +388 -0
  93. package/workflows/cm-plugin/templates/pixel/cm-pixel.sh +212 -0
  94. package/workflows/cm-plugin/templates/pixel/dev/README.md +20 -0
  95. package/workflows/cm-plugin/templates/pixel/dev/atlas-preview.png +0 -0
  96. package/workflows/cm-plugin/templates/pixel/dev/build.py +55 -0
  97. package/workflows/cm-plugin/templates/pixel/dev/sheets/roguelikeChar_transparent.png +0 -0
  98. package/workflows/cm-plugin/templates/pixel/dev/sheets/roguelikeCity_tilemap.png +0 -0
  99. package/workflows/cm-plugin/templates/pixel/dev/sheets/roguelikeIndoor_transparent.png +0 -0
  100. package/workflows/cm-plugin/templates/pixel/dev/template.html +388 -0
  101. package/workflows/cm-plugin/templates/pixel/serve.sh +12 -0
  102. package/workflows/cm-plugin/templates/refactor/cm-refactor-denies.json +14 -0
  103. package/workflows/cm-plugin/templates/rules/backend-api.md +36 -0
  104. package/workflows/cm-plugin/templates/rules/chrome-extension.md +53 -0
  105. package/workflows/cm-plugin/templates/rules/coding-style.md +44 -0
  106. package/workflows/cm-plugin/templates/rules/frontend.md +32 -0
  107. package/workflows/cm-plugin/templates/rules/git-workflow.md +25 -0
  108. package/workflows/cm-plugin/templates/rules/security.md +31 -0
  109. package/workflows/cm-plugin/templates/rules/testing.md +36 -0
  110. package/workflows/cm-plugin/templates/scripts/cm-plugin-codex.sh +52 -0
  111. package/workflows/cm-plugin/templates/scripts/cm-plugin-log.sh +41 -0
  112. package/workflows/cm-plugin/templates/scripts/cm-plugin-preflight.sh +60 -0
  113. package/workflows/cm-plugin/templates/statusline/cm-plugin-statusline.sh +73 -0
  114. package/workflows.lock.json +4 -3
@@ -0,0 +1,388 @@
1
+ <!doctype html>
2
+ <html lang="zh">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>cm 工作流 · 像素办公室</title>
7
+ <style>
8
+ * { margin:0; padding:0; box-sizing:border-box; }
9
+ body { background:#131120; color:#e8e2d0; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
10
+ min-height:100vh; display:flex; flex-direction:column; align-items:center; padding:18px 12px; }
11
+ #wrap { width:min(980px, 100%); }
12
+ #hud { display:flex; align-items:center; gap:12px; padding:8px 2px; font-size:14px; }
13
+ #logo { background:#f0b03c; color:#1c1a14; font-weight:800; padding:3px 9px; border-radius:3px;
14
+ letter-spacing:2px; box-shadow:0 3px 0 #8a6210; }
15
+ #phase { background:#232033; padding:4px 12px; border-radius:3px; letter-spacing:1px; font-weight:700; }
16
+ #phase.paused { background:#f5d95c; color:#1c1a14; }
17
+ #phase.stale { background:#d25050; color:#fff; }
18
+ #task { color:#9a93b8; font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; }
19
+ #coins { color:#f0b03c; font-weight:700; }
20
+ #stage { position:relative; border:4px solid #2a2438; border-radius:8px; overflow:hidden;
21
+ box-shadow:0 18px 50px rgba(0,0,0,.55), inset 0 0 0 2px #0d0b16; background:#0d0b16; }
22
+ canvas { display:block; width:100%; image-rendering:pixelated; image-rendering:crisp-edges; }
23
+ #bubble { position:absolute; transform:translate(-50%,-115%); max-width:380px; padding:8px 12px;
24
+ background:#faf6ec; color:#1c1a14; border:2px solid #1c1a14; border-radius:6px;
25
+ font-size:13px; line-height:1.5; display:none; box-shadow:3px 4px 0 rgba(0,0,0,.4); z-index:2; }
26
+ #bubble.paused { background:#f5d95c; transform:translate(-12%,-115%); }
27
+ #bubble.paused:after { left:12%; }
28
+ #bubble:after { content:""; position:absolute; left:50%; bottom:-10px; margin-left:-5px;
29
+ border:5px solid transparent; border-top-color:#1c1a14; }
30
+ #note { position:absolute; right:14px; bottom:16px; width:250px; display:none;
31
+ background:#f7ecc0; color:#4a3c14; border:2px solid #c8b478; padding:9px 11px;
32
+ font-size:12px; line-height:1.65; white-space:pre-wrap; transform:rotate(-1.6deg);
33
+ box-shadow:4px 5px 0 rgba(0,0,0,.35); border-radius:2px; }
34
+ #note b { color:#8a6210; }
35
+ #barrow { display:flex; align-items:center; gap:10px; margin-top:10px; }
36
+ #bar { flex:1; height:16px; background:#232033; border:2px solid #2a2438; border-radius:4px; overflow:hidden; }
37
+ #fill { height:100%; width:0; transition:width .6s;
38
+ background:repeating-linear-gradient(45deg,#f0b03c 0 10px,#d99a28 10px 20px); }
39
+ #count { font-size:13px; color:#f0b03c; min-width:44px; text-align:right; font-weight:700; }
40
+ #hint { margin-top:8px; font-size:12px; color:#6f6890; }
41
+ </style>
42
+ </head>
43
+ <body>
44
+ <div id="wrap">
45
+ <div id="hud">
46
+ <span id="logo">CM</span><span id="phase">连接中…</span>
47
+ <span id="task"></span><span id="coins"></span>
48
+ </div>
49
+ <div id="stage">
50
+ <canvas id="cv" width="744" height="420"></canvas>
51
+ <div id="bubble"></div>
52
+ <div id="note"></div>
53
+ </div>
54
+ <div id="barrow"><div id="bar"><div id="fill"></div></div><span id="count"></span></div>
55
+ <div id="hint"></div>
56
+ </div>
57
+ <script>
58
+ const ATLAS = "data:image/png;base64,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";
59
+ const T = {"wall": [0, 0], "wall2": [1, 0], "wallB": [2, 0], "flr": [3, 0], "flr2": [4, 0], "flr3": [5, 0], "desk": [6, 0], "chairL": [7, 0], "chairR": [0, 1], "mon": [1, 1], "mon2": [2, 1], "sofaL": [3, 1], "sofaM": [4, 1], "sofaR": [5, 1], "rugL": [6, 1], "rugM": [7, 1], "rugM2": [0, 2], "rugR": [1, 2], "plant": [2, 2], "plant2": [3, 2], "art1": [4, 2], "art2": [5, 2], "art3": [6, 2], "gold": [7, 2], "mirror": [0, 3], "shelfA": [1, 3], "shelfB": [2, 3], "dresser": [3, 3], "vend": [4, 3], "fridge": [5, 3], "bin": [6, 3], "crate": [7, 3], "lamp": [0, 4], "lamp2": [1, 4], "cntL": [2, 4], "cntM": [3, 4], "cntR": [4, 4], "brick": [5, 4], "brick2": [6, 4], "wood": [7, 4], "wood2": [0, 5], "wood3": [1, 5], "rgA": [2, 5], "rgB": [3, 5], "rgC": [4, 5], "rgD": [5, 5], "chairF": [6, 5], "tblO1": [7, 5], "tblO2": [0, 6], "worker": [1, 6], "codex": [2, 6], "sleep": [3, 6], "celA": [4, 6], "celB": [5, 6], "celC": [6, 6]};
60
+ const IMG = new Image(); let loaded = false;
61
+ IMG.onload = () => loaded = true; IMG.src = ATLAS;
62
+
63
+ const SKY = {
64
+ dawn:["#c98d78","#dba98a","#e8c49a"], day:["#7fa8c8","#93b9d4","#a9cbdd"],
65
+ dusk:["#8a5f7a","#b07a6a","#d0996a"], night:["#242138","#2e2a48","#3a3560"],
66
+ };
67
+ const TINT = { dawn:"rgba(230,150,100,.09)", day:"rgba(255,180,90,.05)",
68
+ dusk:"rgba(210,120,70,.12)", night:"rgba(30,34,90,.17)" };
69
+ const NODES = ["N1","N2","N3","N4","N5","N6","N7","N8"];
70
+ const PHASE = { N1:"启动准备", N2:"规划任务", N3:"开发中", N4:"检查代码",
71
+ N5:"保存进度", N6:"质量检测", N7:"整理现场", N8:"收尾总结" };
72
+ const ZONE = i => i<2 ? "dawn" : i<5 ? "day" : i<7 ? "dusk" : "night";
73
+ const PROPS = { 0:"plant2", 1:"crate", 2:"bin", 4:"dresser", 5:"fridge", 6:"shelfB", 7:"plant" };
74
+
75
+ const cv = document.getElementById("cv"), cx = cv.getContext("2d");
76
+ cx.imageSmoothingEnabled = false;
77
+ const W = cv.width, H = cv.height, TOPW = 48; // 顶墙高度
78
+ // 工位:两排四列,像真办公室
79
+ const POD = []; for (let i=0;i<8;i++)
80
+ POD.push({ x: 128 + (i%4)*162, y: 168 + ((i/4)|0)*150 });
81
+ const BENCH = { x:20, y:252, cx:44 }; // 等候长椅(左墙)
82
+ const LOUNGE = { x:W/2, y:252 }; // 中央过道=休息区
83
+ const seed = n => { let x = Math.sin(n*127.1)*43758.5; return x - Math.floor(x); };
84
+
85
+ function rect(x,y,w,h,c){ cx.fillStyle = c; cx.fillRect(x|0, y|0, w, h); }
86
+ function tile(n,dx,dy,z=3){ const [c,r] = T[n];
87
+ cx.drawImage(IMG, c*16, r*16, 16, 16, dx|0, dy|0, 16*z, 16*z); }
88
+ function shadow(x,y,rx,ry,a=.26){ cx.fillStyle = "rgba(12,8,20,"+a+")";
89
+ cx.beginPath(); cx.ellipse(x,y,rx,ry,0,0,7); cx.fill(); }
90
+
91
+ let cur = { node:"N1", state:"running", detail:"", feature:"", task:"" };
92
+ let mode = "wait", staleRun = false, firstSnap = true;
93
+ let wk = { x:POD[0].x-58, y:POD[0].y+18 }; // 小人(2D 自由走动)
94
+ let frame = 0, confetti = [];
95
+
96
+ // ---------- 房间(俯视) ----------
97
+ function room(zone) {
98
+ for (let r=0; r<Math.ceil((H-TOPW)/48); r++) // 暖木地板
99
+ for (let c=0; c<Math.ceil(W/48); c++)
100
+ tile(seed(c*3+r*11)>.62 ? "wood2" : (seed(c*5+r)>.85 ? "wood3" : "wood"), c*48, TOPW + r*48);
101
+ for (let c=0; c<Math.ceil(W/48); c++) tile("brick", c*48, 0); // 红砖顶墙
102
+ rect(0,0,W,6,"#171310");
103
+ rect(0,TOPW-7,W,7,"#4a3626"); rect(0,TOPW-7,W,2,"#5f4630"); // 木梁压边
104
+ rect(0,TOPW,W,5,"rgba(12,8,20,.35)"); // 墙脚阴影
105
+ rect(0,TOPW,4,H-TOPW,"rgba(12,8,20,.30)"); // 左右墙沿
106
+ rect(W-4,TOPW,4,H-TOPW,"rgba(12,8,20,.30)");
107
+ const p = SKY[zone]; // 4 扇窗,窗外=当前进度时辰
108
+ for (let i=0;i<4;i++) {
109
+ const x = 96 + i*168, y = 7, w = 52, h = 32;
110
+ rect(x-3,y-3,w+6,h+6,"#241f1a");
111
+ for (let b=0;b<3;b++) rect(x, y+b*h/3, w, h/3+1, p[b]);
112
+ if (zone==="day" && i===1) { rect(x+34,y+5,9,9,"#f5e8b0"); }
113
+ if (zone==="dawn" && i===0) { rect(x+8,y+h-12,10,5,"#f0c890"); }
114
+ if (zone==="dusk" && i===2) { rect(x+30,y+h-14,11,6,"#e8a060"); }
115
+ if (zone==="night") { if (i===1) { rect(x+34,y+5,8,8,"#e8e2d0");
116
+ rect(x+35,y+6,2,2,"#c8c2b0"); rect(x+38,y+9,2,2,"#c8c2b0"); }
117
+ for (let s2=0;s2<3;s2++) if ((frame>>4)%3 !== s2)
118
+ rect(x+4+seed(s2+i)*44, y+3+seed(s2+9)*24, 2,2,"#e8e2d0"); }
119
+ if (zone==="day" || zone==="dawn") {
120
+ cx.save(); cx.beginPath(); cx.rect(x,y,w,h); cx.clip();
121
+ const cxp = x + ((frame*0.22 + i*53) % (w+30)) - 15;
122
+ rect(cxp, y+6+(i%2)*8, 14, 4, "rgba(250,246,236,.85)"); cx.restore(); }
123
+ rect(x-3, y+h/2-1, w+6, 2, "#241f1a");
124
+ if (i%2===0) tile("plant2", x+w-6, y+h-8, 1); // 窗台盆栽
125
+ else tile("plant", x-12, y+h-8, 1);
126
+ }
127
+ tile("art1", 40, 10, 2); tile("gold", 660, 8, 2); // 顶墙挂画
128
+ const ckx = 24, cky = 12; // 挂钟
129
+ rect(ckx-2,cky-2,16,16,"#241f1a"); rect(ckx,cky,12,12,"#e8e2d0");
130
+ rect(ckx+5,cky+2,2,5,"#241f1a"); rect(ckx+6,cky+6,4,2,"#241f1a");
131
+ }
132
+
133
+ // ---------- 深度排序渲染(y 小在后,y 大在前=离观众近) ----------
134
+ function drawSorted(ents) {
135
+ ents.sort((a,b) => a.by - b.by);
136
+ for (const e of ents) e.f();
137
+ }
138
+
139
+ function podEnts(ents, idx, done) {
140
+ for (let i=0;i<8;i++) {
141
+ const P = POD[i], x = P.x, y = P.y,
142
+ active = i===idx && !done && mode==="run", passed = done || i<idx;
143
+ ents.push({ by: y+30, f: () => {
144
+ tile("rgA", x-32, y-14, 2); tile("rgB", x, y-14, 2); // 奶油地垫
145
+ tile("rgC", x-32, y+18, 2); tile("rgD", x, y+18, 2);
146
+ shadow(x, y+30, 30, 6);
147
+ tile("desk", x-24, y-18);
148
+ tile("lamp2", x+13, y-30, 1.1); // 桌灯
149
+ tile(active ? "mon" : "mon2", x-14, y-38, 1.75);
150
+ if (passed) { rect(x-11, y-35, 22, 17, "rgba(80,170,90,.85)");
151
+ cx.fillStyle="#eaf6ea"; cx.font="bold 11px monospace"; cx.textAlign="center";
152
+ cx.fillText("✓", x, y-22); }
153
+ else if (!active) rect(x-11, y-35, 22, 17, "rgba(24,20,34,.88)");
154
+ else if ((frame>>4)%4===0) rect(x-11, y-35, 22, 17, "rgba(159,216,240,.25)");
155
+ tile("chairF", x-14, y+16, 1.75); // 桌前椅(背对观众)
156
+ }});
157
+ if (i===3) ents.push({ by: P.y+26, f: () => { // Codex 审查员常驻 N4
158
+ shadow(P.x+44, P.y+26, 13, 4); tile("codex", P.x+22, P.y-22); }});
159
+ else if (i===5) ents.push({ by: P.y+28, f: () => { // 服务器机架(CI/测试机)
160
+ shadow(P.x+46, P.y+28, 13, 4);
161
+ tile("mon2", P.x+30, P.y-36, 2); tile("mon2", P.x+30, P.y-4, 2);
162
+ for (let k=0;k<4;k++) if ((frame>>3)%4!==k)
163
+ rect(P.x+36+k*6, P.y-30+((k%2)*32), 3, 3, k%2?"#7fdc6a":"#63b8b0"); }});
164
+ else if (PROPS[i]) ents.push({ by: P.y+28, f: () => {
165
+ shadow(P.x+46, P.y+28, 13, 4); tile(PROPS[i], P.x+30, P.y-4, 2); }});
166
+ }
167
+ }
168
+
169
+ function furnitureEnts(ents) {
170
+ ents.push({ by: 96, f: () => { // 咖啡台(靠顶墙)
171
+ shadow(608, 98, 52, 6);
172
+ tile("cntL", 560, 64, 2); tile("cntM", 592, 64, 2); tile("cntR", 624, 64, 2); }});
173
+ ents.push({ by: 98, f: () => { shadow(40, 98, 20, 5); tile("shelfA", 16, 56, 2.5); }}); // 书架
174
+ ents.push({ by: BENCH.y+34, f: () => { // 等候区:售货机+长椅
175
+ shadow(36, BENCH.y-14, 18, 5); tile("vend", 16, BENCH.y-62, 2);
176
+ shadow(BENCH.cx+22, BENCH.y+32, 26, 5);
177
+ tile("sofaM", BENCH.x, BENCH.y, 2); tile("sofaR", BENCH.x+32, BENCH.y, 2); }});
178
+ ents.push({ by: H-8, f: () => { // 角落绿植
179
+ tile("plant", 12, H-56, 2); tile("plant2", W-46, H-58, 2); }});
180
+ }
181
+
182
+ function person(n, x, y, bob) { // x,y = 脚底中心
183
+ return { by: y, f: () => { shadow(x, y+2, 14, 4, .32);
184
+ tile(n, x-24, y-46 + (bob ? ((frame>>3)%2) : 0)); } };
185
+ }
186
+
187
+ function lighting(idx, paused, done, zone) {
188
+ const lit = [];
189
+ if (mode==="run" && !done && !paused)
190
+ lit.push({x:POD[idx].x, y:POD[idx].y-10, c: staleRun ? "255,95,80" : "255,205,120", a:.26});
191
+ if (paused) lit.push({x:BENCH.cx+16, y:BENCH.y-10, c:"255,205,120", a:.24});
192
+ if (done || mode==="wait") lit.push({x:LOUNGE.x, y:LOUNGE.y, c:"255,205,120", a:.10});
193
+ cx.globalCompositeOperation = "lighter";
194
+ for (const L of lit) {
195
+ const g = cx.createRadialGradient(L.x,L.y,10, L.x,L.y,130);
196
+ g.addColorStop(0,"rgba("+L.c+","+L.a+")"); g.addColorStop(1,"rgba("+L.c+",0)");
197
+ cx.fillStyle = g; cx.fillRect(L.x-130, L.y-130, 260, 260);
198
+ }
199
+ cx.globalCompositeOperation = "source-over";
200
+ rect(0,0,W,H, TINT[zone]); // 时辰环境色
201
+ const v = cx.createRadialGradient(W/2, H*.44, H*.42, W/2, H*.52, W*.62);
202
+ v.addColorStop(0,"rgba(8,6,18,0)"); v.addColorStop(1,"rgba(8,6,18,.48)");
203
+ cx.fillStyle = v; cx.fillRect(0,0,W,H);
204
+ }
205
+
206
+ function labels(idx, done) {
207
+ cx.font = "bold 11px ui-monospace,monospace"; cx.textAlign = "center";
208
+ for (let i=0;i<8;i++) {
209
+ const P = POD[i], active = i===idx && !done && mode==="run", passed = done || i<idx;
210
+ const t = NODES[i]+" "+PHASE[NODES[i]].slice(0,2);
211
+ cx.fillStyle = "rgba(20,14,8,.75)"; cx.fillText(t, P.x+1, P.y+53);
212
+ cx.fillStyle = active ? "#faf6ec" : passed ? "#8fdc78" : "#b8a888";
213
+ cx.fillText(t, P.x, P.y+52);
214
+ }
215
+ const name = (cur.feature ? cur.feature.replace(/^\d+\./,"") : "cm 工作流") + " 的办公室";
216
+ cx.font = "bold 12px ui-monospace,monospace";
217
+ const tw = cx.measureText("★ "+name+" ★").width + 24;
218
+ rect(W/2-tw/2, H-24, tw, 19, "#241f1a"); rect(W/2-tw/2+2, H-22, tw-4, 15, "#3a2c1e");
219
+ cx.fillStyle = "#f0c05a"; cx.fillText("★ "+name+" ★", W/2, H-10);
220
+ }
221
+
222
+ function emote(ch, x, y, col) {
223
+ if ((frame>>4)%2) return;
224
+ cx.fillStyle = col; cx.font = "bold 17px monospace"; cx.textAlign = "center";
225
+ cx.fillText(ch, x, y);
226
+ }
227
+
228
+ function drawIdle() {
229
+ room("night");
230
+ const ents = []; podEnts(ents, 0, false); furnitureEnts(ents);
231
+ ents.push({ by: LOUNGE.y+34, f: () => { // 中央沙发打盹
232
+ shadow(LOUNGE.x, LOUNGE.y+34, 66, 7);
233
+ tile("sofaL", LOUNGE.x-72, LOUNGE.y-6); tile("sofaM", LOUNGE.x-24, LOUNGE.y-6);
234
+ tile("sofaR", LOUNGE.x+24, LOUNGE.y-6); }});
235
+ ents.push(person("sleep", LOUNGE.x, LOUNGE.y+2, false));
236
+ drawSorted(ents); labels(0, false);
237
+ const zi = (frame>>4)%3;
238
+ cx.fillStyle="#cfc8e8"; cx.font="bold 13px monospace"; cx.textAlign="left";
239
+ cx.fillText("z".repeat(zi+1), LOUNGE.x+26, LOUNGE.y-40-zi*4);
240
+ lighting(0,false,false,"night");
241
+ }
242
+
243
+ function draw() {
244
+ frame++;
245
+ if (!loaded) { rect(0,0,W,H,"#0d0b16"); requestAnimationFrame(draw); return; }
246
+ if (mode==="wait") { drawIdle(); requestAnimationFrame(draw); return; }
247
+ const idx = Math.max(0, NODES.indexOf(cur.node)),
248
+ paused = cur.state==="paused_for_human", done = mode==="done",
249
+ zone = done ? "night" : ZONE(idx);
250
+ room(zone);
251
+ const ents = []; podEnts(ents, idx, done); furnitureEnts(ents);
252
+ let sat = false;
253
+ if (!done) {
254
+ const t = paused ? {x:BENCH.cx+16, y:BENCH.y+26} : {x:POD[idx].x-52, y:POD[idx].y+20};
255
+ const dx = t.x-wk.x, dy = t.y-wk.y, d = Math.hypot(dx,dy);
256
+ const spd = isDemo ? 8 : 2.6;
257
+ if (d>3) { wk.x += dx/d*Math.min(spd,d); wk.y += dy/d*Math.min(spd,d); }
258
+ const walking = d>3;
259
+ sat = paused && !walking;
260
+ ents.push(person("worker", wk.x, wk.y, walking));
261
+ if (sat) ents.push({ by: wk.y+1, f: () => { // 补画椅面盖腿=坐下
262
+ tile("sofaM", BENCH.x, BENCH.y, 2); tile("sofaR", BENCH.x+32, BENCH.y, 2); }});
263
+ } else {
264
+ ents.push({ by: LOUNGE.y+18, f: () => { shadow(LOUNGE.x, LOUNGE.y+18, 34, 7);
265
+ tile("tblO1", LOUNGE.x-24, LOUNGE.y-54); tile("tblO2", LOUNGE.x-24, LOUNGE.y-8); }});
266
+ ents.push(person("worker", LOUNGE.x-64, LOUNGE.y+6, true));
267
+ ents.push(person("celA", LOUNGE.x+64, LOUNGE.y+6, true));
268
+ ents.push(person("celB", LOUNGE.x-40, LOUNGE.y+52, true));
269
+ ents.push(person("celC", LOUNGE.x+40, LOUNGE.y+52, true));
270
+ }
271
+ if (paused) { drawSorted(ents.filter(e=>true)); rect(0,0,W,H,"rgba(10,8,22,.32)");
272
+ const again = []; again.push(person("worker", wk.x, wk.y, false));
273
+ if (sat) again.push({ by: wk.y+1, f: () => {
274
+ tile("sofaM", BENCH.x, BENCH.y, 2); tile("sofaR", BENCH.x+32, BENCH.y, 2); }});
275
+ drawSorted(again);
276
+ if (sat) emote("!", wk.x, wk.y-56, "#f5d95c");
277
+ } else {
278
+ drawSorted(ents);
279
+ if (staleRun && !done) emote("?", wk.x, wk.y-56, "#f08a80");
280
+ }
281
+ labels(idx, done);
282
+ if (done) {
283
+ if (frame%3===0) confetti.push({ x:Math.random()*W, y:-6, v:1+Math.random()*1.6,
284
+ w:Math.random()>.5?4:3, c:["#f0b03c","#f06a3c","#63b8b0","#95c86a","#8c78c8","#faf6ec"][Math.random()*6|0] });
285
+ confetti = confetti.filter(p => p.y < H-8);
286
+ for (const p of confetti) { p.y += p.v; rect(p.x + Math.sin((p.y+p.x)/14)*5, p.y, p.w, p.w+2, p.c); }
287
+ rect(W/2-104, 66, 208, 34, "#faf6ec"); rect(W/2-104, 66, 208, 4, "#241f1a");
288
+ rect(W/2-104, 96, 208, 4, "#241f1a"); rect(W/2-104, 66, 4, 34, "#241f1a"); rect(W/2+100, 66, 4, 34, "#241f1a");
289
+ cx.fillStyle="#1c1a14"; cx.font="bold 19px ui-monospace,monospace"; cx.textAlign="center";
290
+ cx.fillText("STAGE CLEAR!", W/2, 90);
291
+ }
292
+ lighting(idx, paused, done, zone);
293
+ requestAnimationFrame(draw);
294
+ }
295
+
296
+ function hud() {
297
+ const paused = cur.state==="paused_for_human", done = mode==="done";
298
+ const ph = document.getElementById("phase");
299
+ if (mode==="wait") { ph.textContent = "😴 休息中(等 /cm-plugin:ai 启动)"; ph.className=""; }
300
+ else if (staleRun) { ph.textContent = "⚠ 可能已中断(去会话确认)"; ph.className="stale"; }
301
+ else { ph.textContent = done ? "STAGE CLEAR — 全部完成" :
302
+ (paused ? "🖐 等你确认" : "STAGE "+cur.node+" · "+(PHASE[cur.node]||"运行中"));
303
+ ph.className = paused ? "paused" : ""; }
304
+ document.getElementById("task").textContent =
305
+ mode==="wait" ? "" : [cur.feature.replace(/^\d+\./,""), cur.task].filter(Boolean).join(" · ");
306
+ const b = document.getElementById("bubble");
307
+ if (mode==="run" && cur.detail) {
308
+ b.style.display="block"; b.className = paused ? "paused" : "";
309
+ b.textContent = (paused ? "🖐 " : "") + cur.detail;
310
+ const idx = Math.max(0, NODES.indexOf(cur.node));
311
+ b.style.left = paused ? (BENCH.cx/W*100+"%")
312
+ : (Math.min(86, Math.max(14, POD[idx].x/W*100)) + "%");
313
+ b.style.top = paused ? ((BENCH.y-58)/H*100+"%") : ((POD[idx].y-34)/H*100 + "%");
314
+ } else b.style.display = "none";
315
+ }
316
+
317
+ // 上次收工小记:读度量汇总.md 最后一节
318
+ async function loadNote() {
319
+ try {
320
+ const md = await (await fetch("度量汇总.md?ts="+Date.now())).text();
321
+ if (md.startsWith("<")) return;
322
+ const secs = md.trim().split(/\n(?=#)/), last = secs[secs.length-1];
323
+ const lines = last.split("\n").filter(l=>l.trim().startsWith("-")).slice(0,4);
324
+ if (!lines.length) return;
325
+ const n = document.getElementById("note");
326
+ n.innerHTML = "<b>📋 上次收工小记</b>\n" + lines.join("\n").replace(/</g,"&lt;");
327
+ n.style.display = "block";
328
+ } catch {}
329
+ }
330
+
331
+ const DEMO = [
332
+ ["N1","running","正在检查项目环境和版本控制","",""],
333
+ ["N2","running","正在读需求、排任务","1.market-board",""],
334
+ ["N3","running","正在开发行情数据接口","1.market-board","T-005"],
335
+ ["N4","running","第1轮代码审查进行中","1.market-board","T-005"],
336
+ ["N4","paused_for_human","确认一下:原型里有3个按钮点了没反应,要做吗?","1.market-board","T-006"],
337
+ ["N5","running","保存进度:行情页面完成","1.market-board","T-006"],
338
+ ["N6","running","质量检测和业务走查","1.market-board",""],
339
+ ["N7","running","整理现场,准备下一个任务","1.market-board",""],
340
+ ["N8","running","收尾:整理文档和发布清单","",""],
341
+ ["N8","done","全部完成","",""]];
342
+ const q = new URLSearchParams(location.search);
343
+ const isDemo = q.has("demo"), isIdleDemo = q.has("idle");
344
+
345
+ async function poll() {
346
+ if (isIdleDemo) { mode="wait"; hud(); loadNote(); return; }
347
+ if (isDemo) {
348
+ const [node,state,detail,feature,task] = DEMO[(Math.floor(Date.now()/2600))%DEMO.length];
349
+ cur = {node,state,detail,feature,task};
350
+ mode = state==="done" ? "done" : "run"; staleRun = false; setBar(4,6);
351
+ } else {
352
+ try {
353
+ const res = await fetch(".cm-status.json?ts="+Date.now());
354
+ if (!res.ok) throw 0;
355
+ const s = await res.json();
356
+ cur = { node:s.node||"N1", state:s.state||"running", detail:s.detail||"",
357
+ feature:s.feature||"", task:s.task||"" };
358
+ mode = cur.state==="done" ? "done" : "run";
359
+ if (firstSnap && mode==="run") { firstSnap = false;
360
+ if (cur.state==="paused_for_human") { wk.x = BENCH.cx+16; wk.y = BENCH.y+26; }
361
+ else { const P = POD[Math.max(0,NODES.indexOf(cur.node))];
362
+ wk.x = P.x-52; wk.y = P.y+20; } }
363
+ const lm = res.headers.get("last-modified"); // 中断检测(阈值 10 分钟)
364
+ staleRun = !!(lm && mode==="run" && cur.state!=="paused_for_human"
365
+ && (Date.now()-new Date(lm).getTime()) > 600_000);
366
+ if (cur.feature && mode!=="wait") {
367
+ const md = await (await fetch(cur.feature+"/tasks.md?ts="+Date.now())).text();
368
+ const items = md.match(/^- \[( |x)\] T-\d+/gm)||[];
369
+ setBar(items.filter(l=>l.includes("[x]")).length, items.length);
370
+ } else setBar(0,0);
371
+ document.getElementById("hint").textContent = "";
372
+ document.getElementById("note").style.display = mode==="done" ? "" : "none";
373
+ if (mode==="done") loadNote();
374
+ } catch { mode = "wait"; setBar(0,0); loadNote();
375
+ document.getElementById("hint").textContent = "😴 值班中"; }
376
+ }
377
+ hud(); setTimeout(poll, isDemo?400:2000);
378
+ }
379
+ function setBar(d,t){
380
+ document.getElementById("fill").style.width = t ? (100*d/t)+"%" : "0";
381
+ document.getElementById("count").textContent = t ? d+"/"+t : "";
382
+ document.getElementById("coins").textContent = d ? "⛁ "+d : "";
383
+ }
384
+
385
+ draw(); poll();
386
+ </script>
387
+ </body>
388
+ </html>
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env bash
2
+ # cm 像素小剧场 · 终端版
3
+ # 8 个像素工位对应 N1–N8,小人走到哪个工位,流水线就跑到哪一步。
4
+ # 纯只读消费 .cm-status.json / tasks.md,零侵入。
5
+ #
6
+ # 用法:
7
+ # ./cm-pixel.sh # 跟随当前 cm 运行实时播放(2s 刷新,Ctrl-C 退出)
8
+ # ./cm-pixel.sh --demo # 演示模式:循环播放 8 节点+暂停+完成,无需真实运行
9
+ # ./cm-pixel.sh --once # 只渲染一帧后退出(截图/调试用)
10
+ MODE="${1:-}"
11
+ python3 - "$HOME/.claude/cm-plugin-current-specs" "$MODE" <<'PYEOF'
12
+ import json, os, re, sys, time
13
+
14
+ PTR, MODE = sys.argv[1], (sys.argv[2] if len(sys.argv) > 2 else "")
15
+
16
+ # ---------- 调色板(GameBoy 绿系 + 功能色) ----------
17
+ P = {
18
+ ".": None, # 透明
19
+ "k": (24, 28, 24), # 深底
20
+ "g": (48, 98, 48), # 暗绿
21
+ "G": (110, 170, 80), # 亮绿
22
+ "W": (224, 240, 208), # 米白
23
+ "y": (240, 200, 60), # 黄(暂停/高亮)
24
+ "o": (230, 140, 60), # 橙(皮肤)
25
+ "b": (90, 140, 220), # 蓝(工装)
26
+ "r": (210, 80, 80), # 红
27
+ "c": (100, 200, 200), # 青(屏幕)
28
+ "d": (60, 66, 60), # 桌灰
29
+ "m": (140, 120, 200), # 紫(Codex机器人)
30
+ # 分关卡底色(huashu 像素横版:晨/昼/暮/夜,暗场压灰)
31
+ "A": (54, 42, 44), "B": (34, 44, 58), "C": (48, 38, 54), "D": (24, 24, 42),
32
+ }
33
+ ZONE = lambda i: "A" if i < 2 else ("B" if i < 5 else ("C" if i < 7 else "D"))
34
+
35
+ PHASE = {
36
+ "N1": "启动", "N2": "规划", "N3": "开发", "N4": "审查",
37
+ "N5": "保存", "N6": "质检", "N7": "整理", "N8": "收尾",
38
+ }
39
+ NODES = list(PHASE)
40
+
41
+ # ---------- 精灵(字符串像素图) ----------
42
+ DESK = [ # 7x4 工位:显示器+桌子
43
+ ".ccc...",
44
+ ".ccc...",
45
+ "ddddddd",
46
+ ".d...d.",
47
+ ]
48
+ WORKER = [ # 5x6 小人,两帧(敲键盘手起手落)
49
+ [".ooo.", ".ooo.", ".bbb.", "bbbbb", ".b.b.", ".b.b."],
50
+ [".ooo.", ".ooo.", "bbbb.", ".bbbb", ".b.b.", ".b.b."],
51
+ ]
52
+ WORKER_HAND_UP = [".ooo.", ".ooo.", "bbbbo", ".bbb.", ".b.b.", ".b.b."] # 举手(暂停)
53
+ ROBOT = [".mmm.", ".mcm.", "mmmmm", ".m.m."] # Codex 机器人(N4 常驻)
54
+ FLAG = ["G", "GG", "GGG"] # 完成小旗
55
+
56
+ CW, CH = 78, 22 # 画布尺寸(像素);半块渲染后 = 78 x 11 行
57
+
58
+
59
+ def blank():
60
+ cv = []
61
+ for y in range(CH):
62
+ row = []
63
+ for x in range(CW):
64
+ i = min(7, max(0, (x - 1) // 10))
65
+ row.append(ZONE(i))
66
+ cv.append(row)
67
+ return cv
68
+
69
+
70
+ def blit(cv, sprite, x, y):
71
+ for dy, row in enumerate(sprite):
72
+ for dx, ch in enumerate(row):
73
+ if ch != "." and 0 <= y + dy < CH and 0 <= x + dx < CW:
74
+ cv[y + dy][x + dx] = ch
75
+
76
+
77
+ def render(cv, dim=False):
78
+ """半块字符渲染:每两行像素合成一行字符,上像素=前景▀,下像素=背景"""
79
+ out = []
80
+ for y in range(0, CH, 2):
81
+ line = []
82
+ for x in range(CW):
83
+ t, b = P[cv[y][x]], P[cv[y + 1][x]]
84
+ if dim:
85
+ t = tuple(v // 2 for v in t)
86
+ b = tuple(v // 2 for v in b)
87
+ line.append(f"\033[38;2;{t[0]};{t[1]};{t[2]}m\033[48;2;{b[0]};{b[1]};{b[2]}m▀")
88
+ out.append("".join(line) + "\033[0m")
89
+ return out
90
+
91
+
92
+ def scene(node, state, frame):
93
+ cv = blank()
94
+ # 地板
95
+ for x in range(CW):
96
+ cv[CH - 1][x] = "g"
97
+ cv[CH - 2][x] = "g"
98
+ # 天体: 昼区太阳 / 夜区星星(隔帧闪烁)
99
+ for dx in range(3):
100
+ for dy in range(2):
101
+ cv[1 + dy][1 + 3 * 10 + 3 + dx] = "y"
102
+ for j, (sx, sy) in enumerate([(72, 1), (75, 3), (73, 6), (76, 8), (71, 4)]):
103
+ if (frame + j) % 4:
104
+ cv[sy][sx] = "W"
105
+ idx = NODES.index(node) if node in NODES else 0
106
+ done_all = state == "done"
107
+ for i in range(8):
108
+ x0 = 1 + i * 10 # 每工位 10px 宽
109
+ blit(cv, DESK, x0, CH - 6)
110
+ if i == 3: # N4 工位常驻 Codex 机器人(坐在桌对面)
111
+ blit(cv, ROBOT, x0 + 4, CH - 10)
112
+ if done_all or i < idx:
113
+ blit(cv, ["..G", ".GG", "GGG"], x0 + 5, CH - 9) # 已过节点插小旗
114
+ if not done_all and i == idx:
115
+ sprite = WORKER_HAND_UP if state == "paused_for_human" else WORKER[frame % 2]
116
+ blit(cv, sprite, x0 + 1, CH - 12)
117
+ cv[CH - 13][x0 + 2] = "y" if state == "paused_for_human" else "W" # 头顶指示点
118
+ if done_all: # 烟花
119
+ import random
120
+ rnd = random.Random(frame)
121
+ for _ in range(26):
122
+ cv[rnd.randrange(2, 10)][rnd.randrange(2, CW - 2)] = rnd.choice("yWrcG")
123
+ return cv
124
+
125
+
126
+ def progress(specs, feature):
127
+ try:
128
+ md = open(os.path.join(specs, feature, "tasks.md"), encoding="utf-8").read()
129
+ items = re.findall(r"^- \[( |x)\] (T-\d+)", md, re.M)
130
+ return sum(1 for c, _ in items if c == "x"), len(items)
131
+ except Exception:
132
+ return None
133
+
134
+
135
+ DEMO = [ # 演示剧本:(node, state, detail, feature)
136
+ ("N1", "running", "正在检查项目环境和版本控制", ""),
137
+ ("N2", "running", "正在读需求、排任务", "1.market-board"),
138
+ ("N3", "running", "正在开发行情数据接口", "1.market-board"),
139
+ ("N4", "running", "第1轮代码审查进行中", "1.market-board"),
140
+ ("N4", "paused_for_human", "确认一下:这个按钮点了以后要跳到哪个页面?", "1.market-board"),
141
+ ("N5", "running", "保存进度:数据接口完成", "1.market-board"),
142
+ ("N6", "running", "质量检测和业务走查", "1.market-board"),
143
+ ("N7", "running", "整理现场,准备下一个任务", "1.market-board"),
144
+ ("N8", "running", "收尾:整理文档和发布清单", ""),
145
+ ("N8", "done", "全部完成", ""),
146
+ ]
147
+
148
+ BAR_W = 30
149
+
150
+
151
+ def draw(frame):
152
+ if MODE == "--demo":
153
+ node, state, detail, feature = DEMO[(frame // 3) % len(DEMO)]
154
+ prog, task, age = (3, 6), "T-005", 0
155
+ else:
156
+ try:
157
+ specs = open(PTR).read().strip()
158
+ s = json.load(open(os.path.join(specs, ".cm-status.json")))
159
+ age = time.time() - os.path.getmtime(os.path.join(specs, ".cm-status.json"))
160
+ except Exception:
161
+ return ["", " ⏳ 没有运行中的 cm 工作流(等待 /cm-plugin:ai 启动…)", ""]
162
+ node, state = s.get("node", "N1"), s.get("state", "running")
163
+ detail, feature, task = s.get("detail", ""), s.get("feature", ""), s.get("task", "")
164
+ prog = progress(specs, feature) if feature else None
165
+
166
+ dim = state == "paused_for_human"
167
+ lines = [" \033[1mcm 像素流水线\033[0m" + (" \033[33m⏸ 等人\033[0m" if dim else "")]
168
+ lines += [" " + l for l in render(scene(node, state, frame), dim=False)]
169
+ # 工位标签
170
+ lab = []
171
+ for i, n in enumerate(NODES):
172
+ name = f"{n}{PHASE[n]}"
173
+ if state == "done" or (n != node and i < NODES.index(node) if node in NODES else False):
174
+ lab.append(f"\033[32m{name}\033[0m")
175
+ elif n == node and state != "done":
176
+ lab.append(f"\033[1;33m{name}\033[0m" if dim else f"\033[1;97m{name}\033[0m")
177
+ else:
178
+ lab.append(f"\033[2m{name}\033[0m")
179
+ lines.append(" " + " ".join(lab))
180
+ # 台词气泡(大白话,与状态条同源)
181
+ icon = "🖐" if dim else ("🎉" if state == "done" else "💬")
182
+ tag = f" \033[2m{task}\033[0m" if task else ""
183
+ lines.append(f" {icon} {detail[:56]}{tag}")
184
+ # 进度条
185
+ if prog:
186
+ d, t = prog
187
+ fill = int(BAR_W * d / t) if t else 0
188
+ lines.append(f" 进度 \033[32m{'█'*fill}\033[2m{'░'*(BAR_W-fill)}\033[0m {d}/{t}"
189
+ + (f" \033[2m{feature}\033[0m" if feature else ""))
190
+ if MODE != "--demo" and state not in ("done", "paused_for_human") and age > 600:
191
+ lines.append(f" \033[33m⚠ 可能已中断\033[0m(最后更新 {int(age//60)} 分钟前,去会话确认)")
192
+ return lines
193
+
194
+
195
+ if MODE == "--once":
196
+ print("\n".join(draw(0)))
197
+ sys.exit(0)
198
+
199
+ sys.stdout.write("\033[?1049h\033[?25l") # 备用屏 + 藏光标
200
+ try:
201
+ f = 0
202
+ while True:
203
+ frame_txt = "\n".join(draw(f))
204
+ sys.stdout.write("\033[H\033[2J" + frame_txt + "\n\n \033[2mCtrl-C 退出\033[0m\n")
205
+ sys.stdout.flush()
206
+ time.sleep(1 if MODE == "--demo" else 2)
207
+ f += 1
208
+ except KeyboardInterrupt:
209
+ pass
210
+ finally:
211
+ sys.stdout.write("\033[?25h\033[?1049l")
212
+ PYEOF
@@ -0,0 +1,20 @@
1
+ # cm-pixel.html 构建工具
2
+
3
+ `cm-pixel.html` 由 `template.html` + Kenney CC0 素材表自动生成——不要直接改 cm-pixel.html,改模板后重新构建。
4
+
5
+ ## 构建
6
+
7
+ ```bash
8
+ python3 build.py # 需要 Pillow;先把脚本里的路径改成本目录
9
+ ```
10
+
11
+ build.py 从三张素材表抠出用到的 tile 打成一张小图集(约 5KB),
12
+ 以 base64 内嵌进模板的 @B64@ 占位符,tile 名称坐标表内嵌进 @TMAP@。
13
+
14
+ ## 素材来源(全部 CC0,可商用)
15
+
16
+ - sheets/roguelikeIndoor_transparent.png — Kenney "Roguelike Indoors"
17
+ - sheets/roguelikeCity_tilemap.png — Kenney "Roguelike Modern City"
18
+ - sheets/roguelikeChar_transparent.png — Kenney "Roguelike Characters"(纸娃娃:身体+裤子+衣服+发型叠加)
19
+
20
+ 下载页: https://kenney.nl/assets (CC0 1.0,无需署名)