quoxcli 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +35 -0
- package/README.md +144 -2
- package/bin/quox.js +265 -0
- package/bin/quoxon.js +8 -0
- package/lib/api/endpoints/admin.js +151 -0
- package/lib/api/endpoints/aee.js +38 -0
- package/lib/api/endpoints/agent-attach.js +126 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +63 -0
- package/lib/api/endpoints/assistants.js +62 -0
- package/lib/api/endpoints/audit.js +14 -0
- package/lib/api/endpoints/auth.js +50 -0
- package/lib/api/endpoints/awareness.js +34 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/backup-schedules.js +20 -0
- package/lib/api/endpoints/backups.js +86 -0
- package/lib/api/endpoints/brain.js +140 -0
- package/lib/api/endpoints/budgets.js +62 -0
- package/lib/api/endpoints/calendarq.js +71 -0
- package/lib/api/endpoints/catalog.js +14 -0
- package/lib/api/endpoints/chat.js +61 -0
- package/lib/api/endpoints/consent.js +26 -0
- package/lib/api/endpoints/contactsq.js +68 -0
- package/lib/api/endpoints/conversations.js +46 -0
- package/lib/api/endpoints/custom-roles.js +92 -0
- package/lib/api/endpoints/divisions.js +56 -0
- package/lib/api/endpoints/domains.js +38 -0
- package/lib/api/endpoints/engagement.js +231 -0
- package/lib/api/endpoints/engine.js +11 -0
- package/lib/api/endpoints/entity.js +50 -0
- package/lib/api/endpoints/executions.js +45 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/file.js +20 -0
- package/lib/api/endpoints/files.js +40 -0
- package/lib/api/endpoints/github.js +21 -0
- package/lib/api/endpoints/governance.js +43 -0
- package/lib/api/endpoints/hat.js +66 -0
- package/lib/api/endpoints/inbox.js +52 -0
- package/lib/api/endpoints/inboxq.js +120 -0
- package/lib/api/endpoints/index.js +242 -0
- package/lib/api/endpoints/invites.js +19 -0
- package/lib/api/endpoints/llm-policy.js +43 -0
- package/lib/api/endpoints/lucid.js +89 -0
- package/lib/api/endpoints/members.js +32 -0
- package/lib/api/endpoints/memory.js +94 -0
- package/lib/api/endpoints/mfa.js +32 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +184 -0
- package/lib/api/endpoints/notesq.js +43 -0
- package/lib/api/endpoints/objectives.js +68 -0
- package/lib/api/endpoints/orgs.js +38 -0
- package/lib/api/endpoints/plugins.js +152 -0
- package/lib/api/endpoints/powers.js +75 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/prompts.js +69 -0
- package/lib/api/endpoints/qlarity.js +122 -0
- package/lib/api/endpoints/quoxbox.js +55 -0
- package/lib/api/endpoints/quoxchat.js +54 -0
- package/lib/api/endpoints/quoxflow.js +32 -0
- package/lib/api/endpoints/quoxplan.js +121 -0
- package/lib/api/endpoints/quoxseo.js +114 -0
- package/lib/api/endpoints/reach.js +47 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/runs.js +38 -0
- package/lib/api/endpoints/safety.js +53 -0
- package/lib/api/endpoints/scim.js +46 -0
- package/lib/api/endpoints/scopes.js +14 -0
- package/lib/api/endpoints/security.js +69 -0
- package/lib/api/endpoints/setup.js +90 -0
- package/lib/api/endpoints/skills.js +200 -0
- package/lib/api/endpoints/sso.js +67 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/tags.js +32 -0
- package/lib/api/endpoints/teams.js +56 -0
- package/lib/api/endpoints/telephony.js +46 -0
- package/lib/api/endpoints/tg.js +56 -0
- package/lib/api/endpoints/tools.js +50 -0
- package/lib/api/endpoints/traces.js +157 -0
- package/lib/api/endpoints/triggers.js +96 -0
- package/lib/api/endpoints/user-library.js +41 -0
- package/lib/api/endpoints/users.js +20 -0
- package/lib/api/endpoints/v1.js +585 -0
- package/lib/api/endpoints/vault.js +192 -0
- package/lib/api/endpoints/voice.js +22 -0
- package/lib/api/endpoints/volt.js +84 -0
- package/lib/api/endpoints/ward.js +47 -0
- package/lib/api/endpoints/webhooks.js +46 -0
- package/lib/api/endpoints/workflows.js +169 -0
- package/lib/api/endpoints/x.js +32 -0
- package/lib/api/endpoints.js +15 -0
- package/lib/commands/actions.js +61 -0
- package/lib/commands/admin.js +375 -0
- package/lib/commands/aee.js +99 -0
- package/lib/commands/agent.js +337 -0
- package/lib/commands/agentAttach.js +1528 -0
- package/lib/commands/approvals.js +119 -0
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +144 -0
- package/lib/commands/assistant.js +169 -0
- package/lib/commands/auth.js +464 -0
- package/lib/commands/awareness.js +88 -0
- package/lib/commands/backup.js +377 -0
- package/lib/commands/brain.js +1085 -0
- package/lib/commands/budget.js +241 -0
- package/lib/commands/build.js +170 -0
- package/lib/commands/calendar.js +216 -0
- package/lib/commands/capabilities.js +34 -0
- package/lib/commands/chat.js +295 -0
- package/lib/commands/claude.js +380 -0
- package/lib/commands/config-cmd.js +106 -0
- package/lib/commands/consent.js +84 -0
- package/lib/commands/contacts.js +1016 -0
- package/lib/commands/contactsSelftest.js +132 -0
- package/lib/commands/conversation.js +105 -0
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +96 -0
- package/lib/commands/dev.js +620 -0
- package/lib/commands/devResident.js +326 -0
- package/lib/commands/devSupervisor.js +1062 -0
- package/lib/commands/discord.js +132 -0
- package/lib/commands/doctor.js +181 -0
- package/lib/commands/domain.js +114 -0
- package/lib/commands/done.js +347 -0
- package/lib/commands/engagement.js +597 -0
- package/lib/commands/entity.js +119 -0
- package/lib/commands/facebook.js +212 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +165 -0
- package/lib/commands/fleet.js +1189 -0
- package/lib/commands/governance.js +647 -0
- package/lib/commands/gripe.js +355 -0
- package/lib/commands/hat.js +374 -0
- package/lib/commands/helpers.js +96 -0
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/inbox.js +173 -0
- package/lib/commands/instagram.js +264 -0
- package/lib/commands/integration.js +100 -0
- package/lib/commands/inventory.js +115 -0
- package/lib/commands/invites.js +45 -0
- package/lib/commands/licence.js +139 -0
- package/lib/commands/linkedin.js +178 -0
- package/lib/commands/llm-policy.js +117 -0
- package/lib/commands/llm.js +45 -0
- package/lib/commands/lucid.js +597 -0
- package/lib/commands/mail.js +531 -0
- package/lib/commands/memory.js +361 -0
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +581 -0
- package/lib/commands/monitoring.js +86 -0
- package/lib/commands/motion.js +22 -0
- package/lib/commands/notes.js +1246 -0
- package/lib/commands/notesBatch.js +160 -0
- package/lib/commands/notesBridges.js +56 -0
- package/lib/commands/notesLinks.js +60 -0
- package/lib/commands/notification.js +57 -0
- package/lib/commands/nucleus.js +123 -0
- package/lib/commands/objective.js +240 -0
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +329 -0
- package/lib/commands/open.js +56 -0
- package/lib/commands/org.js +276 -0
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +944 -0
- package/lib/commands/plugin-doctor.js +1091 -0
- package/lib/commands/plugin.js +649 -0
- package/lib/commands/policy.js +78 -0
- package/lib/commands/powers.js +296 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/prompt.js +350 -0
- package/lib/commands/provider.js +159 -0
- package/lib/commands/qlarity.js +706 -0
- package/lib/commands/quoxbox.js +270 -0
- package/lib/commands/quoxchat.js +275 -0
- package/lib/commands/quoxseo.js +378 -0
- package/lib/commands/reach.js +159 -0
- package/lib/commands/role.js +286 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +358 -0
- package/lib/commands/run.js +187 -0
- package/lib/commands/safety.js +107 -0
- package/lib/commands/scim.js +88 -0
- package/lib/commands/scope.js +58 -0
- package/lib/commands/security.js +157 -0
- package/lib/commands/server.js +292 -0
- package/lib/commands/service.js +89 -0
- package/lib/commands/settings.js +99 -0
- package/lib/commands/setup.js +569 -0
- package/lib/commands/skillManifest.js +113 -0
- package/lib/commands/skillRightsFormat.js +38 -0
- package/lib/commands/skills.js +687 -0
- package/lib/commands/slot.js +1451 -0
- package/lib/commands/sso.js +229 -0
- package/lib/commands/status.js +137 -0
- package/lib/commands/tag.js +91 -0
- package/lib/commands/team.js +142 -0
- package/lib/commands/tg.js +240 -0
- package/lib/commands/tiktok.js +218 -0
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +304 -0
- package/lib/commands/trace.js +530 -0
- package/lib/commands/trigger.js +223 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +1447 -0
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +213 -0
- package/lib/commands/volt.js +570 -0
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +167 -0
- package/lib/commands/webhook.js +137 -0
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +660 -0
- package/lib/commands/x.js +448 -0
- package/lib/core/actingAs.js +136 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/aee.js +53 -0
- package/lib/core/agentAlias.js +163 -0
- package/lib/core/approvalContext.js +61 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +501 -0
- package/lib/core/banner.js +143 -0
- package/lib/core/brainPrime.js +90 -0
- package/lib/core/callerSource.js +129 -0
- package/lib/core/claudeLs.js +94 -0
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +693 -0
- package/lib/core/commandRegistrars.js +121 -0
- package/lib/core/completions.js +163 -0
- package/lib/core/config.js +669 -0
- package/lib/core/confirm.js +37 -0
- package/lib/core/customHats.js +168 -0
- package/lib/core/deeplinks.js +76 -0
- package/lib/core/doneEngineFetch.js +227 -0
- package/lib/core/errors.js +441 -0
- package/lib/core/global-flags.js +190 -0
- package/lib/core/governedSession.js +402 -0
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +122 -0
- package/lib/core/liveSessionsReport.js +112 -0
- package/lib/core/offload.js +504 -0
- package/lib/core/org-override.js +52 -0
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +239 -0
- package/lib/core/peek.js +126 -0
- package/lib/core/prodEnv.js +7 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/quoxglyph.js +158 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentAlarm.js +282 -0
- package/lib/core/residentAuth.js +243 -0
- package/lib/core/residentPump.js +229 -0
- package/lib/core/residentSeed.js +106 -0
- 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 +192 -0
- package/lib/core/sessionsOverview.js +224 -0
- package/lib/core/slotKeeper.js +527 -0
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +933 -0
- package/lib/core/systemdInstaller.js +256 -0
- package/lib/core/version.js +87 -0
- package/lib/core/vm.js +361 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +92 -0
- package/lib/lucid/blind.js +71 -0
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/deck.js +32 -0
- package/lib/lucid/fmt.js +139 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/lucid/sitrep.js +173 -0
- package/lib/plugins/loader.js +93 -0
- package/lib/quoxon/activity.js +116 -0
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +952 -0
- package/lib/quoxon/app.js +4032 -0
- package/lib/quoxon/approvalFacts.js +94 -0
- package/lib/quoxon/approvalsView.js +325 -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 +82 -0
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +112 -0
- package/lib/quoxon/commands.js +479 -0
- package/lib/quoxon/connect.js +654 -0
- package/lib/quoxon/connectCard.js +45 -0
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/context.js +68 -0
- package/lib/quoxon/copy.js +34 -0
- package/lib/quoxon/countdownLine.js +11 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +24 -0
- package/lib/quoxon/demo.js +76 -0
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +191 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonModes.js +150 -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 +36 -0
- package/lib/quoxon/events.js +677 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +237 -0
- 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/indicator.js +197 -0
- package/lib/quoxon/intent.js +52 -0
- package/lib/quoxon/keys.js +164 -0
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/main.js +176 -0
- package/lib/quoxon/markdown.js +341 -0
- 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 +311 -0
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +70 -0
- package/lib/quoxon/motion/color.js +35 -0
- package/lib/quoxon/motion/frame.js +14 -0
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/motion/reveal.js +54 -0
- package/lib/quoxon/motion/ring.js +79 -0
- package/lib/quoxon/mouse.js +211 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objects/agent.js +61 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/crystal.js +100 -0
- package/lib/quoxon/objects/fetch.js +182 -0
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +106 -0
- package/lib/quoxon/objects/ladder.js +71 -0
- package/lib/quoxon/objects/memory.js +44 -0
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +118 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +110 -0
- 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/skill.js +105 -0
- package/lib/quoxon/objects/view.js +87 -0
- package/lib/quoxon/objects/workflow.js +119 -0
- package/lib/quoxon/observers.js +42 -0
- package/lib/quoxon/onboarding/copy.js +263 -0
- package/lib/quoxon/onboarding/credential.js +108 -0
- package/lib/quoxon/onboarding/detect.js +22 -0
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +134 -0
- package/lib/quoxon/onboarding/index.js +94 -0
- package/lib/quoxon/onboarding/installRunner.js +284 -0
- package/lib/quoxon/onboarding/intent.js +53 -0
- package/lib/quoxon/onboarding/journey.js +73 -0
- package/lib/quoxon/onboarding/preflight.js +156 -0
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/runner.js +58 -0
- package/lib/quoxon/onboarding/view.js +682 -0
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +33 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/print.js +98 -0
- 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 +348 -0
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +61 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qglyph.js +143 -0
- package/lib/quoxon/qhello.js +58 -0
- package/lib/quoxon/qmotion.js +132 -0
- package/lib/quoxon/render.js +1167 -0
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +307 -0
- package/lib/quoxon/route.js +39 -0
- package/lib/quoxon/rows.js +711 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/rules.js +130 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/session.js +238 -0
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/skills.js +30 -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 +53 -0
- package/lib/quoxon/slash/cliLeaves.json +948 -0
- package/lib/quoxon/slash/compact.js +90 -0
- 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/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 +88 -0
- package/lib/quoxon/slash/listView.js +30 -0
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +118 -0
- package/lib/quoxon/slash/paletteController.js +169 -0
- package/lib/quoxon/slash/panels.js +116 -0
- package/lib/quoxon/slash/picker.js +56 -0
- package/lib/quoxon/slash/restart.js +202 -0
- package/lib/quoxon/slash/resume.js +59 -0
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +60 -0
- 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/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/index.js +186 -0
- package/lib/quoxon/sound/mapping.js +120 -0
- package/lib/quoxon/sound/output.js +52 -0
- package/lib/quoxon/sound/synth.js +122 -0
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +59 -0
- package/lib/quoxon/term.js +141 -0
- package/lib/quoxon/textBuffer.js +416 -0
- package/lib/quoxon/theme.js +286 -0
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +188 -0
- package/lib/quoxon/web/qface.js +89 -0
- package/lib/quoxon/welcome.js +52 -0
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +89 -0
- package/lib/quoxon/windowOrg.js +113 -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/app.jsx +289 -0
- package/lib/tui/components/Breadcrumb.jsx +42 -0
- package/lib/tui/components/ChatInput.jsx +68 -0
- package/lib/tui/components/Confirm.jsx +32 -0
- package/lib/tui/components/DataTable.jsx +270 -0
- package/lib/tui/components/DetailPanel.jsx +241 -0
- package/lib/tui/components/HelpOverlay.jsx +67 -0
- package/lib/tui/components/Layout.jsx +129 -0
- package/lib/tui/components/Palette.jsx +141 -0
- package/lib/tui/components/SearchInput.jsx +125 -0
- package/lib/tui/components/Sparkline.jsx +55 -0
- package/lib/tui/components/StatusBar.jsx +133 -0
- package/lib/tui/components/Toast.jsx +52 -0
- package/lib/tui/hooks/useApi.js +44 -0
- package/lib/tui/hooks/useHealth.js +90 -0
- package/lib/tui/hooks/usePolledApi.js +86 -0
- package/lib/tui/hooks/useTerminalSize.js +46 -0
- package/lib/tui/index.js +56 -0
- package/lib/tui/intro/Intro.jsx +332 -0
- package/lib/tui/jsx-loader.js +32 -0
- package/lib/tui/keys.js +87 -0
- package/lib/tui/registry.js +49 -0
- package/lib/tui/theme.js +103 -0
- package/lib/tui/views/AdminView.jsx +398 -0
- package/lib/tui/views/AgentView.jsx +204 -0
- package/lib/tui/views/BackupView.jsx +225 -0
- package/lib/tui/views/BrainView.jsx +223 -0
- package/lib/tui/views/ChatView.jsx +468 -0
- package/lib/tui/views/FilesView.jsx +218 -0
- package/lib/tui/views/FleetView.jsx +234 -0
- package/lib/tui/views/GovernanceView.jsx +453 -0
- package/lib/tui/views/HealthView.jsx +216 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/InboxView.jsx +228 -0
- package/lib/tui/views/LucidDeck.jsx +719 -0
- package/lib/tui/views/MainMenu.jsx +123 -0
- package/lib/tui/views/MemoryView.jsx +377 -0
- package/lib/tui/views/ObjectivesView.jsx +230 -0
- package/lib/tui/views/OrgView.jsx +229 -0
- package/lib/tui/views/ToolView.jsx +221 -0
- package/lib/tui/views/VaultView.jsx +422 -0
- package/lib/tui/views/WorkflowView.jsx +329 -0
- package/package.json +75 -4
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataTable — Paginated, selectable data table for list views
|
|
3
|
+
*
|
|
4
|
+
* Designed for the left panel of dual-panel layouts.
|
|
5
|
+
* Supports vim-style navigation, pagination, and keyboard selection.
|
|
6
|
+
*
|
|
7
|
+
* Props:
|
|
8
|
+
* columns - array of { key, label, width? } — a column with no
|
|
9
|
+
* `width` (or explicit `flex: true`) absorbs a share
|
|
10
|
+
* of the remaining terminal width alongside other
|
|
11
|
+
* flex columns. Columns with a numeric `width` are
|
|
12
|
+
* fixed and unaffected by flex distribution.
|
|
13
|
+
* rows - array of data objects
|
|
14
|
+
* selected - controlled selected row index
|
|
15
|
+
* onSelect - callback on Enter (receives row object)
|
|
16
|
+
* onSelectionChange- callback on cursor move (receives index)
|
|
17
|
+
* pageSize - rows per page (default: derived from terminal rows)
|
|
18
|
+
* loading - show spinner overlay
|
|
19
|
+
* emptyMessage - text when rows is empty
|
|
20
|
+
* active - accept keyboard input (default: true)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import React, { useState, useMemo, useEffect } from 'react';
|
|
24
|
+
import { Box, Text, useInput } from 'ink';
|
|
25
|
+
import { Spinner } from '@inkjs/ui';
|
|
26
|
+
import { matchKey } from '../keys.js';
|
|
27
|
+
import { colors } from '../theme.js';
|
|
28
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
29
|
+
|
|
30
|
+
// Minimum rows per page when deriving from a short terminal.
|
|
31
|
+
const MIN_PAGE_SIZE = 5;
|
|
32
|
+
// Rows reserved for header/footer/borders/etc. when deriving page size.
|
|
33
|
+
const CHROME_ROWS = 8;
|
|
34
|
+
// Width reserved for the selection marker ("▶ " / " ") before columns start.
|
|
35
|
+
const MARKER_WIDTH = 2;
|
|
36
|
+
// Floor width for a flex column so it never collapses to unreadable size.
|
|
37
|
+
const MIN_FLEX_WIDTH = 6;
|
|
38
|
+
|
|
39
|
+
export function DataTable({
|
|
40
|
+
columns,
|
|
41
|
+
rows = [],
|
|
42
|
+
selected: controlledSelected,
|
|
43
|
+
onSelect,
|
|
44
|
+
onSelectionChange,
|
|
45
|
+
pageSize: pageSizeProp,
|
|
46
|
+
loading = false,
|
|
47
|
+
emptyMessage = 'No items',
|
|
48
|
+
active = true,
|
|
49
|
+
}) {
|
|
50
|
+
// ── Responsive page size (only when caller doesn't pass one) ────────
|
|
51
|
+
const { rows: termRows, columns: termColumns } = useTerminalSize();
|
|
52
|
+
const pageSize = pageSizeProp != null
|
|
53
|
+
? pageSizeProp
|
|
54
|
+
: Math.max(MIN_PAGE_SIZE, termRows - CHROME_ROWS);
|
|
55
|
+
|
|
56
|
+
// ── Internal cursor state ───────────────────────────────────────────
|
|
57
|
+
const [internalSelected, setInternalSelected] = useState(0);
|
|
58
|
+
|
|
59
|
+
// Use controlled selection if provided, otherwise internal
|
|
60
|
+
const isControlled = controlledSelected !== undefined;
|
|
61
|
+
const selected = isControlled ? controlledSelected : internalSelected;
|
|
62
|
+
|
|
63
|
+
const setSelected = (valOrFn) => {
|
|
64
|
+
const nextVal = typeof valOrFn === 'function' ? valOrFn(selected) : valOrFn;
|
|
65
|
+
if (!isControlled) {
|
|
66
|
+
setInternalSelected(nextVal);
|
|
67
|
+
}
|
|
68
|
+
if (onSelectionChange) {
|
|
69
|
+
onSelectionChange(nextVal);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// ── Pagination ──────────────────────────────────────────────────────
|
|
74
|
+
const totalPages = Math.max(1, Math.ceil(rows.length / pageSize));
|
|
75
|
+
|
|
76
|
+
const currentPage = useMemo(() => {
|
|
77
|
+
return Math.floor(selected / pageSize);
|
|
78
|
+
}, [selected, pageSize]);
|
|
79
|
+
|
|
80
|
+
const pageRows = useMemo(() => {
|
|
81
|
+
const start = currentPage * pageSize;
|
|
82
|
+
return rows.slice(start, start + pageSize);
|
|
83
|
+
}, [rows, currentPage, pageSize]);
|
|
84
|
+
|
|
85
|
+
const pageOffset = currentPage * pageSize;
|
|
86
|
+
|
|
87
|
+
// ── Reset selection when rows change ────────────────────────────────
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (rows.length > 0 && selected >= rows.length) {
|
|
90
|
+
setSelected(Math.max(0, rows.length - 1));
|
|
91
|
+
}
|
|
92
|
+
}, [rows.length]);
|
|
93
|
+
|
|
94
|
+
// ── Keyboard handling ───────────────────────────────────────────────
|
|
95
|
+
useInput((input, key) => {
|
|
96
|
+
if (!active || rows.length === 0) return;
|
|
97
|
+
|
|
98
|
+
// Cursor movement: j/down, k/up
|
|
99
|
+
if (matchKey('down', input, key)) {
|
|
100
|
+
setSelected((s) => (s < rows.length - 1 ? s + 1 : 0));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (matchKey('up', input, key)) {
|
|
104
|
+
setSelected((s) => (s > 0 ? s - 1 : rows.length - 1));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Page navigation: n = next page, p = prev page
|
|
109
|
+
if (matchKey('nextPage', input, key)) {
|
|
110
|
+
const nextPageStart = Math.min((currentPage + 1) * pageSize, rows.length - 1);
|
|
111
|
+
if (currentPage < totalPages - 1) {
|
|
112
|
+
setSelected(nextPageStart);
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (matchKey('prevPage', input, key)) {
|
|
117
|
+
const prevPageStart = Math.max((currentPage - 1) * pageSize, 0);
|
|
118
|
+
if (currentPage > 0) {
|
|
119
|
+
setSelected(prevPageStart);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Select with Enter
|
|
125
|
+
if (matchKey('select', input, key)) {
|
|
126
|
+
if (onSelect && rows[selected]) {
|
|
127
|
+
onSelect(rows[selected]);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ── Column widths ───────────────────────────────────────────────────
|
|
134
|
+
// A column with an explicit numeric `width` is fixed (unchanged from
|
|
135
|
+
// legacy behavior). A column with `flex: true` or no `width` at all is a
|
|
136
|
+
// flex column: it absorbs an equal share of whatever terminal width is
|
|
137
|
+
// left over after fixed columns and the selection marker are accounted
|
|
138
|
+
// for, so long values (titles, URLs) expand on wide terminals instead of
|
|
139
|
+
// always being clipped at the old hard-coded 40-char cap.
|
|
140
|
+
const colWidths = useMemo(() => {
|
|
141
|
+
if (columns.length === 0) return [];
|
|
142
|
+
|
|
143
|
+
const isFlex = (col) => col.flex === true || col.width == null;
|
|
144
|
+
|
|
145
|
+
// Content-based width (legacy auto-calc): used as the fallback when no
|
|
146
|
+
// usable terminal width is available.
|
|
147
|
+
const contentWidth = (col) => {
|
|
148
|
+
const headerLen = col.label.length;
|
|
149
|
+
const maxDataLen = rows.reduce((max, row) => {
|
|
150
|
+
const val = String(row[col.key] ?? '');
|
|
151
|
+
return Math.max(max, val.length);
|
|
152
|
+
}, 0);
|
|
153
|
+
return Math.min(Math.max(headerLen, maxDataLen) + 2, 40);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const flexIndices = columns
|
|
157
|
+
.map((col, i) => (isFlex(col) ? i : -1))
|
|
158
|
+
.filter((i) => i !== -1);
|
|
159
|
+
|
|
160
|
+
// No flex columns — every width is explicit, behavior is unchanged.
|
|
161
|
+
if (flexIndices.length === 0) {
|
|
162
|
+
return columns.map((col) => col.width);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const fixedTotal = columns.reduce(
|
|
166
|
+
(sum, col) => sum + (isFlex(col) ? 0 : col.width),
|
|
167
|
+
0
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// No terminal width info at all — fall back to legacy content-based sizing.
|
|
171
|
+
if (!termColumns) {
|
|
172
|
+
return columns.map((col) => (isFlex(col) ? contentWidth(col) : col.width));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const available = termColumns - MARKER_WIDTH - fixedTotal;
|
|
176
|
+
const perFlex = Math.floor(available / flexIndices.length);
|
|
177
|
+
const flexWidth = Math.max(MIN_FLEX_WIDTH, perFlex);
|
|
178
|
+
|
|
179
|
+
return columns.map((col) => (isFlex(col) ? flexWidth : col.width));
|
|
180
|
+
}, [columns, rows, termColumns]);
|
|
181
|
+
|
|
182
|
+
// ── Render: Loading state ───────────────────────────────────────────
|
|
183
|
+
if (loading) {
|
|
184
|
+
return (
|
|
185
|
+
<Box flexDirection="column" paddingY={1}>
|
|
186
|
+
<Spinner label="Loading..." />
|
|
187
|
+
</Box>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ── Render: Empty state ─────────────────────────────────────────────
|
|
192
|
+
if (rows.length === 0) {
|
|
193
|
+
return (
|
|
194
|
+
<Box flexDirection="column" paddingY={1}>
|
|
195
|
+
<Text color={colors.dim}>{emptyMessage}</Text>
|
|
196
|
+
</Box>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ── Render: Table ───────────────────────────────────────────────────
|
|
201
|
+
return (
|
|
202
|
+
<Box flexDirection="column">
|
|
203
|
+
{/* Column headers */}
|
|
204
|
+
<Box>
|
|
205
|
+
<Text color={colors.dim}> </Text>
|
|
206
|
+
{columns.map((col, ci) => (
|
|
207
|
+
<Box key={col.key} width={colWidths[ci]}>
|
|
208
|
+
<Text bold dimColor>
|
|
209
|
+
{col.label}
|
|
210
|
+
</Text>
|
|
211
|
+
</Box>
|
|
212
|
+
))}
|
|
213
|
+
</Box>
|
|
214
|
+
|
|
215
|
+
{/* Header separator */}
|
|
216
|
+
<Box>
|
|
217
|
+
<Text color={colors.subtle}>
|
|
218
|
+
{' '}{columns.map((_, ci) => '\u2500'.repeat(Math.max(1, colWidths[ci] - 1))).join(' ')}
|
|
219
|
+
</Text>
|
|
220
|
+
</Box>
|
|
221
|
+
|
|
222
|
+
{/* Data rows */}
|
|
223
|
+
{pageRows.map((row, ri) => {
|
|
224
|
+
const absoluteIndex = pageOffset + ri;
|
|
225
|
+
const isSelected = absoluteIndex === selected;
|
|
226
|
+
return (
|
|
227
|
+
<Box key={absoluteIndex}>
|
|
228
|
+
{/* Selection marker */}
|
|
229
|
+
<Text color={isSelected ? colors.cyberBlue : colors.subtle}>
|
|
230
|
+
{isSelected ? '\u25B6 ' : ' '}
|
|
231
|
+
</Text>
|
|
232
|
+
|
|
233
|
+
{/* Cell values */}
|
|
234
|
+
{columns.map((col, ci) => {
|
|
235
|
+
const rawVal = row[col.key];
|
|
236
|
+
const val = rawVal === null || rawVal === undefined ? '' : String(rawVal);
|
|
237
|
+
return (
|
|
238
|
+
<Box key={col.key} width={colWidths[ci]}>
|
|
239
|
+
<Text
|
|
240
|
+
color={isSelected ? colors.text : colors.textMuted}
|
|
241
|
+
bold={isSelected}
|
|
242
|
+
>
|
|
243
|
+
{val.length > colWidths[ci] - 1
|
|
244
|
+
? val.slice(0, colWidths[ci] - 2) + '\u2026'
|
|
245
|
+
: val}
|
|
246
|
+
</Text>
|
|
247
|
+
</Box>
|
|
248
|
+
);
|
|
249
|
+
})}
|
|
250
|
+
</Box>
|
|
251
|
+
);
|
|
252
|
+
})}
|
|
253
|
+
|
|
254
|
+
{/* Page indicator */}
|
|
255
|
+
{totalPages > 1 && (
|
|
256
|
+
<Box marginTop={1}>
|
|
257
|
+
<Text color={colors.dim}>
|
|
258
|
+
Page {currentPage + 1} of {totalPages}
|
|
259
|
+
{' '}({rows.length} items)
|
|
260
|
+
{' '}
|
|
261
|
+
</Text>
|
|
262
|
+
<Text color={colors.cyberBlue}>n</Text>
|
|
263
|
+
<Text color={colors.dim}>/</Text>
|
|
264
|
+
<Text color={colors.cyberBlue}>p</Text>
|
|
265
|
+
<Text color={colors.dim}> next/prev</Text>
|
|
266
|
+
</Box>
|
|
267
|
+
)}
|
|
268
|
+
</Box>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DetailPanel — Scrollable key-value display for the right panel
|
|
3
|
+
*
|
|
4
|
+
* Shows properties of a selected item as labeled key-value pairs.
|
|
5
|
+
* Handles nested objects (JSON.stringify) and arrays (join with ', ').
|
|
6
|
+
*
|
|
7
|
+
* When content exceeds the available vertical space (derived from
|
|
8
|
+
* useTerminalSize().rows, or the `maxHeight` prop), the panel renders a
|
|
9
|
+
* windowed slice of its content lines instead of silently overflowing, and
|
|
10
|
+
* shows a scroll indicator (`▲ N more` / `▼ N more`) for whatever is
|
|
11
|
+
* hidden off-screen.
|
|
12
|
+
*
|
|
13
|
+
* Props:
|
|
14
|
+
* data - object to display (keys become labels, values become text)
|
|
15
|
+
* title - optional heading string
|
|
16
|
+
* loading - show spinner when true
|
|
17
|
+
* focused - when true, the panel handles Up/Down/k/j/PgUp/PgDn itself
|
|
18
|
+
* to move the scroll offset (default: false — content is
|
|
19
|
+
* top-anchored, clamped, with an overflow hint if needed)
|
|
20
|
+
* maxHeight - override the auto-derived (terminal-size-based) viewport
|
|
21
|
+
* height used for windowing
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
25
|
+
import { Box, Text, useInput } from 'ink';
|
|
26
|
+
import { Spinner } from '@inkjs/ui';
|
|
27
|
+
import { matchKey } from '../keys.js';
|
|
28
|
+
import { colors } from '../theme.js';
|
|
29
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
30
|
+
|
|
31
|
+
// Rows reserved for title/borders/scroll-indicator when deriving viewport height.
|
|
32
|
+
const CHROME_ROWS = 6;
|
|
33
|
+
// Minimum viewport height when deriving from a short terminal.
|
|
34
|
+
const MIN_HEIGHT = 5;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format a key string into a readable label.
|
|
38
|
+
* Converts camelCase and snake_case to Title Case.
|
|
39
|
+
*/
|
|
40
|
+
function formatLabel(key) {
|
|
41
|
+
return key
|
|
42
|
+
.replace(/_/g, ' ')
|
|
43
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
44
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Format a value for display.
|
|
49
|
+
* - null/undefined => dim dash
|
|
50
|
+
* - arrays => comma-separated
|
|
51
|
+
* - objects => JSON string
|
|
52
|
+
* - booleans => Yes/No with color
|
|
53
|
+
* - everything else => string
|
|
54
|
+
*/
|
|
55
|
+
function formatValue(value) {
|
|
56
|
+
if (value === null || value === undefined) {
|
|
57
|
+
return { text: '—', color: colors.dim };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof value === 'boolean') {
|
|
61
|
+
return {
|
|
62
|
+
text: value ? 'Yes' : 'No',
|
|
63
|
+
color: value ? colors.success : colors.dim,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
if (value.length === 0) return { text: '(empty)', color: colors.dim };
|
|
69
|
+
return { text: value.join(', '), color: colors.text };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof value === 'object') {
|
|
73
|
+
try {
|
|
74
|
+
return { text: JSON.stringify(value, null, 2), color: colors.textMuted };
|
|
75
|
+
} catch {
|
|
76
|
+
return { text: String(value), color: colors.textMuted };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { text: String(value), color: colors.text };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Flatten data entries into a linear list of renderable "lines" so the
|
|
85
|
+
* panel can window/scroll through them regardless of whether an entry is a
|
|
86
|
+
* single inline row or a multi-line JSON block.
|
|
87
|
+
*/
|
|
88
|
+
function buildLines(entries) {
|
|
89
|
+
const lines = [];
|
|
90
|
+
|
|
91
|
+
for (const [key, value] of entries) {
|
|
92
|
+
const { text, color } = formatValue(value);
|
|
93
|
+
const label = formatLabel(key);
|
|
94
|
+
const isBlock = typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
95
|
+
|
|
96
|
+
if (isBlock) {
|
|
97
|
+
lines.push({ id: `${key}:label`, kind: 'label', label: `${label}:` });
|
|
98
|
+
text.split('\n').forEach((jsonLine, i) => {
|
|
99
|
+
lines.push({ id: `${key}:${i}`, kind: 'block', text: jsonLine, color });
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
lines.push({ id: key, kind: 'inline', label: `${label}: `, text, color });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function DetailPanel({ data, title, loading = false, focused = false, maxHeight }) {
|
|
110
|
+
const { rows: termRows } = useTerminalSize();
|
|
111
|
+
const viewportHeight = maxHeight != null
|
|
112
|
+
? maxHeight
|
|
113
|
+
: Math.max(MIN_HEIGHT, termRows - CHROME_ROWS);
|
|
114
|
+
|
|
115
|
+
const entries = useMemo(() => (data ? Object.entries(data) : []), [data]);
|
|
116
|
+
const lines = useMemo(() => buildLines(entries), [entries]);
|
|
117
|
+
|
|
118
|
+
const total = lines.length;
|
|
119
|
+
const needsScroll = total > viewportHeight;
|
|
120
|
+
const maxOffset = needsScroll ? total - viewportHeight : 0;
|
|
121
|
+
|
|
122
|
+
const [offset, setOffset] = useState(0);
|
|
123
|
+
|
|
124
|
+
// Reset scroll position whenever the underlying data changes.
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
setOffset(0);
|
|
127
|
+
}, [data]);
|
|
128
|
+
|
|
129
|
+
// Clamp if the viewport grew (e.g. terminal resize) past the current offset.
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
setOffset((o) => Math.min(o, maxOffset));
|
|
132
|
+
}, [maxOffset]);
|
|
133
|
+
|
|
134
|
+
useInput((input, key) => {
|
|
135
|
+
if (!focused || !needsScroll) return;
|
|
136
|
+
|
|
137
|
+
if (matchKey('down', input, key)) {
|
|
138
|
+
setOffset((o) => Math.min(maxOffset, o + 1));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (matchKey('up', input, key)) {
|
|
142
|
+
setOffset((o) => Math.max(0, o - 1));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (key.pageDown) {
|
|
146
|
+
setOffset((o) => Math.min(maxOffset, o + viewportHeight));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (key.pageUp) {
|
|
150
|
+
setOffset((o) => Math.max(0, o - viewportHeight));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ── Loading state ───────────────────────────────────────────────────
|
|
156
|
+
if (loading) {
|
|
157
|
+
return (
|
|
158
|
+
<Box flexDirection="column" paddingY={1}>
|
|
159
|
+
<Spinner label="Loading details..." />
|
|
160
|
+
</Box>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ── Empty state ─────────────────────────────────────────────────────
|
|
165
|
+
if (!data) {
|
|
166
|
+
return (
|
|
167
|
+
<Box flexDirection="column" paddingY={1}>
|
|
168
|
+
<Text color={colors.dim}>No item selected</Text>
|
|
169
|
+
</Box>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const visibleLines = needsScroll ? lines.slice(offset, offset + viewportHeight) : lines;
|
|
174
|
+
// Gate on needsScroll, not just offset>0: `offset` state can be briefly
|
|
175
|
+
// stale for one render after `data` changes (before the reset effect
|
|
176
|
+
// runs), and without this guard that stale offset would flash a false
|
|
177
|
+
// "more above" hint even though the new content fits without scrolling.
|
|
178
|
+
const hiddenAbove = needsScroll ? offset : 0;
|
|
179
|
+
const hiddenBelow = needsScroll ? total - (offset + visibleLines.length) : 0;
|
|
180
|
+
|
|
181
|
+
// ── Render ──────────────────────────────────────────────────────────
|
|
182
|
+
return (
|
|
183
|
+
<Box flexDirection="column">
|
|
184
|
+
{/* Title */}
|
|
185
|
+
{title && (
|
|
186
|
+
<Box marginBottom={1}>
|
|
187
|
+
<Text color={colors.accent} bold>
|
|
188
|
+
{title}
|
|
189
|
+
</Text>
|
|
190
|
+
</Box>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
{/* Scroll-up indicator */}
|
|
194
|
+
{hiddenAbove > 0 && (
|
|
195
|
+
<Box>
|
|
196
|
+
<Text color={colors.dim}>{'▲'} {hiddenAbove} more</Text>
|
|
197
|
+
</Box>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
{/* Windowed key-value lines */}
|
|
201
|
+
{visibleLines.map((line, _i) => {
|
|
202
|
+
if (line.kind === 'label') {
|
|
203
|
+
return (
|
|
204
|
+
<Box key={`${line.id}-${_i}`}>
|
|
205
|
+
<Text color={colors.textMuted} bold>
|
|
206
|
+
{line.label}
|
|
207
|
+
</Text>
|
|
208
|
+
</Box>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (line.kind === 'block') {
|
|
212
|
+
return (
|
|
213
|
+
<Box key={`${line.id}-${_i}`} marginLeft={2}>
|
|
214
|
+
<Text color={line.color}>{line.text}</Text>
|
|
215
|
+
</Box>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return (
|
|
219
|
+
<Box key={`${line.id}-${_i}`}>
|
|
220
|
+
<Text color={colors.textMuted} bold>
|
|
221
|
+
{line.label}
|
|
222
|
+
</Text>
|
|
223
|
+
<Text color={line.color}>{line.text}</Text>
|
|
224
|
+
</Box>
|
|
225
|
+
);
|
|
226
|
+
})}
|
|
227
|
+
|
|
228
|
+
{/* Scroll-down indicator */}
|
|
229
|
+
{hiddenBelow > 0 && (
|
|
230
|
+
<Box>
|
|
231
|
+
<Text color={colors.dim}>{'▼'} {hiddenBelow} more</Text>
|
|
232
|
+
</Box>
|
|
233
|
+
)}
|
|
234
|
+
|
|
235
|
+
{/* Empty data object */}
|
|
236
|
+
{entries.length === 0 && (
|
|
237
|
+
<Text color={colors.dim}>No properties to display</Text>
|
|
238
|
+
)}
|
|
239
|
+
</Box>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlay — Keybinding reference overlay
|
|
3
|
+
*
|
|
4
|
+
* Renders on top of the current view when `?` is pressed.
|
|
5
|
+
* Shows all keybindings organized in a clean layout.
|
|
6
|
+
* Press `?` or `Esc` to dismiss.
|
|
7
|
+
*
|
|
8
|
+
* Props:
|
|
9
|
+
* visible - whether the overlay is shown
|
|
10
|
+
* onClose - called when user dismisses (? or Esc)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import { Box, Text, useInput } from 'ink';
|
|
15
|
+
import { KEY_HELP } from '../keys.js';
|
|
16
|
+
import { colors } from '../theme.js';
|
|
17
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
18
|
+
|
|
19
|
+
// Preferred overlay width; clamped down on narrow terminals.
|
|
20
|
+
const PREFERRED_WIDTH = 44;
|
|
21
|
+
const MIN_WIDTH = 20;
|
|
22
|
+
|
|
23
|
+
export function HelpOverlay({ visible = false, onClose }) {
|
|
24
|
+
const { columns } = useTerminalSize();
|
|
25
|
+
|
|
26
|
+
useInput((input, key) => {
|
|
27
|
+
if (!visible) return;
|
|
28
|
+
if (input === '?' || key.escape) {
|
|
29
|
+
onClose?.();
|
|
30
|
+
}
|
|
31
|
+
}, { isActive: visible });
|
|
32
|
+
|
|
33
|
+
if (!visible) return null;
|
|
34
|
+
|
|
35
|
+
const width = Math.max(MIN_WIDTH, Math.min(PREFERRED_WIDTH, columns - 4));
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Box
|
|
39
|
+
flexDirection="column"
|
|
40
|
+
borderStyle="round"
|
|
41
|
+
borderColor={colors.primary}
|
|
42
|
+
paddingX={2}
|
|
43
|
+
paddingY={1}
|
|
44
|
+
width={width}
|
|
45
|
+
>
|
|
46
|
+
{/* Header */}
|
|
47
|
+
<Box justifyContent="center" marginBottom={1}>
|
|
48
|
+
<Text color={colors.primary} bold>Keyboard Shortcuts</Text>
|
|
49
|
+
</Box>
|
|
50
|
+
|
|
51
|
+
{/* Key entries */}
|
|
52
|
+
{KEY_HELP.map((entry, i) => (
|
|
53
|
+
<Box key={i} marginBottom={0}>
|
|
54
|
+
<Box width={14}>
|
|
55
|
+
<Text color={colors.cyberBlue} bold>{entry.keys}</Text>
|
|
56
|
+
</Box>
|
|
57
|
+
<Text color={colors.textMuted}>{entry.desc}</Text>
|
|
58
|
+
</Box>
|
|
59
|
+
))}
|
|
60
|
+
|
|
61
|
+
{/* Dismiss hint */}
|
|
62
|
+
<Box marginTop={1} justifyContent="center">
|
|
63
|
+
<Text color={colors.dim}>Press ? or Esc to close</Text>
|
|
64
|
+
</Box>
|
|
65
|
+
</Box>
|
|
66
|
+
);
|
|
67
|
+
}
|