dsh-blue/blue--packages-interaction ↗★ 1
@dsh-blue/blue-interaction
Blue terminal UI interaction layer: input editor, slash commands, approval and user-question overlays over dsh-blue-core
安装
此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 README ↗
说明文档
阅读完整 README ↗@dsh-blue/blue-interaction
English | 中文
Blue terminal UI interaction layer over dsh-blue-core: the bottom input editor with slash-command dispatch, the built-in /quit, /resume, /new, /fork, /sessions, /help, /theme, /model, /effort, /provider, /yolo, /status, /context, /version, /export, and /copy commands, the ctx.userQuestions overlay provider (one tabbed questionnaire per request), and the interactive four-choice approval/request answerer (with the S24a yolo auto-approve stance and the Shift+Tab three-state cycle — normal → plan → yolo — over it). The package imports no pi-tui: the main editor is the pi-tui Editor behind ctx.blueComponents.createEditor (multi-line, history, kill-ring, undo, and paste markers built in), single-select lists come from ctx.blueComponents.createSelectList, and BlueSelect survives only as the package-internal multi-select list (pi-tui ships no multi-select). Every dialog renders through the core framePanel chrome (S12) as a kimi pull-up panel, and since the S16 dogfood (D30) each dialog takes over the editor's dock slot through mountEditorReplacement instead of floating an overlay: the editor leaves the tree for the panel's lifetime (its buffer, draft, and history survive in the component), only the two-row footer shell remains below the open panel, and dismissing restores the editor with focus. The stack emits the core event 'blue/editor-slot-swapped' on each empty↔occupied transition (nested panels re-emit nothing, and unloading with a panel open releases the occupancy) — blue-pane-activity hides its row while any panel is up and blue-pane-btw re-asserts its editor splice around the editor's absence. BluePanel is the package's sole public component export; dialogs resolve keys through ctx.blueKeymap and style through ctx.blueTheme.
Plugins
The single entry plugin blue-interaction mounts five sub-plugins; every registration is effect-bound, so unloading the fiber reverts every contribution (HMR-safe: the provider/command/key registrations disappear with the fiber).
blue-interaction-keys— registers the shared key-action batch (blue.interaction.submit/cancel/move-up/move-down/toggle) onctx.blueKeymapas one validated unit, plus the editor-context actionsblue.interaction.interrupt(Ctrl-C) andblue.interaction.steer(Ctrl-S). The latter two carry no handler — they stay contextual and are resolved by the main editor'sonKeyhook, never by the global dispatcher, so they cannot steal a key from an overlay'sescape=cancelroute. The multi-selectBlueSelectresolves keys against these actions and generates footer hints withgetKeys; text-editing keys are owned by the pi-tui Editor itself.blue-input— mounts the focused bottom editor: the pi-tui Editor fromctx.blueComponents.createEditor({ paddingX: 4 })with the>prompt symbol set at mount (the padding and symbol feed the core adapter's rounded-box chrome), with the hint line as a separateHintLinecomponent pinned below. The hint line carries its transient tiers only — one-shot notices and slash-command discovery inmuted— and renders zero rows otherwise (the S15 dogfood verdict retired the persistent key-affordance row: kimi teaches affordances through the footer's rotating tips, and the tips pool already covers every fragment the row carried). Slash-prefixed input recolors the editor frameprimarythroughsetBorderColor; any other text restores the neutral border. Submit parses the line withparseCommand: a bare/permissionline — but only while the permission-preset service is composed — opens the preset picker instead of dispatching (see Shared panels); any other slash command dispatches throughctx.commands.execute(never reaching the model; success/error text flashes in the hint line), anything else becomes acreateUserMessage({ source: { kind: 'user' } })follow-up on the current agent — queued by the harness inbox when the agent is running. TheonSubmitcallback argument already carries the paste-expanded, trimmed text. Slash-prefixed input shows up to three fuzzy-matching commands fromctx.commands.list(the sameslash-filterhelper the dropdown uses) as a discovery hint. The mounted editor and its submit router are published through a package-local shared ref soblue-editor-pluscan layer input modes and autocomplete over the same component. The editor'sonKeypre-dispatch hook resolves the editor-context key chain: Escape lets a showing autocomplete popup keep the key, then closes the side-question pane when one is docked above the editor (the draft survives), then clears the draft, then interrupts a running agent; Ctrl-C clears the draft, then interrupts a running agent, and a second press within 1 s exits through the launcher-ownedappExit(0)(a single press flashes the double-press hint in the hint line); Ctrl-S steers a non-empty draft into the current turn and clears the buffer; Shift-Tab cycles the session mode (normal → plan → yolo, one explicit command per step, also live in bash mode — the input mode and the session mode are orthogonal); Up/Down with an empty buffer scroll the docked side-question pane ahead of the queue recall. The unsubmitted draft, the input mode, and the prompt history are mirrored into a module-level stash (src/draft-stash.ts), so the theme-swap reload that rebuilds this fiber restores all three into the fresh editor (text viasetText, history replayed throughaddToHistory— a/themesubmission would otherwise vanish from Up-recall the moment its own command rebuilds the editor); submit and steer clear the draft while the history persists.blue-commands— registers/quit(requests exit through the launcher-ownedctx.appExit; an error result when the launcher provided none; the aliases/qand/exitanswer to it through thesrc/command-meta.tsregistry — kimi-style, aliases are not separate registrations, the input layer rewrites an alias line to/quitbefore dispatch, so discovery surfaces stay canonical-only),/sessions [](bare opens the persisted-session picker; an id argument emitsblue/request-resumedirectly —/resumeis the command's alias since the S24a dogfood merge, rewritten by the input layer),/new(emitsblue/request-new),/fork(emitsblue/request-fork; an error result while the current agent is not idle),/sessions(lists persisted sessions newest-first fromsessionPersistence.listin a framed picker — the sharedSelectListPanelrows (src/select-list.ts, S24b) carry a❯pointer and the live session a← currentbadge, picking another emitsblue/request-resume, and the outcome flashes through the shared editor'snoticeseam),/help(the framed, scrollableHelpOverlay: two-column sections of the registered commands fromctx.commands.list(labels inprimary; aliases join the label as/quit (/q, /exit)) and the key bindings fromctx.blueKeymap.list()(labels inwarning), ashowing 1-N of Mscroll window with arrows and PageUp/PageDown; Escape, Enter, orqcloses it), and/theme(swaps the live theme provider:/themelists the known themes, marking the current one;/theme dark|light|autoswitches to a built-in palette;/theme custom [dark|light]mounts a file-backed palette; the switch disposes the current provider's fibers throughctx.registry.deleteand mounts the replacement, and a failed mount restores the built-in dark palette). The model family lives insrc/model-commands.tsbehind the same registration discipline:/model(no argument opens theModelPanelpicker overctx.llm's advertised catalog — the kimi tabbed-selector geometry: the title with its(type to search)suffix, the key-hint row beneath it, a provider tab strip (All + each provider, Tab/Shift-Tab toggles) on multi-provider catalogs, a live fuzzy query overProvider Name/modellabels (printable keys grow it, Backspace shrinks it, Escape clears it before cancelling — the kimi rule), rows carrying the name column, a muted provider, a· ctx kcontext suffix, and the← currentbadge, the cache-warning row once the session has a logged request header, and the kimi footer thinking-segment control adjusting the highlighted model's effort draft with ←/→; Enter switches and persists the new default throughagentDefaultModel.saveSelection, Alt+S switches session-only, an argument switches straight to that model id with the live provider breaking an ambiguity), and/effort(theEffortPanelhorizontal segments —Defaultrestores the provider's own default by omitting the effort — plus directlow/high/defaultarguments listing the valid ids on a miss;/thinkingis an alias). Both write through the app layer'sblueSession.modelRefseam (a write routes the next step's request; the default service persists the choice for future sessions), resolve the llm/default-model services lazily throughctx.get, and report through the kimi five-state notice family insrc/model-commands.ts(Switched to … · thinking …/Thinking set to …/Already using …, with· session onlyand— failed to save default: …tails)./providercompletes the family: the sharedSelectListPanellists the configured routes only (badge← current; Enter opens the edit flow — a rounded form pre-filled with the stored display name, base URL (hint following the profile's protocol), and a masked API key whose empty field keeps the stored value; Ctrl+D deletes the route after a typedyconfirmation, and save keeps every untouched field while normalizing the base URL by protocol), with the trailing+ Add providerCTA row (a plain list row since S24b — it windows and wraps like any other, Enter routes to the wizard). The wizard (src/provider-add.ts, the kimi promise-per-panel flow over the slot stack) offers two branches: adopting a known vendor (optional baseURL override plus a masked key), or declaring a custom endpoint (route id, one of the three baseURL protocols, baseURL, masked key — discovery feeds a multi-select adopt step, and the model step is listing-only: discovery must answer or the form re-opens with the classified reason (full cause chain) in its error-red line — fix the URL or key and resubmit, only Escape leaves; manual model entry is disabled for now, documented, and may return behind a flag if a listing-less gateway ever appears). The models step itself is metadata-aware: the adopted ids are matched against the models.dev catalog (src/models-dev.ts, the kimicatalog-fetchport —https://models.dev/api.jsonwith a 10-minute cache and a 4-second best-effort timeout, offline falls back to the manual pass) filling each match's context window and pi-ai-gated effort levels (reasoning_optionsminus the off tier; a catalog-marked non-reasoning model writesreasoningEfforts: false), and a fully described set skips the optional Model defaults form outright — the form remains for the gap (one context window for undescribed models, one effort set), its subtitle reporting whether the catalog was reached. The Base URL hint follows the protocol's path convention (anthropic-messages: no trailing/v1, the transport appends/v1/messages; the OpenAI pair: include/v1), the discovery probe walks the entered base plus its/v1and bare variants until one answers, and the written profile base is normalized the same way — an anthropic base never keeps a trailing/v1(thePOST /v1/v1/messagesdogfood), an OpenAI base that only listed under the/v1candidate carries it. The commit is the harness Web Models page's sequence —settings.mutatewrites thellm-pi-aiprovider profile first (validated at the write by the registering plugin's schema, against the revision read fromdescribe()),credentials.setstores the key under the conventional_API_KEYref second — and the flow then opens the scoped picker over the fresh route (Escape keeps the provider with no default change). The form surface issrc/form-panel.ts(the kimi ApiKeyInput dialog geometry over Blue editors): a rounded box with the bold title, subtitle-or-error line, per-field labels and>input rows (bullets when masked, a trailing cursor block on the active field), and the key footer inside the box; Tab/↑/↓ route fields, Enter advances and submits from the last field, the in-panel error line never closes the panel, and a pasted key is never echoed. The custom branch's model step is listing-only (see above). The mode family lives insrc/mode-commands.tsbehind the same discipline:/yolo [on|off](alias/yes) toggles Blue-side auto-approval of tool calls — the harness approval policy stays'ask'because'never'resolves asks as rejected before the waterfall dispatch, so the answerer insrc/approval-plugin.tsis the auto-approve surface (an aborted request still settles'cancelled'; questions keep popping — kimi semantics). The live flag and its persistence live insrc/mode-state.ts: a per-agent WeakMap plus the purecommand/runfold (lastyolorun with recorded args decides —offafter trim turns it off, anything else on; Blue owns no session-event vocabulary, and the bare toggle turning OFF re-dispatches the explicit/yolo offso the log's last record disambiguates).setupModeTrackingrestores the flag on session switches (resume/fork fold back,/newstarts clean) and enforces the plan/yolo exclusivity: a committed plan entry while yolo is on dispatches/yolo offfrom a microtask (session.appendrejects re-entry and its observers run inside the append).cycleModeanswers the contextualshift+tabaction (blue.interaction.cycle-mode, matched in blue-input's editor key chain): it cycles normal → plan → yolo → normal — two states when dsh-plan-mode is not composed — dispatching exactly one explicit command per transition (/planand/plan offbelong upstream), with the result text as the single notice. The session-info family (S25) lives insrc/session-commands.tsbehind the same discipline:/statusopens the read-onlyInfoPanelover the session header (id, cwd, a fixed UTC creation stamp, whole-log turns/steps, the agent's lifecycle state), the live model selection (blueSession.modelRef, falling back to the latest logged request header), the Blue/harness version line, and the context-window bar;/context(named for CC's context panel, not kimi's/usage, by the 2026-08-21 ruling) lists the four disjoint provider token buckets (uncached input, cache read, cache write, output — reasoning included per the provider contract) plus their total, the same context bar, and — projection-only — the CC-style composition section (thecontextBreakdownunit's heuristic system/tools/message tokens as a stacked█▓▒░bar over the window with per-component rows and the free remainder; heading carries the heuristic caveat, sub-half-percent shares read an honest0%, and a host without the seam omits the section);/versionopens the read-onlyInfoPanelover the two release lines — Blue's own number (BLUE_VERSION, the first release the website advertises) and the independent harness pin line (HARNESS_LINE,rc.7) — version-only, no model; unlike the status family it needs no live session, so the panel opens on an empty slot too. The numbers come from the session-projection seam (ctx.sessionProjections.snapshot— the base composition'sdsh-token-meter/dsh-session-statsunits fold the whole durable log, so the figures are replay-correct across resumes);src/usage.tsis the thin read layer with a pureassistant/*usage fold as the degraded host's fallback, and Blue owns no accumulator of its own.
The ./mode-status subpath plugin (blue-status-mode, inject ['blueStatus', 'blueScreen', 'blueTheme', 'blueComponents']) is the session-mode foo