dsh-blue/blue--packages-core ↗★ 1
@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
AI Analysis
管理 TUI 终端的生命周期、键盘交互及主题配色,是 Blue 终端界面的底层渲染核心。适合作为构建 Blue 终端应用的基础依赖。
Install
This plugin has no verified bundle, or compatibility checks failed. Read the repository notes first. Read the full README ↗
README
Read the full 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.addChildreturns a disposer,showOverlayreturns a focus/unfocus handle,setFocusowns the single focus slot,requestRenderschedules throttled redraws,columnsreports the terminal width.BlueComponentis structurally compatible with pi-tui'sComponentbut type-independent.ctx.blueTheme(BlueTheme) — the semantic color table contract. Every value is a(text: string) => stringANSI wrapper over the 28-tokenBlueSemanticColorsset (all required; v2 addsprimary— the interactive color: selection, links, spinner, running indicators — andtextMuted, the deepest gray tier for counters, key hints, and truncation rows). The contract lives in this package'ssrc/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, exportingDARK_COLORS),./theme-light(blue-theme-light, the built-in light palette, exportingLIGHT_COLORS),./theme-auto(blue-theme-auto, injectsblueTerminalInfoto 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#rrggbblayered 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 internalsrc/theme-palette.tsmodule: the hex→ANSI helpers, thecolorsFromForegroundsfrozen-table builder, and thedefineThemeServiceService-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 optionalhandleris a focus-independent global action; one without it is contextual, resolved by components throughmatches.dispatch(data)runs the global half: handler-carrying actions fire in registration order and it reports whether any handler consumed the input. This plugin'sapplywires the global half itself — a pi-tui input listener mounted ahead of focus routing consumes whateverdispatchclaims — and instantiates the service directly (new BlueKeymapService(ctx)) rather than throughctx.plugin, because the CordisContextproxy 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) andkittyKeyboard(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/createSettingsListbuild pi-tui-backed components behind pi-tui-independent interfaces, mapping the activeblueThemepalette 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 thehighlightCodehook — 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 neutralbordertoken, with slash/bash contexts recoloring it throughsetBorderColor;visibleWidth/wrapText/truncateToWidthare the shared width pure functions, and — since S14 — the fuzzy pairfuzzyMatch(query, text)(pi-tui's subsequence matcher,{matches, score}with lower-is-better) andfuzzyFilter(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 internalWrappingSelectList(the repo's only pi-tui subclass — descriptions wrap onto at most two lines), other completions keep the stock list, andsetGhostHint(text | undefined)(its first consumer) drives the argument-hint ghost the chrome layer splices in. The factory also providescreateImage(options)(aBlueImage, theBlueComponentalias wrapping pi-tui's Image; terminals without an image protocol get a styled text fallback) andimageDimensions(data)(a pure dimension probe over PNG/JPEG/GIF/WebP bytes,undefinedwhen unrecognized). TheBlueEditorcontract also exposessetAutocompleteProvider(BlueAutocompleteProvider)— the type-independentBlueAutocompleteItem/BlueAutocompleteSuggestions/BlueAutocompleteProvidertrio the L0 adapter passes straight through to the underlying renderer — andgetExpandedText(), which expands paste markers to their full pasted content for submission.BlueEditorfurther carries the pre-dispatch hookonKey?(data)— invoked before the pi-tui Editor handles a sequence, returningtrueconsumes it (the editor-context key chains live here, because pi-tui's Editor swallows Ctrl-C with no fallback exit) —isShowingAutocomplete(), andinsertText(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 injectsblueTheme, 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 default —
TuiMainScreenwrites its width-overflow crash log to~/.pi/agent(orPI_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.