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
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LucidDeck: the full-screen live LUCID situation deck (`quox lucid deck`).
|
|
2
|
+
* LucidDeck: the full-screen live LUCID situation deck (`quox lucid deck`), in the Quoxon look.
|
|
3
3
|
*
|
|
4
4
|
* One vertical cursor. Up/Down (j/k) move through the focused pane's list,
|
|
5
5
|
* Tab / Shift-Tab switch pane (Needs you, Since you looked, Areas), Enter
|
|
6
6
|
* opens / acts, Esc backs out, ? is help, q quits. Every action echoes what it
|
|
7
7
|
* did and why in a toast; keys that do nothing say so.
|
|
8
8
|
*
|
|
9
|
+
* Look (Quoxon design system): theme roles for every colour, headings in the sky to violet gradient, the
|
|
10
|
+
* perpetual Q as the status glyph (needs you = waiting, fetching = working, unreachable = failing, quiet =
|
|
11
|
+
* resting), areas and Needs-you items as inline object cards (lucid/cards.js) that drop in through katakana.
|
|
12
|
+
* Motion: ONE shared clock (quoxon/motion/clock.js). Frames are demanded only while something moves; idle
|
|
13
|
+
* the deck has no animation timer and writes nothing (the only recurring work is the fetch interval).
|
|
14
|
+
*
|
|
9
15
|
* Props are injected (api, intervalMs, intro) so the component is testable
|
|
10
16
|
* without a network. api = { situation(), measure(ref, id), teach(body), ack(seq) }.
|
|
11
17
|
* Never shows an empty all-quiet view on failure: errors render in the header
|
|
@@ -13,21 +19,22 @@
|
|
|
13
19
|
*/
|
|
14
20
|
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
15
21
|
import { Box, Text, useInput, useApp, useStdout } from 'ink';
|
|
16
|
-
import {
|
|
17
|
-
GLYPH, STATE_HEX, URGENCY, PALETTE, SPINNER, wrap, clip, ageOf, meter, clockLondonSec,
|
|
18
|
-
whenText, changedWords, blindSpots,
|
|
19
|
-
} from '../../lucid/fmt.js';
|
|
22
|
+
import { wrap, clip, ageOf, meter, clockLondon, whenText, changedWords, blindSpots } from '../../lucid/fmt.js';
|
|
20
23
|
import { LucidIntro } from '../../lucid/LucidIntro.jsx';
|
|
24
|
+
import { LucidCard } from '../../lucid/LucidCard.jsx';
|
|
25
|
+
import { lucidArea, lucidNeed, cardHeight } from '../../lucid/cards.js';
|
|
26
|
+
import { glyphStateOf, glyphAnimating, revealT, deckDemands, nextMinuteAt, SETTLE_MS, REVEAL_MS, STAGGER_MS } from '../../lucid/motion.js';
|
|
27
|
+
import { QGlyph } from '../../quoxon/qmotion.js';
|
|
28
|
+
import { c, gradient, mono } from '../../quoxon/theme.js';
|
|
29
|
+
import { Clock, motionAllowed } from '../../quoxon/motion/clock.js';
|
|
21
30
|
|
|
22
31
|
const PANES = ['needs', 'changed', 'areas'];
|
|
23
32
|
const PANE_TITLE = { needs: 'NEEDS YOU', changed: 'SINCE YOU LOOKED', areas: 'AREAS' };
|
|
24
33
|
const PANE_WORD = { needs: 'Needs you', changed: 'Since you looked', areas: 'Areas' };
|
|
25
34
|
const BAR_BG = '#1E3A8A'; // selected row bar
|
|
26
|
-
const BAR_BG_DIM = '#27272A'; // selected row in an unfocused pane
|
|
27
35
|
const NEW_MS = 4000;
|
|
28
36
|
const GHOST_MS = 4000;
|
|
29
37
|
const TOAST_MS = 3000;
|
|
30
|
-
const GRAD = ['#38BDF8', '#6366F1', '#8B5CF6', '#C084FC', '#8B5CF6'];
|
|
31
38
|
|
|
32
39
|
const pad = (s, w) => { const a = [...String(s)]; return a.length >= w ? a.join('') : s + ' '.repeat(w - a.length); };
|
|
33
40
|
const nm = (x) => (typeof x === 'string' ? x : (x && (x.label || x.name || x.L0 || x.id)) || '');
|
|
@@ -44,9 +51,8 @@ export function itemsLines(it, expanded, w, base = 1) {
|
|
|
44
51
|
return n + 1;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
/** Window
|
|
48
|
-
export function
|
|
49
|
-
const heights = items.map((it) => itemsLines(it, it.ref === expandedRef, w, base ? base(it) : 1));
|
|
54
|
+
/** Window over items of known line heights so the selected one is visible within `rows` lines. */
|
|
55
|
+
export function windowHeights(heights, sel, rows) {
|
|
50
56
|
let start = 0;
|
|
51
57
|
const fits = (s) => {
|
|
52
58
|
let used = 0;
|
|
@@ -55,26 +61,29 @@ export function windowItems(items, sel, expandedRef, rows, w, base) {
|
|
|
55
61
|
};
|
|
56
62
|
while (start < sel && !fits(start)) start++;
|
|
57
63
|
let used = 0; let end = start;
|
|
58
|
-
while (end <
|
|
59
|
-
if (end === start &&
|
|
64
|
+
while (end < heights.length && used + heights[end] <= rows) { used += heights[end]; end++; }
|
|
65
|
+
if (end === start && heights.length) end = start + 1;
|
|
60
66
|
return { start, end };
|
|
61
67
|
}
|
|
62
68
|
|
|
69
|
+
/** Window of items to render so the selected one is visible within `rows` lines. */
|
|
70
|
+
export function windowItems(items, sel, expandedRef, rows, w, base) {
|
|
71
|
+
const heights = items.map((it) => itemsLines(it, it.ref === expandedRef, w, base ? base(it) : 1));
|
|
72
|
+
return windowHeights(heights, sel, rows);
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
/** Window by rendered line heights; when the list overflows, 2 lines are kept for the "N more" markers. */
|
|
76
|
+
export function fitHeights(heights, sel, rows) {
|
|
77
|
+
let win = windowHeights(heights, sel, rows);
|
|
78
|
+
if (win.start > 0 || win.end < heights.length) win = windowHeights(heights, sel, Math.max(1, rows - 2));
|
|
79
|
+
return win;
|
|
80
|
+
}
|
|
64
81
|
export function fitWindow(items, sel, expandedRef, rows, w, base) {
|
|
65
82
|
let win = windowItems(items, sel, expandedRef, rows, w, base);
|
|
66
83
|
if (win.start > 0 || win.end < items.length) win = windowItems(items, sel, expandedRef, Math.max(1, rows - 2), w, base);
|
|
67
84
|
return win;
|
|
68
85
|
}
|
|
69
86
|
|
|
70
|
-
/** Simple window for single-line lists. */
|
|
71
|
-
function winLines(n, sel, rows) {
|
|
72
|
-
rows = Math.max(1, rows);
|
|
73
|
-
if (n <= rows) return { start: 0, end: n };
|
|
74
|
-
let start = Math.max(0, Math.min(sel - Math.floor(rows / 2), n - rows));
|
|
75
|
-
return { start, end: start + rows };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
87
|
export function signature(sit) {
|
|
79
88
|
if (!sit) return '';
|
|
80
89
|
return JSON.stringify([
|
|
@@ -84,56 +93,51 @@ export function signature(sit) {
|
|
|
84
93
|
]);
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const label = ` ${u.label} `;
|
|
91
|
-
return hot
|
|
92
|
-
? <Text backgroundColor={u.hex} color="#0B1220" bold>{pad(label, 11)}</Text>
|
|
93
|
-
: <Text color={u.hex}>{pad(label, 11)}</Text>;
|
|
96
|
+
/** A heading in the Quoxon gradient (plain muted under NO_COLOR). */
|
|
97
|
+
function Grad({ text, bold = true }) {
|
|
98
|
+
return <>{gradient(text).map((g, i) => <Text key={i} bold={bold} color={g.color}>{g.ch}</Text>)}</>;
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
function Hints({ items
|
|
101
|
+
function Hints({ items }) {
|
|
97
102
|
return (
|
|
98
|
-
<Text wrap="truncate" color={
|
|
103
|
+
<Text wrap="truncate" color={c('muted')}>
|
|
99
104
|
{items.map(([k, l], i) => (
|
|
100
|
-
<Text key={k + l}>{i ? ' ' : ''}<Text bold color={
|
|
105
|
+
<Text key={k + l}>{i ? ' ' : ''}<Text bold color={c('key')}>{k}</Text><Text color={c('muted')}> {l}</Text></Text>
|
|
101
106
|
))}
|
|
102
107
|
</Text>
|
|
103
108
|
);
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
/** A pane title rule: gradient heading when focused, quiet when not, a rule running to the pane's edge. */
|
|
112
|
+
function PaneTitle({ title, count, focused, width, note }) {
|
|
113
|
+
const head = ` ${title} `;
|
|
114
|
+
const tail = `${count === '' ? '' : ` ${count}`}${note ? ` ${note}` : ''} `;
|
|
115
|
+
const fill = Math.max(0, width - 3 - [...head].length - [...tail].length);
|
|
107
116
|
return (
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
{note ? <Text color={PALETTE.dim}> {note}</Text> : null}
|
|
116
|
-
</Box>
|
|
117
|
-
{children}
|
|
118
|
-
</Box>
|
|
117
|
+
<Text wrap="truncate">
|
|
118
|
+
<Text color={focused ? c('sky') : c('faint')}>{focused ? '▶' : '─'}</Text>
|
|
119
|
+
<Text color={focused ? c('sky') : c('faint')}>─</Text>
|
|
120
|
+
{focused ? <Grad text={head} /> : <Text color={c('muted')} bold>{head}</Text>}
|
|
121
|
+
<Text color={focused ? c('value') : c('muted')}>{tail}</Text>
|
|
122
|
+
<Text color={focused ? c('sky') : c('faint')}>{'─'.repeat(fill)}</Text>
|
|
123
|
+
</Text>
|
|
119
124
|
);
|
|
120
125
|
}
|
|
121
126
|
|
|
122
|
-
export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onExit, intro = false }) {
|
|
127
|
+
export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onExit, intro = false, motion }) {
|
|
123
128
|
const { exit } = useApp();
|
|
124
129
|
const { stdout } = useStdout();
|
|
125
130
|
const [, setSizeTick] = useState(0);
|
|
126
131
|
const cols = (stdout && stdout.columns) || 100;
|
|
127
132
|
const rows = (stdout && stdout.rows) || 30;
|
|
133
|
+
const motionOn = useMemo(() => (motion != null ? Boolean(motion) : motionAllowed(stdout && stdout.isTTY)), [motion, stdout]);
|
|
128
134
|
|
|
129
135
|
const [phase, setPhase] = useState(intro ? 'intro' : 'deck');
|
|
130
|
-
const [slide, setSlide] = useState(intro ? 3 : 0);
|
|
131
136
|
const [data, setData] = useState(null); // last good response
|
|
132
137
|
const [err, setErr] = useState(null);
|
|
133
138
|
const [fetchedAt, setFetchedAt] = useState(null);
|
|
134
139
|
const [fetching, setFetching] = useState(false);
|
|
135
|
-
const [
|
|
136
|
-
const [spin, setSpin] = useState(0);
|
|
140
|
+
const [, setFrame] = useState(0); // bumped by the shared clock
|
|
137
141
|
const [pane, setPane] = useState('needs');
|
|
138
142
|
const [filter, setFilter] = useState(null); // area key or null
|
|
139
143
|
const [sel, setSel] = useState(0);
|
|
@@ -151,6 +155,10 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
151
155
|
const newAt = useRef(new Map());
|
|
152
156
|
const ghosts = useRef([]);
|
|
153
157
|
const inflight = useRef(false);
|
|
158
|
+
const startAt = useRef(new Map()); // card key -> when its drop-in starts (may be a few ms ahead)
|
|
159
|
+
const revealUntil = useRef(0);
|
|
160
|
+
const errAt = useRef(null);
|
|
161
|
+
const glyph = useRef({ state: null, since: null });
|
|
154
162
|
|
|
155
163
|
const flash = useCallback((text, tone = 'ok') => {
|
|
156
164
|
setNote({ text, tone, at: Date.now() });
|
|
@@ -171,6 +179,12 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
171
179
|
apiRef.current = api;
|
|
172
180
|
nowRef.current = now;
|
|
173
181
|
|
|
182
|
+
const startCard = (key, at) => {
|
|
183
|
+
startAt.current.set(key, at);
|
|
184
|
+
revealUntil.current = Math.max(revealUntil.current, at + REVEAL_MS);
|
|
185
|
+
if (startAt.current.size > 400) for (const [k, v] of startAt.current) if (Date.now() - v > 60000) startAt.current.delete(k);
|
|
186
|
+
};
|
|
187
|
+
|
|
174
188
|
const refresh = useCallback(async () => {
|
|
175
189
|
if (inflight.current) return;
|
|
176
190
|
inflight.current = true;
|
|
@@ -179,20 +193,28 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
179
193
|
const res = await apiRef.current.situation();
|
|
180
194
|
const sit = res && res.situation;
|
|
181
195
|
const cur = (sit && sit.needs_you) || [];
|
|
196
|
+
const at = Date.now();
|
|
182
197
|
if (prevNeeds.current) {
|
|
183
198
|
const have = new Set(prevNeeds.current.map((i) => i.ref));
|
|
184
199
|
const nowRefs = new Set(cur.map((i) => i.ref));
|
|
185
200
|
let fresh = 0;
|
|
186
|
-
for (const i of cur) if (!have.has(i.ref)) { newAt.current.set(i.ref,
|
|
187
|
-
for (const i of prevNeeds.current) if (!nowRefs.has(i.ref)) ghosts.current.push({ item: i, at
|
|
201
|
+
for (const i of cur) if (!have.has(i.ref)) { newAt.current.set(i.ref, at); startCard('n:' + i.ref, at); fresh++; }
|
|
202
|
+
for (const i of prevNeeds.current) if (!nowRefs.has(i.ref)) ghosts.current.push({ item: i, at });
|
|
188
203
|
if (fresh) setArrived((n) => n + fresh);
|
|
204
|
+
} else {
|
|
205
|
+
// the first board: cards drop in top to bottom
|
|
206
|
+
cur.forEach((i, k) => startCard('n:' + i.ref, at + k * STAGGER_MS));
|
|
207
|
+
((sit && sit.areas) || []).forEach((a, k) => startCard('a:' + a.area, at + (k + 1) * STAGGER_MS));
|
|
208
|
+
startCard('a:', at);
|
|
189
209
|
}
|
|
190
210
|
prevNeeds.current = cur;
|
|
191
211
|
sigRef.current = signature(sit);
|
|
192
212
|
setData(res);
|
|
193
213
|
setErr(null);
|
|
214
|
+
errAt.current = null;
|
|
194
215
|
setFetchedAt(nowRef.current());
|
|
195
216
|
} catch (e) {
|
|
217
|
+
if (errAt.current == null) errAt.current = Date.now();
|
|
196
218
|
setErr(e);
|
|
197
219
|
} finally {
|
|
198
220
|
inflight.current = false;
|
|
@@ -203,21 +225,16 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
203
225
|
useEffect(() => {
|
|
204
226
|
refresh();
|
|
205
227
|
const t = setInterval(refresh, intervalMs);
|
|
206
|
-
|
|
207
|
-
return () => { clearInterval(t); clearInterval(h); };
|
|
228
|
+
return () => clearInterval(t);
|
|
208
229
|
}, [refresh, intervalMs]);
|
|
209
230
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
useEffect(() =>
|
|
217
|
-
if (phase !== 'deck' || slide <= 0) return undefined;
|
|
218
|
-
const s = setTimeout(() => setSlide((x) => x - 1), 60);
|
|
219
|
-
return () => clearTimeout(s);
|
|
220
|
-
}, [phase, slide]);
|
|
231
|
+
// ---- the one clock -------------------------------------------------------------------------------
|
|
232
|
+
// Each render declares what it still needs (see lucid/motion.js); with nothing declared there is no timer.
|
|
233
|
+
const clockRef = useRef(null);
|
|
234
|
+
if (!clockRef.current) clockRef.current = new Clock(() => setFrame((x) => x + 1));
|
|
235
|
+
const demandRef = useRef(null);
|
|
236
|
+
useEffect(() => { if (demandRef.current) clockRef.current.schedule(demandRef.current()); });
|
|
237
|
+
useEffect(() => () => clockRef.current.stop(), []);
|
|
221
238
|
|
|
222
239
|
const introDone = useCallback(() => setPhase((p) => (p === 'intro' ? 'deck' : p)), []);
|
|
223
240
|
|
|
@@ -241,8 +258,8 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
241
258
|
}, [allNeeds, expanded]);
|
|
242
259
|
|
|
243
260
|
const runConfirmed = () => {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
261
|
+
const c0 = confirm; setConfirm(null);
|
|
262
|
+
if (c0) c0.run();
|
|
246
263
|
};
|
|
247
264
|
|
|
248
265
|
const askMeasure = (item, m) => {
|
|
@@ -310,9 +327,9 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
310
327
|
}
|
|
311
328
|
setPane('needs'); setSel(0); setExpanded(null);
|
|
312
329
|
} else {
|
|
313
|
-
const
|
|
314
|
-
if (!
|
|
315
|
-
flash(`${
|
|
330
|
+
const c1 = changed[safeCh];
|
|
331
|
+
if (!c1) { flash('Nothing is selected.', 'dim'); return; }
|
|
332
|
+
flash(`${c1.L0 || 'Change'}${changedWords(c1) ? ': ' + changedWords(c1) : ''}`, 'ok');
|
|
316
333
|
}
|
|
317
334
|
};
|
|
318
335
|
|
|
@@ -379,11 +396,10 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
379
396
|
keyRef.current = onKey;
|
|
380
397
|
useInput((input, key) => keyRef.current(input, key));
|
|
381
398
|
|
|
382
|
-
|
|
383
399
|
if (phase === 'intro') {
|
|
384
400
|
return (
|
|
385
401
|
<Box flexDirection="column" height={Math.max(10, rows - 1)} width={cols}>
|
|
386
|
-
<LucidIntro data={data} err={err} onDone={introDone} rows={rows} />
|
|
402
|
+
<LucidIntro data={data} err={err} onDone={introDone} rows={rows} cols={cols} motion={motionOn} />
|
|
387
403
|
</Box>
|
|
388
404
|
);
|
|
389
405
|
}
|
|
@@ -391,63 +407,82 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
391
407
|
const wall = Date.now();
|
|
392
408
|
const t = now();
|
|
393
409
|
const cov = (sit && sit.cannot_see && sit.cannot_see.coverage) || { live: 0, expected: 0 };
|
|
394
|
-
const agoS = fetchedAt ? Math.max(0, Math.floor((t - fetchedAt) / 1000)) : null;
|
|
395
410
|
const stale = Boolean(err && data);
|
|
411
|
+
const ageText = fetchedAt ? ageOf(fetchedAt, t) : null;
|
|
396
412
|
const noteLive = note && wall - note.at < (note.tone === 'bad' ? 6000 : TOAST_MS) ? note : null;
|
|
397
|
-
const
|
|
413
|
+
const noteRole = noteLive ? ({ ok: 'ok', bad: 'error', busy: 'sky', dim: 'muted' }[noteLive.tone]) : null;
|
|
398
414
|
const spots = blindSpots(sit && sit.cannot_see);
|
|
399
415
|
const dau = (sit && sit.doesnt_add_up) || [];
|
|
400
|
-
const flashOn = tick % 2 === 0;
|
|
401
416
|
ghosts.current = ghosts.current.filter((g) => wall - g.at < GHOST_MS);
|
|
402
417
|
const liveGhosts = ghosts.current.filter((g) => !filter || g.item.area === filter);
|
|
403
418
|
|
|
419
|
+
// ---- the Q: LUCID state -> glyph state, animated only for a short window after a change ----------------
|
|
420
|
+
const gState = glyphStateOf({ err, fetching, needs: allNeeds.length });
|
|
421
|
+
if (glyph.current.state !== gState) glyph.current = { state: gState, since: wall };
|
|
422
|
+
const gAnimating = glyphAnimating({ state: gState, since: glyph.current.since, now: wall, motion: motionOn });
|
|
423
|
+
const headGlyph = <QGlyph state={gState} ms={wall} since={gState === 'failing' ? glyph.current.since : null} quiet={!gAnimating} />;
|
|
424
|
+
|
|
425
|
+
// ---- what this render still needs from the clock --------------------------------------------------------
|
|
426
|
+
const newExpiries = [...newAt.current.values()].map((v) => v + NEW_MS);
|
|
427
|
+
demandRef.current = () => {
|
|
428
|
+
const w = Date.now();
|
|
429
|
+
return deckDemands({
|
|
430
|
+
motion: motionOn,
|
|
431
|
+
frames: gAnimating || w < revealUntil.current,
|
|
432
|
+
now: w,
|
|
433
|
+
at: [
|
|
434
|
+
noteLive ? noteLive.at + (noteLive.tone === 'bad' ? 6000 : TOAST_MS) : null,
|
|
435
|
+
...ghosts.current.map((g) => g.at + GHOST_MS),
|
|
436
|
+
...newExpiries,
|
|
437
|
+
motionOn && glyph.current.since != null ? glyph.current.since + SETTLE_MS : null,
|
|
438
|
+
nextMinuteAt(w, t),
|
|
439
|
+
],
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
const revealOf = (key) => revealT(startAt.current.get(key), wall, motionOn);
|
|
443
|
+
|
|
404
444
|
// ── layout ──────────────────────────────────────────────────────────
|
|
405
445
|
const wide = cols >= 80;
|
|
406
446
|
const big = cols >= 110;
|
|
407
447
|
const leftW = big ? 24 : 22;
|
|
408
448
|
const avail = cols - 2 - leftW;
|
|
409
449
|
const rightW = big ? avail - Math.round(avail * 0.55) : 0; // below 110 cols Since-you-looked stacks under Needs-you
|
|
410
|
-
const mainW = avail - rightW
|
|
411
|
-
const bannerOn = Boolean(err) || hinted;
|
|
450
|
+
const mainW = avail - rightW;
|
|
412
451
|
const headerH = 3 + (err ? 1 : 0);
|
|
413
452
|
const hintH = hinted && !err ? 1 : 0;
|
|
414
453
|
const footerH = 3;
|
|
415
454
|
const bodyH = Math.max(8, rows - headerH - hintH - footerH - 1);
|
|
416
|
-
const listRows = bodyH -
|
|
417
|
-
const innerOf = (w) => Math.max(10, w - 4);
|
|
455
|
+
const listRows = bodyH - 1; // one line for the pane title rule
|
|
418
456
|
|
|
419
457
|
// ── header ──────────────────────────────────────────────────────────
|
|
420
|
-
const
|
|
421
|
-
const
|
|
458
|
+
const covRole = cov.expected && cov.live < cov.expected ? 'warn' : 'ok';
|
|
459
|
+
const hot = arrived > 0 && gState === 'waiting' && gAnimating;
|
|
422
460
|
const header = (
|
|
423
461
|
<Box flexDirection="column">
|
|
424
|
-
<Box borderStyle="round" borderColor={err ? (stale ?
|
|
462
|
+
<Box borderStyle="round" borderColor={err ? (stale ? c('warn') : c('error')) : c('faint')} paddingX={1}>
|
|
425
463
|
<Text wrap="truncate">
|
|
426
|
-
{
|
|
427
|
-
<Text color={
|
|
428
|
-
<Text bold>{
|
|
429
|
-
<Text color={
|
|
430
|
-
{sit ? <Text color={
|
|
431
|
-
<Text color={
|
|
432
|
-
{!sit ? <Text color={
|
|
433
|
-
? <Text
|
|
434
|
-
: <Text bold color={
|
|
435
|
-
{filterLabel ? <Text bold color={
|
|
436
|
-
{arrived > 0 ? <Text bold color={
|
|
437
|
-
<Text color={
|
|
438
|
-
<Text color={
|
|
439
|
-
{fetching ? <Text color={PALETTE.brand}>{SPINNER[spin % SPINNER.length]} </Text> : null}
|
|
440
|
-
{agoS == null ? 'connecting' : `updated ${agoS}s ago`}
|
|
441
|
-
</Text>
|
|
464
|
+
{headGlyph}<Text> </Text><Grad text="LUCID" />
|
|
465
|
+
<Text color={c('muted')}> │ </Text>
|
|
466
|
+
<Text bold color={c('value')}>{clockLondon(t)}</Text><Text color={c('muted')}> London</Text>
|
|
467
|
+
<Text color={c('muted')}> │ </Text>
|
|
468
|
+
{sit ? <Text color={c(covRole)}>{meter(cov.live, cov.expected)} {cov.live}/{cov.expected}</Text> : <Text color={c('muted')}>▱▱▱▱▱ -/-</Text>}
|
|
469
|
+
<Text color={c('muted')}> │ </Text>
|
|
470
|
+
{!sit ? <Text color={c('muted')}>- need you</Text> : allNeeds.length
|
|
471
|
+
? <Text bold color={c('warn')}>{allNeeds.length} need you</Text>
|
|
472
|
+
: <Text bold color={c('ok')}>0 need you</Text>}
|
|
473
|
+
{filterLabel ? <Text bold color={c('key')}> │ filter: {filterLabel}</Text> : null}
|
|
474
|
+
{arrived > 0 ? <Text bold color={hot ? c('warn') : c('ok')}> +{arrived} new</Text> : null}
|
|
475
|
+
<Text color={c('muted')}> │ </Text>
|
|
476
|
+
<Text color={c('muted')}>{ageText == null ? 'connecting' : `updated ${ageText} ago`}</Text>
|
|
442
477
|
</Text>
|
|
443
478
|
</Box>
|
|
444
479
|
{err ? (
|
|
445
|
-
<Text bold wrap="truncate" color={stale ?
|
|
446
|
-
{stale ? `⚠ STALE ${
|
|
480
|
+
<Text bold wrap="truncate" color={stale ? c('warn') : c('error')}>
|
|
481
|
+
{stale ? `⚠ STALE ${ageText}` : '✖ NO DATA'} · LUCID unreachable: {err.status || err.code || err.message} · retrying, {stale ? 'showing the last good data' : 'nothing is known yet (this is not "all quiet")'}
|
|
447
482
|
</Text>
|
|
448
483
|
) : null}
|
|
449
484
|
{hinted && !err ? (
|
|
450
|
-
<Text wrap="truncate" backgroundColor={
|
|
485
|
+
<Text wrap="truncate" backgroundColor={mono() ? undefined : BAR_BG} color={mono() ? undefined : '#FFFFFF'} inverse={mono()} bold>{pad(' ↑↓ move · Enter open · Tab switch panel · ? help ', cols - 2)}</Text>
|
|
451
486
|
) : null}
|
|
452
487
|
</Box>
|
|
453
488
|
);
|
|
@@ -457,143 +492,105 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
457
492
|
<Box flexDirection="column" paddingX={1}>
|
|
458
493
|
{header}
|
|
459
494
|
<Box marginTop={1}>
|
|
460
|
-
<Text color={err ?
|
|
495
|
+
<Text color={err ? c('error') : c('muted')}>
|
|
461
496
|
{err ? 'No data yet. LUCID cannot be reached, so nothing is known (this is not "all quiet").' : 'Connecting to LUCID...'}
|
|
462
497
|
</Text>
|
|
463
498
|
</Box>
|
|
464
|
-
<Box marginTop={1}><Text color={
|
|
499
|
+
<Box marginTop={1}><Text color={c('muted')}>r refresh · q quit</Text></Box>
|
|
465
500
|
</Box>
|
|
466
501
|
);
|
|
467
502
|
}
|
|
468
503
|
|
|
469
504
|
// ── panes ───────────────────────────────────────────────────────────
|
|
470
505
|
const focus = (p) => pane === p;
|
|
506
|
+
const isNewOf = (it) => newAt.current.has(it.ref) && wall - newAt.current.get(it.ref) < NEW_MS;
|
|
507
|
+
const areaLabelOf = (k) => ((areas.find((a) => a.area === k) || {}).label || '');
|
|
471
508
|
|
|
472
509
|
const needsBody = (w, rowsAvail) => {
|
|
473
|
-
const
|
|
510
|
+
const cw = Math.max(24, w - 1);
|
|
474
511
|
const f = focus('needs');
|
|
475
512
|
if (needs.length === 0 && !liveGhosts.length) {
|
|
476
513
|
return (
|
|
477
|
-
<Text color={
|
|
514
|
+
<Text color={c('ok')}>{filter ? 'Nothing needs you in this area.' : 'Nothing needs you right now.'}{spots.length ? <Text color={c('muted')}> (check what I can't see)</Text> : null}</Text>
|
|
478
515
|
);
|
|
479
516
|
}
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
517
|
+
const views = needs.map((it, i) => lucidNeed({
|
|
518
|
+
item: it, areaLabel: filter ? '' : areaLabelOf(it.area), isNew: isNewOf(it), selected: i === safeSel, focused: f,
|
|
519
|
+
open: expandedRef === it.ref, mSel, now: t,
|
|
520
|
+
}, cw));
|
|
483
521
|
const nGhost = Math.min(2, liveGhosts.length);
|
|
484
|
-
const win =
|
|
522
|
+
const win = fitHeights(views.map(cardHeight), safeSel, rowsAvail - nGhost);
|
|
485
523
|
const above = win.start;
|
|
486
524
|
const below = needs.length - win.end;
|
|
487
525
|
return (
|
|
488
526
|
<>
|
|
489
|
-
{above > 0 ? <Text color={
|
|
490
|
-
{needs.slice(win.start, win.end).map((it, k) =>
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const open = expandedRef === it.ref;
|
|
494
|
-
const a = it.L1 || {};
|
|
495
|
-
const ms = (it.L2 && it.L2.measures) || [];
|
|
496
|
-
const isNew = isNewOf(it);
|
|
497
|
-
const bg = on ? (f ? BAR_BG : BAR_BG_DIM) : undefined;
|
|
498
|
-
const lines = wrap(textOf(it), tw);
|
|
499
|
-
const fg = isNew ? STATE_HEX.ok : on ? '#FFFFFF' : PALETTE.muted;
|
|
500
|
-
return (
|
|
501
|
-
<Box key={it.ref} flexDirection="column">
|
|
502
|
-
{lines.map((ln, li) => (
|
|
503
|
-
<Text key={li} wrap="truncate" backgroundColor={bg} bold={on && f} dimColor={!f && !on}>
|
|
504
|
-
<Text bold color={on ? (f ? '#FFFFFF' : PALETTE.muted) : PALETTE.dim}>{li === 0 ? (on ? '▶ ' : ' ') : ' '}</Text>
|
|
505
|
-
{li === 0 ? <Pill urgency={it.urgency} /> : <Text>{' '.repeat(11)}</Text>}
|
|
506
|
-
<Text color={fg}> {pad(ln, tw)}</Text>
|
|
507
|
-
</Text>
|
|
508
|
-
))}
|
|
509
|
-
{open ? (
|
|
510
|
-
<Box flexDirection="column" marginLeft={3} marginBottom={1}>
|
|
511
|
-
{[['Who', a.who], ['What', a.what], ['Why', a.why], ['When', whenText(a.when, t)]].filter(([, v]) => v).map(([lbl, v]) => (
|
|
512
|
-
<Box key={lbl}><Box width={6}><Text color={PALETTE.dim}>{lbl}</Text></Box><Box width={iw - 9}><Text color={PALETTE.text}>{wrap(v, iw - 9).join('\n')}</Text></Box></Box>
|
|
513
|
-
))}
|
|
514
|
-
{(a.affected || []).length ? (
|
|
515
|
-
<Text wrap="truncate" color={PALETTE.dim}>{pad('Affects', 6)} <Text color={PALETTE.muted}>{clip(a.affected.map(nm).filter(Boolean).join(', '), iw - 14)}</Text></Text>
|
|
516
|
-
) : null}
|
|
517
|
-
{ms.length ? <Text bold color={PALETTE.accent}>What you can do</Text> : <Text color={PALETTE.dim}>No actions to offer here. Esc closes.</Text>}
|
|
518
|
-
{ms.map((m, mi) => {
|
|
519
|
-
const mOn = mi === mSel;
|
|
520
|
-
return (
|
|
521
|
-
<Text key={m.id} wrap="truncate" backgroundColor={mOn ? '#6D28D9' : undefined} bold={mOn}>
|
|
522
|
-
<Text color={mOn ? '#FFFFFF' : PALETTE.dim}>{mOn ? '❯ ' : ' '}</Text>
|
|
523
|
-
<Text color={mOn ? '#FFFFFF' : PALETTE.text}>{pad(clip(m.say || m.label, iw - 24), iw - 24)}</Text>
|
|
524
|
-
<Text color={m.risk === 'dangerous' ? STATE_HEX.blind : m.risk === 'gated' ? STATE_HEX.attention : mOn ? '#FFFFFF' : PALETTE.dim}> {m.risk}</Text>
|
|
525
|
-
</Text>
|
|
526
|
-
);
|
|
527
|
-
})}
|
|
528
|
-
{ms.some((m) => m.command) && ms[mSel] && ms[mSel].command
|
|
529
|
-
? <Text wrap="truncate" color={PALETTE.code}> $ {clip(ms[mSel].command, iw - 12)}</Text> : (ms.some((m) => m.command) ? <Text> </Text> : null)}
|
|
530
|
-
</Box>
|
|
531
|
-
) : null}
|
|
532
|
-
</Box>
|
|
533
|
-
);
|
|
534
|
-
})}
|
|
527
|
+
{above > 0 ? <Text color={c('muted')}> ↑ {above} more</Text> : null}
|
|
528
|
+
{needs.slice(win.start, win.end).map((it, k) => (
|
|
529
|
+
<LucidCard key={it.ref} view={views[win.start + k]} t={revealOf('n:' + it.ref)} />
|
|
530
|
+
))}
|
|
535
531
|
{liveGhosts.slice(0, 2).map((g) => (
|
|
536
|
-
<Text key={'g' + g.item.ref} wrap="truncate" color={
|
|
532
|
+
<Text key={'g' + g.item.ref} wrap="truncate" color={c('muted')} dimColor> ✓ resolved {clip(g.item.L0 || '', Math.max(10, w - 16))}</Text>
|
|
537
533
|
))}
|
|
538
|
-
{below > 0 ? <Text color={
|
|
534
|
+
{below > 0 ? <Text color={c('muted')}> ↓ {below} more</Text> : null}
|
|
539
535
|
</>
|
|
540
536
|
);
|
|
541
537
|
};
|
|
542
538
|
|
|
543
539
|
const changedBody = (w, rowsAvail) => {
|
|
544
|
-
const iw =
|
|
540
|
+
const iw = Math.max(10, w - 2);
|
|
545
541
|
const f = focus('changed');
|
|
546
|
-
if (!changed.length) return <Text color={
|
|
542
|
+
if (!changed.length) return <Text color={c('muted')}>Nothing has changed.</Text>;
|
|
547
543
|
const tw = iw - 7;
|
|
548
|
-
const textOf = (
|
|
549
|
-
const words = changedWords(
|
|
550
|
-
return (
|
|
544
|
+
const textOf = (c0) => {
|
|
545
|
+
const words = changedWords(c0);
|
|
546
|
+
return (c0.L0 || '') + (words ? ` · ${words}` : '');
|
|
551
547
|
};
|
|
552
|
-
const win =
|
|
548
|
+
const win = fitHeights(changed.map((c0) => wrap(textOf(c0), tw).length), safeCh, rowsAvail);
|
|
553
549
|
const above = win.start;
|
|
554
550
|
const below = changed.length - win.end;
|
|
555
551
|
return (
|
|
556
552
|
<>
|
|
557
|
-
{above > 0 ? <Text color={
|
|
558
|
-
{changed.slice(win.start, win.end).map((
|
|
553
|
+
{above > 0 ? <Text color={c('muted')}> ↑ {above} more</Text> : null}
|
|
554
|
+
{changed.slice(win.start, win.end).map((c0, k) => {
|
|
559
555
|
const i = win.start + k;
|
|
560
556
|
const on = i === safeCh && f;
|
|
561
557
|
return (
|
|
562
|
-
<Box key={
|
|
563
|
-
{wrap(textOf(
|
|
564
|
-
<Text key={li} wrap="truncate" backgroundColor={on ? BAR_BG : undefined}
|
|
565
|
-
<Text color={on ? '#FFFFFF' :
|
|
566
|
-
<Text color={on ? '#FFFFFF' :
|
|
558
|
+
<Box key={c0.ref + c0.ts} flexDirection="column">
|
|
559
|
+
{wrap(textOf(c0), tw).map((ln, li) => (
|
|
560
|
+
<Text key={li} wrap="truncate" backgroundColor={on && !mono() ? BAR_BG : undefined} inverse={on && mono()} bold={on}>
|
|
561
|
+
<Text color={on ? '#FFFFFF' : c('muted')}>{li === 0 ? `${on ? '▶ ' : ' '}${pad(ageOf(c0.ts, t), 3)} ` : ' '.repeat(7)}</Text>
|
|
562
|
+
<Text color={on ? '#FFFFFF' : (li === 0 ? c('text') : c('muted'))}>{pad(ln, tw)}</Text>
|
|
567
563
|
</Text>
|
|
568
564
|
))}
|
|
569
565
|
</Box>
|
|
570
566
|
);
|
|
571
567
|
})}
|
|
572
|
-
{below > 0 ? <Text color={
|
|
568
|
+
{below > 0 ? <Text color={c('muted')}> ↓ {below} more</Text> : null}
|
|
573
569
|
</>
|
|
574
570
|
);
|
|
575
571
|
};
|
|
576
572
|
|
|
577
573
|
const areasBody = (w, rowsAvail) => {
|
|
578
|
-
const
|
|
574
|
+
const cw = Math.max(18, w - 1);
|
|
579
575
|
const f = focus('areas');
|
|
580
576
|
const list = [{ area: null, label: 'All areas', state: null }, ...areas];
|
|
581
|
-
const
|
|
582
|
-
return list.slice(win.start, win.end).map((a, k) => {
|
|
583
|
-
const i = win.start + k;
|
|
584
|
-
const on = i === safeAr && f;
|
|
585
|
-
const active = (a.area || null) === filter;
|
|
577
|
+
const views = list.map((a, i) => {
|
|
586
578
|
const n = a.area ? allNeeds.filter((x) => x.area === a.area).length : allNeeds.length;
|
|
587
|
-
return (
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
<Text color={on ? '#FFFFFF' : PALETTE.text}>{pad(clip(a.label || a.area, iw - 9), iw - 9)}</Text>
|
|
592
|
-
<Text color={n ? STATE_HEX.attention : PALETTE.dim}> {n ? String(n).padStart(2) : ' ·'}</Text>
|
|
593
|
-
<Text color={PALETTE.accent}>{active ? ' ●' : ' '}</Text>
|
|
594
|
-
</Text>
|
|
595
|
-
);
|
|
579
|
+
return lucidArea({
|
|
580
|
+
area: a.area, label: a.label, state: a.state, L0: a.L0, last: a.last, n, active: (a.area || null) === filter && filter != null,
|
|
581
|
+
selected: i === safeAr && f, focused: f, expanded: i === safeAr && f, now: t,
|
|
582
|
+
}, cw);
|
|
596
583
|
});
|
|
584
|
+
const win = fitHeights(views.map(cardHeight), safeAr, rowsAvail);
|
|
585
|
+
return (
|
|
586
|
+
<>
|
|
587
|
+
{win.start > 0 ? <Text color={c('muted')}> ↑ {win.start} more</Text> : null}
|
|
588
|
+
{list.slice(win.start, win.end).map((a, k) => (
|
|
589
|
+
<LucidCard key={a.area || 'all'} view={views[win.start + k]} t={revealOf('a:' + (a.area || ''))} />
|
|
590
|
+
))}
|
|
591
|
+
{win.end < list.length ? <Text color={c('muted')}> ↓ {list.length - win.end} more</Text> : null}
|
|
592
|
+
</>
|
|
593
|
+
);
|
|
597
594
|
};
|
|
598
595
|
|
|
599
596
|
const counts = {
|
|
@@ -602,14 +599,21 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
602
599
|
areas: areas.length,
|
|
603
600
|
};
|
|
604
601
|
const bodies = { needs: needsBody, changed: changedBody, areas: areasBody };
|
|
602
|
+
const col = (p, w, h0, children, note0) => (
|
|
603
|
+
<Box flexDirection="column" width={w} height={h0}>
|
|
604
|
+
<PaneTitle title={PANE_TITLE[p]} count={counts[p]} focused={focus(p)} width={w - 1} note={note0} />
|
|
605
|
+
{children}
|
|
606
|
+
</Box>
|
|
607
|
+
);
|
|
605
608
|
|
|
606
609
|
let body;
|
|
607
610
|
if (help) {
|
|
608
611
|
const K = (k, d) => (
|
|
609
|
-
<Text key={k}><Text bold color={
|
|
612
|
+
<Text key={k}><Text bold color={c('key')}>{pad(k, 14)}</Text><Text color={c('text')}>{d}</Text></Text>
|
|
610
613
|
);
|
|
611
614
|
body = (
|
|
612
|
-
<
|
|
615
|
+
<Box flexDirection="column" height={bodyH}>
|
|
616
|
+
<PaneTitle title="KEYS" count="" focused width={cols - 2} />
|
|
613
617
|
{K('↑ ↓ j k', 'move the highlighted row (inside an open item: pick an action)')}
|
|
614
618
|
{K('Tab / S-Tab', 'switch panel: Needs you, Since you looked, Areas')}
|
|
615
619
|
{K('Enter', 'open an item / do the highlighted action / filter by area')}
|
|
@@ -621,35 +625,25 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
621
625
|
{K('r', 'refresh now (it also refreshes every 10s)')}
|
|
622
626
|
{K('?', 'close this help')}
|
|
623
627
|
{K('q Ctrl-C', 'quit')}
|
|
624
|
-
<Text color={
|
|
625
|
-
</
|
|
628
|
+
<Text color={c('muted')}>Press ? or Esc to go back.</Text>
|
|
629
|
+
</Box>
|
|
626
630
|
);
|
|
627
631
|
} else if (wide) {
|
|
628
|
-
const sl = slide ? slide * 4 : 0;
|
|
629
632
|
const needsH = big ? bodyH : Math.ceil(bodyH * 0.6);
|
|
630
633
|
const chH = bodyH - needsH;
|
|
634
|
+
const nNote = filterLabel ? `· ${filterLabel}` : '';
|
|
631
635
|
body = (
|
|
632
|
-
<Box width={cols - 2
|
|
633
|
-
|
|
634
|
-
{areasBody(leftW, listRows)}
|
|
635
|
-
</Panel>
|
|
636
|
+
<Box width={cols - 2}>
|
|
637
|
+
{col('areas', leftW, bodyH, areasBody(leftW, listRows))}
|
|
636
638
|
{big ? (
|
|
637
639
|
<>
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
</Panel>
|
|
641
|
-
<Panel title={PANE_TITLE.changed} count={counts.changed} focused={focus('changed')} width={rightW} height={bodyH}>
|
|
642
|
-
{changedBody(rightW, listRows)}
|
|
643
|
-
</Panel>
|
|
640
|
+
{col('needs', mainW, bodyH, needsBody(mainW, listRows), nNote)}
|
|
641
|
+
{col('changed', rightW, bodyH, changedBody(rightW, listRows))}
|
|
644
642
|
</>
|
|
645
643
|
) : (
|
|
646
|
-
<Box flexDirection="column">
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
</Panel>
|
|
650
|
-
<Panel title={PANE_TITLE.changed} count={counts.changed} focused={focus('changed')} width={mainW + sl} height={chH}>
|
|
651
|
-
{changedBody(mainW, chH - 3)}
|
|
652
|
-
</Panel>
|
|
644
|
+
<Box flexDirection="column" width={mainW}>
|
|
645
|
+
{col('needs', mainW, needsH, needsBody(mainW, needsH - 1), nNote)}
|
|
646
|
+
{col('changed', mainW, chH, changedBody(mainW, chH - 1))}
|
|
653
647
|
</Box>
|
|
654
648
|
)}
|
|
655
649
|
</Box>
|
|
@@ -660,11 +654,9 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
660
654
|
const focH = Math.max(6, bodyH - others.length);
|
|
661
655
|
body = (
|
|
662
656
|
<Box flexDirection="column">
|
|
663
|
-
|
|
664
|
-
{bodies[pane](stackW, focH - 3)}
|
|
665
|
-
</Panel>
|
|
657
|
+
{col(pane, stackW, focH, bodies[pane](stackW, focH - 1), filterLabel && pane === 'needs' ? `· ${filterLabel}` : '')}
|
|
666
658
|
{others.map((p) => (
|
|
667
|
-
<Text key={p} wrap="truncate" color={
|
|
659
|
+
<Text key={p} wrap="truncate" color={c('muted')}> ▸ {PANE_TITLE[p]} {counts[p]} (Tab)</Text>
|
|
668
660
|
))}
|
|
669
661
|
</Box>
|
|
670
662
|
);
|
|
@@ -687,8 +679,8 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
687
679
|
act = [['Enter', 'open'], ['d', 'do something'], ['u', 'not urgent'], ['c', 'I care'], ['m', 'mark all seen']];
|
|
688
680
|
}
|
|
689
681
|
} else if (pane === 'changed') {
|
|
690
|
-
const
|
|
691
|
-
where =
|
|
682
|
+
const c1 = changed[safeCh];
|
|
683
|
+
where = c1 ? `▶ Since you looked · ${safeCh + 1} of ${changed.length} · ${clip(c1.L0 || '', 60)}` : '▶ Since you looked · nothing changed';
|
|
692
684
|
act = [['Enter', 'show detail'], ['m', 'mark all seen'], ['Tab', 'next panel']];
|
|
693
685
|
} else {
|
|
694
686
|
const a = areas[safeAr - 1];
|
|
@@ -706,22 +698,22 @@ export function LucidDeck({ api, intervalMs = 10000, now = () => Date.now(), onE
|
|
|
706
698
|
{header}
|
|
707
699
|
{body}
|
|
708
700
|
{confirm ? (
|
|
709
|
-
<Text wrap="truncate" backgroundColor={
|
|
701
|
+
<Text wrap="truncate" backgroundColor={mono() ? undefined : c('warn')} inverse={mono()} color={mono() ? undefined : '#0B1220'} bold> {confirm.text.endsWith('y/n') ? confirm.text : confirm.text + ' y/n'}{confirm.extra ? ' · ' + confirm.extra : ''} </Text>
|
|
710
702
|
) : noteLive ? (
|
|
711
|
-
<Text wrap="truncate" bold color={
|
|
703
|
+
<Text wrap="truncate" bold color={c(noteRole)}>{noteLive.tone === 'ok' ? '✔ ' : noteLive.tone === 'bad' ? '✖ ' : noteLive.tone === 'busy' ? '◐ ' : 'ℹ '}{noteLive.text}</Text>
|
|
712
704
|
) : (
|
|
713
|
-
<Text wrap="truncate" color={spots.length || dau.length ?
|
|
705
|
+
<Text wrap="truncate" color={spots.length || dau.length ? c('warn') : c('muted')}>
|
|
714
706
|
{dau.length ? `doesn't add up (${dau.length}): ${clip(dau[0].L0 || '', 60)} · ` : ''}{spots.length ? `can't see: ${spots.map((s) => s.text).join(' · ')}` : 'Every expected source is speaking.'}
|
|
715
707
|
</Text>
|
|
716
708
|
)}
|
|
717
709
|
<Text wrap="truncate">
|
|
718
|
-
<Text bold color={
|
|
719
|
-
<Text color={
|
|
710
|
+
<Text bold color={c('key')}>{where}</Text>
|
|
711
|
+
<Text color={c('muted')}> · </Text>
|
|
720
712
|
{act.map(([k, l], i) => (
|
|
721
|
-
<Text key={k}>{i ? ' ' : ''}<Text bold color={
|
|
713
|
+
<Text key={k}>{i ? ' ' : ''}<Text bold color={c('sky')}>{k}:</Text><Text color={c('muted')}> {l}</Text></Text>
|
|
722
714
|
))}
|
|
723
715
|
</Text>
|
|
724
|
-
<Hints items={hints}
|
|
716
|
+
<Hints items={hints} />
|
|
725
717
|
</Box>
|
|
726
718
|
);
|
|
727
719
|
}
|