dsh-blue/blue--packages-core1

@dsh-blue/blue-core

Blue terminal UI core: the tree's only @earendil-works/pi-tui adapter, owning terminal lifecycle and the blueScreen/blueKeymap/blueTerminalInfo/blueComponents services plus the blue-theme-dark/light/auto/custom palette entries

包名
@dsh-blue/blue-core
版本
0.1.0-rc.1
最近更新
2026年8月21日

安装

此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 README ↗

@dsh-blue/blue-core

English | 中文

Blue terminal UI core: the only package in the tree that imports @earendil-works/pi-tui. Loading the plugin probes the terminal background (OSC 11, before raw mode), starts the terminal (a main-screen TuiMainScreen renderer over ProcessTerminal: raw mode, bracketed paste, Kitty keyboard negotiation), and registers the L1 services; unloading the plugin stops the terminal and restores its state. The package imports no harness package — only pi-tui and Cordis.

L1 services

The L1 contracts in src/types.ts are self-owned narrow interfaces: no pi-tui type, harness business type, or concrete renderer class appears in them; L0 (src/terminal.ts) delegates to pi-tui internally.

  • ctx.blueScreen (BlueScreen) — component mounting. addChild returns a disposer, showOverlay returns a focus/unfocus handle, setFocus owns the single focus slot, requestRender schedules throttled redraws, columns reports the terminal width. BlueComponent is structurally compatible with pi-tui's Component but type-independent.
  • ctx.blueTheme (BlueTheme) — the semantic color table contract. Every value is a (text: string) => string ANSI wrapper over the 28-token BlueSemanticColors set (all required; v2 adds primary — the interactive color: selection, links, spinner, running indicators — and textMuted, the deepest gray tier for counters, key hints, and truncation rows). The contract lives in this package's src/types.ts; implementations ship as four subpath plugins, so a theme provider swap needs no consumer change: ./theme-dark (blue-theme-dark, the built-in dark palette and plain-baseline default, exporting DARK_COLORS), ./theme-light (blue-theme-light, the built-in light palette, exporting LIGHT_COLORS), ./theme-auto (blue-theme-auto, injects blueTerminalInfo to pick dark/light from the probed terminal background and re-provides the palette — swaps serialized through a promise chain — whenever 'blue/terminal-theme-changed' fires), and ./theme-custom (blue-theme-custom, a JSON file mapping token names to #rrggbb layered over a built-in base palette; its config is validated with @deepseek-ai/schemastery, invalid entries fall back to the base entry with a warning, and an unreadable file falls back to the whole base palette). All four are built on the internal src/theme-palette.ts module: the hex→ANSI helpers, the colorsFromForegrounds frozen-table builder, and the defineThemeService Service-subclass factory.
  • ctx.blueKeymap (BlueKeymap) — the keybinding registry, split into a contextual and a global half. register(actions) validates the batch (duplicate ids, keys already claimed by another action) before committing and returns a disposer; matches(data, action) tests an input sequence; getKeys(action) resolves bound keys. An action carrying the optional handler is a focus-independent global action; one without it is contextual, resolved by components through matches. dispatch(data) runs the global half: handler-carrying actions fire in registration order and it reports whether any handler consumed the input. This plugin's apply wires the global half itself — a pi-tui input listener mounted ahead of focus routing consumes whatever dispatch claims — and instantiates the service directly (new BlueKeymapService(ctx)) rather than through ctx.plugin, because the Cordis Context proxy rejects a service that was never injected and a service cannot inject itself.
  • ctx.blueTerminalInfo (BlueTerminalInfo) — read-only terminal facts: background ('dark' | 'light' | undefined, from the startup OSC 11 probe) and kittyKeyboard (whether the Kitty keyboard protocol negotiated). Later DEC theme reports emit 'blue/terminal-theme-changed' ('dark' | 'light').
  • ctx.blueComponents (BlueComponents) — the component factory. createEditor / createMarkdown / createSelectList / createSettingsList build pi-tui-backed components behind pi-tui-independent interfaces, mapping the active blueTheme palette onto pi-tui themes (the markdown mapping is the S10 v2 one: headings carry their level through bold, unordered markers normalize to , and fenced code is colored by cli-highlight behind the highlightCode hook — unknown languages fall back to plain text, and highlighting never changes line count; the S17 dogfood adds a horizontal-rule post-process to the markdown adapter: pi-tui caps rules at 80 columns regardless of the render width, so the adapter re-paints the capped rule to the full render width — exact string match on the theme's known output, tolerating pi-tui's row padding, so fenced code lines keep their own styling); the editor theme's default border is the neutral border token, with slash/bash contexts recoloring it through setBorderColor; visibleWidth / wrapText / truncateToWidth are the shared width pure functions, and — since S14 — the fuzzy pair fuzzyMatch(query, text) (pi-tui's subsequence matcher, {matches, score} with lower-is-better) and fuzzyFilter(items, query, getText) re-export through the contract for every completion surface. The editor built here also owns the S14 dropdown treatment: a /-prefixed autocomplete menu renders through the internal WrappingSelectList (the repo's only pi-tui subclass — descriptions wrap onto at most two lines), other completions keep the stock list, and setGhostHint(text | undefined) (its first consumer) drives the argument-hint ghost the chrome layer splices in. The factory also provides createImage(options) (a BlueImage, the BlueComponent alias wrapping pi-tui's Image; terminals without an image protocol get a styled text fallback) and imageDimensions(data) (a pure dimension probe over PNG/JPEG/GIF/WebP bytes, undefined when unrecognized). The BlueEditor contract also exposes setAutocompleteProvider(BlueAutocompleteProvider) — the type-independent BlueAutocompleteItem / BlueAutocompleteSuggestions / BlueAutocompleteProvider trio the L0 adapter passes straight through to the underlying renderer — and getExpandedText(), which expands paste markers to their full pasted content for submission. BlueEditor further carries the pre-dispatch hook onKey?(data) — invoked before the pi-tui Editor handles a sequence, returning true consumes it (the editor-context key chains live here, because pi-tui's Editor swallows Ctrl-C with no fallback exit) — isShowingAutocomplete(), and insertText(text) — atomic insertion at the cursor, the seam clipboard-image markers use. Since S11 the editor renders as a rounded box: the adapter post-processes each render through the shared chrome helpers (setPromptSymbol('>' | '!' | undefined) overlays a prompt symbol on the first content row — the bash ! shares the border hue, the neutral > stays default-foreground; setBorderLabel(text) lays pre-styled text into the top rule, never into scroll indicators; setConnectedAbove(bool) switches the top corners to ├┤ for a docked panel above), and corners/bars repaint through the live border color so a host recolor stays in sync. The factory injects blueTheme, so a provider swap rebuilds it through Cordis reload semantics.

Shared chrome helpers

The ./chrome subpath (src/chrome.ts, opened by S11) exports the theme-agnostic pure functions the framed surfaces are drawn with: withSideBorders(lines, paint, {connectedAbove?, label?}) turns pi-tui-style rule rows into ╭╮/╰╯-cornered boxes (repainting pre-painted rules as one span, overlaying only on literal outer spaces so the inverse-video cursor survives, and fitting labels only into pure dash runs), injectPromptSymbol(line, symbol, paint?) overlays a prompt symbol on a paddingX: 4 content row, and — since S12 — framePanel(body, width, opts) frames a dialog body in kimi's full-width flat rules (an indented title (kimi: help · Esc …) with an optional muted hint and a key-row footer, every paint defaulting to identity, ANSI-safe truncated) with hintRow(parts, paint) joining key-hint parts with ·. S13 adds the panel chrome: topRule(width, {title, titlePaint, hint, hintPaint, paint}) renders the kimi in-border title row (╭ BTW ─ Esc close ────╮ — the joiner only when both a title and a hint are present, the composite clipped ANSI-safe with the dash fill taking the remainder) and padColumns(lines, n) prefixes a gutter of literal spaces (the pure GutterContainer equivalent; consumption landed with the S17 dogfood margin ruling — the GutterComponent exported from the package root wraps a child at width - 2n and pads the left gutter through it). S14 adds the completion chrome: highlightLeadingSlashToken(line, paint) re-paints the leading /command token through visible-index math (ANSI pass-through survives, so an inverse-video cursor on the row is kept) and injectGhostHint(line, hint, textLength, width, paint) splices the argument-hint ghost after the inverse-video cursor block, consuming trailing padding so the row width holds, ellipsizing on overflow, and returning the row unchanged when the cursor sits mid-text. Paint functions are injected — the module holds no theme and no pi-tui component machinery; further helpers land with their first consumers.

All five contracts are mounted as Cordis Service subclasses (blueTheme by a theme subpath plugin, the rest by this plugin's apply); each unregisters automatically when its plugin's fiber unloads. Components consume the interfaces, never pi-tui types.

Terminal lifecycle

createTerminalRelease() returns the release function for installFailLoud(binName, proc, release) from @deepseek-ai/dsh-app-boot: on a fatal load failure it stops the active terminal stack (draining pending input first) so raw mode and bracketed paste are restored before the process exits. It is a no-op when no Blue terminal is active. Services delegate through a stable proxy reference so a future renderer swap (main/alt screen) needs no consumer change.

Model Experience

None, as the terminal UI core renders to the user and registers nothing model-facing.

KV Cache effect

None; the package adds nothing to any model request prefix.

Known Limitations and Deferred Work

  • Crash-log directory is pi's defaultTuiMainScreen writes its width-overflow crash log to ~/.pi/agent (or PI_CODING_AGENT_DIR) because pi-tui hardcodes that default and Blue has no dsh-owned path to thread through yet; a dsh-side log directory is deferred to the alt-screen phase.
  • Main-screen renderer only — the alternate-screen viewport and runtime renderer swap are deferred; the stable proxy reference is the only seam in place.
  • Keymap conflict scope — conflict detection covers actions registered through ctx.blueKeymap; pi-tui components (Editor, SelectList) resolve their own bindings from pi-tui's global keybindings table, which this package leaves untouched.