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.
Files changed (442) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -0
  3. package/bin/quox.js +99 -126
  4. package/lib/api/endpoints/actions.js +15 -0
  5. package/lib/api/endpoints/admin.js +46 -0
  6. package/lib/api/endpoints/agent-schedules.js +26 -0
  7. package/lib/api/endpoints/agents.js +7 -0
  8. package/lib/api/endpoints/assistants.js +12 -0
  9. package/lib/api/endpoints/away.js +15 -0
  10. package/lib/api/endpoints/blog.js +16 -0
  11. package/lib/api/endpoints/chat.js +14 -0
  12. package/lib/api/endpoints/contactsq.js +6 -0
  13. package/lib/api/endpoints/conversations.js +14 -0
  14. package/lib/api/endpoints/devServerRegistry.js +25 -0
  15. package/lib/api/endpoints/engagement.js +26 -0
  16. package/lib/api/endpoints/evidence.js +20 -0
  17. package/lib/api/endpoints/executions.js +13 -0
  18. package/lib/api/endpoints/fetchAllowlist.js +27 -0
  19. package/lib/api/endpoints/files.js +14 -0
  20. package/lib/api/endpoints/hat.js +44 -0
  21. package/lib/api/endpoints/inbox.js +20 -0
  22. package/lib/api/endpoints/index.js +26 -0
  23. package/lib/api/endpoints/loops.js +26 -0
  24. package/lib/api/endpoints/memory.js +19 -0
  25. package/lib/api/endpoints/mind.js +13 -0
  26. package/lib/api/endpoints/mon.js +179 -0
  27. package/lib/api/endpoints/objectives.js +12 -0
  28. package/lib/api/endpoints/plugins.js +18 -0
  29. package/lib/api/endpoints/presence.js +33 -0
  30. package/lib/api/endpoints/quoxplan.js +7 -0
  31. package/lib/api/endpoints/quoxseo.js +12 -0
  32. package/lib/api/endpoints/router.js +17 -0
  33. package/lib/api/endpoints/safety.js +7 -0
  34. package/lib/api/endpoints/server.js +21 -0
  35. package/lib/api/endpoints/setup.js +6 -0
  36. package/lib/api/endpoints/skills.js +20 -0
  37. package/lib/api/endpoints/stepup.js +20 -0
  38. package/lib/api/endpoints/teams.js +2 -2
  39. package/lib/api/endpoints/triggers.js +26 -0
  40. package/lib/api/endpoints/v1.js +120 -0
  41. package/lib/api/endpoints/ward.js +7 -0
  42. package/lib/api/endpoints/workflows.js +7 -0
  43. package/lib/api/endpoints/x.js +8 -0
  44. package/lib/commands/actions.js +131 -0
  45. package/lib/commands/admin.js +16 -0
  46. package/lib/commands/agent.js +170 -27
  47. package/lib/commands/agentAttach.js +140 -13
  48. package/lib/commands/approvals.js +78 -1
  49. package/lib/commands/aside.js +24 -0
  50. package/lib/commands/ask.js +146 -0
  51. package/lib/commands/assistant.js +59 -4
  52. package/lib/commands/auth.js +8 -2
  53. package/lib/commands/backup.js +8 -4
  54. package/lib/commands/blog.js +110 -0
  55. package/lib/commands/brain.js +105 -14
  56. package/lib/commands/build.js +19 -5
  57. package/lib/commands/chat.js +180 -26
  58. package/lib/commands/chatCompanion.js +27 -0
  59. package/lib/commands/chatState.js +45 -0
  60. package/lib/commands/claude.js +35 -38
  61. package/lib/commands/contacts.js +134 -4
  62. package/lib/commands/contactsSendAt.js +21 -0
  63. package/lib/commands/conversation.js +35 -4
  64. package/lib/commands/dashboard.js +43 -0
  65. package/lib/commands/department.js +28 -7
  66. package/lib/commands/devResident.js +15 -3
  67. package/lib/commands/devSupervisor.js +13 -1
  68. package/lib/commands/discord.js +75 -39
  69. package/lib/commands/done.js +98 -28
  70. package/lib/commands/engagement.js +107 -6
  71. package/lib/commands/evidence.js +154 -0
  72. package/lib/commands/ext.js +23 -0
  73. package/lib/commands/fetchAllowlist.js +71 -0
  74. package/lib/commands/file.js +38 -1
  75. package/lib/commands/fleet.js +336 -4
  76. package/lib/commands/governance.js +12 -3
  77. package/lib/commands/gripe.js +44 -13
  78. package/lib/commands/hat.js +254 -9
  79. package/lib/commands/helpers.js +18 -1
  80. package/lib/commands/hyperion.js +58 -0
  81. package/lib/commands/image.js +68 -0
  82. package/lib/commands/inbox.js +74 -23
  83. package/lib/commands/kuma.js +1050 -0
  84. package/lib/commands/kumaSla.js +174 -0
  85. package/lib/commands/licence.js +52 -155
  86. package/lib/commands/loop.js +291 -0
  87. package/lib/commands/lucid.js +10 -4
  88. package/lib/commands/memory.js +85 -8
  89. package/lib/commands/mind.js +71 -0
  90. package/lib/commands/mon.js +69 -2
  91. package/lib/commands/monitoring.js +1 -1
  92. package/lib/commands/motion.js +2 -2
  93. package/lib/commands/nucleus.js +48 -17
  94. package/lib/commands/objective.js +59 -1
  95. package/lib/commands/offload.js +89 -0
  96. package/lib/commands/onboard.js +2 -1
  97. package/lib/commands/org.js +30 -3
  98. package/lib/commands/peek.js +28 -0
  99. package/lib/commands/plan.js +38 -0
  100. package/lib/commands/plugin.js +110 -0
  101. package/lib/commands/presence.js +89 -0
  102. package/lib/commands/provider.js +217 -0
  103. package/lib/commands/qlarity.js +19 -3
  104. package/lib/commands/quoxchat.js +13 -4
  105. package/lib/commands/quoxseo.js +69 -0
  106. package/lib/commands/reach.js +2 -1
  107. package/lib/commands/research.js +79 -0
  108. package/lib/commands/router.js +77 -0
  109. package/lib/commands/rows.js +367 -0
  110. package/lib/commands/run.js +82 -10
  111. package/lib/commands/scope.js +2 -2
  112. package/lib/commands/server.js +405 -0
  113. package/lib/commands/setup.js +33 -7
  114. package/lib/commands/shortcut.js +41 -0
  115. package/lib/commands/skills.js +68 -9
  116. package/lib/commands/slot.js +260 -7
  117. package/lib/commands/status.js +1 -1
  118. package/lib/commands/team.js +3 -1
  119. package/lib/commands/tips.js +23 -0
  120. package/lib/commands/tool.js +133 -45
  121. package/lib/commands/trace.js +12 -7
  122. package/lib/commands/trigger.js +95 -0
  123. package/lib/commands/update.js +46 -0
  124. package/lib/commands/vault.js +31 -8
  125. package/lib/commands/vm.js +237 -0
  126. package/lib/commands/voice.js +34 -3
  127. package/lib/commands/volt.js +45 -3
  128. package/lib/commands/walkthrough.js +32 -0
  129. package/lib/commands/watch.js +73 -21
  130. package/lib/commands/welcome.js +14 -0
  131. package/lib/commands/workflow.js +189 -32
  132. package/lib/commands/x.js +13 -4
  133. package/lib/commands/youtube.js +172 -0
  134. package/lib/core/actingAs.js +2 -2
  135. package/lib/core/actionItems.js +115 -0
  136. package/lib/core/activeSummary.js +186 -0
  137. package/lib/core/agentAlias.js +2 -0
  138. package/lib/core/agentFamilies.js +7 -0
  139. package/lib/core/aside.js +54 -0
  140. package/lib/core/askDecision.js +60 -0
  141. package/lib/core/auth.js +164 -13
  142. package/lib/core/callerSource.js +1 -1
  143. package/lib/core/cliBuild.js +62 -0
  144. package/lib/core/client.js +152 -35
  145. package/lib/core/commandRegistrars.js +130 -0
  146. package/lib/core/config.js +143 -25
  147. package/lib/core/customHats.js +168 -0
  148. package/lib/core/doneEngineFetch.js +8 -0
  149. package/lib/core/errors.js +329 -20
  150. package/lib/core/findingReconcile.js +90 -0
  151. package/lib/core/global-flags.js +105 -5
  152. package/lib/core/governedSession.js +1 -3
  153. package/lib/core/hyperion.js +102 -0
  154. package/lib/core/inboxResolution.js +65 -0
  155. package/lib/core/lazyCommands.js +4 -1
  156. package/lib/core/liveSessionsReport.js +1 -1
  157. package/lib/core/loopGlyph.js +97 -0
  158. package/lib/core/offload.js +533 -0
  159. package/lib/core/org-override.js +1 -1
  160. package/lib/core/orgResolve.js +70 -0
  161. package/lib/core/output.js +11 -11
  162. package/lib/core/peek.js +126 -0
  163. package/lib/core/profileUsers.js +51 -0
  164. package/lib/core/promptCancel.js +18 -0
  165. package/lib/core/repoPath.js +11 -0
  166. package/lib/core/residentSeed.js +3 -1
  167. package/lib/core/rootOptions.js +39 -0
  168. package/lib/core/router.js +168 -0
  169. package/lib/core/selfUpdate.js +188 -0
  170. package/lib/core/sessionHeartbeat.js +12 -8
  171. package/lib/core/sessionsOverview.js +64 -0
  172. package/lib/core/shortcut.js +83 -0
  173. package/lib/core/slotKeeper.js +248 -3
  174. package/lib/core/slotWake.js +120 -0
  175. package/lib/core/supervisorEngine.js +71 -2
  176. package/lib/core/version.js +1 -1
  177. package/lib/core/vm.js +366 -0
  178. package/lib/core/vmTui.js +86 -0
  179. package/lib/core/walkthrough.js +35 -0
  180. package/lib/dragon/companionView.js +34 -0
  181. package/lib/dragon/frames.js +118 -0
  182. package/lib/dragon/jobs.js +123 -0
  183. package/lib/dragon/renderWorker.js +15 -0
  184. package/lib/dragon/select.js +72 -0
  185. package/lib/evidence/bundleExport.js +47 -0
  186. package/lib/evidence/bundleVerify.js +77 -0
  187. package/lib/hyperion/deck.js +29 -0
  188. package/lib/lucid/LucidCard.jsx +68 -0
  189. package/lib/lucid/LucidIntro.jsx +45 -58
  190. package/lib/lucid/cards.js +86 -0
  191. package/lib/lucid/markdown.js +81 -0
  192. package/lib/lucid/motion.js +56 -0
  193. package/lib/quoxon/EXTENSIONS.md +23 -0
  194. package/lib/quoxon/activity.js +14 -2
  195. package/lib/quoxon/aocl.js +117 -0
  196. package/lib/quoxon/api.js +690 -25
  197. package/lib/quoxon/app.js +3586 -250
  198. package/lib/quoxon/approvalFacts.js +96 -0
  199. package/lib/quoxon/approvalSentence.js +44 -0
  200. package/lib/quoxon/approvalsView.js +384 -0
  201. package/lib/quoxon/asideView.js +40 -0
  202. package/lib/quoxon/ask.js +121 -0
  203. package/lib/quoxon/away.js +52 -0
  204. package/lib/quoxon/bang.js +224 -0
  205. package/lib/quoxon/bulkRisk.js +17 -0
  206. package/lib/quoxon/capabilities.js +13 -6
  207. package/lib/quoxon/cells.js +53 -0
  208. package/lib/quoxon/commandShort.js +124 -0
  209. package/lib/quoxon/commands.js +429 -39
  210. package/lib/quoxon/connect.js +210 -23
  211. package/lib/quoxon/connectCard.js +19 -2
  212. package/lib/quoxon/consent.js +86 -0
  213. package/lib/quoxon/copy.js +4 -3
  214. package/lib/quoxon/countdownLine.js +20 -0
  215. package/lib/quoxon/deck.js +30 -0
  216. package/lib/quoxon/demo.autoplay.js +29 -0
  217. package/lib/quoxon/demo.fixtures.js +1 -1
  218. package/lib/quoxon/demo.js +46 -3
  219. package/lib/quoxon/devObserver.js +20 -0
  220. package/lib/quoxon/dividerAocl.js +192 -0
  221. package/lib/quoxon/dividerRule.js +57 -0
  222. package/lib/quoxon/dragonCompanion.js +255 -0
  223. package/lib/quoxon/dragonModes.js +151 -0
  224. package/lib/quoxon/dragonMoments.js +82 -0
  225. package/lib/quoxon/dragonReads.js +68 -0
  226. package/lib/quoxon/dragonView.js +41 -0
  227. package/lib/quoxon/earlyInput.js +40 -0
  228. package/lib/quoxon/echo.js +15 -0
  229. package/lib/quoxon/egg.js +2 -2
  230. package/lib/quoxon/events.js +266 -26
  231. package/lib/quoxon/extensions.js +276 -0
  232. package/lib/quoxon/feedback.js +50 -0
  233. package/lib/quoxon/gallery.js +21 -7
  234. package/lib/quoxon/handoffOffer.js +78 -0
  235. package/lib/quoxon/hatSwitch.js +37 -0
  236. package/lib/quoxon/headless.js +161 -0
  237. package/lib/quoxon/hostUrls.js +41 -0
  238. package/lib/quoxon/inboxView.js +79 -0
  239. package/lib/quoxon/indicator.js +6 -2
  240. package/lib/quoxon/installCli.js +79 -0
  241. package/lib/quoxon/intent.js +12 -1
  242. package/lib/quoxon/keys.js +51 -8
  243. package/lib/quoxon/launcher.js +103 -0
  244. package/lib/quoxon/loopCard.js +456 -0
  245. package/lib/quoxon/loopCards.js +180 -0
  246. package/lib/quoxon/loopStream.js +87 -0
  247. package/lib/quoxon/main.js +101 -160
  248. package/lib/quoxon/markdown.js +45 -21
  249. package/lib/quoxon/markdownSettle.js +19 -0
  250. package/lib/quoxon/matrixDomains.json +46 -0
  251. package/lib/quoxon/mentions.js +90 -0
  252. package/lib/quoxon/mind.js +103 -0
  253. package/lib/quoxon/mode.js +46 -0
  254. package/lib/quoxon/model.js +132 -26
  255. package/lib/quoxon/modelPrefs.js +345 -0
  256. package/lib/quoxon/motion/clock.js +3 -2
  257. package/lib/quoxon/motion/level.js +91 -0
  258. package/lib/quoxon/mouse.js +222 -0
  259. package/lib/quoxon/noai.js +57 -0
  260. package/lib/quoxon/objectiveOffer.js +82 -0
  261. package/lib/quoxon/objects/away.js +72 -0
  262. package/lib/quoxon/objects/fetch.js +17 -5
  263. package/lib/quoxon/objects/fleet.js +149 -0
  264. package/lib/quoxon/objects/health.js +186 -0
  265. package/lib/quoxon/objects/index.js +46 -7
  266. package/lib/quoxon/objects/mirror.js +137 -0
  267. package/lib/quoxon/objects/model.js +33 -0
  268. package/lib/quoxon/objects/peek.js +17 -0
  269. package/lib/quoxon/objects/proof.js +12 -4
  270. package/lib/quoxon/objects/prove.js +130 -0
  271. package/lib/quoxon/objects/replay.js +125 -0
  272. package/lib/quoxon/objects/sitrep.js +58 -0
  273. package/lib/quoxon/objects/view.js +11 -0
  274. package/lib/quoxon/onboarding/copy.js +32 -4
  275. package/lib/quoxon/onboarding/credential.js +17 -4
  276. package/lib/quoxon/onboarding/dockerOffer.js +45 -0
  277. package/lib/quoxon/onboarding/failureCause.js +33 -0
  278. package/lib/quoxon/onboarding/finish.js +15 -0
  279. package/lib/quoxon/onboarding/index.js +34 -7
  280. package/lib/quoxon/onboarding/installRunner.js +64 -5
  281. package/lib/quoxon/onboarding/preflight.js +17 -3
  282. package/lib/quoxon/onboarding/probe.js +65 -0
  283. package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
  284. package/lib/quoxon/onboarding/view.js +76 -19
  285. package/lib/quoxon/orgLabel.js +10 -0
  286. package/lib/quoxon/orgSwitch.js +59 -0
  287. package/lib/quoxon/ownBroadcast.js +22 -0
  288. package/lib/quoxon/palette.js +43 -0
  289. package/lib/quoxon/planSteps.js +51 -0
  290. package/lib/quoxon/print.js +12 -5
  291. package/lib/quoxon/profileSwitch.js +53 -0
  292. package/lib/quoxon/promptHistory.js +41 -0
  293. package/lib/quoxon/providers.js +48 -0
  294. package/lib/quoxon/qart.js +20 -51
  295. package/lib/quoxon/qdragon.js +410 -0
  296. package/lib/quoxon/qdragonFly.js +62 -0
  297. package/lib/quoxon/qdragonScenes.js +282 -0
  298. package/lib/quoxon/qeyes.js +213 -0
  299. package/lib/quoxon/qmotion.js +75 -37
  300. package/lib/quoxon/render.js +740 -124
  301. package/lib/quoxon/resend.js +72 -0
  302. package/lib/quoxon/roster.js +117 -17
  303. package/lib/quoxon/route.js +6 -5
  304. package/lib/quoxon/rows.js +954 -0
  305. package/lib/quoxon/rowsStore.js +80 -0
  306. package/lib/quoxon/screenContext.js +150 -0
  307. package/lib/quoxon/screenFrames.js +254 -0
  308. package/lib/quoxon/serverChip.js +42 -0
  309. package/lib/quoxon/session.js +182 -2
  310. package/lib/quoxon/sessionDead.js +17 -0
  311. package/lib/quoxon/signin.js +125 -0
  312. package/lib/quoxon/slash/agents.js +112 -0
  313. package/lib/quoxon/slash/argSources.js +65 -0
  314. package/lib/quoxon/slash/args.js +65 -0
  315. package/lib/quoxon/slash/automate.js +89 -0
  316. package/lib/quoxon/slash/broadcast.js +140 -0
  317. package/lib/quoxon/slash/broadcastRun.js +108 -0
  318. package/lib/quoxon/slash/card.js +30 -0
  319. package/lib/quoxon/slash/challenge.js +114 -0
  320. package/lib/quoxon/slash/cliCommand.js +94 -0
  321. package/lib/quoxon/slash/cliLeaves.json +1023 -0
  322. package/lib/quoxon/slash/compact.js +1 -1
  323. package/lib/quoxon/slash/confirm.js +30 -0
  324. package/lib/quoxon/slash/crystallize.js +69 -0
  325. package/lib/quoxon/slash/dashboard.js +136 -0
  326. package/lib/quoxon/slash/draft.js +54 -0
  327. package/lib/quoxon/slash/evidenceCmd.js +34 -0
  328. package/lib/quoxon/slash/hatWalk.js +206 -0
  329. package/lib/quoxon/slash/inverses.js +77 -0
  330. package/lib/quoxon/slash/killer.js +24 -0
  331. package/lib/quoxon/slash/killerApi.js +16 -0
  332. package/lib/quoxon/slash/ledger.js +13 -8
  333. package/lib/quoxon/slash/listView.js +3 -1
  334. package/lib/quoxon/slash/modelCmd.js +261 -0
  335. package/lib/quoxon/slash/palette.js +22 -8
  336. package/lib/quoxon/slash/paletteController.js +117 -7
  337. package/lib/quoxon/slash/panels.js +21 -6
  338. package/lib/quoxon/slash/picker.js +20 -2
  339. package/lib/quoxon/slash/qpeCmd.js +35 -0
  340. package/lib/quoxon/slash/restart.js +95 -13
  341. package/lib/quoxon/slash/resume.js +18 -1
  342. package/lib/quoxon/slash/rowCmd.js +157 -0
  343. package/lib/quoxon/slash/rowWalk.js +175 -0
  344. package/lib/quoxon/slash/settings.js +16 -6
  345. package/lib/quoxon/slash/spawn.js +177 -0
  346. package/lib/quoxon/slash/stepUp.js +274 -0
  347. package/lib/quoxon/slash/stepUpCard.js +60 -0
  348. package/lib/quoxon/slash/stepUpCmds.js +78 -0
  349. package/lib/quoxon/slash/todoCmd.js +122 -0
  350. package/lib/quoxon/slash/undoApi.js +38 -0
  351. package/lib/quoxon/slash/undoCmd.js +49 -0
  352. package/lib/quoxon/slash/undoWish.js +15 -0
  353. package/lib/quoxon/slash/watch.js +164 -0
  354. package/lib/quoxon/slash/wish.js +23 -0
  355. package/lib/quoxon/slash/wishCmd.js +32 -0
  356. package/lib/quoxon/slotChat.js +300 -0
  357. package/lib/quoxon/slotFormat.js +195 -0
  358. package/lib/quoxon/slotRun.js +131 -0
  359. package/lib/quoxon/sound/mapping.js +2 -1
  360. package/lib/quoxon/stray.js +22 -0
  361. package/lib/quoxon/suggestCommand.js +90 -0
  362. package/lib/quoxon/term.js +46 -11
  363. package/lib/quoxon/textBuffer.js +176 -2
  364. package/lib/quoxon/theme.js +58 -25
  365. package/lib/quoxon/tips.js +433 -0
  366. package/lib/quoxon/toolCards.js +30 -0
  367. package/lib/quoxon/toolError.js +41 -0
  368. package/lib/quoxon/transcript.js +42 -4
  369. package/lib/quoxon/web/qface.js +18 -10
  370. package/lib/quoxon/welcome.js +1 -1
  371. package/lib/quoxon/whatsnew.js +136 -0
  372. package/lib/quoxon/whatsnew.jsonl +134 -0
  373. package/lib/quoxon/windowOrg.js +123 -0
  374. package/lib/server/devServers.js +137 -0
  375. package/lib/server/diag.js +129 -0
  376. package/lib/server/doctor.js +71 -0
  377. package/lib/server/install.js +149 -0
  378. package/lib/server/pluginEnable.js +161 -0
  379. package/lib/server/skew.js +56 -0
  380. package/lib/server/snapshot.js +131 -0
  381. package/lib/server/status.js +128 -0
  382. package/lib/server/sys.js +65 -0
  383. package/lib/server/uninstall.js +63 -0
  384. package/lib/server/update.js +234 -0
  385. package/lib/toolOutputShape.js +315 -0
  386. package/lib/tui/views/HyperionDeck.jsx +79 -0
  387. package/lib/tui/views/LucidDeck.jsx +209 -217
  388. package/package.json +5 -1
  389. package/vendor/dragonlab/SOURCE +4 -0
  390. package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
  391. package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
  392. package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
  393. package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
  394. package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
  395. package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
  396. package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
  397. package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
  398. package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
  399. package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
  400. package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
  401. package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
  402. package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
  403. package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
  404. package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
  405. package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
  406. package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
  407. package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
  408. package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
  409. package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
  410. package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
  411. package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
  412. package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
  413. package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
  414. package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
  415. package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
  416. package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
  417. package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
  418. package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
  419. package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
  420. package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
  421. package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
  422. package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
  423. package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
  424. package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
  425. package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
  426. package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
  427. package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
  428. package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
  429. package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
  430. package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
  431. package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
  432. package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
  433. package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
  434. package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
  435. package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
  436. package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
  437. package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
  438. package/vendor/dragonlab/lib/power-effects.js +294 -0
  439. package/vendor/dragonlab/lib/power-registry.js +74 -0
  440. package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
  441. package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
  442. 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 of items to render so the selected one is visible within `rows` lines. */
48
- export function windowItems(items, sel, expandedRef, rows, w, base) {
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 < items.length && used + heights[end] <= rows) { used += heights[end]; end++; }
59
- if (end === start && items.length) end = start + 1;
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
- function Pill({ urgency }) {
88
- const u = URGENCY[urgency] || URGENCY.whenever;
89
- const hot = urgency === 'now' || urgency === 'today' || urgency === 'this week';
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, cols }) {
101
+ function Hints({ items }) {
97
102
  return (
98
- <Text wrap="truncate" color={PALETTE.dim}>
103
+ <Text wrap="truncate" color={c('muted')}>
99
104
  {items.map(([k, l], i) => (
100
- <Text key={k + l}>{i ? ' ' : ''}<Text bold color={PALETTE.brand}>{k}</Text><Text color={PALETTE.muted}> {l}</Text></Text>
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
- function Panel({ title, count, focused, width, height, note, children }) {
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
- <Box flexDirection="column" width={width} height={height} borderStyle="round"
109
- borderColor={focused ? PALETTE.brand : '#3F3F46'} paddingX={1}>
110
- <Box>
111
- {focused
112
- ? <Text backgroundColor={PALETTE.brand} color="#0B1220" bold> ▶ {title} </Text>
113
- : <Text color={PALETTE.dim} bold> {title} </Text>}
114
- <Text color={focused ? PALETTE.text : PALETTE.dim}> {count}</Text>
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 [tick, setTick] = useState(0);
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, Date.now()); fresh++; }
187
- for (const i of prevNeeds.current) if (!nowRefs.has(i.ref)) ghosts.current.push({ item: i, at: Date.now() });
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
- const h = setInterval(() => setTick((x) => x + 1), 500);
207
- return () => { clearInterval(t); clearInterval(h); };
228
+ return () => clearInterval(t);
208
229
  }, [refresh, intervalMs]);
209
230
 
210
- useEffect(() => {
211
- if (!fetching) return undefined;
212
- const s = setInterval(() => setSpin((x) => x + 1), 120);
213
- return () => clearInterval(s);
214
- }, [fetching]);
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 c = confirm; setConfirm(null);
245
- if (c) c.run();
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 c = changed[safeCh];
314
- if (!c) { flash('Nothing is selected.', 'dim'); return; }
315
- flash(`${c.L0 || 'Change'}${changedWords(c) ? ': ' + changedWords(c) : ''}`, 'ok');
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 noteHex = noteLive ? ({ ok: STATE_HEX.ok, bad: STATE_HEX.blind, busy: PALETTE.brand, dim: PALETTE.muted }[noteLive.tone]) : null;
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 - (slide ? slide * 4 : 0);
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 - 3;
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 covHex = cov.expected && cov.live < cov.expected ? STATE_HEX.attention : STATE_HEX.ok;
421
- const badgeBg = allNeeds.length ? (arrived > 0 && !flashOn ? '#B45309' : STATE_HEX.attention) : null;
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 ? STATE_HEX.attention : STATE_HEX.blind) : '#3F3F46'} paddingX={1}>
462
+ <Box borderStyle="round" borderColor={err ? (stale ? c('warn') : c('error')) : c('faint')} paddingX={1}>
425
463
  <Text wrap="truncate">
426
- {[...'◉ LUCID'].map((ch, i) => <Text key={i} bold color={GRAD[i % GRAD.length]}>{ch}</Text>)}
427
- <Text color={PALETTE.dim}> │ </Text>
428
- <Text bold>{clockLondonSec(t)}</Text><Text color={PALETTE.dim}> London</Text>
429
- <Text color={PALETTE.dim}> │ </Text>
430
- {sit ? <Text color={covHex}>{meter(cov.live, cov.expected)} {cov.live}/{cov.expected}</Text> : <Text color={PALETTE.dim}>▱▱▱▱▱ -/-</Text>}
431
- <Text color={PALETTE.dim}> │ </Text>
432
- {!sit ? <Text color={PALETTE.dim}>- need you</Text> : badgeBg
433
- ? <Text backgroundColor={badgeBg} color="#0B1220" bold> {allNeeds.length} need you </Text>
434
- : <Text bold color={STATE_HEX.ok}>0 need you</Text>}
435
- {filterLabel ? <Text bold color={PALETTE.accent}> │ filter: {filterLabel}</Text> : null}
436
- {arrived > 0 ? <Text bold color={STATE_HEX.ok}> +{arrived} new</Text> : null}
437
- <Text color={PALETTE.dim}> │ </Text>
438
- <Text color={PALETTE.muted}>
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 ? STATE_HEX.attention : STATE_HEX.blind}>
446
- {stale ? `⚠ STALE ${agoS}s` : '✖ 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")'}
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={PALETTE.brand} color="#0B1220" bold>{pad(' ↑↓ move · Enter open · Tab switch panel · ? help ', cols - 2)}</Text>
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 ? STATE_HEX.blind : PALETTE.dim}>
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={PALETTE.dim}>r refresh · q quit</Text></Box>
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 iw = innerOf(w);
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={STATE_HEX.ok}>{filter ? 'Nothing needs you in this area.' : 'Nothing needs you right now.'}{spots.length ? <Text color={PALETTE.dim}> (check what I can't see)</Text> : null}</Text>
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 tw = iw - 14;
481
- const isNewOf = (it) => newAt.current.has(it.ref) && wall - newAt.current.get(it.ref) < NEW_MS;
482
- const textOf = (it) => (isNewOf(it) ? '● ' : '') + (it.L0 || '');
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 = fitWindow(needs, safeSel, expandedRef, rowsAvail - nGhost, iw, (it) => wrap(textOf(it), tw).length);
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={PALETTE.dim}> ↑ {above} more</Text> : null}
490
- {needs.slice(win.start, win.end).map((it, k) => {
491
- const i = win.start + k;
492
- const on = i === safeSel;
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={PALETTE.dim} dimColor> ✓ resolved {clip(g.item.L0 || '', iw - 14)}</Text>
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={PALETTE.dim}> ↓ {below} more</Text> : null}
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 = innerOf(w);
540
+ const iw = Math.max(10, w - 2);
545
541
  const f = focus('changed');
546
- if (!changed.length) return <Text color={PALETTE.dim}>Nothing has changed.</Text>;
542
+ if (!changed.length) return <Text color={c('muted')}>Nothing has changed.</Text>;
547
543
  const tw = iw - 7;
548
- const textOf = (c) => {
549
- const words = changedWords(c);
550
- return (c.L0 || '') + (words ? ` · ${words}` : '');
544
+ const textOf = (c0) => {
545
+ const words = changedWords(c0);
546
+ return (c0.L0 || '') + (words ? ` · ${words}` : '');
551
547
  };
552
- const win = fitWindow(changed, safeCh, null, rowsAvail, iw, (c) => wrap(textOf(c), tw).length);
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={PALETTE.dim}> ↑ {above} more</Text> : null}
558
- {changed.slice(win.start, win.end).map((c, k) => {
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={c.ref + c.ts} flexDirection="column">
563
- {wrap(textOf(c), tw).map((ln, li) => (
564
- <Text key={li} wrap="truncate" backgroundColor={on ? BAR_BG : undefined} bold={on} dimColor={!f}>
565
- <Text color={on ? '#FFFFFF' : PALETTE.dim}>{li === 0 ? `${on ? '▶ ' : ' '}${pad(ageOf(c.ts, t), 3)} ` : ' '.repeat(7)}</Text>
566
- <Text color={on ? '#FFFFFF' : PALETTE.text}>{pad(ln, tw)}</Text>
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={PALETTE.dim}> ↓ {below} more</Text> : null}
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 iw = innerOf(w);
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 win = winLines(list.length, safeAr, rowsAvail);
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
- <Text key={a.area || 'all'} wrap="truncate" backgroundColor={on ? BAR_BG : undefined} bold={on} dimColor={!f && !active}>
589
- <Text color={on ? '#FFFFFF' : PALETTE.dim}>{on ? '▶ ' : ' '}</Text>
590
- <Text color={a.state ? (STATE_HEX[a.state] || PALETTE.muted) : PALETTE.muted}>{a.state ? (GLYPH[a.state] || '?') : '◉'} </Text>
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={PALETTE.brand}>{pad(k, 14)}</Text><Text color={PALETTE.text}>{d}</Text></Text>
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
- <Panel title="KEYS" count="" focused width={cols - 2} height={bodyH}>
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={PALETTE.dim}>Press ? or Esc to go back.</Text>
625
- </Panel>
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 - sl} marginLeft={sl}>
633
- <Panel title={PANE_TITLE.areas} count={counts.areas} focused={focus('areas')} width={leftW} height={bodyH}>
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
- <Panel title={PANE_TITLE.needs} count={counts.needs} focused={focus('needs')} width={mainW + sl} height={bodyH} note={filterLabel ? `· ${filterLabel}` : ''}>
639
- {needsBody(mainW, listRows)}
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
- <Panel title={PANE_TITLE.needs} count={counts.needs} focused={focus('needs')} width={mainW + sl} height={needsH} note={filterLabel ? `· ${filterLabel}` : ''}>
648
- {needsBody(mainW, needsH - 3)}
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
- <Panel title={PANE_TITLE[pane]} count={counts[pane]} focused width={stackW} height={focH} note={filterLabel && pane === 'needs' ? `· ${filterLabel}` : ''}>
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={PALETTE.dim}> ▸ {PANE_TITLE[p]} {counts[p]} (Tab)</Text>
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 c = changed[safeCh];
691
- where = c ? `▶ Since you looked · ${safeCh + 1} of ${changed.length} · ${clip(c.L0 || '', 60)}` : '▶ Since you looked · nothing changed';
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={STATE_HEX.attention} color="#0B1220" bold> {confirm.text.endsWith('y/n') ? confirm.text : confirm.text + ' y/n'}{confirm.extra ? ' · ' + confirm.extra : ''} </Text>
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={noteHex}>{noteLive.tone === 'ok' ? '✔ ' : noteLive.tone === 'bad' ? '✖ ' : noteLive.tone === 'busy' ? '◐ ' : 'ℹ '}{noteLive.text}</Text>
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 ? STATE_HEX.attention : PALETTE.dim}>
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={PALETTE.accent}>{where}</Text>
719
- <Text color={PALETTE.dim}> · </Text>
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={PALETTE.brand}>{k}:</Text><Text color={PALETTE.muted}> {l}</Text></Text>
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} cols={cols} />
716
+ <Hints items={hints} />
725
717
  </Box>
726
718
  );
727
719
  }