Files
oh-my-pi/packages/coding-agent/src/modes/magic-keywords.ts
T
roboomp 784fd56505 fix(magic-keywords): glow keywords through the cursor seam and animate a focused shimmer
The whole-line editor decorate ran on `displayText` after the editor appended
the zero-width CURSOR_MARKER and cursor glyph; both start with ESC, so the
magic-keyword regex's right-boundary `(?!\S)` rejected `ultrathink` glued to
the marker and dropped the gradient until a trailing character was typed.

- `Editor.#decorate` (pi-tui) now splits around CURSOR_MARKER and decorates
  each user-text segment independently, so word-boundary lookarounds resolve
  correctly on both sides; the matching comment block is corrected.
- `KeywordHighlighter` / `highlightMagicKeywords` gain an optional `phase` in
  [0, 1) that cyclically rotates the gradient stops. `0` (default) yields
  the static palette, so sent-bubble rendering is unaffected.
- `CustomEditor.decorateText` derives `phase` from `Date.now()` and chains
  `setTimeout(SHIMMER_FRAME_MS)` ticks while focused, the buffer holds a
  magic keyword, and `magicKeywords.enabled` is on — the render itself
  schedules the next frame, so losing focus, deleting the keyword, or
  flipping the setting stops the animation on its own. `interactive-mode`
  wires the repaint hook to `requestComponentRender(editor)` on construction
  and after `setEditorComponent`.
- Adds `hasMagicKeyword(text)` (cheap prose-aware probe) and tests for the
  seam fix, the phase cycle, the gating, and timer cleanup.

Fixes #2475
2026-06-13 20:50:30 +00:00

43 lines
2.0 KiB
TypeScript

import { containsOrchestrate, highlightOrchestrate } from "./orchestrate";
import { containsUltrathink, highlightUltrathink } from "./ultrathink";
import { containsWorkflow, highlightWorkflow } from "./workflow";
/**
* Gradient-highlight every magic keyword ("ultrathink", "orchestrate",
* "workflowz") that appears as standalone prose, skipping any occurrence inside a
* code block, inline code span, or XML/HTML section. Each highlighter paints its
* own keyword with its own gradient, so chaining is order-independent — the
* earlier passes only inject zero-width SGR escapes (no backticks or angle
* brackets), which never confuse the later passes' markdown masking.
*
* `resetTo` is the SGR foreground sequence restored after each painted keyword;
* pass the surrounding text color when decorating already-colored content (e.g.
* a themed message bubble) so the gradient does not bleed into the rest of the
* line. Defaults to a plain foreground reset for default-colored editor text.
*
* `phase` ∈ [0, 1) cyclically rotates each gradient — the editor passes a
* `Date.now()`-derived value to animate a Claude-Code-style shimmer while a
* keyword is on screen and the prompt is focused; sent message bubbles omit it
* to keep the static gradient.
*/
export function highlightMagicKeywords(text: string, resetTo?: string, phase?: number): string {
return highlightWorkflow(
highlightOrchestrate(highlightUltrathink(text, resetTo, phase), resetTo, phase),
resetTo,
phase,
);
}
/**
* Cheap test for "does this text contain any magic keyword as standalone prose?".
* Short-circuits on a substring probe before paying for the markdown-aware
* prose check, so the common "no keyword in buffer" path is just three
* `String#indexOf`s. Used by the live editor to gate the shimmer timer.
*/
export function hasMagicKeyword(text: string): boolean {
if (!text.includes("ultrathink") && !text.includes("orchestrate") && !text.includes("workflowz")) {
return false;
}
return containsUltrathink(text) || containsOrchestrate(text) || containsWorkflow(text);
}