quoxcli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +2 -0
- package/bin/quox.js +99 -126
- package/lib/api/endpoints/actions.js +15 -0
- package/lib/api/endpoints/admin.js +46 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +7 -0
- package/lib/api/endpoints/assistants.js +12 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/blog.js +16 -0
- package/lib/api/endpoints/chat.js +14 -0
- package/lib/api/endpoints/contactsq.js +6 -0
- package/lib/api/endpoints/conversations.js +14 -0
- package/lib/api/endpoints/devServerRegistry.js +25 -0
- package/lib/api/endpoints/engagement.js +26 -0
- package/lib/api/endpoints/evidence.js +20 -0
- package/lib/api/endpoints/executions.js +13 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/files.js +14 -0
- package/lib/api/endpoints/hat.js +44 -0
- package/lib/api/endpoints/inbox.js +20 -0
- package/lib/api/endpoints/index.js +26 -0
- package/lib/api/endpoints/loops.js +26 -0
- package/lib/api/endpoints/memory.js +19 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +179 -0
- package/lib/api/endpoints/objectives.js +12 -0
- package/lib/api/endpoints/plugins.js +18 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/quoxplan.js +7 -0
- package/lib/api/endpoints/quoxseo.js +12 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/safety.js +7 -0
- package/lib/api/endpoints/server.js +21 -0
- package/lib/api/endpoints/setup.js +6 -0
- package/lib/api/endpoints/skills.js +20 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/teams.js +2 -2
- package/lib/api/endpoints/triggers.js +26 -0
- package/lib/api/endpoints/v1.js +120 -0
- package/lib/api/endpoints/ward.js +7 -0
- package/lib/api/endpoints/workflows.js +7 -0
- package/lib/api/endpoints/x.js +8 -0
- package/lib/commands/actions.js +131 -0
- package/lib/commands/admin.js +16 -0
- package/lib/commands/agent.js +170 -27
- package/lib/commands/agentAttach.js +140 -13
- package/lib/commands/approvals.js +78 -1
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +146 -0
- package/lib/commands/assistant.js +59 -4
- package/lib/commands/auth.js +8 -2
- package/lib/commands/backup.js +8 -4
- package/lib/commands/blog.js +110 -0
- package/lib/commands/brain.js +105 -14
- package/lib/commands/build.js +19 -5
- package/lib/commands/chat.js +180 -26
- package/lib/commands/chatCompanion.js +27 -0
- package/lib/commands/chatState.js +45 -0
- package/lib/commands/claude.js +35 -38
- package/lib/commands/contacts.js +134 -4
- package/lib/commands/contactsSendAt.js +21 -0
- package/lib/commands/conversation.js +35 -4
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +28 -7
- package/lib/commands/devResident.js +15 -3
- package/lib/commands/devSupervisor.js +13 -1
- package/lib/commands/discord.js +75 -39
- package/lib/commands/done.js +98 -28
- package/lib/commands/engagement.js +107 -6
- package/lib/commands/evidence.js +154 -0
- package/lib/commands/ext.js +23 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +38 -1
- package/lib/commands/fleet.js +336 -4
- package/lib/commands/governance.js +12 -3
- package/lib/commands/gripe.js +44 -13
- package/lib/commands/hat.js +254 -9
- package/lib/commands/helpers.js +18 -1
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/image.js +68 -0
- package/lib/commands/inbox.js +74 -23
- package/lib/commands/kuma.js +1050 -0
- package/lib/commands/kumaSla.js +174 -0
- package/lib/commands/licence.js +52 -155
- package/lib/commands/loop.js +291 -0
- package/lib/commands/lucid.js +10 -4
- package/lib/commands/memory.js +85 -8
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +69 -2
- package/lib/commands/monitoring.js +1 -1
- package/lib/commands/motion.js +2 -2
- package/lib/commands/nucleus.js +48 -17
- package/lib/commands/objective.js +59 -1
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +2 -1
- package/lib/commands/org.js +30 -3
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +38 -0
- package/lib/commands/plugin.js +110 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/provider.js +217 -0
- package/lib/commands/qlarity.js +19 -3
- package/lib/commands/quoxchat.js +13 -4
- package/lib/commands/quoxseo.js +69 -0
- package/lib/commands/reach.js +2 -1
- package/lib/commands/research.js +79 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +367 -0
- package/lib/commands/run.js +82 -10
- package/lib/commands/scope.js +2 -2
- package/lib/commands/server.js +405 -0
- package/lib/commands/setup.js +33 -7
- package/lib/commands/shortcut.js +41 -0
- package/lib/commands/skills.js +68 -9
- package/lib/commands/slot.js +260 -7
- package/lib/commands/status.js +1 -1
- package/lib/commands/team.js +3 -1
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +133 -45
- package/lib/commands/trace.js +12 -7
- package/lib/commands/trigger.js +95 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +31 -8
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +34 -3
- package/lib/commands/volt.js +45 -3
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +73 -21
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +189 -32
- package/lib/commands/x.js +13 -4
- package/lib/commands/youtube.js +172 -0
- package/lib/core/actingAs.js +2 -2
- package/lib/core/actionItems.js +115 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/agentAlias.js +2 -0
- package/lib/core/agentFamilies.js +7 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +164 -13
- package/lib/core/callerSource.js +1 -1
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +152 -35
- package/lib/core/commandRegistrars.js +130 -0
- package/lib/core/config.js +143 -25
- package/lib/core/customHats.js +168 -0
- package/lib/core/doneEngineFetch.js +8 -0
- package/lib/core/errors.js +329 -20
- package/lib/core/findingReconcile.js +90 -0
- package/lib/core/global-flags.js +105 -5
- package/lib/core/governedSession.js +1 -3
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +4 -1
- package/lib/core/liveSessionsReport.js +1 -1
- package/lib/core/loopGlyph.js +97 -0
- package/lib/core/offload.js +533 -0
- package/lib/core/org-override.js +1 -1
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +11 -11
- package/lib/core/peek.js +126 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentSeed.js +3 -1
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionHeartbeat.js +12 -8
- package/lib/core/sessionsOverview.js +64 -0
- package/lib/core/shortcut.js +83 -0
- package/lib/core/slotKeeper.js +248 -3
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +71 -2
- package/lib/core/version.js +1 -1
- package/lib/core/vm.js +366 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/dragon/companionView.js +34 -0
- package/lib/dragon/frames.js +118 -0
- package/lib/dragon/jobs.js +123 -0
- package/lib/dragon/renderWorker.js +15 -0
- package/lib/dragon/select.js +72 -0
- package/lib/evidence/bundleExport.js +47 -0
- package/lib/evidence/bundleVerify.js +77 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +45 -58
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/quoxon/EXTENSIONS.md +23 -0
- package/lib/quoxon/activity.js +14 -2
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +690 -25
- package/lib/quoxon/app.js +3586 -250
- package/lib/quoxon/approvalFacts.js +96 -0
- package/lib/quoxon/approvalSentence.js +44 -0
- package/lib/quoxon/approvalsView.js +384 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +13 -6
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +124 -0
- package/lib/quoxon/commands.js +429 -39
- package/lib/quoxon/connect.js +210 -23
- package/lib/quoxon/connectCard.js +19 -2
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/copy.js +4 -3
- package/lib/quoxon/countdownLine.js +20 -0
- package/lib/quoxon/deck.js +30 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +1 -1
- package/lib/quoxon/demo.js +46 -3
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +192 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonCompanion.js +255 -0
- package/lib/quoxon/dragonModes.js +151 -0
- package/lib/quoxon/dragonMoments.js +82 -0
- package/lib/quoxon/dragonReads.js +68 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +2 -2
- package/lib/quoxon/events.js +266 -26
- package/lib/quoxon/extensions.js +276 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +21 -7
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/inboxView.js +79 -0
- package/lib/quoxon/indicator.js +6 -2
- package/lib/quoxon/installCli.js +79 -0
- package/lib/quoxon/intent.js +12 -1
- package/lib/quoxon/keys.js +51 -8
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/loopCard.js +456 -0
- package/lib/quoxon/loopCards.js +180 -0
- package/lib/quoxon/loopStream.js +87 -0
- package/lib/quoxon/main.js +101 -160
- package/lib/quoxon/markdown.js +45 -21
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +132 -26
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +3 -2
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/mouse.js +222 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objectiveOffer.js +82 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/fetch.js +17 -5
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +46 -7
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +33 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +12 -4
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/view.js +11 -0
- package/lib/quoxon/onboarding/copy.js +32 -4
- package/lib/quoxon/onboarding/credential.js +17 -4
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +15 -0
- package/lib/quoxon/onboarding/index.js +34 -7
- package/lib/quoxon/onboarding/installRunner.js +64 -5
- package/lib/quoxon/onboarding/preflight.js +17 -3
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
- package/lib/quoxon/onboarding/view.js +76 -19
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +59 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/planSteps.js +51 -0
- package/lib/quoxon/print.js +12 -5
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +20 -51
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +62 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qmotion.js +75 -37
- package/lib/quoxon/render.js +740 -124
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +117 -17
- package/lib/quoxon/route.js +6 -5
- package/lib/quoxon/rows.js +954 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/serverChip.js +42 -0
- package/lib/quoxon/session.js +182 -2
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +94 -0
- package/lib/quoxon/slash/cliLeaves.json +1023 -0
- package/lib/quoxon/slash/compact.js +1 -1
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/evidenceCmd.js +34 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +13 -8
- package/lib/quoxon/slash/listView.js +3 -1
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +22 -8
- package/lib/quoxon/slash/paletteController.js +117 -7
- package/lib/quoxon/slash/panels.js +21 -6
- package/lib/quoxon/slash/picker.js +20 -2
- package/lib/quoxon/slash/qpeCmd.js +35 -0
- package/lib/quoxon/slash/restart.js +95 -13
- package/lib/quoxon/slash/resume.js +18 -1
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +16 -6
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/todoCmd.js +122 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/mapping.js +2 -1
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +90 -0
- package/lib/quoxon/term.js +46 -11
- package/lib/quoxon/textBuffer.js +176 -2
- package/lib/quoxon/theme.js +58 -25
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolCards.js +30 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +42 -4
- package/lib/quoxon/web/qface.js +18 -10
- package/lib/quoxon/welcome.js +1 -1
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +134 -0
- package/lib/quoxon/windowOrg.js +123 -0
- package/lib/server/devServers.js +137 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/LucidDeck.jsx +209 -217
- package/package.json +5 -1
- package/vendor/dragonlab/SOURCE +4 -0
- package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
- package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
- package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
- package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
- package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
- package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
- package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
- package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
- package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
- package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
- package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
- package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
- package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
- package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
- package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
- package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
- package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
- package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
- package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
- package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
- package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
- package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
- package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
- package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
- package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
- package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
- package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
- package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
- package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
- package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
- package/vendor/dragonlab/lib/power-effects.js +294 -0
- package/vendor/dragonlab/lib/power-registry.js +74 -0
- package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
- package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
- package/lib/core/websiteSession.js +0 -148
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480">
|
|
2
|
+
<title>Hyperion — gentle bronze guardian, first terminal anatomy</title>
|
|
3
|
+
<g id="far-wing">
|
|
4
|
+
<path id="far-wing-membrane" fill="#a66b32" d="M 298 288 C 242 247 206 210 182 157 C 151 99 130 61 111 32 C 115 127 94 227 51 326 C 90 292 110 271 130 268 C 145 280 155 291 162 307 C 190 272 220 269 242 297 L 298 323 Z"/>
|
|
5
|
+
<path id="far-wing-spar" fill="none" stroke="#e9b966" stroke-width="10" d="M 294 294 C 210 224 158 125 111 32 M 111 32 C 118 141 96 238 51 326 M 120 123 C 134 199 145 256 162 307 M 153 179 C 195 210 220 249 242 297"/>
|
|
6
|
+
<path id="far-wing-claw" fill="#f7dfac" d="M 106 55 C 98 35 104 18 127 16 C 121 30 115 45 106 55 Z"/>
|
|
7
|
+
</g>
|
|
8
|
+
<g id="near-wing">
|
|
9
|
+
<path id="near-wing-membrane" fill="#bf813e" d="M 409 280 C 470 241 507 187 540 118 C 561 71 582 43 608 28 C 611 134 665 223 718 310 C 677 281 644 270 623 282 C 599 291 589 307 582 326 C 552 294 524 285 499 304 C 474 325 462 344 459 362 L 412 324 Z"/>
|
|
10
|
+
<path id="near-wing-spar" fill="none" stroke="#edb967" stroke-width="11" d="M 420 293 C 500 227 541 112 608 28 M 608 28 C 609 142 660 228 718 310 M 608 28 C 559 142 563 233 582 326 M 555 134 C 519 188 504 246 499 304"/>
|
|
11
|
+
<path id="near-wing-claw" fill="#ffe6b0" d="M 600 47 C 582 31 597 11 624 16 C 617 25 611 36 600 47 Z"/>
|
|
12
|
+
</g>
|
|
13
|
+
<g id="tail">
|
|
14
|
+
<path id="tail-mass" fill="#b77935" d="M 421 363 C 477 386 523 410 590 413 C 652 416 716 401 733 362 C 753 317 735 277 703 270 C 670 261 645 277 641 302 C 635 327 650 348 671 349 C 694 351 705 335 701 320 C 696 336 678 336 671 326 C 659 311 674 293 691 299 C 710 304 714 331 699 351 C 676 378 626 382 580 368 C 522 351 486 326 440 321 Z"/>
|
|
15
|
+
<path id="tail-belly" fill="#e2c18a" d="M 438 370 C 509 404 557 425 622 417 C 665 414 707 396 720 377 C 672 399 625 403 586 396 C 528 387 489 362 448 342 Z"/>
|
|
16
|
+
<path id="tail-ridges" fill="#e9b968" d="M 503 349 L 516 330 L 529 361 M 548 365 L 563 346 L 576 376 M 596 380 L 612 363 L 624 382 M 651 375 L 665 354 L 676 368 M 699 348 L 719 343 L 707 365"/>
|
|
17
|
+
</g>
|
|
18
|
+
<g id="rear-feet">
|
|
19
|
+
<path id="far-rear-leg" fill="#80532d" d="M 409 333 C 447 321 486 343 497 378 L 510 410 C 525 421 522 440 502 442 L 452 440 C 440 428 444 406 429 392 Z"/>
|
|
20
|
+
<path id="far-foot" fill="#d5ad72" d="M 448 420 Q 460 406 469 424 Q 477 405 487 424 Q 501 410 510 431 L 453 434 Z"/>
|
|
21
|
+
</g>
|
|
22
|
+
<g id="body">
|
|
23
|
+
<path id="body-mass" fill="#b97f3c" d="M 299 223 C 260 242 245 300 262 355 C 280 404 332 422 401 404 C 454 412 483 370 466 327 C 452 289 415 272 391 244 Z"/>
|
|
24
|
+
<path id="shoulder-light" fill="#dba04f" d="M 363 236 C 399 243 431 280 437 317 C 413 300 395 291 373 301 L 331 270 Z"/>
|
|
25
|
+
<path id="belly" fill="#f4dcb0" d="M 290 220 C 270 262 269 310 288 350 C 301 378 331 392 361 383 C 387 375 395 342 385 308 C 375 274 368 244 356 219 Z"/>
|
|
26
|
+
<path id="belly-bands" fill="none" stroke="#b89159" stroke-width="5" d="M 281 254 Q 325 277 370 252 M 277 285 Q 323 309 379 281 M 280 319 Q 328 340 386 314 M 295 350 Q 337 371 382 345"/>
|
|
27
|
+
</g>
|
|
28
|
+
<g id="front-feet">
|
|
29
|
+
<path id="far-front-leg" fill="#a76e34" d="M 274 296 C 248 300 241 341 239 378 C 224 393 212 412 222 432 C 235 447 276 446 289 434 C 299 421 287 400 290 379 L 306 332 Z"/>
|
|
30
|
+
<path id="far-front-toes" fill="#efd6a5" d="M 220 424 C 218 411 231 404 238 421 C 240 400 253 402 258 422 C 266 403 278 411 278 433 L 225 435 Z"/>
|
|
31
|
+
<path id="near-front-leg" fill="#cf9247" d="M 385 289 C 358 291 352 321 356 351 C 363 376 349 402 338 414 C 325 433 335 451 357 454 L 403 454 C 423 448 428 434 417 416 C 407 398 421 369 423 343 C 425 315 411 292 385 289 Z"/>
|
|
32
|
+
<path id="near-front-toes" fill="#ffe4ae" d="M 339 441 C 337 423 349 417 357 438 C 359 412 374 412 380 438 C 387 417 401 418 405 442 C 409 431 416 432 417 445 Q 380 458 339 449 Z"/>
|
|
33
|
+
<path id="toe-cuts" fill="none" stroke="#080d19" stroke-width="4" d="M 357 438 L 358 451 M 380 438 L 381 452 M 405 442 L 405 448"/>
|
|
34
|
+
</g>
|
|
35
|
+
<g id="head">
|
|
36
|
+
<path id="far-horn" fill="#e7cf9a" d="M 283 124 C 254 101 243 76 246 53 C 262 70 282 80 304 89 Z"/>
|
|
37
|
+
<path id="neck" fill="#c98c43" d="M 290 173 C 263 209 265 247 280 264 C 302 280 356 278 380 254 C 390 233 379 201 369 174 Z"/>
|
|
38
|
+
<path id="throat" fill="#f8deb0" d="M 284 188 C 271 212 277 242 293 254 C 314 263 344 252 353 232 L 351 192 Z"/>
|
|
39
|
+
<path id="head-mass" fill="#d39b50" d="M 271 113 C 278 82 318 73 353 91 C 381 105 391 131 386 158 C 385 188 366 212 339 220 C 306 229 273 210 262 186 C 251 162 257 137 271 113 Z"/>
|
|
40
|
+
<path id="near-horn" fill="#ffe7b7" d="M 350 112 C 360 86 383 76 402 69 C 425 62 436 49 446 33 C 449 64 428 91 405 106 C 385 119 367 123 350 112 Z"/>
|
|
41
|
+
<path id="crest" fill="#efc57e" d="M 291 98 L 298 64 L 315 86 L 335 55 L 342 87 L 361 70 L 365 102 Z"/>
|
|
42
|
+
<path id="cheek-fin" fill="#e9b86b" d="M 372 148 Q 403 128 421 155 Q 399 154 384 170 Q 411 172 414 192 Q 393 182 377 188 Z"/>
|
|
43
|
+
<path id="muzzle" fill="#efd09a" d="M 261 166 C 241 166 235 180 242 195 C 250 211 283 216 310 214 C 340 215 360 201 358 189 C 338 196 318 193 304 181 C 289 169 273 166 261 166 Z"/>
|
|
44
|
+
<path id="nostril" fill="#080d19" d="M 256 180 C 256 173 267 173 268 179 C 269 185 257 186 256 180 Z"/>
|
|
45
|
+
<path id="mouth-line" fill="none" stroke="#080d19" stroke-width="4" d="M 252 199 Q 299 219 347 196"/>
|
|
46
|
+
</g>
|
|
47
|
+
<g id="eyes">
|
|
48
|
+
<path id="far-eye-socket" fill="#080d19" d="M 271 139 C 271 121 282 112 293 122 C 303 131 302 150 295 161 C 280 173 270 158 271 139 Z"/>
|
|
49
|
+
<path id="far-eye-white" fill="#f8faff" d="M 276 140 C 276 125 284 119 291 127 C 298 136 297 150 291 157 C 281 163 275 153 276 140 Z"/>
|
|
50
|
+
<path id="far-pupil" fill="#080d19" d="M 283 143 C 283 133 293 133 293 143 C 293 153 283 153 283 143 Z"/>
|
|
51
|
+
<path id="near-eye-socket" fill="#080d19" d="M 314 144 C 314 121 334 109 351 121 C 368 134 365 157 353 172 C 335 185 314 171 314 144 Z"/>
|
|
52
|
+
<path id="near-eye-white" fill="#f8faff" d="M 320 144 C 320 126 334 117 347 128 C 360 139 356 157 348 165 C 335 175 320 163 320 144 Z"/>
|
|
53
|
+
<path id="near-pupil" fill="#080d19" d="M 331 146 C 331 133 346 133 346 146 C 346 159 331 159 331 146 Z"/>
|
|
54
|
+
<path id="near-brow" fill="none" stroke="#f0bf71" stroke-width="5" d="M 315 116 Q 336 102 355 117"/>
|
|
55
|
+
</g>
|
|
56
|
+
</svg>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {renderSvgRig} from '../../../factory/svg-rig.mjs';
|
|
2
|
+
const clamp=x=>Math.max(0,Math.min(1,x));
|
|
3
|
+
const ease=x=>{x=clamp(x);return x*x*(3-2*x);};
|
|
4
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
5
|
+
export const HYPERION_CLIPS=Object.freeze([
|
|
6
|
+
{id:'idle',title:'Steady Company',duration:12000},
|
|
7
|
+
{id:'attach',title:'At Your Service',duration:3400},
|
|
8
|
+
{id:'curious',title:'Let Me Look',duration:2600},
|
|
9
|
+
{id:'focus',title:'Careful Work',duration:2800},
|
|
10
|
+
{id:'concerned',title:'Something Needs Care',duration:2600},
|
|
11
|
+
{id:'proud',title:'A Job Well Done',duration:2600},
|
|
12
|
+
{id:'standby',title:'Keeping Watch',duration:3000},
|
|
13
|
+
{id:'cluster',title:'Cluster Forge',duration:4800,power:true},
|
|
14
|
+
]);
|
|
15
|
+
export const STORY=[['attach',3400],['idle',1800],['curious',2200],['focus',2200],['cluster',4800],['proud',2200],['concerned',2200],['standby',2200],['idle',3000]];
|
|
16
|
+
export const STORY_DURATION=STORY.reduce((n,b)=>n+b[1],0);
|
|
17
|
+
const captions={idle:'Steady company.',attach:'I am here.',curious:'Let me take a closer look.',focus:'One careful step at a time.',concerned:'This needs attention.',proud:'A satisfying result · expression demo',standby:'Keeping watch.',cluster:'Cluster Forge · visual demonstration'};
|
|
18
|
+
export function bodyPose(ms){const t=ms%12000;return {wing:.018*pulse(t,2200,5400),far:.012*pulse(t,2600,5800),tail:.65*pulse(t,5500,9500),nod:2*pulse(t,800,3700),turn:.012*pulse(t,800,3700)};}
|
|
19
|
+
function performance(scene,t){
|
|
20
|
+
const base={gx:0,open:t%6000>4500&&t%6000<4680?.18:1};
|
|
21
|
+
switch(scene){
|
|
22
|
+
case 'curious':return {gx:-.65*ease(t/700),open:1.12};
|
|
23
|
+
case 'focus':return {gx:.6,open:.65};
|
|
24
|
+
case 'concerned':return {gx:.45*Math.sin(t/600),open:.85};
|
|
25
|
+
case 'proud':return {gx:0,open:1.15};
|
|
26
|
+
case 'standby':return {gx:0,open:.8};
|
|
27
|
+
default:return base;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const rot=(x,y,cx,cy,a)=>[cx+(x-cx)*Math.cos(a)-(y-cy)*Math.sin(a),cy+(x-cx)*Math.sin(a)+(y-cy)*Math.cos(a)];
|
|
31
|
+
export function poseRig(rig,pose,face){
|
|
32
|
+
const paths=rig.paths.map(p=>({...p,polygons:p.polygons.map(poly=>poly.map(([x,y])=>{
|
|
33
|
+
if(p.id.endsWith('-eye-white')||p.id.endsWith('-pupil')){
|
|
34
|
+
const cy=p.id.startsWith('far-')?141:145;y=cy+(y-cy)*face.open;
|
|
35
|
+
if(p.id.endsWith('-pupil'))x+=face.gx*5;
|
|
36
|
+
}
|
|
37
|
+
if(p.group==='near-wing')return rot(x,y,420,293,pose.wing);
|
|
38
|
+
if(p.group==='far-wing')return rot(x,y,294,294,-pose.far);
|
|
39
|
+
if(p.group==='tail'){const w=ease((x-490)/210);return [x-4*pose.tail*w,y-5*pose.tail*w];}
|
|
40
|
+
if(p.group==='head'||p.group==='eyes'){
|
|
41
|
+
const w=p.id==='neck'||p.id==='throat'?ease((275-y)/85):1;
|
|
42
|
+
const [rx,ry]=rot(x,y,330,255,pose.turn*w);return [rx,ry+pose.nod*w];
|
|
43
|
+
}
|
|
44
|
+
return [x,y];
|
|
45
|
+
}))}));
|
|
46
|
+
const byId=new Map(paths.map(p=>[p.id,p]));
|
|
47
|
+
return {...rig,paths,eyes:rig.eyes.map(e=>({...e,socket:byId.get(e.socket.id),white:byId.get(e.white.id),pupil:byId.get(e.pupil.id)}))};
|
|
48
|
+
}
|
|
49
|
+
function cluster(frame,t,mono){
|
|
50
|
+
// Ordered points in the empty gap above the right shoulder, never over ink.
|
|
51
|
+
const rows=frame.length,cols=frame[0].length;
|
|
52
|
+
const nodes=[[.51,.22],[.57,.12],[.64,.22]],progress=ease(t/3300),fade=1-ease((t-3900)/900);let count=0;
|
|
53
|
+
const put=(x,y,bright=false)=>{x=Math.round(x*cols);y=Math.round(y*rows);if(x<1||x>=cols-1||y<1||y>=rows-1||frame[y][x].ch!==' '||fade<.05)return;frame[y][x]={ch:bright?'⠿':'⠂',bg:null,rgb:mono?null:(bright?[246,247,255]:[104,211,229]).map(v=>Math.round(v*fade))};count++;};
|
|
54
|
+
for(let i=0;i<3;i++)if(progress>(i+1)/8)put(...nodes[i],true);
|
|
55
|
+
for(let e=0;e<3;e++){const a=nodes[e],b=nodes[(e+1)%3];for(let j=1;j<8;j++)if(progress>.45+(e+j/8)*.16)put(a[0]+(b[0]-a[0])*j/8,a[1]+(b[1]-a[1])*j/8);}
|
|
56
|
+
return count;
|
|
57
|
+
}
|
|
58
|
+
export function renderHyperion(rig,scene='idle',ms=0,options={}){
|
|
59
|
+
const clip=HYPERION_CLIPS.find(c=>c.id===scene);
|
|
60
|
+
if(!clip)return {ok:false,code:'UNKNOWN_CLIP'};
|
|
61
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
62
|
+
const t=options.still?(clip.power?clip.duration*.65:0):ms;
|
|
63
|
+
let face=performance(scene,t),pose=bodyPose(options.motionTime??t);
|
|
64
|
+
if(scene==='attach'){
|
|
65
|
+
const duration=options.deck?4400:3400;
|
|
66
|
+
if(t>=duration){pose=bodyPose(t-duration);face=performance('idle',t-duration);}
|
|
67
|
+
else{const u=t/duration;pose={wing:.025*pulse(u,.15,.85),far:.018*pulse(u,.22,.92),tail:.55*pulse(u,.25,1),nod:4*pulse(u,.35,.9),turn:.016*pulse(u,0,.8)};face={gx:.5*pulse(u,0,.85),open:1+.1*pulse(u,.1,.85)};}
|
|
68
|
+
}
|
|
69
|
+
if(scene==='proud')pose={...pose,nod:pose.nod-2*pulse(t,0,2600)};
|
|
70
|
+
if(options.still)pose={wing:0,far:0,tail:0,nod:0,turn:0};
|
|
71
|
+
const posed=poseRig(rig,pose,face),r=renderSvgRig(posed,options);
|
|
72
|
+
const effectCells=clip.power?cluster(r.frame,t,options.mono):0;
|
|
73
|
+
return {...r,scene,clip:clip.title,caption:captions[scene],pose,face,effectCells,staticStudy:false};
|
|
74
|
+
}
|
|
75
|
+
export function renderStory(rig,ms,options={}){
|
|
76
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
77
|
+
let t=options.still?0:Math.min(ms,STORY_DURATION-1);
|
|
78
|
+
for(const [scene,duration]of STORY){if(t<duration)return renderHyperion(rig,scene,t,{...options,deck:false,motionTime:Math.max(0,ms-3400)});t-=duration;}
|
|
79
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import {parseSvgRig} from '../../../factory/svg-rig.mjs';
|
|
4
|
+
import {renderHyperion,HYPERION_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('Hyperion V2 acting 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(HYPERION_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):slot.cols)),rows=Number(value('--rows',large?Math.min(36,h-4):slot.rows));
|
|
19
|
+
const message=value('--message','HYPERION\nSteady company.');
|
|
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('../v1/hyperion-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=renderHyperion(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
|
+
console.log((large?[...cellsToAnsi(r.frame),...composeMessage(null,message,{terminalCols:w})]:composeMessage(r.frame,message,{terminalCols:w})).join('\n'));
|
|
33
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {performance} from 'node:perf_hooks';
|
|
2
|
+
import {HYPERION_CLIPS,renderHyperion,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?HYPERION_CLIPS.find(c=>c.id===scene):null;
|
|
11
|
+
const duration=(config.arrival&&options.deck?4400: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?renderHyperion(paths,scene,t,options):renderStory(paths,t,options);
|
|
16
|
+
if(!r?.ok)throw new Error(r?.code??'Invalid story frame');
|
|
17
|
+
const caption=message??'HYPERION · '+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/12);
|
|
54
|
+
await done;
|
|
55
|
+
}catch(e){cleanup(e);await done;}
|
|
56
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Notice → acknowledge → settle. Pure authored timing; no visibility wipe. */
|
|
2
|
+
import {motionAt} from './motion.mjs';
|
|
3
|
+
export const ARRIVAL_DURATION=3200;
|
|
4
|
+
export const DECK_ARRIVAL_DURATION=4400;
|
|
5
|
+
const clamp=x=>Math.max(0,Math.min(1,x));
|
|
6
|
+
const ease=x=>{x=clamp(x);return x*x*(3-2*x);};
|
|
7
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
8
|
+
export function arrivalPose(ms,{deck=false}={}){
|
|
9
|
+
if(!Number.isFinite(ms)||ms<0)throw new RangeError('Arrival time must be nonnegative.');
|
|
10
|
+
const duration=deck?DECK_ARRIVAL_DURATION:ARRIVAL_DURATION;
|
|
11
|
+
if(ms>=duration)return {face:null,motion:motionAt(ms-duration),phase:'present',duration};
|
|
12
|
+
const t=ms/duration*ARRIVAL_DURATION,gain=deck?1.12:1;
|
|
13
|
+
const attention=pulse(t,100,2300),welcome=pulse(t,650,2750);
|
|
14
|
+
return {
|
|
15
|
+
phase:t<700?'notice':t<1800?'acknowledge':'settle',duration,
|
|
16
|
+
face:{gx:.65*ease(t/450)*(1-ease((t-1700)/1100)),gy:-.18*attention,
|
|
17
|
+
open:1+.15*welcome},
|
|
18
|
+
motion:{tail:gain*(.82*pulse(t,150,1850)-.32*pulse(t,1800,3050)),
|
|
19
|
+
wing:gain*.052*pulse(t,650,2650),farWing:gain*.032*pulse(t,850,2850),
|
|
20
|
+
neck:gain*.031*attention,nod:gain*(-2*attention+4*pulse(t,1400,2800))},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Lucid's pure acting layer: clip metadata, deterministic performances, story. */
|
|
2
|
+
import {arrivalPose,ARRIVAL_DURATION,DECK_ARRIVAL_DURATION} from './arrival.mjs';
|
|
3
|
+
import {motionAt} from './motion.mjs';
|
|
4
|
+
import {renderMaster} from './render.mjs';
|
|
5
|
+
const clamp=x=>Math.max(0,Math.min(1,x));
|
|
6
|
+
const ease=x=>{x=clamp(x);return x*x*(3-2*x);};
|
|
7
|
+
export const LUCID_CLIPS=Object.freeze([
|
|
8
|
+
{id:'attach',title:'I See You',duration:ARRIVAL_DURATION,message:'Hello. I am here.'},
|
|
9
|
+
{id:'deck-arrival',title:'Lucid Arrives',duration:DECK_ARRIVAL_DURATION,message:'LUCID · Awareness in the present'},
|
|
10
|
+
{id:'idle',title:'Here and Now',duration:12000,message:'Aware. Attentive. Present.'},
|
|
11
|
+
{id:'greeting',title:'Hello There',duration:1800,message:'Hello. What shall we look at?'},
|
|
12
|
+
{id:'curious',title:'A Closer Look',duration:2600,message:'That is interesting…'},
|
|
13
|
+
{id:'notice',title:'Something Changed',duration:1800,message:'I noticed a change.'},
|
|
14
|
+
{id:'focus',title:'Hold That Thought',duration:2200,message:'Looking closely.'},
|
|
15
|
+
{id:'uncertain',title:'Missing Piece',duration:2200,message:'There is a part I cannot see yet.'},
|
|
16
|
+
{id:'delighted',title:'Oh, There It Is',duration:1800,message:'Oh! There it is.'},
|
|
17
|
+
{id:'standby',title:'Quiet Company',duration:3000,message:'I am here.'},
|
|
18
|
+
{id:'sweep',title:'Horizon Sweep',duration:3200,message:'Horizon Sweep · visual demo',power:true},
|
|
19
|
+
{id:'threadlight',title:'Threadlight',duration:3600,message:'Threadlight · visual demo',power:true},
|
|
20
|
+
{id:'constellation',title:'Living Constellation',duration:4200,message:'Living Constellation · visual demo',power:true},
|
|
21
|
+
]);
|
|
22
|
+
export const LUCID_STORY=Object.freeze([
|
|
23
|
+
{scene:'greeting',duration:1800},{scene:'idle',duration:2400},
|
|
24
|
+
{scene:'curious',duration:2200},{scene:'notice',duration:1500},
|
|
25
|
+
{scene:'sweep',duration:3200},{scene:'focus',duration:1600},
|
|
26
|
+
{scene:'threadlight',duration:3600},{scene:'uncertain',duration:1800},
|
|
27
|
+
{scene:'constellation',duration:4200},{scene:'delighted',duration:1800},
|
|
28
|
+
{scene:'idle',duration:2000},
|
|
29
|
+
]);
|
|
30
|
+
export const STORY_DURATION=LUCID_STORY.reduce((n,s)=>n+s.duration,0);
|
|
31
|
+
export const LUCID_MANIFEST=Object.freeze({id:'lucid',displayName:'Lucid',
|
|
32
|
+
role:'Attentive observer of the living present',defaultClip:'idle',
|
|
33
|
+
clips:LUCID_CLIPS,story:LUCID_STORY,
|
|
34
|
+
powers:LUCID_CLIPS.filter(c=>c.power).map(c=>({name:c.title,scene:c.id,visualOnly:true})),
|
|
35
|
+
notes:'SVG recovery rig. White round eyes, black centre pupil. Not yet registered in the shared Character Pack.'});
|
|
36
|
+
|
|
37
|
+
export function eyePerformance(scene,t){
|
|
38
|
+
const blink=t%4800;
|
|
39
|
+
const base={gx:0,gy:0,open:blink>3600&&blink<3800?.12:1};
|
|
40
|
+
switch(scene){
|
|
41
|
+
case 'attach':case 'deck-arrival':return {gx:0,gy:0,open:1};
|
|
42
|
+
case 'idle':return base;
|
|
43
|
+
case 'greeting':return {gx:0,gy:0,open:t>700&&t<900?.12:1.12};
|
|
44
|
+
case 'curious':return {gx:-.85*ease(t/650),gy:-.3,open:1.18};
|
|
45
|
+
case 'notice':return {gx:ease((t-120)/220),gy:-.4,open:1.2};
|
|
46
|
+
case 'focus':return {gx:.9,gy:0,open:.65};
|
|
47
|
+
case 'uncertain':return {gx:t<1100?-.7:.65,gy:.45,open:.9};
|
|
48
|
+
case 'delighted':return {gx:0,gy:0,open:t>1200&&t<1360?.2:1.2};
|
|
49
|
+
case 'standby':return {...base,open:blink>2500?.2:.85};
|
|
50
|
+
case 'sweep':return {gx:-1+2*ease(t/2200),gy:-.3,open:1};
|
|
51
|
+
case 'threadlight':return {gx:.9,gy:-.3,open:1};
|
|
52
|
+
case 'constellation':return {gx:.8,gy:-.6,open:1.15};
|
|
53
|
+
default:throw new RangeError('Unknown expression');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function powerOverlay(frame,scene,t,duration,mono){
|
|
58
|
+
const rows=frame.length,cols=frame[0].length;
|
|
59
|
+
const fade=ease(t/450)*(1-ease((t-duration+650)/650));
|
|
60
|
+
if(fade<.08)return 0;
|
|
61
|
+
let count=0;
|
|
62
|
+
const point=(x,y,strong=false)=>{
|
|
63
|
+
x=Math.round(x);y=Math.round(y);
|
|
64
|
+
if(x<1||y<1||x>=cols-1||y>=rows-1||frame[y][x].ch!==' ')return;
|
|
65
|
+
frame[y][x]={ch:strong?'⠿':'⠂',rgb:mono?null:(strong?[232,245,255]:[94,205,230]).map(v=>Math.round(v*fade)),bg:null};count++;
|
|
66
|
+
};
|
|
67
|
+
// Deliberately use only the empty sky to the right of the sail.
|
|
68
|
+
const x0=cols*.74,x1=cols-.06*cols,y0=rows*.10,y1=rows*.43;
|
|
69
|
+
const nodes=[[x0,y1],[x0+(x1-x0)*.45,y0],[x1,y1*.85]];
|
|
70
|
+
if(scene==='sweep'){
|
|
71
|
+
const u=clamp(t/(duration-500));
|
|
72
|
+
for(let i=0;i<7;i++){const a=-1.2+i*.4;point(x0+u*(x1-x0)+Math.cos(a)*2,y0+(y1-y0)*.5+Math.sin(a)*(y1-y0)*.5,i===3);}
|
|
73
|
+
}else {
|
|
74
|
+
const amount=clamp((t-300)/(duration-1100));
|
|
75
|
+
for(let i=0;i<3;i++)if(amount>(i/5))point(...nodes[i],true);
|
|
76
|
+
for(let edge=0;edge<(scene==='constellation'?3:2);edge++){
|
|
77
|
+
const a=nodes[edge],b=nodes[(edge+1)%3];
|
|
78
|
+
for(let k=1;k<8;k++)if(amount>(edge+k/8)/3)
|
|
79
|
+
point(a[0]+(b[0]-a[0])*k/8,a[1]+(b[1]-a[1])*k/8);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return count;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function renderLucidCharacter(paths,scene='idle',ms=0,options={}){
|
|
86
|
+
const clip=LUCID_CLIPS.find(c=>c.id===scene);
|
|
87
|
+
if(!clip)return {ok:false,code:'UNKNOWN_CLIP'};
|
|
88
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
89
|
+
const arrival=scene==='attach'||scene==='deck-arrival';
|
|
90
|
+
const time=options.still?0:Math.min(ms,clip.duration);
|
|
91
|
+
const pose=arrival&&!options.still?arrivalPose(ms,{deck:scene==='deck-arrival'}):null;
|
|
92
|
+
const idleTime=pose?.phase==='present'?ms-pose.duration:ms;
|
|
93
|
+
const face=pose?.face??eyePerformance(pose?.phase==='present'?'idle':scene,options.still?0:arrival?idleTime:time);
|
|
94
|
+
const motionTime=options.motionTime??ms;
|
|
95
|
+
const motion=options.still?null:pose?.motion??motionAt(motionTime);
|
|
96
|
+
const result=renderMaster(paths,{...options,face,motion});
|
|
97
|
+
const effectCells=clip.power?powerOverlay(result.frame,scene,options.still?clip.duration/2:time,clip.duration,options.mono):0;
|
|
98
|
+
return {...result,ok:true,scene,clip:clip.title,time,duration:clip.duration,face,motion,
|
|
99
|
+
phase:pose?.phase??scene,caption:clip.message,visualOnly:true,demo:true,effectCells};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function renderLucidStory(paths,ms,options={}){
|
|
103
|
+
if(!Number.isFinite(ms)||ms<0)return {ok:false,code:'INVALID_TIME'};
|
|
104
|
+
let t=options.still?0:options.loop?ms%STORY_DURATION:Math.min(ms,STORY_DURATION-1);
|
|
105
|
+
for(const beat of LUCID_STORY){if(t<beat.duration)return renderLucidCharacter(paths,beat.scene,t,{...options,motionTime:ms});t-=beat.duration;}
|
|
106
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {cellsToAnsi} from '../../../vendor/quox-dragon/qart.js';
|
|
2
|
+
|
|
3
|
+
/** Half-width by half-height, minus margins: below a quarter of the screen. */
|
|
4
|
+
export function artSlot(terminalCols=80,terminalRows=24) {
|
|
5
|
+
if(![terminalCols,terminalRows].every(n=>Number.isSafeInteger(n)&&n>0))
|
|
6
|
+
throw new RangeError('Terminal dimensions must be positive integers.');
|
|
7
|
+
const cols=Math.min(54,Math.floor(terminalCols/2)-1);
|
|
8
|
+
const rows=Math.min(16,Math.floor(terminalRows/2)-1);
|
|
9
|
+
return {cols:Math.max(0,cols),rows:Math.max(0,rows),
|
|
10
|
+
showArt:cols>=28&&rows>=10,terminalCols,terminalRows};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Terminal cell widths for ordinary message text, including CJK and emoji.
|
|
14
|
+
// Never pass message control sequences through to the terminal.
|
|
15
|
+
export function cleanText(text) {
|
|
16
|
+
return String(text).replace(/\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|$))/g,'')
|
|
17
|
+
.replace(/[\x00-\x09\x0b-\x1f\x7f-\x9f]/g,'');
|
|
18
|
+
}
|
|
19
|
+
const segmenter=new Intl.Segmenter(undefined,{granularity:'grapheme'});
|
|
20
|
+
const width=g=>/\p{Extended_Pictographic}|[\u1100-\u115f\u2329\u232a\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe10-\ufe6f\uff01-\uff60\uffe0-\uffe6]/u.test(g)?2:1;
|
|
21
|
+
export function wrapMessage(text,cols) {
|
|
22
|
+
if(cols<1)return [];
|
|
23
|
+
const result=[];
|
|
24
|
+
for(const paragraph of cleanText(text).split('\n')) {
|
|
25
|
+
let line='',n=0;
|
|
26
|
+
for(const {segment} of segmenter.segment(paragraph)) {
|
|
27
|
+
const w=width(segment);
|
|
28
|
+
if(n+w>cols){result.push(line.trimEnd());line='';n=0;}
|
|
29
|
+
if(w>cols)continue;
|
|
30
|
+
if(!line&&segment===' ')continue;
|
|
31
|
+
line+=segment;n+=w;
|
|
32
|
+
}
|
|
33
|
+
result.push(line.trimEnd());
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function composeMessage(frame,message,{terminalCols=80}={}) {
|
|
39
|
+
const margin=1,gap=2,artCols=frame?.[0]?.length??0;
|
|
40
|
+
const available=terminalCols-margin*2-artCols-gap;
|
|
41
|
+
if(!frame||available<12) {
|
|
42
|
+
const text=wrapMessage(message,Math.max(1,terminalCols-margin*2)).map(l=>' '.repeat(Math.min(margin,terminalCols-1))+l);
|
|
43
|
+
return frame?[...cellsToAnsi(frame).map(l=>' '.repeat(margin)+l),...text]:text;
|
|
44
|
+
}
|
|
45
|
+
const text=wrapMessage(message,available),art=cellsToAnsi(frame);
|
|
46
|
+
// Messages keep all their content and flow below if taller than the art.
|
|
47
|
+
const offset=text.length<frame.length?Math.floor((frame.length-text.length)/2):0;
|
|
48
|
+
return Array.from({length:Math.max(frame.length,offset+text.length)},(_,i)=>
|
|
49
|
+
' '.repeat(margin)+(art[i]??' '.repeat(artCols))+' '.repeat(gap)+(text[i-offset]??''));
|
|
50
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 480" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">Lucid — the living present</title>
|
|
3
|
+
<desc id="desc">Full-body vector study: a poised left-facing watcher, swept horns, white eye, tall curved neck, resting haunches, articulated claws, sail wings and a spiral tail. Unapproved art study.</desc>
|
|
4
|
+
<g id="tail">
|
|
5
|
+
<path id="tail-mass" fill="#7453ce" d="M480 352 C533 360 569 405 633 405 C692 405 741 375 741 326 C741 298 721 283 701 285 C679 287 666 305 670 324 C673 341 688 348 702 341 C711 336 715 326 710 317 C723 326 719 347 704 354 C682 365 655 349 651 326 C645 293 668 267 700 266 C740 265 762 292 761 326 C759 390 705 431 638 433 C573 435 519 408 475 393 Z"/>
|
|
6
|
+
<path id="tail-light" fill="#49bdd3" d="M509 384 C565 413 607 428 652 422 C712 415 753 375 752 327 C752 297 731 277 706 278 C733 274 754 293 757 322 C763 378 718 425 655 432 C601 438 552 419 509 397 Z"/>
|
|
7
|
+
<path id="tail-coil-light" fill="#b09af2" d="M700 275 C673 275 655 297 659 323 C662 345 679 355 696 350 C682 350 669 339 667 323 C664 302 678 284 700 282 Z"/>
|
|
8
|
+
<path id="tail-tip" fill="#b5a2f7" d="M702 341 C712 337 714 327 710 317 C706 307 698 305 691 310 C697 299 709 301 715 312 C722 325 715 339 702 341 Z"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g id="far-wing">
|
|
11
|
+
<path id="far-sail" fill="#44357c" d="M314 339 C338 265 361 194 409 114 C413 186 444 231 484 249 C424 245 379 281 346 343 Z"/>
|
|
12
|
+
<path id="far-spar" fill="#9b80e6" d="M314 339 C341 245 371 175 409 114 C388 185 365 254 333 342 Z"/>
|
|
13
|
+
</g>
|
|
14
|
+
<g id="far-feet">
|
|
15
|
+
<path id="far-foreleg" fill="#4b397f" d="M270 330 C285 353 278 385 265 407 L215 418 L199 412 L210 399 L242 392 L245 350 Z"/>
|
|
16
|
+
<path id="far-hindleg" fill="#514080" d="M472 363 C490 378 488 394 473 410 L418 420 L404 414 L420 399 L450 391 Z"/>
|
|
17
|
+
<path id="far-claw-1" fill="#b1c9e6" d="M211 402 C201 408 197 417 197 425 L207 418 L219 408 Z"/>
|
|
18
|
+
<path id="far-claw-2" fill="#b1c9e6" d="M424 404 C414 412 411 420 412 427 L422 419 L434 408 Z"/>
|
|
19
|
+
</g>
|
|
20
|
+
<g id="body">
|
|
21
|
+
<path id="body-mass" fill="#7250bc" d="M239 291 C267 284 296 298 322 319 C359 342 393 319 441 320 C493 320 532 350 554 384 C532 401 490 408 453 402 C421 396 395 390 364 392 C321 402 283 399 252 378 C224 359 219 320 239 291 Z"/>
|
|
22
|
+
<path id="back-plane" fill="#a184e2" d="M288 311 C333 346 370 319 416 320 C469 310 504 333 530 358 C492 337 464 333 433 340 C389 351 365 359 332 346 Z"/>
|
|
23
|
+
<path id="haunch" fill="#8563ce" d="M439 344 C477 331 506 352 516 378 C511 395 485 404 456 400 C431 397 413 384 416 367 C418 356 427 349 439 344 Z"/>
|
|
24
|
+
<path id="haunch-shadow" fill="#4f3b88" d="M454 350 C434 354 427 368 438 380 C451 390 477 389 494 383 C478 399 450 401 433 389 C415 376 427 353 454 350 Z"/>
|
|
25
|
+
</g>
|
|
26
|
+
<g id="near-wing">
|
|
27
|
+
<path id="near-sail" fill="#456cba" d="M299 346 C342 291 371 230 418 170 C453 125 504 90 569 69 C535 106 518 144 516 181 C512 218 533 248 558 265 C519 256 488 259 462 274 C424 297 395 325 361 340 Z"/>
|
|
28
|
+
<path id="upper-membrane" fill="#618fd0" d="M328 325 C376 252 403 192 450 145 C480 116 514 95 552 82 C500 123 464 170 433 222 C403 269 367 303 328 325 Z"/>
|
|
29
|
+
<path id="lower-membrane" fill="#408aa9" d="M345 333 C405 299 457 273 514 252 C494 225 493 199 500 170 C457 225 414 285 345 333 Z"/>
|
|
30
|
+
<path id="wing-leading-edge" fill="#b79af1" d="M295 347 C343 282 367 224 415 165 C457 116 509 86 569 69 C519 93 469 126 428 174 C381 231 357 294 318 345 Z"/>
|
|
31
|
+
<path id="wing-finger-1" fill="#9fcde7" d="M316 340 C379 298 423 247 458 191 C487 144 521 105 561 75 C521 118 494 155 465 201 C431 258 385 308 330 339 Z"/>
|
|
32
|
+
<path id="wing-finger-2" fill="#8cc8e0" d="M330 340 C406 316 466 282 549 263 C478 289 420 324 350 342 Z"/>
|
|
33
|
+
<path id="wing-fold-shadow" fill="#383168" d="M317 343 C364 323 405 296 443 264 C411 303 384 331 360 341 Z"/>
|
|
34
|
+
</g>
|
|
35
|
+
<g id="neck">
|
|
36
|
+
<path id="neck-mass" fill="#7753c5" d="M217 166 C247 181 263 210 266 245 C269 282 254 310 265 339 C273 359 294 372 319 379 C291 397 256 394 229 377 C205 362 199 338 206 309 C214 280 226 256 222 231 C219 210 203 197 188 191 Z"/>
|
|
37
|
+
<path id="neck-shadow" fill="#4e3a89" d="M223 196 C251 216 257 245 248 274 C234 309 238 340 260 360 C247 355 229 337 230 316 C230 291 244 269 244 244 C244 223 235 209 223 196 Z"/>
|
|
38
|
+
<path id="throat" fill="#87bfd6" d="M194 189 C214 206 230 221 229 245 C229 272 211 302 212 327 C213 352 226 367 247 378 C227 366 218 345 224 321 C229 301 245 277 245 249 C245 221 230 203 210 190 Z"/>
|
|
39
|
+
<path id="chest-plane" fill="#91a9df" d="M214 317 C213 341 229 365 254 374 C272 381 289 382 307 378 C292 390 263 389 242 381 C221 372 206 349 208 329 Z"/>
|
|
40
|
+
<path id="throat-segments" fill="none" stroke="#5263a0" stroke-width="3" d="M218 226 Q229 229 240 225 M227 246 Q235 250 245 247 M224 266 Q234 270 241 269 M217 287 Q226 291 234 292 M212 309 Q220 313 227 313 M213 331 Q221 335 227 333 M220 352 Q230 353 236 350"/>
|
|
41
|
+
</g>
|
|
42
|
+
<g id="near-feet">
|
|
43
|
+
<path id="foreleg" fill="#9273d6" d="M247 337 C261 341 268 354 263 371 C258 385 243 394 225 402 L215 414 L200 414 L194 422 L182 417 L171 422 L161 416 C167 401 184 395 203 388 C218 382 224 371 226 354 Z"/>
|
|
44
|
+
<path id="foreleg-plane" fill="#b29de9" d="M246 346 C251 362 245 378 227 388 L193 402 L179 414 L169 413 C176 403 188 400 204 392 C227 381 234 368 233 355 Z"/>
|
|
45
|
+
<path id="hindfoot" fill="#9476d5" d="M472 379 C480 388 477 398 463 403 L430 410 L415 425 L401 423 L395 430 L383 424 L371 429 L362 421 C369 404 386 397 408 395 L432 386 Z"/>
|
|
46
|
+
<path id="hindfoot-plane" fill="#ae95e1" d="M449 392 L411 403 C392 404 379 414 372 424 L364 420 C372 405 389 400 407 398 Z"/>
|
|
47
|
+
<path id="fore-claw-1" fill="#d2e3ef" d="M169 412 C159 414 154 426 157 439 C161 429 165 426 175 423 Z"/>
|
|
48
|
+
<path id="fore-claw-2" fill="#d2e3ef" d="M188 414 C180 420 178 430 181 443 C185 433 189 428 196 425 Z"/>
|
|
49
|
+
<path id="fore-claw-3" fill="#d2e3ef" d="M209 409 C203 415 201 425 204 436 C208 426 212 422 218 420 Z"/>
|
|
50
|
+
<path id="hind-claw-1" fill="#c4d9eb" d="M372 420 C362 423 359 434 361 445 C366 436 370 433 378 431 Z"/>
|
|
51
|
+
<path id="hind-claw-2" fill="#c4d9eb" d="M392 423 C385 429 384 438 387 449 C391 440 395 436 402 433 Z"/>
|
|
52
|
+
<path id="hind-claw-3" fill="#c4d9eb" d="M413 418 C407 424 406 432 409 442 C414 433 418 430 424 427 Z"/>
|
|
53
|
+
</g>
|
|
54
|
+
<g id="horns">
|
|
55
|
+
<path id="far-horn" fill="#8ba1d7" d="M197 148 C222 114 256 87 302 57 C277 84 254 110 237 133 L218 159 Z"/>
|
|
56
|
+
<path id="far-horn-tip" fill="#c5dce9" d="M239 112 C259 91 278 75 302 57 C286 74 270 91 256 107 Z"/>
|
|
57
|
+
<path id="near-horn" fill="#a68be4" d="M165 151 C171 127 190 102 219 78 C239 62 261 45 281 28 C265 53 244 74 226 95 C205 118 196 139 194 157 Z"/>
|
|
58
|
+
<path id="near-horn-light" fill="#d1daf3" d="M174 143 C181 121 200 99 221 81 C242 63 263 46 281 28 C267 50 243 73 228 90 C204 116 191 138 188 153 Z"/>
|
|
59
|
+
<path id="horn-root" fill="#8461ce" d="M168 141 L184 129 L204 144 L220 133 L231 153 L214 174 L181 167 Z"/>
|
|
60
|
+
</g>
|
|
61
|
+
<g id="head">
|
|
62
|
+
<path id="head-mass" fill="#8963d4" d="M156.48 146.84 C172.16 130.2 197.92 130.2 218.08 141.72 L243.84 162.2 L227.04 180.12 L236 195.48 L211.36 199.32 C201.28 214.68 182.24 219.8 166.56 213.4 L136.32 212.12 L118.4 223.64 L91.52 219.8 L83.68 209.56 L103.84 185.24 L119.52 162.2 Z"/>
|
|
63
|
+
<path id="brow-plane" fill="#b797f0" d="M119.52 162.2 C143.04 153.24 158.72 139.16 181.12 139.16 C197.92 137.88 211.36 144.28 222.56 151.96 L204.64 155.8 L181.12 149.4 L158.72 166.04 L131.84 173.72 Z"/>
|
|
64
|
+
<path id="muzzle-plane" fill="#629fca" d="M103.84 185.24 L126.24 180.12 L137.44 187.8 L124 201.88 L96 209.56 L83.68 209.56 Z"/>
|
|
65
|
+
<path id="jaw-plane" fill="#b4a2e2" d="M92.64 212.12 L120.64 213.4 L140.8 200.6 L162.08 200.6 C175.52 212.12 193.44 209.56 208 200.6 C195.68 222.36 177.76 226.2 159.84 215.96 L138.56 214.68 L120.64 224.92 L93.76 221.08 Z"/>
|
|
66
|
+
<path id="cheek-plane" fill="#6846a8" d="M204.64 172.44 L225.92 181.4 L220.32 191.64 L202.4 195.48 L187.84 207 L175.52 200.6 Z"/>
|
|
67
|
+
<path id="ear-fin" fill="#a184e4" d="M209.12 153.24 C231.52 143 251.68 144.28 269.6 136.6 L247.2 162.2 L227.04 169.88 Z"/>
|
|
68
|
+
<path id="ear-fold" fill="#4d4286" d="M225.92 157.08 L255.04 145.56 L240.48 159.64 L225.92 164.76 Z"/>
|
|
69
|
+
<path id="cheek-fin" fill="#6caac9" d="M221.44 181.4 L258.4 185.24 L237.12 195.48 L220.32 192.92 Z"/>
|
|
70
|
+
<path id="nostril" fill="#24213c" d="M104.96 194.2 Q110.56 187.8 113.92 191.64 Q110.56 198.04 104.96 198.04 Z"/>
|
|
71
|
+
<path id="mouth-line" fill="none" stroke="#312542" stroke-width="3" d="M96 212.12 Q118.4 218.52 138.56 204.44 Q150.88 200.6 163.2 204.44"/>
|
|
72
|
+
</g>
|
|
73
|
+
<g id="eye" data-cell-owner="eye">
|
|
74
|
+
<path id="eye-socket" fill="#29213d" d="M146 175 C146 158 157 149 178 149 C199 149 211 159 211 176 C211 195 198 205 178 205 C158 205 146 194 146 175 Z"/>
|
|
75
|
+
<path id="iris" fill="#f2f6ff" d="M149 175 C149 160 160 152 178 152 C196 152 208 161 208 176 C208 192 196 202 178 202 C161 202 149 192 149 175 Z"/>
|
|
76
|
+
<path id="pupil" fill="#111520" d="M186 177 C186 182 182 185 178 185 C173 185 170 182 170 177 C170 172 173 169 178 169 C182 169 186 172 186 177 Z"/>
|
|
77
|
+
</g>
|
|
78
|
+
</svg>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Small joint movements over a twelve-second cycle, with deliberate rests. */
|
|
2
|
+
const clamp=x=>Math.max(0,Math.min(1,x));
|
|
3
|
+
const smooth=x=>{x=clamp(x);return x*x*(3-2*x);};
|
|
4
|
+
const pulse=(t,a,b)=>t<=a||t>=b?0:Math.sin(Math.PI*(t-a)/(b-a))**2;
|
|
5
|
+
export const MOTION_PERIOD=12000;
|
|
6
|
+
export function motionAt(ms){
|
|
7
|
+
if(!Number.isFinite(ms)||ms<0)throw new RangeError('Motion time must be nonnegative.');
|
|
8
|
+
const t=ms%MOTION_PERIOD;
|
|
9
|
+
return {
|
|
10
|
+
tail:pulse(t,600,4200)-.45*pulse(t,4500,6200)+.22*pulse(t,6500,8000),
|
|
11
|
+
wing:.045*pulse(t,2600,4400)-.010*pulse(t,4400,5100)+.025*pulse(t,9200,10800),
|
|
12
|
+
farWing:.026*pulse(t,2800,4600)-.006*pulse(t,4600,5300)+.016*pulse(t,9400,11000),
|
|
13
|
+
neck:.032*pulse(t,1100,4700)-.014*pulse(t,7200,9900),
|
|
14
|
+
nod:3*pulse(t,1300,4900)+2*pulse(t,7400,10100),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const rotate=(x,y,cx,cy,a)=>[
|
|
18
|
+
cx+(x-cx)*Math.cos(a)-(y-cy)*Math.sin(a),
|
|
19
|
+
cy+(x-cx)*Math.sin(a)+(y-cy)*Math.cos(a),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/** Deform authored paths together, never append procedural anatomy. */
|
|
23
|
+
export function movePoint(group,x,y,pose){
|
|
24
|
+
if(!pose)return [x,y];
|
|
25
|
+
if(group==='tail'){
|
|
26
|
+
// Root is pinned; only the distal tail and spiral tighten/open/recoil.
|
|
27
|
+
const w=smooth((x-515)/145),curl=pose.tail*w;
|
|
28
|
+
const [rx,ry]=rotate(x,y,700,325,curl*.15);
|
|
29
|
+
const scale=1-.14*curl;
|
|
30
|
+
return [700+(rx-700)*scale-8*Math.abs(curl),325+(ry-325)*scale-10*curl];
|
|
31
|
+
}
|
|
32
|
+
if(group==='near-wing')return rotate(x,y,310,341,pose.wing);
|
|
33
|
+
if(group==='far-wing')return rotate(x,y,330,340,pose.farWing);
|
|
34
|
+
if(['neck','head','horns','eye'].includes(group)){
|
|
35
|
+
// The neck smoothly blends to its anchored chest. Face, eye and horns
|
|
36
|
+
// share the upper joint transform, so the eye never slides off the head.
|
|
37
|
+
const w=group==='neck'?smooth((330-y)/130):1;
|
|
38
|
+
const [rx,ry]=rotate(x,y,235,300,pose.neck*w);
|
|
39
|
+
return [rx,ry+pose.nod*w];
|
|
40
|
+
}
|
|
41
|
+
return [x,y];
|
|
42
|
+
}
|