quoxcli 0.5.0 → 0.6.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/CHANGELOG.md +65 -1
- package/lib/api/endpoints/admin.js +21 -0
- package/lib/api/endpoints/calendarq.js +8 -0
- package/lib/api/endpoints/governance.js +7 -0
- package/lib/api/endpoints/inboxq.js +39 -0
- package/lib/api/endpoints/index.js +4 -0
- package/lib/api/endpoints/lens.js +34 -0
- package/lib/api/endpoints/lucid.js +18 -0
- package/lib/api/endpoints/mcp.js +35 -0
- package/lib/api/endpoints/mon.js +6 -7
- package/lib/api/endpoints/notesq.js +2 -0
- package/lib/api/endpoints/quoxplan.js +51 -0
- package/lib/api/endpoints/v1.js +57 -0
- package/lib/commands/actions.js +51 -13
- package/lib/commands/admin.js +86 -0
- package/lib/commands/approvals.js +5 -1
- package/lib/commands/calendar.js +27 -0
- package/lib/commands/chat.js +1 -1
- package/lib/commands/contacts.js +2 -0
- package/lib/commands/fleet.js +164 -0
- package/lib/commands/fleetRecipe.js +174 -0
- package/lib/commands/lens.js +268 -0
- package/lib/commands/lucid.js +6 -0
- package/lib/commands/lucidGoals.js +423 -0
- package/lib/commands/mail.js +287 -7
- package/lib/commands/mcp.js +190 -0
- package/lib/commands/mon.js +14 -0
- package/lib/commands/monitoring.js +73 -1
- package/lib/commands/notesSeal.js +380 -22
- package/lib/commands/offload.js +51 -10
- package/lib/commands/plan.js +507 -54
- package/lib/commands/research.js +78 -0
- package/lib/commands/settings.js +7 -1
- package/lib/commands/volt.js +6 -1
- package/lib/commands/wazuh.js +74 -9
- package/lib/commands/wazuhIntake.js +55 -6
- package/lib/core/commandRegistrars.js +2 -0
- package/lib/core/config.js +14 -1
- package/lib/core/offload.js +83 -0
- package/lib/core/output.js +29 -4
- package/lib/core/selfUpdate.js +2 -0
- package/lib/core/version.js +15 -2
- package/lib/dragon/select.js +2 -2
- package/lib/quoxon/api.js +4 -0
- package/lib/quoxon/app.js +258 -49
- package/lib/quoxon/approvalsView.js +3 -1
- package/lib/quoxon/ask.js +4 -2
- package/lib/quoxon/bang.js +7 -1
- package/lib/quoxon/commandShort.js +1 -1
- package/lib/quoxon/commands.js +5 -5
- package/lib/quoxon/delegationDragon.js +49 -0
- package/lib/quoxon/dnd.js +47 -0
- package/lib/quoxon/dragonCompanion.js +24 -12
- package/lib/quoxon/events.js +12 -1
- package/lib/quoxon/extensions.js +2 -0
- package/lib/quoxon/foreignText.js +49 -0
- package/lib/quoxon/inboxView.js +5 -5
- package/lib/quoxon/launcher.js +2 -1
- package/lib/quoxon/loopCard.js +2 -1
- package/lib/quoxon/main.js +8 -2
- package/lib/quoxon/onboarding/dockerOffer.js +3 -1
- package/lib/quoxon/onboarding/view.js +1 -1
- package/lib/quoxon/render.js +5 -2
- package/lib/quoxon/resend.js +14 -0
- package/lib/quoxon/rows.js +3 -1
- package/lib/quoxon/secretShape.js +2 -0
- package/lib/quoxon/slash/broadcast.js +30 -6
- package/lib/quoxon/slash/broadcastRun.js +12 -6
- package/lib/quoxon/slash/cliLeaves.json +54 -0
- package/lib/quoxon/slash/dashboard.js +2 -0
- package/lib/quoxon/slash/listView.js +2 -1
- package/lib/quoxon/slash/palette.js +41 -0
- package/lib/quoxon/slash/paletteController.js +6 -2
- package/lib/quoxon/slash/restart.js +2 -0
- package/lib/quoxon/slotChat.js +7 -3
- package/lib/quoxon/slotRun.js +2 -0
- package/lib/quoxon/webPty.js +15 -0
- package/lib/quoxon/whatsnew.jsonl +7 -0
- package/lib/render/planBrief.js +614 -0
- package/lib/toolOutputShape.js +6 -3
- package/package.json +1 -1
- package/vendor/dragonlab/SOURCE +3 -3
- package/vendor/dragonlab/design/architect/v2/architect-master.svg +67 -0
- package/vendor/dragonlab/design/architect/v2/character.mjs +83 -0
- package/vendor/dragonlab/design/architect/v2/review.mjs +35 -0
- package/vendor/dragonlab/design/architect/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/archivist/v2/archivist-master.svg +77 -0
- package/vendor/dragonlab/design/archivist/v3/character.mjs +56 -0
- package/vendor/dragonlab/design/archivist/v4/character.mjs +46 -0
- package/vendor/dragonlab/design/archivist/v4/review.mjs +35 -0
- package/vendor/dragonlab/design/archivist/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/argus/v2/character.mjs +69 -0
- package/vendor/dragonlab/design/argus/v3/argus-master.svg +68 -0
- package/vendor/dragonlab/design/argus/v3/character.mjs +63 -0
- package/vendor/dragonlab/design/argus/v3/review.mjs +35 -0
- package/vendor/dragonlab/design/argus/v3/watch.mjs +56 -0
- package/vendor/dragonlab/design/daedalus/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/daedalus/v3/daedalus-master.svg +65 -0
- package/vendor/dragonlab/design/daedalus/v4/character.mjs +47 -0
- package/vendor/dragonlab/design/daedalus/v5/character.mjs +66 -0
- package/vendor/dragonlab/design/daedalus/v6/character.mjs +32 -0
- package/vendor/dragonlab/design/daedalus/v6/review.mjs +35 -0
- package/vendor/dragonlab/design/daedalus/v6/watch.mjs +56 -0
- package/vendor/dragonlab/design/janus/v1/janus-master.svg +70 -0
- package/vendor/dragonlab/design/janus/v2/character.mjs +56 -0
- package/vendor/dragonlab/design/janus/v3/character.mjs +7 -0
- package/vendor/dragonlab/design/janus/v4/character.mjs +150 -0
- package/vendor/dragonlab/design/janus/v5/character.mjs +55 -0
- package/vendor/dragonlab/design/janus/v5/review.mjs +35 -0
- package/vendor/dragonlab/design/janus/v5/watch.mjs +56 -0
- package/vendor/dragonlab/design/roady/v1/roady-master.svg +67 -0
- package/vendor/dragonlab/design/roady/v2/character.mjs +69 -0
- package/vendor/dragonlab/design/roady/v3/character.mjs +91 -0
- package/vendor/dragonlab/design/roady/v3/review.mjs +35 -0
- package/vendor/dragonlab/design/roady/v3/watch.mjs +56 -0
- package/vendor/dragonlab/design/weaver/v3/weaver-master.svg +57 -0
- package/vendor/dragonlab/design/weaver/v4/character.mjs +56 -0
- package/vendor/dragonlab/design/weaver/v5/character.mjs +52 -0
- package/vendor/dragonlab/design/weaver/v6/character.mjs +88 -0
- package/vendor/dragonlab/design/weaver/v6/review.mjs +35 -0
- package/vendor/dragonlab/design/weaver/v6/watch.mjs +56 -0
- package/vendor/dragonlab/integration/companions-v1/architect.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/archivist.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/argus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +86 -0
- package/vendor/dragonlab/integration/companions-v1/daedalus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/janus.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/roady.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +4 -1
- package/vendor/dragonlab/integration/companions-v1/weaver.mjs +2 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480">
|
|
2
|
+
<title>Architect — ivory keeper of the Grand Design</title>
|
|
3
|
+
<g id="far-wing">
|
|
4
|
+
<path id="far-membrane" fill="#514763" d="M 349 305 C 379 203 436 103 533 53 C 499 117 499 174 546 210 Q 483 184 453 239 Q 409 225 383 293 Z"/>
|
|
5
|
+
<path id="far-vault" fill="none" stroke="#b6a9c6" stroke-width="8" d="M 349 305 C 379 203 436 103 533 53 M 349 305 Q 444 164 533 53 M 349 305 Q 457 182 546 210"/>
|
|
6
|
+
</g>
|
|
7
|
+
<g id="near-wing">
|
|
8
|
+
<path id="vaulted-membrane" fill="#6d647c" d="M 305 305 C 260 206 205 100 92 62 C 139 142 111 211 57 248 Q 128 218 162 273 Q 214 237 251 309 L 292 331 Z"/>
|
|
9
|
+
<path id="wing-lit-panel" fill="#968a9f" d="M 305 305 Q 230 141 92 62 Q 189 162 176 237 Q 224 229 251 309 Z"/>
|
|
10
|
+
<path id="vault-ribs" fill="none" stroke="#e5dcc8" stroke-width="9" d="M 305 305 C 260 206 205 100 92 62 M 305 305 Q 218 182 92 62 M 305 305 Q 197 155 57 248 M 305 305 Q 224 201 162 273"/>
|
|
11
|
+
<path id="wing-tip" fill="#f3e8d0" d="M 92 62 Q 71 46 62 25 Q 69 61 103 84 Z"/>
|
|
12
|
+
<path id="wing-violet-vein" fill="none" stroke="#a57ee1" stroke-width="4" d="M 295 304 Q 215 192 115 128"/>
|
|
13
|
+
</g>
|
|
14
|
+
<g id="tail">
|
|
15
|
+
<path id="long-tail" fill="#8c8490" d="M 306 371 C 225 397 227 446 145 440 C 75 436 53 384 88 349 C 111 326 150 342 145 371 Q 136 388 121 382 Q 141 363 121 356 C 91 347 84 387 110 402 C 166 440 212 379 256 351 Z"/>
|
|
16
|
+
<path id="tail-ivory-ridge" fill="none" stroke="#d7cfbd" stroke-width="7" d="M 287 374 C 219 406 212 430 154 428 C 106 428 77 397 92 367"/>
|
|
17
|
+
<path id="tail-tip" fill="#c5acee" d="M 125 382 Q 146 381 146 359 Q 163 379 147 395 Z"/>
|
|
18
|
+
</g>
|
|
19
|
+
<g id="body">
|
|
20
|
+
<path id="torso" fill="#8e8790" d="M 309 273 Q 351 258 379 289 Q 416 336 389 397 Q 348 430 285 409 Q 254 354 278 313 Z"/>
|
|
21
|
+
<path id="breast" fill="#e8dfcd" d="M 340 285 Q 376 295 379 335 L 361 389 Q 331 408 307 383 L 303 333 Z"/>
|
|
22
|
+
<path id="breast-scales" fill="none" stroke="#a8a1a4" stroke-width="5" d="M 312 329 Q 340 343 376 329 M 307 353 Q 335 369 370 353 M 313 377 Q 335 389 360 375"/>
|
|
23
|
+
<path id="far-leg" fill="#625e6b" d="M 367 351 Q 404 344 405 379 L 401 413 L 446 423 Q 459 430 445 440 L 369 440 L 352 414 Z"/>
|
|
24
|
+
<path id="near-haunch" fill="#a49ca2" d="M 292 343 Q 332 332 341 367 Q 340 392 320 409 L 344 430 L 332 442 L 258 441 Q 245 421 261 402 Q 253 363 292 343 Z"/>
|
|
25
|
+
<path id="feet-claws" fill="#f4e9d2" d="M 270 425 L 258 444 L 280 436 L 293 424 L 289 443 L 310 436 L 323 424 L 321 442 L 344 436 M 389 425 L 377 443 L 398 437 L 413 425 L 411 443 L 431 437 L 443 426 L 442 440 L 458 433"/>
|
|
26
|
+
<path id="shoulder" fill="#ded3c0" d="M 297 276 Q 314 261 331 284 L 332 313 Q 306 327 292 304 Z"/>
|
|
27
|
+
</g>
|
|
28
|
+
<g id="neck">
|
|
29
|
+
<path id="swan-neck" fill="#c8c0b3" d="M 371 169 Q 401 190 380 228 C 363 258 333 261 345 304 L 304 321 Q 277 270 313 237 C 347 210 351 200 339 180 Z"/>
|
|
30
|
+
<path id="throat" fill="#f2e9d5" d="M 374 187 Q 387 208 366 232 C 344 256 322 267 337 301 L 318 304 C 302 268 329 247 346 229 Q 370 207 360 190 Z"/>
|
|
31
|
+
<path id="graphite-crest" fill="#575365" d="M 344 193 L 322 203 L 337 216 L 309 224 L 317 239 L 291 247 L 298 265 L 278 280 L 295 291 L 307 264 L 338 233 L 359 200 Z"/>
|
|
32
|
+
</g>
|
|
33
|
+
<g id="head">
|
|
34
|
+
<path id="far-horn" fill="#c3b9ad" d="M 370 127 C 340 111 310 87 304 57 Q 331 86 389 103 Z"/>
|
|
35
|
+
<path id="near-horn" fill="#f2e6cc" d="M 405 121 C 371 92 340 61 345 29 Q 359 66 425 97 Z"/>
|
|
36
|
+
<path id="horn-bands" fill="none" stroke="#686171" stroke-width="6" d="M 354 65 L 366 57 M 372 85 L 386 75 M 329 89 L 339 81"/>
|
|
37
|
+
<path id="skull" fill="#e9dfca" d="M 346 129 Q 359 105 390 112 Q 425 104 443 139 L 441 173 Q 428 204 392 207 L 353 184 Q 337 158 346 129 Z"/>
|
|
38
|
+
<path id="ear" fill="#bdb2bd" d="M 351 139 L 313 120 Q 316 143 327 153 L 310 166 Q 340 181 359 164 Z"/>
|
|
39
|
+
<path id="long-muzzle" fill="#f4ead7" d="M 413 164 Q 445 153 475 173 Q 490 183 478 194 L 433 210 Q 403 207 398 190 Z"/>
|
|
40
|
+
<path id="mouth-opening" fill="#282633" d="M 412 193 Q 445 200 479 186 L 469 205 Q 440 222 417 208 Z"/>
|
|
41
|
+
<path id="lower-jaw" fill="#c8bdad" d="M 414 207 Q 440 220 469 204 Q 455 228 429 225 L 404 208 Z"/>
|
|
42
|
+
<path id="fang" fill="#fff4dd" d="M 434 200 L 445 199 L 441 210 Z"/>
|
|
43
|
+
<path id="nostril" fill="#383340" d="M 465 177 Q 474 173 474 182 L 464 184 Z"/>
|
|
44
|
+
<path id="brow-ridge" fill="none" stroke="#9f949b" stroke-width="5" d="M 356 133 Q 372 123 386 133 M 395 130 Q 414 121 430 134"/>
|
|
45
|
+
</g>
|
|
46
|
+
<g id="arms">
|
|
47
|
+
<path id="far-arm" fill="#a59ba4" d="M 354 292 Q 377 277 398 301 L 427 331 L 478 335 L 501 326 L 516 334 L 503 351 L 421 355 L 382 329 Z"/>
|
|
48
|
+
<path id="raised-arm" fill="#dfd4c2" d="M 328 289 Q 338 269 359 280 L 391 300 Q 421 298 447 268 L 470 244 L 487 247 Q 478 268 460 282 L 420 319 Q 393 336 368 316 Z"/>
|
|
49
|
+
<path id="drawing-finger" fill="#f5e9d2" d="M 466 247 Q 480 225 502 225 L 527 233 L 523 242 L 499 238 L 485 251 L 502 253 L 499 264 L 478 263 Z"/>
|
|
50
|
+
<path id="lower-fingers" fill="#f1e5d1" d="M 477 335 L 494 317 L 501 321 L 493 335 L 513 325 L 521 332 L 506 346 L 483 349 Z"/>
|
|
51
|
+
<path id="armour-cuffs" fill="none" stroke="#686071" stroke-width="9" d="M 440 275 L 455 290 M 466 334 L 466 352"/>
|
|
52
|
+
</g>
|
|
53
|
+
<g id="design">
|
|
54
|
+
<path id="seed" fill="#e6caff" d="M 548 226 L 555 233 L 548 240 L 541 233 Z"/>
|
|
55
|
+
<path id="draft-plane" fill="none" stroke="#9165c5" stroke-width="5" d="M 541 354 L 614 325 L 692 354 L 616 390 Z M 577 369 L 650 339 M 577 340 L 652 372"/>
|
|
56
|
+
<path id="grand-arch" fill="none" stroke="#d0a4ff" stroke-width="6" d="M 563 349 L 563 301 Q 590 252 617 301 L 617 372 M 617 301 L 657 283 Q 683 292 683 320 L 683 349 M 563 301 L 603 283 Q 630 234 657 283"/>
|
|
57
|
+
<path id="design-ribs" fill="none" stroke="#a07bd1" stroke-width="4" d="M 590 277 L 630 259 M 590 277 L 590 358 M 617 331 L 683 309 M 563 325 L 617 348"/>
|
|
58
|
+
</g>
|
|
59
|
+
<g id="eyes">
|
|
60
|
+
<path id="far-eye-socket" fill="#080d19" d="M 349 151 C 349 136.08831175 359.745166 124 373 124 C 386.254834 124 397 136.08831175 397 151 C 397 165.91168825 386.254834 178 373 178 C 359.745166 178 349 165.91168825 349 151 Z"/>
|
|
61
|
+
<path id="far-eye-white" fill="#ffffff" d="M 358.38 151 C 358.38 139.954305 364.9256 131 373.0 131 C 381.0744 131 387.62 139.954305 387.62 151 C 387.62 162.045695 381.0744 171 373.0 171 C 364.9256 171 358.38 162.045695 358.38 151 Z"/>
|
|
62
|
+
<path id="far-pupil" fill="#282633" d="M 373 154 C 373 150.13400675 375.23857625 147 378 147 C 380.76142375 147 383 150.13400675 383 154 C 383 157.86599325 380.76142375 161 378 161 C 375.23857625 161 373 157.86599325 373 154 Z"/>
|
|
63
|
+
<path id="near-eye-socket" fill="#080d19" d="M 382 150 C 382 132.87917275 394.98374225 119 411 119 C 427.01625775 119 440 132.87917275 440 150 C 440 167.12082725 427.01625775 181 411 181 C 394.98374225 181 382 167.12082725 382 150 Z"/>
|
|
64
|
+
<path id="near-eye-white" fill="#ffffff" d="M 392.08 150 C 392.08 136.745166 400.5508 126 411.0 126 C 421.4492 126 429.92 136.745166 429.92 150 C 429.92 163.254834 421.4492 174 411.0 174 C 400.5508 174 392.08 163.254834 392.08 150 Z"/>
|
|
65
|
+
<path id="near-pupil" fill="#282633" d="M 411 153 C 411 149.13400675 413.23857625 146 416 146 C 418.76142375 146 421 149.13400675 421 153 C 421 156.86599325 418.76142375 160 416 160 C 413.23857625 160 411 156.86599325 411 153 Z"/>
|
|
66
|
+
</g>
|
|
67
|
+
</svg>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
|
|
2
|
+
export const ARCHITECT_CLIPS=Object.freeze([
|
|
3
|
+
{id:'idle',title:'Considering possibilities',duration:12000},
|
|
4
|
+
{id:'attach',title:'A first thought',duration:3000},
|
|
5
|
+
{id:'design',title:'Grand Design',duration:12000},
|
|
6
|
+
{id:'rethink',title:'Fold a possibility',duration:10000}
|
|
7
|
+
]);
|
|
8
|
+
export const STORY_DURATION=15000;
|
|
9
|
+
const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
|
|
10
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
11
|
+
const mix=(a,b,u)=>a.map((c,i)=>Math.round(c+(b[i]-c)*u));
|
|
12
|
+
|
|
13
|
+
export function poseRig(rig,scene,ms,{deck=false}={}){
|
|
14
|
+
if(scene==='design'&&ms>=12000)return poseRig(rig,'idle',ms-12000,{deck});
|
|
15
|
+
if(scene==='attach'&&ms>=3000)return poseRig(rig,'idle',ms-3000,{deck});
|
|
16
|
+
|
|
17
|
+
if(scene==='rethink'){
|
|
18
|
+
if(ms>=10000)return poseRig(rig,'idle',ms-10000,{deck});
|
|
19
|
+
if(ms>=8000)return poseRig(rig,'design',10000+ms-8000,{deck});
|
|
20
|
+
const base=poseRig(rig,'design',5200+4800*ease((ms-7400)/600),{deck});
|
|
21
|
+
const fold=pulse(ms,900,7400),angle=Math.PI*fold;
|
|
22
|
+
const paths=base.paths.filter(p=>p.id!=='design-ribs').map(p=>{
|
|
23
|
+
if(p.id!=='grand-arch')return p;
|
|
24
|
+
return {...p,rgb:mix(p.rgb,[246,220,255],fold),polygons:p.polygons.map((poly,i)=>i<2?poly:poly.map(([x,y])=>[617+(x-617)*Math.cos(angle),y+8*Math.sin(angle)]))};
|
|
25
|
+
});
|
|
26
|
+
return {...base,paths};
|
|
27
|
+
}
|
|
28
|
+
const active=scene==='design',t=active?ms:ms%12000;
|
|
29
|
+
const attention=active?pulse(t,0,11500):scene==='attach'?pulse(t,0,3000):.22*pulse(t,1500,7500);
|
|
30
|
+
const lift=active?ease((t-1800)/2600)*(1-ease((t-9500)/2500)):0;
|
|
31
|
+
const plane=ease((t-700)/1700)*(1-ease((t-10500)/1500));
|
|
32
|
+
const height=ease((t-2400)/2800)*(1-ease((t-10000)/1600));
|
|
33
|
+
const turn=.65*pulse(t,5200,8000);
|
|
34
|
+
const paths=[];
|
|
35
|
+
for(const p of rig.paths){
|
|
36
|
+
if(p.group==='design')continue;
|
|
37
|
+
const q={...p,polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
|
|
38
|
+
if(p.group==='head'||p.group==='eyes')return [x+3*attention,y+5*attention];
|
|
39
|
+
if(p.group==='neck'){const w=Math.max(0,Math.min(1,(305-y)/105));return [x+3*attention*w,y+5*attention*w];}
|
|
40
|
+
if(p.group==='arms'){const w=Math.max(0,Math.min(1,(x-360)/170));return [x+7*attention*w,y+(y<300?-19*lift:6*lift)*w];}
|
|
41
|
+
if(p.group==='tail'){const w=Math.max(0,(260-x)/210);return [x,y-3*attention*w*w];}
|
|
42
|
+
return [x,y];
|
|
43
|
+
}))};
|
|
44
|
+
paths.push(q);
|
|
45
|
+
}
|
|
46
|
+
const seed=rig.paths.find(p=>p.id==='seed');
|
|
47
|
+
const seedTravel=active?plane:0;
|
|
48
|
+
paths.push({...seed,polygons:seed.polygons.map(poly=>poly.map(([x,y])=>[x+68*seedTravel,y+121*seedTravel]))});
|
|
49
|
+
if(active&&plane>.002){
|
|
50
|
+
const draft=rig.paths.find(p=>p.id==='draft-plane');
|
|
51
|
+
paths.push({...draft,polygons:draft.polygons.map(poly=>poly.map(([x,y])=>[616+(x-616)*plane,354+(y-354)*plane]))});
|
|
52
|
+
if(height>.002)for(const id of deck?['grand-arch','design-ribs']:['grand-arch']){
|
|
53
|
+
const p=rig.paths.find(p=>p.id===id);
|
|
54
|
+
const light=pulse(t,id==='grand-arch'?7900:8500,id==='grand-arch'?9900:10500);
|
|
55
|
+
paths.push({...p,rgb:mix(p.rgb,[255,243,225],light),polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
|
|
56
|
+
const base=354+.12*(x-616),yy=base+(y-base)*height;
|
|
57
|
+
return [616+(x-616)*Math.cos(turn)+(yy-330)*Math.sin(turn)*.25,yy];
|
|
58
|
+
}))});
|
|
59
|
+
}
|
|
60
|
+
if(t>=8000&&t<10000){
|
|
61
|
+
// Light travels along the completed design, never outside its reserved space.
|
|
62
|
+
const u=(t-8000)/2000,x=563+120*u,y=354-74*Math.sin(Math.PI*u);
|
|
63
|
+
paths.push({id:'travelling-light',group:'effect',rgb:[255,244,230],stroke:0,erase:false,polygons:[[[x-4,y],[x,y-5],[x+4,y],[x,y+5]]]});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const map=new Map(paths.map(p=>[p.id,p]));
|
|
67
|
+
return {...rig,paths,eyes:rig.eyes.map(e=>({...e,socket:map.get(e.socket.id),white:map.get(e.white.id),pupil:map.get(e.pupil.id)}))};
|
|
68
|
+
}
|
|
69
|
+
export function renderArchitect(rig,scene='idle',ms=0,options={}){
|
|
70
|
+
if(!ARCHITECT_CLIPS.some(c=>c.id===scene))return {ok:false,code:'UNKNOWN_CLIP'};
|
|
71
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
72
|
+
if(!options.still&&scene==='rethink'&&ms>=10000)return renderArchitect(rig,'idle',ms-10000,options);
|
|
73
|
+
if(!options.still&&scene==='design'&&ms>=12000)return renderArchitect(rig,'idle',ms-12000,options);
|
|
74
|
+
if(!options.still&&scene==='attach'&&ms>=3000)return renderArchitect(rig,'idle',ms-3000,options);
|
|
75
|
+
const t=options.still?({idle:0,attach:1500,design:6500,rethink:4200}[scene]):ms;
|
|
76
|
+
const caption=scene==='rethink'?(t<900?'Hold the design.':t<4200?'Fold a possibility through itself.':t<7400?'Consider it from the other side.':t<8000?'The shape of an idea.':'Fold the thought away.'):scene==='design'?(t<700?'A single possibility.':t<2400?'Stretch a plane of light.':t<5200?'Lift the Grand Design.':t<8000?'Turn it. Consider every angle.':t<10000?'Illuminate the connections.':'Fold the thought away.'):scene==='attach'?'An idea takes shape.':'Imagine. Shape. Illuminate.';
|
|
77
|
+
return {...renderSvgRig(poseRig(rig,scene,t,options),options),clip:scene,scene,caption};
|
|
78
|
+
}
|
|
79
|
+
export function renderStory(rig,ms,options={}){
|
|
80
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
81
|
+
if(options.still)return renderArchitect(rig,'design',6500,options);
|
|
82
|
+
return ms<3000?renderArchitect(rig,'attach',ms,options):renderArchitect(rig,'design',ms-3000,options);
|
|
83
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import {parseSvgRig} from '../../../factory/svg-rig.mjs';
|
|
4
|
+
import {renderArchitect,ARCHITECT_CLIPS} from './character.mjs';
|
|
5
|
+
import {artSlot,composeMessage} from '../../lucid/recovery/layout.mjs';
|
|
6
|
+
import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
|
|
7
|
+
const args=process.argv.slice(2),values=new Set(['--cols','--rows','--terminal-cols','--terminal-rows','--message','--scene','--at']);
|
|
8
|
+
for(let i=0;i<args.length;i++){
|
|
9
|
+
if(values.has(args[i])){if(!args[i+1]||args[i+1].startsWith('--'))throw Error('Missing option value');i++;}
|
|
10
|
+
else if(!['--large','--mono','--help','--watch','--attach','--intro','--still','--list'].includes(args[i]))throw Error('Unknown option: '+args[i]);
|
|
11
|
+
}
|
|
12
|
+
const value=(name,fallback)=>args.includes(name)?args[args.indexOf(name)+1]:fallback;
|
|
13
|
+
if(args.includes('--help')){console.log('Architect V2 forge studio. --watch --attach --intro --still --scene ID --at MS --list --large --mono --message TEXT --cols N --rows N --terminal-cols N --terminal-rows N');process.exit(0);}
|
|
14
|
+
if(args.includes('--list')){console.log(ARCHITECT_CLIPS.map(c=>c.id+' · '+c.title).join('\n'));process.exit(0);}
|
|
15
|
+
const w=Number(value('--terminal-cols',process.stdout.columns||80)),h=Number(value('--terminal-rows',process.stdout.rows||24));
|
|
16
|
+
if(!Number.isSafeInteger(w)||!Number.isSafeInteger(h)||w<1||h<1)throw Error('Invalid terminal size');
|
|
17
|
+
const slot=artSlot(w,h),large=args.includes('--large')||args.includes('--intro'),explicit=large||args.includes('--cols')||args.includes('--rows');
|
|
18
|
+
const cols=Number(value('--cols',large?Math.min(120,w-4):Math.min(39,slot.cols))),rows=Number(value('--rows',large?Math.min(36,h-4):Math.min(11,slot.rows)));
|
|
19
|
+
const message=value('--message','ARCHITECT\nImagine. Shape. Illuminate.');
|
|
20
|
+
if(!slot.showArt&&!explicit){console.log(composeMessage(null,message,{terminalCols:w}).join('\n'));}
|
|
21
|
+
else{
|
|
22
|
+
if(cols>w-4||rows>h-2)throw Error('Requested art exceeds terminal margins');
|
|
23
|
+
const rig=parseSvgRig(await fs.readFile(new URL('./architect-master.svg',import.meta.url),'utf8'));
|
|
24
|
+
const opts={cols,rows,mono:args.includes('--mono'),still:args.includes('--still'),deck:large};
|
|
25
|
+
const scene=value('--scene',args.includes('--attach')?'attach':'idle');
|
|
26
|
+
const r=renderArchitect(rig,scene,Number(value('--at',0)),opts);
|
|
27
|
+
if(!r.ok)throw Error(r.code);
|
|
28
|
+
if(args.includes('--watch')||args.includes('--attach')||args.includes('--intro')){
|
|
29
|
+
const {watch}=await import('./watch.mjs');
|
|
30
|
+
await watch(rig,opts,{terminalCols:w,terminalRows:h,large,scene:args.includes('--scene')||args.includes('--attach')?scene:null,arrival:args.includes('--attach'),message:args.includes('--message')?message:null});
|
|
31
|
+
}else{
|
|
32
|
+
const shownMessage=args.includes('--message')?message:'ARCHITECT\n'+r.caption;
|
|
33
|
+
console.log((large?[...cellsToAnsi(r.frame),...composeMessage(null,shownMessage,{terminalCols:w})]:composeMessage(r.frame,shownMessage,{terminalCols:w})).join('\n'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {performance} from 'node:perf_hooks';
|
|
2
|
+
import {ARCHITECT_CLIPS,renderArchitect,renderStory,STORY_DURATION} from './character.mjs';
|
|
3
|
+
import {composeMessage} from '../../lucid/recovery/layout.mjs';
|
|
4
|
+
import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
|
|
5
|
+
|
|
6
|
+
/** One bounded performance. No alternate screen or full-screen clearing. */
|
|
7
|
+
export async function watch(paths,options,config){
|
|
8
|
+
if(!process.stdout.isTTY||!process.stdin.isTTY)throw new Error('--watch/--intro needs an interactive terminal. Use --scene and --at for snapshots.');
|
|
9
|
+
const {terminalCols,terminalRows,scene,large,message}=config;
|
|
10
|
+
const clip=scene?ARCHITECT_CLIPS.find(c=>c.id===scene):null;
|
|
11
|
+
const duration=(clip?.duration??STORY_DURATION)+(config.arrival?6000:0);
|
|
12
|
+
const realCols=process.stdout.columns,realRows=process.stdout.rows;
|
|
13
|
+
const start=performance.now();
|
|
14
|
+
const render=t=>{
|
|
15
|
+
const r=scene?renderArchitect(paths,scene,t,options):renderStory(paths,t,options);
|
|
16
|
+
if(!r?.ok)throw new Error(r?.code??'Invalid story frame');
|
|
17
|
+
const caption=message??'ARCHITECT · '+r.clip+'\n'+r.caption;
|
|
18
|
+
return large?[...cellsToAnsi(r.frame),...caption.split('\n')]:composeMessage(r.frame,caption,{terminalCols});
|
|
19
|
+
};
|
|
20
|
+
// Reserve enough lines for captions too; keep within the real terminal.
|
|
21
|
+
const height=options.rows+3;
|
|
22
|
+
if(height>=terminalRows)throw new Error('Animation needs room for its caption; reduce --rows.');
|
|
23
|
+
let ended=false,drawn=false,timer,resolveDone,rejectDone;
|
|
24
|
+
const wasRaw=Boolean(process.stdin.isRaw);
|
|
25
|
+
const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject;});
|
|
26
|
+
const cleanup=(error)=>{
|
|
27
|
+
if(ended)return;ended=true;clearInterval(timer);
|
|
28
|
+
process.stdin.off('data',key);process.stdout.off('resize',resized);
|
|
29
|
+
process.off('SIGINT',sigint);process.off('SIGTERM',sigterm);
|
|
30
|
+
process.stdin.setRawMode(wasRaw);process.stdin.pause();
|
|
31
|
+
if(drawn)process.stdout.write(`\x1b[${height}B\r`);
|
|
32
|
+
process.stdout.write('\x1b[0m\x1b[?25h');
|
|
33
|
+
error?rejectDone(error):resolveDone();
|
|
34
|
+
};
|
|
35
|
+
const key=data=>{if(data.includes(3)||data.toString().includes('q'))cleanup();};
|
|
36
|
+
const sigint=()=>cleanup(),sigterm=()=>cleanup();
|
|
37
|
+
const resized=()=>cleanup(new Error('Terminal resized; rerun for the new art slot.'));
|
|
38
|
+
try{
|
|
39
|
+
process.stdin.setRawMode(true);process.stdin.resume();process.stdin.on('data',key);
|
|
40
|
+
process.stdout.on('resize',resized);process.on('SIGINT',sigint);process.on('SIGTERM',sigterm);
|
|
41
|
+
process.stdout.write('\x1b[?25l');
|
|
42
|
+
// Allocate the block once; each frame replaces only these lines.
|
|
43
|
+
process.stdout.write('\n'.repeat(height)+`\x1b[${height}A`);drawn=true;
|
|
44
|
+
const tick=()=>{
|
|
45
|
+
try{
|
|
46
|
+
if(process.stdout.columns!==realCols||process.stdout.rows!==realRows)return resized();
|
|
47
|
+
const t=Math.min(performance.now()-start,duration-1),lines=render(t);
|
|
48
|
+
if(lines.length>height)throw new Error('Message exceeds the animation block; use a shorter message or a static preview.');
|
|
49
|
+
process.stdout.write(Array.from({length:height},(_,i)=>'\r\x1b[2K'+(lines[i]??'')+'\n').join('')+`\x1b[${height}A`);
|
|
50
|
+
if(performance.now()-start>=duration)cleanup();
|
|
51
|
+
}catch(e){cleanup(e);}
|
|
52
|
+
};
|
|
53
|
+
tick();if(!ended)timer=setInterval(tick,1000/6);
|
|
54
|
+
await done;
|
|
55
|
+
}catch(e){cleanup(e);await done;}
|
|
56
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480">
|
|
2
|
+
<title>Archivist — keeper of remembered things</title>
|
|
3
|
+
<g id="far-wing">
|
|
4
|
+
<path id="far-sail" fill="#365e82" d="M 370 285 Q 301 166 300 92 Q 330 165 355 187 Q 335 206 346 244 L 365 275 Z"/>
|
|
5
|
+
<path id="far-ribs" fill="none" stroke="#c88f5d" stroke-width="8" d="M 370 285 Q 301 166 300 92 M 370 285 Q 326 185 355 187 M 370 285 L 346 244"/>
|
|
6
|
+
</g>
|
|
7
|
+
<g id="tail">
|
|
8
|
+
<path id="tapered-tail" fill="#287d79" d="M 257 336 C 197 335 185 410 125 406 C 75 402 64 356 89 331 Q 104 316 127 321 Q 87 325 89 355 C 90 389 131 391 158 368 Q 200 310 253 306 Z"/>
|
|
9
|
+
<path id="tail-ridge" fill="none" stroke="#62bdb0" stroke-width="6" d="M 241 332 C 193 339 179 394 131 394 Q 85 394 83 360"/>
|
|
10
|
+
</g>
|
|
11
|
+
<g id="body">
|
|
12
|
+
<path id="round-body" fill="#357f79" d="M 220 251 C 258 208 357 208 402 256 Q 450 302 412 365 Q 357 404 260 383 Q 202 361 199 309 Z"/>
|
|
13
|
+
<path id="belly" fill="#c4b78a" d="M 332 262 Q 401 251 411 305 Q 423 349 379 373 L 325 364 Q 297 317 332 262 Z"/>
|
|
14
|
+
<path id="belly-plates" fill="none" stroke="#946e4d" stroke-width="6" d="M 321 291 Q 361 309 409 295 M 315 321 Q 359 341 410 323 M 326 351 Q 360 364 396 349"/>
|
|
15
|
+
<path id="haunch" fill="#286663" d="M 234 301 Q 281 278 302 314 L 295 356 L 321 382 L 305 404 L 219 400 Q 195 375 212 347 Z"/>
|
|
16
|
+
<path id="haunch-highlight" fill="none" stroke="#67b8aa" stroke-width="8" d="M 228 342 Q 232 310 264 313 Q 285 315 283 344"/>
|
|
17
|
+
<path id="rear-toes" fill="#f0cd96" d="M 226 383 L 215 406 L 238 400 L 251 382 L 248 407 L 273 400 L 284 383 L 283 405 L 311 399"/>
|
|
18
|
+
</g>
|
|
19
|
+
<g id="near-wing">
|
|
20
|
+
<path id="folded-sail" fill="#294f77" d="M 316 277 Q 263 192 151 155 Q 189 215 153 261 Q 192 251 218 287 Q 251 269 283 308 Z"/>
|
|
21
|
+
<path id="folded-highlight" fill="#487fa3" d="M 312 279 Q 254 221 151 155 Q 223 224 218 269 Q 260 264 283 308 Z"/>
|
|
22
|
+
<path id="folded-ribs" fill="none" stroke="#d5a06c" stroke-width="8" d="M 316 277 Q 263 192 151 155 M 316 277 Q 233 219 153 261 M 316 277 Q 253 250 218 287"/>
|
|
23
|
+
<path id="wing-root" fill="#56a69b" d="M 303 264 Q 323 252 335 275 Q 333 299 314 303 L 296 288 Z"/>
|
|
24
|
+
</g>
|
|
25
|
+
<g id="neck">
|
|
26
|
+
<path id="neck-mass" fill="#47968b" d="M 350 202 L 405 192 Q 435 224 419 263 L 391 298 L 344 283 Q 370 247 350 202 Z"/>
|
|
27
|
+
<path id="throat" fill="#d8d1ae" d="M 386 211 L 407 211 Q 426 241 400 270 L 384 287 L 363 276 Q 398 249 386 211 Z"/>
|
|
28
|
+
</g>
|
|
29
|
+
<g id="head">
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
<path id="head-mass" fill="#66ada0" d="M 331 167 Q 349 136 390 141 Q 430 131 449 166 L 449 211 Q 422 242 374 231 L 336 209 Z"/>
|
|
33
|
+
<path id="ear" fill="#4e9690" d="M 339 170 L 298 158 L 306 184 L 291 199 Q 324 210 346 194 Z"/>
|
|
34
|
+
<path id="forehead" fill="#86c7b6" d="M 352 155 Q 390 138 418 152 L 400 168 L 371 166 Z"/>
|
|
35
|
+
<path id="far-horn" fill="#f0f2ee" d="M 349 157 Q 317 139 328 103 Q 330 128 370 143 L 369 158 Z"/>
|
|
36
|
+
<path id="near-horn" fill="#ffffff" d="M 402 151 Q 375 117 402 78 Q 391 112 430 133 L 430 148 Z"/>
|
|
37
|
+
<path id="horn-roots" fill="none" stroke="#bed4ce" stroke-width="5" d="M 350 153 Q 359 158 369 151 M 405 145 Q 419 151 428 140"/>
|
|
38
|
+
<path id="broad-muzzle" fill="#f2d29b" d="M 412 198 Q 452 185 476 208 Q 487 226 466 236 L 418 241 L 390 221 Z"/>
|
|
39
|
+
<path id="mouth" fill="#080d19" d="M 411 229 Q 440 237 475 221 L 469 234 Q 440 249 417 240 Z"/>
|
|
40
|
+
<path id="jaw" fill="#67a597" d="M 415 241 Q 446 251 469 235 Q 453 258 427 253 L 405 240 Z"/>
|
|
41
|
+
<path id="nose" fill="#080d19" d="M 462 211 Q 473 206 473 217 L 462 221 Z"/>
|
|
42
|
+
<path id="chin-whisker" fill="#e9d5ac" d="M 406 237 Q 396 261 416 276 Q 389 272 387 250 Z"/>
|
|
43
|
+
</g>
|
|
44
|
+
<g id="arms">
|
|
45
|
+
<path id="far-hand" fill="#3c817b" d="M 404 278 Q 438 263 452 291 L 484 317 L 499 327 L 485 341 L 452 330 L 414 307 Z"/>
|
|
46
|
+
<path id="reading-arm" fill="#59a597" d="M 358 286 Q 378 278 393 301 L 412 331 L 456 329 L 475 343 L 462 360 L 405 357 Q 384 351 373 328 Z"/>
|
|
47
|
+
<path id="reading-claws" fill="#f6ddb0" d="M 445 330 L 460 316 L 462 331 L 477 324 L 474 340 L 486 336 L 473 355 L 454 354 Z"/>
|
|
48
|
+
</g>
|
|
49
|
+
<g id="reading-stand">
|
|
50
|
+
<path id="stand" fill="none" stroke="#789da5" stroke-width="9" d="M 489 397 L 481 439 M 651 399 L 662 439 M 491 419 L 656 419"/>
|
|
51
|
+
</g>
|
|
52
|
+
<g id="book">
|
|
53
|
+
<path id="book-cover" fill="#2c6867" d="M 452 349 L 526 335 L 567 350 L 609 331 L 689 348 L 681 397 L 571 418 L 460 398 Z"/>
|
|
54
|
+
<path id="page-left" fill="#e9cd96" d="M 459 338 Q 513 326 567 350 L 567 404 Q 512 380 469 389 Z"/>
|
|
55
|
+
<path id="page-right" fill="#f6dfb0" d="M 567 350 Q 620 321 680 336 L 675 385 Q 616 378 567 404 Z"/>
|
|
56
|
+
<path id="spine" fill="none" stroke="#966846" stroke-width="6" d="M 567 350 L 567 405"/>
|
|
57
|
+
<path id="page-lines" fill="none" stroke="#a78459" stroke-width="4" d="M 478 352 Q 514 348 550 363 M 481 365 Q 516 362 550 376 M 587 359 Q 623 343 661 350 M 587 373 Q 620 358 658 363"/>
|
|
58
|
+
<path id="bookmark" fill="#439e98" d="M 607 340 L 619 337 L 619 396 L 612 388 L 606 399 Z"/>
|
|
59
|
+
</g>
|
|
60
|
+
<g id="memory">
|
|
61
|
+
<path id="memory-shard" fill="none" stroke="#91fff0" stroke-width="5" d="M 557 277 L 578 261 L 599 277 L 578 299 Z M 578 261 L 578 299 M 557 277 L 599 277"/>
|
|
62
|
+
<path id="memory-thread" fill="none" stroke="#51ddcb" stroke-width="4" d="M 574 339 Q 553 323 578 304"/>
|
|
63
|
+
</g>
|
|
64
|
+
<g id="glasses">
|
|
65
|
+
<path id="far-spectacle" fill="none" stroke="#f6ce76" stroke-width="5" d="M 338 184 C 338 167.98374225 349.19288125 155 363 155 C 376.80711875 155 388 167.98374225 388 184 C 388 200.01625775 376.80711875 213 363 213 C 349.19288125 213 338 200.01625775 338 184 Z"/>
|
|
66
|
+
<path id="near-spectacle" fill="none" stroke="#f6ce76" stroke-width="5" d="M 381 181 C 381 162.77460325 394.4314575 148 411 148 C 427.5685425 148 441 162.77460325 441 181 C 441 199.22539675 427.5685425 214 411 214 C 394.4314575 214 381 199.22539675 381 181 Z"/>
|
|
67
|
+
<path id="spectacle-bridge" fill="none" stroke="#f6ce76" stroke-width="5" d="M 385 179 Q 389 171 391 177 M 339 181 L 316 173 M 441 177 L 451 169"/>
|
|
68
|
+
</g>
|
|
69
|
+
<g id="eyes">
|
|
70
|
+
<path id="far-eye-socket" fill="#080d19" d="M 342 184 C 342 170.19288125 351.40202025 159 363 159 C 374.59797975 159 384 170.19288125 384 184 C 384 197.80711875 374.59797975 209 363 209 C 351.40202025 209 342 197.80711875 342 184 Z"/>
|
|
71
|
+
<path id="far-eye-white" fill="#ffffff" d="M 349 184 C 349 174.0588745 355.2680135 166 363 166 C 370.7319865 166 377 174.0588745 377 184 C 377 193.9411255 370.7319865 202 363 202 C 355.2680135 202 349 193.9411255 349 184 Z"/>
|
|
72
|
+
<path id="far-pupil" fill="#080d19" d="M 363 188 C 363 184.13400675 365.23857625 181 368 181 C 370.76142375 181 373 184.13400675 373 188 C 373 191.86599325 370.76142375 195 368 195 C 365.23857625 195 363 191.86599325 363 188 Z"/>
|
|
73
|
+
<path id="near-eye-socket" fill="#080d19" d="M 385 181 C 385 164.98374225 396.6405965 152 411 152 C 425.3594035 152 437 164.98374225 437 181 C 437 197.01625775 425.3594035 210 411 210 C 396.6405965 210 385 197.01625775 385 181 Z"/>
|
|
74
|
+
<path id="near-eye-white" fill="#ffffff" d="M 392 181 C 392 168.8497355 400.50658975 159 411 159 C 421.49341025 159 430 168.8497355 430 181 C 430 193.1502645 421.49341025 203 411 203 C 400.50658975 203 392 193.1502645 392 181 Z"/>
|
|
75
|
+
<path id="near-pupil" fill="#080d19" d="M 411 185 C 411 181.13400675 413.23857625 178 416 178 C 418.76142375 178 421 181.13400675 421 185 C 421 188.86599325 418.76142375 192 416 192 C 413.23857625 192 411 188.86599325 411 185 Z"/>
|
|
76
|
+
</g>
|
|
77
|
+
</svg>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
|
|
2
|
+
export const ARCHIVIST_CLIPS=Object.freeze([{id:'idle',title:'Quiet reading',duration:12000},{id:'attach',title:'Look up from the pages',duration:3000},{id:'recall',title:'Recall a memory',duration:10000}]);
|
|
3
|
+
export const STORY_DURATION=13000;
|
|
4
|
+
const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
|
|
5
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
6
|
+
export function poseRig(rig,scene,ms,{deck=false}={}){
|
|
7
|
+
if(scene==='recall'&&ms>=10000)return poseRig(rig,'idle',ms-10000,{deck});
|
|
8
|
+
if(scene==='attach'&&ms>=3000)return poseRig(rig,'idle',ms-3000,{deck});
|
|
9
|
+
const active=scene==='recall',t=ms%12000;
|
|
10
|
+
const lean=active?pulse(t,0,10000):scene==='attach'?-pulse(t,0,3000):.18*pulse(t,1000,7000);
|
|
11
|
+
const lift=active?ease((t-1200)/2500)*(1-ease((t-7600)/2400)):0;
|
|
12
|
+
const paths=rig.paths.filter(p=>p.group!=='memory').map(p=>({...p,polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
|
|
13
|
+
if(['head','eyes','glasses'].includes(p.group))return [x+4*lean,y+5*lean];
|
|
14
|
+
if(p.group==='neck'){const w=Math.max(0,Math.min(1,(290-y)/85));return [x+4*lean*w,y+5*lean*w];}
|
|
15
|
+
if(p.group==='arms'){const w=Math.max(0,Math.min(1,(x-380)/105));return [x+4*lean*w,y-10*lift*w];}
|
|
16
|
+
if(p.group==='tail'){const w=Math.max(0,(220-x)/160);return [x,y-3*lean*w*w];}
|
|
17
|
+
return [x,y];
|
|
18
|
+
}))}));
|
|
19
|
+
const add=(id,rgb,polygons,stroke=3)=>paths.push({id,group:'effect',rgb,polygons,stroke,erase:false});
|
|
20
|
+
if(active&&t>500&&t<2300){
|
|
21
|
+
const u=(t-500)/1800,x=567-95*Math.cos(Math.PI*u),y=350-42*Math.sin(Math.PI*u);
|
|
22
|
+
add('turning-page',[255,235,182],[[[567,350],[x,y],[x+5,y+38],[567,404]]],3);
|
|
23
|
+
}
|
|
24
|
+
if(active&&lift>.003){
|
|
25
|
+
const cy=350-76*lift,scale=.35+.65*lift;
|
|
26
|
+
const source=rig.paths.find(p=>p.id==='memory-shard');
|
|
27
|
+
paths.push({...source,polygons:source.polygons.map(poly=>poly.map(([x,y])=>[578+(x-578)*scale,cy+(y-277)*scale]))});
|
|
28
|
+
add('recall-thread',[63,181,168],[[[574,339],[565,325],[578,cy+23*scale]]],3);
|
|
29
|
+
if(deck){
|
|
30
|
+
const unfold=18*pulse(t,3500,7900);
|
|
31
|
+
for(const side of [-1,1]){
|
|
32
|
+
const x=578+side*(26+unfold),y=cy+20;
|
|
33
|
+
add('memory-fragment-'+side,[77,211,204],[[[x-7,y],[x,y-9],[x+7,y],[x,y+9],[x-7,y]]],3);
|
|
34
|
+
add('memory-link-'+side,[51,125,132],[[[578+side*18,cy+4],[x,y]]],2);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const glint=pulse(t,4400,7100);
|
|
38
|
+
if(glint>.01)add('memory-heart',[211,255,243],[[[578-4*glint,cy],[578,cy-6*glint],[578+4*glint,cy],[578,cy+6*glint]]],0);
|
|
39
|
+
}
|
|
40
|
+
const map=new Map(paths.map(p=>[p.id,p]));
|
|
41
|
+
return {...rig,paths,eyes:rig.eyes.map(e=>({...e,socket:map.get(e.socket.id),white:map.get(e.white.id),pupil:map.get(e.pupil.id)}))};
|
|
42
|
+
}
|
|
43
|
+
export function renderArchivist(rig,scene='idle',ms=0,options={}){
|
|
44
|
+
if(!ARCHIVIST_CLIPS.some(c=>c.id===scene))return {ok:false,code:'UNKNOWN_CLIP'};
|
|
45
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
46
|
+
if(!options.still&&scene==='recall'&&ms>=10000)return renderArchivist(rig,'idle',ms-10000,options);
|
|
47
|
+
if(!options.still&&scene==='attach'&&ms>=3000)return renderArchivist(rig,'idle',ms-3000,options);
|
|
48
|
+
const t=options.still?({idle:0,attach:1500,recall:5500}[scene]):ms;
|
|
49
|
+
const caption=scene==='recall'?(t<1200?'Find the page.':t<3700?'Draw the memory into the light.':t<7600?'A remembered connection.':'Return it safely to the pages.'):scene==='attach'?'A moment away from the book.':'Remember. Connect. Preserve.';
|
|
50
|
+
return {...renderSvgRig(poseRig(rig,scene,t,options),options),scene,clip:scene,caption};
|
|
51
|
+
}
|
|
52
|
+
export function renderStory(rig,ms,options={}){
|
|
53
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
54
|
+
if(options.still)return renderArchivist(rig,'recall',5500,options);
|
|
55
|
+
return ms<3000?renderArchivist(rig,'attach',ms,options):renderArchivist(rig,'recall',ms-3000,options);
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
|
|
2
|
+
import {poseRig as previousPose,renderArchivist as previousRender,ARCHIVIST_CLIPS as clips} from '../v3/character.mjs';
|
|
3
|
+
export const ARCHIVIST_CLIPS=Object.freeze([...clips,{id:'cross-reference',title:'Connect two memories',duration:10000}]);
|
|
4
|
+
export const STORY_DURATION=13000;
|
|
5
|
+
const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
|
|
6
|
+
export function poseCrossReference(rig,t,options={}){
|
|
7
|
+
if(t>=10000)return previousPose(rig,'idle',t-10000,options);
|
|
8
|
+
const base=previousPose(rig,'recall',t,options);
|
|
9
|
+
const paths=base.paths.filter(p=>p.group!=='memory'&&p.group!=='effect');
|
|
10
|
+
const add=(id,rgb,polygons,stroke=3)=>paths.push({id,group:'effect',rgb,polygons,stroke,erase:false});
|
|
11
|
+
const rise=ease((t-600)/2200),returning=1-ease((t-8000)/2000),lift=rise*returning;
|
|
12
|
+
const y=354-85*lift,r=15*lift;
|
|
13
|
+
if(lift>.002){
|
|
14
|
+
for(const [id,x,c]of [['left',526,[99,239,215]],['right',630,[209,155,255]]]){
|
|
15
|
+
add(id+'-memory',c,[[[x-r,y],[x,y-r],[x+r,y],[x,y+r],[x-r,y]]],4);
|
|
16
|
+
if(options.deck)add(id+'-page-thread',c,[[[x,348],[x-6,320],[x,y+r]]],2);
|
|
17
|
+
}
|
|
18
|
+
const connect=ease((t-3000)/1700);
|
|
19
|
+
const curve=u=>[541+74*u,y-29*Math.sin(Math.PI*u)*returning];
|
|
20
|
+
if(connect>.002){
|
|
21
|
+
const points=Array.from({length:25},(_,i)=>curve(connect*i/24));
|
|
22
|
+
add('reference-thread',[93,180,201],[points],options.deck?3:4);
|
|
23
|
+
if(t>=4900&&t<7600){
|
|
24
|
+
const u=ease((t-4900)/2700),[x,cy]=curve(u);
|
|
25
|
+
add('reference-pulse',[235,255,243],[[[x-4,cy],[x,cy-6],[x+4,cy],[x,cy+6]]],0);
|
|
26
|
+
}
|
|
27
|
+
if(t>=7600&&t<8000){
|
|
28
|
+
for(const x of [526,630])add('linked-glint-'+x,[224,255,241],[[[x-4,y],[x,y-5],[x+4,y],[x,y+5]]],0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {...base,paths};
|
|
33
|
+
}
|
|
34
|
+
export function renderArchivist(rig,scene='idle',ms=0,options={}){
|
|
35
|
+
if(scene!=='cross-reference')return previousRender(rig,scene,ms,options);
|
|
36
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
37
|
+
if(ms>=10000&&!options.still)return previousRender(rig,'idle',ms-10000,options);
|
|
38
|
+
const t=options.still?6200:ms;
|
|
39
|
+
const caption=t<2800?'Bring two memories into view.':t<4900?'Trace the connection.':t<7600?'Follow the reference.':t<8000?'Two memories, one connection.':'Return both to the pages.';
|
|
40
|
+
return {...renderSvgRig(poseCrossReference(rig,t,options),options),scene,clip:scene,caption};
|
|
41
|
+
}
|
|
42
|
+
export function renderStory(rig,ms,options={}){
|
|
43
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
44
|
+
if(options.still)return renderArchivist(rig,'cross-reference',6200,options);
|
|
45
|
+
return ms<3000?renderArchivist(rig,'attach',ms,options):renderArchivist(rig,'cross-reference',ms-3000,options);
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import {parseSvgRig} from '../../../factory/svg-rig.mjs';
|
|
4
|
+
import {renderArchivist,ARCHIVIST_CLIPS} from './character.mjs';
|
|
5
|
+
import {artSlot,composeMessage} from '../../lucid/recovery/layout.mjs';
|
|
6
|
+
import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
|
|
7
|
+
const args=process.argv.slice(2),values=new Set(['--cols','--rows','--terminal-cols','--terminal-rows','--message','--scene','--at']);
|
|
8
|
+
for(let i=0;i<args.length;i++){
|
|
9
|
+
if(values.has(args[i])){if(!args[i+1]||args[i+1].startsWith('--'))throw Error('Missing option value');i++;}
|
|
10
|
+
else if(!['--large','--mono','--help','--watch','--attach','--intro','--still','--list'].includes(args[i]))throw Error('Unknown option: '+args[i]);
|
|
11
|
+
}
|
|
12
|
+
const value=(name,fallback)=>args.includes(name)?args[args.indexOf(name)+1]:fallback;
|
|
13
|
+
if(args.includes('--help')){console.log('Archivist V4 forge studio. --watch --attach --intro --still --scene ID --at MS --list --large --mono --message TEXT --cols N --rows N --terminal-cols N --terminal-rows N');process.exit(0);}
|
|
14
|
+
if(args.includes('--list')){console.log(ARCHIVIST_CLIPS.map(c=>c.id+' · '+c.title).join('\n'));process.exit(0);}
|
|
15
|
+
const w=Number(value('--terminal-cols',process.stdout.columns||80)),h=Number(value('--terminal-rows',process.stdout.rows||24));
|
|
16
|
+
if(!Number.isSafeInteger(w)||!Number.isSafeInteger(h)||w<1||h<1)throw Error('Invalid terminal size');
|
|
17
|
+
const slot=artSlot(w,h),large=args.includes('--large')||args.includes('--intro'),explicit=large||args.includes('--cols')||args.includes('--rows');
|
|
18
|
+
const cols=Number(value('--cols',large?Math.min(120,w-4):Math.min(39,slot.cols))),rows=Number(value('--rows',large?Math.min(36,h-4):Math.min(11,slot.rows)));
|
|
19
|
+
const message=value('--message','ARCHIVIST\nRemember. Connect. Preserve.');
|
|
20
|
+
if(!slot.showArt&&!explicit){console.log(composeMessage(null,message,{terminalCols:w}).join('\n'));}
|
|
21
|
+
else{
|
|
22
|
+
if(cols>w-4||rows>h-2)throw Error('Requested art exceeds terminal margins');
|
|
23
|
+
const rig=parseSvgRig(await fs.readFile(new URL('../v2/archivist-master.svg',import.meta.url),'utf8'));
|
|
24
|
+
const opts={cols,rows,mono:args.includes('--mono'),still:args.includes('--still'),deck:large};
|
|
25
|
+
const scene=value('--scene',args.includes('--attach')?'attach':'idle');
|
|
26
|
+
const r=renderArchivist(rig,scene,Number(value('--at',0)),opts);
|
|
27
|
+
if(!r.ok)throw Error(r.code);
|
|
28
|
+
if(args.includes('--watch')||args.includes('--attach')||args.includes('--intro')){
|
|
29
|
+
const {watch}=await import('./watch.mjs');
|
|
30
|
+
await watch(rig,opts,{terminalCols:w,terminalRows:h,large,scene:args.includes('--scene')||args.includes('--attach')?scene:null,arrival:args.includes('--attach'),message:args.includes('--message')?message:null});
|
|
31
|
+
}else{
|
|
32
|
+
const shownMessage=args.includes('--message')?message:'ARCHIVIST\n'+r.caption;
|
|
33
|
+
console.log((large?[...cellsToAnsi(r.frame),...composeMessage(null,shownMessage,{terminalCols:w})]:composeMessage(r.frame,shownMessage,{terminalCols:w})).join('\n'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {performance} from 'node:perf_hooks';
|
|
2
|
+
import {ARCHIVIST_CLIPS,renderArchivist,renderStory,STORY_DURATION} from './character.mjs';
|
|
3
|
+
import {composeMessage} from '../../lucid/recovery/layout.mjs';
|
|
4
|
+
import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
|
|
5
|
+
|
|
6
|
+
/** One bounded performance. No alternate screen or full-screen clearing. */
|
|
7
|
+
export async function watch(paths,options,config){
|
|
8
|
+
if(!process.stdout.isTTY||!process.stdin.isTTY)throw new Error('--watch/--intro needs an interactive terminal. Use --scene and --at for snapshots.');
|
|
9
|
+
const {terminalCols,terminalRows,scene,large,message}=config;
|
|
10
|
+
const clip=scene?ARCHIVIST_CLIPS.find(c=>c.id===scene):null;
|
|
11
|
+
const duration=(clip?.duration??STORY_DURATION)+(config.arrival?6000:0);
|
|
12
|
+
const realCols=process.stdout.columns,realRows=process.stdout.rows;
|
|
13
|
+
const start=performance.now();
|
|
14
|
+
const render=t=>{
|
|
15
|
+
const r=scene?renderArchivist(paths,scene,t,options):renderStory(paths,t,options);
|
|
16
|
+
if(!r?.ok)throw new Error(r?.code??'Invalid story frame');
|
|
17
|
+
const caption=message??'ARCHIVIST · '+r.clip+'\n'+r.caption;
|
|
18
|
+
return large?[...cellsToAnsi(r.frame),...caption.split('\n')]:composeMessage(r.frame,caption,{terminalCols});
|
|
19
|
+
};
|
|
20
|
+
// Reserve enough lines for captions too; keep within the real terminal.
|
|
21
|
+
const height=options.rows+3;
|
|
22
|
+
if(height>=terminalRows)throw new Error('Animation needs room for its caption; reduce --rows.');
|
|
23
|
+
let ended=false,drawn=false,timer,resolveDone,rejectDone;
|
|
24
|
+
const wasRaw=Boolean(process.stdin.isRaw);
|
|
25
|
+
const done=new Promise((resolve,reject)=>{resolveDone=resolve;rejectDone=reject;});
|
|
26
|
+
const cleanup=(error)=>{
|
|
27
|
+
if(ended)return;ended=true;clearInterval(timer);
|
|
28
|
+
process.stdin.off('data',key);process.stdout.off('resize',resized);
|
|
29
|
+
process.off('SIGINT',sigint);process.off('SIGTERM',sigterm);
|
|
30
|
+
process.stdin.setRawMode(wasRaw);process.stdin.pause();
|
|
31
|
+
if(drawn)process.stdout.write(`\x1b[${height}B\r`);
|
|
32
|
+
process.stdout.write('\x1b[0m\x1b[?25h');
|
|
33
|
+
error?rejectDone(error):resolveDone();
|
|
34
|
+
};
|
|
35
|
+
const key=data=>{if(data.includes(3)||data.toString().includes('q'))cleanup();};
|
|
36
|
+
const sigint=()=>cleanup(),sigterm=()=>cleanup();
|
|
37
|
+
const resized=()=>cleanup(new Error('Terminal resized; rerun for the new art slot.'));
|
|
38
|
+
try{
|
|
39
|
+
process.stdin.setRawMode(true);process.stdin.resume();process.stdin.on('data',key);
|
|
40
|
+
process.stdout.on('resize',resized);process.on('SIGINT',sigint);process.on('SIGTERM',sigterm);
|
|
41
|
+
process.stdout.write('\x1b[?25l');
|
|
42
|
+
// Allocate the block once; each frame replaces only these lines.
|
|
43
|
+
process.stdout.write('\n'.repeat(height)+`\x1b[${height}A`);drawn=true;
|
|
44
|
+
const tick=()=>{
|
|
45
|
+
try{
|
|
46
|
+
if(process.stdout.columns!==realCols||process.stdout.rows!==realRows)return resized();
|
|
47
|
+
const t=Math.min(performance.now()-start,duration-1),lines=render(t);
|
|
48
|
+
if(lines.length>height)throw new Error('Message exceeds the animation block; use a shorter message or a static preview.');
|
|
49
|
+
process.stdout.write(Array.from({length:height},(_,i)=>'\r\x1b[2K'+(lines[i]??'')+'\n').join('')+`\x1b[${height}A`);
|
|
50
|
+
if(performance.now()-start>=duration)cleanup();
|
|
51
|
+
}catch(e){cleanup(e);}
|
|
52
|
+
};
|
|
53
|
+
tick();if(!ended)timer=setInterval(tick,1000/6);
|
|
54
|
+
await done;
|
|
55
|
+
}catch(e){cleanup(e);await done;}
|
|
56
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {renderSvgRig} from '../../../factory/svg-rig-contour.mjs';
|
|
2
|
+
export const ARGUS_CLIPS=Object.freeze([{id:'idle',title:'Quiet watch',duration:12000},{id:'attach',title:'Acknowledge',duration:3000},{id:'sweep',title:'Sweep the fleet',duration:10000},{id:'triage',title:'Focus the alert',duration:10000},{id:'inspect',title:'Inspect the change',duration:12000}]);
|
|
3
|
+
export const STORY_DURATION=35000;
|
|
4
|
+
const ease=x=>{x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);};
|
|
5
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
6
|
+
const sensors=['left-0','left-1','left-2','right-2','right-1','right-0'];
|
|
7
|
+
const xy=[[179,173],[157,236],[242,276],[514,276],[603,234],[574,170]];
|
|
8
|
+
export function poseRig(rig,scene,ms,{deck=false}={}){
|
|
9
|
+
const duration=ARGUS_CLIPS.find(c=>c.id===scene).duration;
|
|
10
|
+
if(scene!=='idle'&&ms>=duration)return poseRig(rig,'idle',ms-duration,{deck});
|
|
11
|
+
const t=ms%duration,env=ease(t/1800)*(1-ease((t-duration+2200)/2200));
|
|
12
|
+
const focus=['triage','inspect'].includes(scene)?env:0;
|
|
13
|
+
const dx=7*focus+(scene==='sweep'?4*Math.sin(t/10000*Math.PI*2)*env:0),dy=scene==='attach'?5*pulse(t,0,3000):3*focus;
|
|
14
|
+
const paths=rig.paths.map(p=>({...p,rgb:p.rgb&&[...p.rgb],polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
|
|
15
|
+
if(p.group==='head'||p.group==='eyes')return [x+dx,y+dy];
|
|
16
|
+
if(p.group==='neck'){const w=Math.max(0,Math.min(1,(280-y)/80));return [x+dx*w,y+dy*w];}
|
|
17
|
+
return [x,y];
|
|
18
|
+
}))}));
|
|
19
|
+
for(let i=0;i<sensors.length;i++){
|
|
20
|
+
const [side,n]=sensors[i].split('-'),p=paths.find(p=>p.id===side+'-sensor-glow-'+n);
|
|
21
|
+
let light=scene==='sweep'?pulse(t,600+i*1100,2600+i*1100):scene==='attach'?pulse(t,700+i*120,2500+i*50):scene==='idle'?pulse(t,1200+i*1400,2400+i*1400)*.45:focus*(i>=3?1:.15);
|
|
22
|
+
p.rgb=[Math.round(65+93*light),Math.round(130+114*light),Math.round(153+86*light)];
|
|
23
|
+
}
|
|
24
|
+
const add=(id,c,poly,stroke=3)=>paths.push({id,group:'effect',rgb:c,polygons:[poly],stroke:stroke*env,erase:false});
|
|
25
|
+
if(scene==='sweep'&&deck){
|
|
26
|
+
for(let i=0;i<6;i++){
|
|
27
|
+
const v=pulse(t,600+i*1100,2600+i*1100);if(v<.02)continue;
|
|
28
|
+
const [x,y]=xy[i],r=24+14*v;
|
|
29
|
+
add('scan-'+i,[77,Math.round(150+90*v),225],Array.from({length:20},(_,j)=>{const a=-Math.PI*.8+Math.PI*1.6*j/19;return [x+r*Math.cos(a),y+r*.65*Math.sin(a)];}),2);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if(scene==='triage'&&env>0){
|
|
33
|
+
const select=ease((t-3000)/2000),fade=1-select;
|
|
34
|
+
for(let i=0;i<3;i++){
|
|
35
|
+
const x=546+i*47,y=371-(i===1?17*select:0),r=(i===1?10:8*(.3+.7*fade))*env;
|
|
36
|
+
add('alert-'+i,i===1?[255,185,68]:[Math.round(70*fade+20),Math.round(160*fade+35),Math.round(180*fade+40)],[[x-r,y],[x,y-r],[x+r,y],[x,y+r],[x-r,y]],3);
|
|
37
|
+
}
|
|
38
|
+
if(select>0){const x=593,y=354,r=20*select*env;add('focus-bracket',[255,203,103],[[x-r,y-10],[x-r,y+12],[x+r,y+12],[x+r,y-10]],3);}
|
|
39
|
+
}
|
|
40
|
+
if(scene==='inspect'&&env>0){
|
|
41
|
+
const open=ease((t-600)/2000)*(1-ease((t-9800)/2200)),reveal=ease((t-3500)/1700),width=48*open;
|
|
42
|
+
for(let i=0;i<2;i++){
|
|
43
|
+
const x=534+i*71;
|
|
44
|
+
add('file-'+i,[91,185,203],[[x,337],[x+width,337],[x+width,407],[x,407],[x,337]],3);
|
|
45
|
+
for(let j=0;j<3;j++)add('line-'+i+'-'+j,i===1&&j===1&&reveal>0?[255,185,68]:[111,191,204],[[x+8*open,352+j*18],[x+(j===1&&i===1?38:31)*open,352+j*18]],i===1&&j===1?5:3);
|
|
46
|
+
}
|
|
47
|
+
if(reveal>0&&deck)add('changed-link',[255,185,68],[[582,370],[605,370]],3);
|
|
48
|
+
}
|
|
49
|
+
if(scene==='inspect')for(const p of paths)if(p.group==='effect')p.polygons=p.polygons.map(poly=>poly.map(([x,y])=>[x,372+(y-372)*env]));
|
|
50
|
+
const map=new Map(paths.map(p=>[p.id,p]));
|
|
51
|
+
return {...rig,paths,eyes:rig.eyes.map(e=>({...e,socket:map.get(e.socket.id),white:map.get(e.white.id),pupil:map.get(e.pupil.id)}))};
|
|
52
|
+
}
|
|
53
|
+
export function renderArgus(rig,scene='idle',ms=0,options={}){
|
|
54
|
+
const c=ARGUS_CLIPS.find(c=>c.id===scene);if(!c)return {ok:false,code:'UNKNOWN_CLIP'};
|
|
55
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
56
|
+
if(!options.still&&scene!=='idle'&&ms>=c.duration)return renderArgus(rig,'idle',ms-c.duration,options);
|
|
57
|
+
const t=options.still?({idle:0,attach:1400,sweep:4000,triage:6000,inspect:6500}[scene]):ms;
|
|
58
|
+
const caption={idle:'Watching quietly.',attach:'Argus acknowledges you.',sweep:'Sweep · six watchful sensors.',triage:t<3000?'Triage · three incoming signals.':'Triage · focus the amber alert.',inspect:t<3500?'Inspect · unfold both file traces.':'Inspect · highlight the changed line.'}[scene];
|
|
59
|
+
return {...renderSvgRig(poseRig(rig,scene,t,options),options),scene,clip:scene,caption};
|
|
60
|
+
}
|
|
61
|
+
export function renderStory(rig,ms,options={}){
|
|
62
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
63
|
+
if(options.still)return renderArgus(rig,'inspect',6500,options);
|
|
64
|
+
if(ms<3000)return renderArgus(rig,'attach',ms,options);
|
|
65
|
+
if(ms<13000)return renderArgus(rig,'sweep',ms-3000,options);
|
|
66
|
+
if(ms<23000)return renderArgus(rig,'triage',ms-13000,options);
|
|
67
|
+
if(ms<35000)return renderArgus(rig,'inspect',ms-23000,options);
|
|
68
|
+
return renderArgus(rig,'idle',ms-35000,options);
|
|
69
|
+
}
|