You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Related work:#13 bounds optional menu-bar title length; #50 bounds retained output storage. This issue controls how retained output is projected into AppKit surfaces.
Problem and intended outcome
Pinchos can retain up to max_output bytes independently for stdout and stderr, then place large portions of that content directly into native UI strings.
The lifecycle menu currently creates an NSMenuItem whose title is Value: <fullOutput>. A configured tooltip can substitute {output}, which is the complete retained stdout including newlines. Action diagnostics similarly project command output and error content into menu items.
At the 64-KiB default, a single item can create a very large multi-line menu title and tooltip. At larger user-configured limits, opening a right-click menu can allocate/copy large strings and force AppKit to measure and lay out them on the main actor. Raw command output may also contain tabs, carriage returns, NUL/control characters, bidi controls, or thousands of lines that make the menu unreadable or visually misleading.
Issue #13 deliberately preserves the full value for diagnostics, but “available to diagnostics” should not require rendering the complete byte tail as one menu label.
The intended outcome is a compact, predictable native menu:
menu titles and tooltips use bounded, sanitized previews;
the user can still retrieve the complete retained stdout/stderr on demand;
opening a menu has cost bounded independently of max_output;
multi-line and control-heavy output cannot distort the global actions or make the menu unusable;
accessibility text remains concise and meaningful.
Current evidence
Relevant implementation in Sources/pinchos/StatusItemController.swift:
makeLifecycleMenu obtains ItemRuntimeSnapshot and action snapshots on the main actor;
runtime diagnostics append NSMenuItem(title: "Value: \(fullOutput)", ...) with the full retained successful output;
command/action diagnostics build additional menu titles from execution text;
global Open Config, Reload Config, and Quit Pinchos items are appended after dynamic output-derived content.
Relevant implementation in Sources/pinchos/ManagedItem.swift and PinchosCore/Format.swift:
tooltip templates can expand {output} to the full retained output;
the resulting string is assigned to the native status-item tooltip;
formatting and presentation can be recomputed repeatedly as runtime state changes.
The default retained limit is already large enough to create pathological menu layout. #50 may reduce allocation overhead and add hard budgets, but any retained limit suitable for diagnostics can still be too large for an NSMenuItem.title.
Recommended UX baseline
Define one pure preview formatter with separate, documented limits for:
menu value preview;
menu stderr/error preview;
tooltip preview;
action diagnostics.
The formatter should:
cap UTF-8 bytes and/or grapheme clusters at a modest value selected through AppKit measurement;
cap line count;
collapse or visibly escape embedded newlines for menu titles;
replace unsafe C0/C1 controls and NUL with visible placeholders;
preserve ordinary Unicode without splitting grapheme clusters;
append an explicit truncation marker with total byte/line information when known.
Add non-executing menu actions such as Copy Full Output, Copy Full Error, or Copy Diagnostics only when relevant content exists. Clipboard materialization happens on demand and may use the complete retained string. Keep those actions separate from command actions and global lifecycle controls.
A tooltip template that expands beyond the tooltip limit should receive the same deterministic preview/truncation treatment. Full data remains available through the menu copy action.
The exact preview constants should be centralized and covered by performance/visual tests rather than scattered magic numbers.
Bounded scope and non-goals
In scope:
Bound every dynamic command-derived NSMenuItem.title and native tooltip.
Sanitize control characters and multi-line content for native presentation.
Preserve complete retained stdout/stderr through on-demand copy actions.
Keep global actions visible, stable, and reachable regardless of output.
Add accessible labels/help for truncated previews and copy actions.
Avoid eagerly creating duplicate full-output strings while merely opening a menu.
Add pure formatter tests and AppKit menu performance tests.
Document preview versus retained-output semantics.
Opening an item menu has a documented maximum amount of output-derived text to format and lay out, independent of max_output.
A 64-KiB single-line value produces a concise preview with an explicit truncation indicator, not a 64-KiB menu title.
A value with thousands of lines does not create a thousands-line menu item.
NUL, escape, carriage return, tab, bidi-control, and other selected control fixtures are rendered according to a deterministic sanitization policy and cannot obscure the global action section.
Preview truncation does not split extended grapheme clusters.
Copy Full Output places the complete retained stdout on NSPasteboard exactly as retained.
Equivalent copy access exists for relevant stderr/action diagnostics without displaying it all in the title.
Copy actions are absent or disabled when no corresponding content exists.
Tooltip expansion is bounded and clearly indicates truncation while preserving ordinary configured text.
Global Open Config, Reload Config, and Quit Pinchos remain visible and usable for pathological output fixtures.
VoiceOver/accessibility inspection receives concise labels and can identify copy actions and truncation state.
Menu construction for maximum allowed retained output stays under a measured main-thread latency budget in a release build.
Repeated menu opens do not grow retained memory or keep obsolete full-output copies alive.
Add table-driven formatter tests for long lines, many lines, CRLF, tabs, NUL, escape sequences, bidi controls, emoji ZWJ sequences, combining marks, and exact-boundary cases.
Add headless menu tests that assert title lengths, global action placement, and copy payloads.
Build menus repeatedly with the selected maximum output and measure allocations and main-thread duration.
Run a release GUI with pathological command fixtures and verify menu geometry, responsiveness, tooltip behavior, clipboard contents, and global actions.
Perform VoiceOver/accessibility inspection in a permitted logged-in session.
Clipboard actions are native and cheap, but copying a maximum-size value necessarily materializes it once. That work is user-initiated and bounded by #50, unlike the current automatic menu-open cost.
Sanitization must be for display only. The clipboard should receive the exact retained text unless the user selects a separately named sanitized-copy action.
Severity and category
Severity: Medium-high UI responsiveness and usability risk
Category: AppKit presentation / untrusted command output / memory
Related work: #13 bounds optional menu-bar title length; #50 bounds retained output storage. This issue controls how retained output is projected into AppKit surfaces.
Problem and intended outcome
Pinchos can retain up to
max_outputbytes independently for stdout and stderr, then place large portions of that content directly into native UI strings.The lifecycle menu currently creates an
NSMenuItemwhose title isValue: <fullOutput>. A configured tooltip can substitute{output}, which is the complete retained stdout including newlines. Action diagnostics similarly project command output and error content into menu items.At the 64-KiB default, a single item can create a very large multi-line menu title and tooltip. At larger user-configured limits, opening a right-click menu can allocate/copy large strings and force AppKit to measure and lay out them on the main actor. Raw command output may also contain tabs, carriage returns, NUL/control characters, bidi controls, or thousands of lines that make the menu unreadable or visually misleading.
Issue #13 deliberately preserves the full value for diagnostics, but “available to diagnostics” should not require rendering the complete byte tail as one menu label.
The intended outcome is a compact, predictable native menu:
max_output;Current evidence
Relevant implementation in
Sources/pinchos/StatusItemController.swift:makeLifecycleMenuobtainsItemRuntimeSnapshotand action snapshots on the main actor;NSMenuItem(title: "Value: \(fullOutput)", ...)with the full retained successful output;Relevant implementation in
Sources/pinchos/ManagedItem.swiftandPinchosCore/Format.swift:{output}to the full retained output;The default retained limit is already large enough to create pathological menu layout. #50 may reduce allocation overhead and add hard budgets, but any retained limit suitable for diagnostics can still be too large for an
NSMenuItem.title.Recommended UX baseline
Define one pure preview formatter with separate, documented limits for:
The formatter should:
Add non-executing menu actions such as Copy Full Output, Copy Full Error, or Copy Diagnostics only when relevant content exists. Clipboard materialization happens on demand and may use the complete retained string. Keep those actions separate from command actions and global lifecycle controls.
A tooltip template that expands beyond the tooltip limit should receive the same deterministic preview/truncation treatment. Full data remains available through the menu copy action.
The exact preview constants should be centralized and covered by performance/visual tests rather than scattered magic numbers.
Bounded scope and non-goals
In scope:
NSMenuItem.titleand native tooltip.Explicit non-goals:
max_outputdirectly determine native menu width/height.Testable acceptance criteria
max_output.NSPasteboardexactly as retained.swift testandswift build -c releasepass.Likely implementation areas
DiagnosticPreviewFormatterinSources/PinchosCoreor a UI-free helper moduleSources/pinchos/StatusItemController.swiftSources/pinchos/ManagedItem.swiftSources/PinchosCore/Format.swiftfor bounded tooltip expansion if appropriateTests/PinchosCoreTests/FormatTests.swiftTests/pinchosTests/StatusItemControllerTests.swiftTests/pinchosTests/RecoveryLifecycleTests.swiftdocs/manual-qavisual/accessibility evidenceVerification plan
Risks and dependencies
Clipboard actions are native and cheap, but copying a maximum-size value necessarily materializes it once. That work is user-initiated and bounded by #50, unlike the current automatic menu-open cost.
Sanitization must be for display only. The clipboard should receive the exact retained text unless the user selects a separately named sanitized-copy action.