feat(editor): added atomic deletion and metadata display for image and paste markers

- Added atomicTokenPattern support to treat image and paste markers as indivisible tokens, enabling atomic deletion on backspace and forward-delete instead of character-by-character removal.
- Extended image marker format to include pixel dimensions [Image #N, WxH] and updated paste marker format to [Paste #N, +X lines] or [Paste #N, Y chars] with consistent comma-separated metadata.
- Implemented accent styling for paste markers in editor rendering while preserving hyperlinks for image markers, improving visual distinction between marker types.
- Refactored image reference handling to unified placeholder system supporting both image and paste markers with kind-aware rendering callbacks and regex patterns.
This commit is contained in:
can1357
2026-06-08 13:34:22 +02:00
parent 0a5e1438e7
commit d0d5b547ff
11 changed files with 275 additions and 43 deletions
@@ -1,6 +1,6 @@
import { addKeyAliases, canonicalKeyId, Editor, type KeyId, parseKey, parseKittySequence } from "@oh-my-pi/pi-tui";
import type { AppKeybinding } from "../../config/keybindings";
import { imageReferenceHyperlink, renderImageReferences } from "../image-references";
import { imageReferenceHyperlink, PLACEHOLDER_REGEX, renderPlaceholders } from "../image-references";
import { highlightMagicKeywords } from "../magic-keywords";
import { theme } from "../theme/theme";
@@ -76,16 +76,22 @@ export function extractBracketedImagePastePath(data: string): string | undefined
export class CustomEditor extends Editor {
imageLinks?: readonly (string | undefined)[];
/** Treat image/paste markers as indivisible: a stray backspace deletes the whole token
* instead of corrupting `[Paste #1, +30 lines]` into plain text. */
override atomicTokenPattern = PLACEHOLDER_REGEX;
/** Gradient-highlight the "ultrathink" / "orchestrate" / "workflowz" keywords as the user types
* them, skipping any occurrence inside code spans, fenced blocks, or XML sections. Also make
* pasted image placeholders visually distinct and hyperlink them once their blob file exists. */
decorateText = (text: string): string =>
renderImageReferences(text, {
renderPlaceholders(text, {
renderText: value => highlightMagicKeywords(value),
renderReference: (value, index) =>
imageReferenceHyperlink(value, index, this.imageLinks, label =>
theme.fg("accent", `\x1b[1m\x1b[4m${label}\x1b[24m\x1b[22m`),
),
renderReference: (value, kind, index) =>
kind === "image"
? imageReferenceHyperlink(value, index, this.imageLinks, label =>
theme.fg("accent", `\x1b[1m\x1b[4m${label}\x1b[24m\x1b[22m`),
)
: theme.fg("accent", `\x1b[1m${value}\x1b[22m`),
});
onEscape?: () => void;
onClear?: () => void;