Files
music_library/backlog/docs/doc-5 - Log-Line-Copy-in-Prod-Logs-Extension.md
T
2026-05-04 21:22:27 +01:00

5.8 KiB

id, title, type, created_date, updated_date
id title type created_date updated_date
doc-5 Log Line Copy in Prod-Logs Extension other 2026-05-03 21:06 2026-05-04 06:55

Implementation Analysis: Log Line Copy in Prod-Logs Extension

Problem Summary

The /prod-logs extension (.pi/extensions/prod-logs/index.ts) displays log lines in a scrollable viewer but offers no way to copy lines. The user wants:

  1. Copy a single line (cursor line) and exit
  2. Select and copy multiple lines and exit
  3. Copied lines in ascending (chronological) order

Architecture Context

  • Current component: LogViewer class — manages scroll offset, renders lines, handles keyboard input
  • Current UI return type: ctx.ui.custom<void> — no value returned on close
  • Current key bindings: Escape/q (close), r (refresh), Up/k Down/j (scroll), PgUp/PgDn (page), Home/End (jump)
  • Line ordering: logLines.reverse() is called before display, so newest-first visually. Copied lines should be oldest-first (ascending).
  • No cursor concept exists — only a scroll offset

Route A: Cursor + Toggle Selection + setEditorText

Concept

Add a visual cursor line. Enter to copy single line. Space to toggle line selection (multi-select mode). y to copy all selected lines and exit. After exit, call ctx.ui.setEditorText() with the copied text.

Changes to LogViewer

  1. Add cursorIndex: number (absolute line index into this.lines)
  2. Add selectedIndices: Set<number> (absolute indices)
  3. New key bindings:
    • Enter → copy cursor line, done(line)
    • Space → toggle selection of cursor line
    • y → copy selected lines (or cursor line if none selected), done(text)
  4. Render changes:
    • > prefix on cursor line (styled accent color)
    • prefix on selected lines (styled success color)
    • Non-selected, non-cursor lines keep existing prefix
  5. Help text update: add "y copy · Space select · Enter copy line"

Return value handling

const copiedText = await ctx.ui.custom<string | null>(...);
if (copiedText !== null) {
  ctx.ui.setEditorText(copiedText);
}

Pros

  • No external dependencies
  • setEditorText is already available in pi's ExtensionAPI
  • Aligns with the "paste" use case (text in editor = ready to send or copy)
  • Simple, contained change to one file

Cons

  • Modifies editor content (replaces whatever the user had typed)
  • Not a true "system clipboard" copy — the user can't paste outside pi without an extra step
  • Toggle-based selection for multi-line is somewhat unusual UX (vs. range selection)

Route B: Visual Mode (Vim-style) + setEditorText

Concept

Vim-inspired visual mode: v enters linewise visual selection, j/k extend the range, y copies and exits. Enter still copies single line. Selection is always a contiguous range.

Changes to LogViewer

  1. Add cursorIndex: number (absolute)
  2. Add visualMode: boolean + visualAnchor: number (start of visual selection)
  3. New key bindings:
    • Enter → copy cursor line, done(line)
    • v → enter visual mode (set anchor to cursor)
    • In visual mode: j/k extend selection range
    • y (in visual mode) → copy range [min(anchor,cursor)..max(anchor,cursor)], done(text)
    • Escape → exit visual mode
  4. Render changes:
    • Selected range lines get prefix (styled)
    • Cursor line gets > prefix
  5. Help text: show visual mode help when active

Pros

  • Familiar UX for vim users
  • Contiguous range selection is intuitive
  • Same setEditorText approach — no external deps

Cons

  • Slightly more complex state machine (normal mode vs. visual mode)
  • Doesn't allow non-contiguous selection (but the user said "contiguous")
  • Still modifies editor content

Route C: System Clipboard + Either Selection Model

Concept

Either selection model (A or B), but instead of setEditorText, write to the system clipboard. Requires a package like clipboardy.

Additional Changes

  • Add clipboardy dependency (or use node:child_process with pbcopy/xclip)
  • On copy: clipboardy.writeSync(text) or similar
  • No need to change return type — can stay void since we don't pass text back

Pros

  • True "copy" behavior — paste anywhere
  • Doesn't touch editor content
  • Works across applications

Cons

  • External dependency (clipboardy — native module, may have install issues)
  • Platform-specific behavior (macOS pbcopy, Linux xclip/wl-copy, Windows)
  • pi extensions run in the pi process; clipboard access may have security implications
  • May not work in headless/server environments where pi runs

Recommendation: Route B (Visual Mode + setEditorText)

Rationale:

  1. No external dependenciessetEditorText is already available
  2. Familiar UX — vim-style visual mode is intuitive for developers
  3. Contiguous selection — matches the user's "contiguous lines" description
  4. Simple implementation — contained change to one file, ~100 lines of code
  5. Immediate value — text in editor can be sent as a prompt or manually copied

Why not clipboard (Route C): The use case for /prod-logs is typically "I found an error line, I want to paste it into my next prompt to ask pi about it." setEditorText serves this perfectly. System clipboard adds complexity and platform fragility without proportional benefit for this specific use case.

Why not toggle selection (Route A): The user said "contiguous lines." Toggle-based selection allows non-contiguous selection, which they don't need. Visual mode is simpler to implement and more conventional.


Outstanding Question for User

How should the copied text be placed in the editor?

  • Append to current editor content (preserves what's there)
  • Replace current editor content (clean slate)

My leaning: Replace, because the user's intent is to paste a log line as a new prompt.