@dungle-scrubs/tallow 0.8.21 → 0.8.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +35 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/interactive-mode-patch.d.ts +2 -0
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +82 -0
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/sdk.d.ts +17 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +68 -1
- package/dist/sdk.js.map +1 -1
- package/dist/workspace-transition-relay.d.ts +40 -7
- package/dist/workspace-transition-relay.d.ts.map +1 -1
- package/dist/workspace-transition-relay.js +81 -16
- package/dist/workspace-transition-relay.js.map +1 -1
- package/extensions/__integration__/background-task-widget-ownership.test.ts +216 -0
- package/extensions/__integration__/claude-hooks-compat.test.ts +156 -0
- package/extensions/__integration__/slash-command-bridge.test.ts +169 -23
- package/extensions/_shared/atomic-write.ts +1 -1
- package/extensions/_shared/bordered-box.ts +102 -0
- package/extensions/_shared/interop-events.ts +5 -0
- package/extensions/_shared/pid-registry.ts +1 -1
- package/extensions/agent-commands-tool/index.ts +4 -1
- package/extensions/background-task-tool/__tests__/lifecycle.test.ts +50 -25
- package/extensions/background-task-tool/index.ts +139 -221
- package/extensions/bash-tool-enhanced/index.ts +1 -75
- package/extensions/cd-tool/index.ts +2 -2
- package/extensions/context-fork/spawn.ts +4 -1
- package/extensions/health/index.ts +6 -6
- package/extensions/hooks/__tests__/claude-compat.test.ts +35 -0
- package/extensions/hooks/__tests__/subprocess-hardening.test.ts +73 -0
- package/extensions/hooks/index.ts +27 -4
- package/extensions/loop/__tests__/loop.test.ts +168 -4
- package/extensions/loop/extension.json +6 -5
- package/extensions/loop/index.ts +242 -31
- package/extensions/plan-mode-tool/__tests__/agent-end-execution.test.ts +373 -0
- package/extensions/plan-mode-tool/index.ts +103 -41
- package/extensions/prompt-suggestions/__tests__/editor-compatibility.test.ts +42 -0
- package/extensions/prompt-suggestions/index.ts +41 -6
- package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +267 -671
- package/extensions/slash-command-bridge/extension.json +1 -1
- package/extensions/slash-command-bridge/index.ts +230 -116
- package/extensions/subagent-tool/index.ts +2 -2
- package/extensions/subagent-tool/process.ts +4 -5
- package/extensions/tasks/commands/register-tasks-extension.ts +41 -0
- package/extensions/teams-tool/__tests__/peer-messaging.test.ts +29 -24
- package/extensions/teams-tool/dashboard.ts +3 -5
- package/extensions/teams-tool/dispatch/auto-dispatch.ts +18 -1
- package/extensions/teams-tool/tools/teammate-tools.ts +9 -6
- package/extensions/wezterm-pane-control/__tests__/index.test.ts +88 -4
- package/extensions/wezterm-pane-control/index.ts +113 -8
- package/package.json +6 -4
- package/packages/tallow-tui/README.md +51 -0
- package/packages/tallow-tui/dist/autocomplete.d.ts +48 -0
- package/packages/tallow-tui/dist/autocomplete.d.ts.map +1 -0
- package/packages/tallow-tui/dist/autocomplete.js +564 -0
- package/packages/tallow-tui/dist/autocomplete.js.map +1 -0
- package/packages/tallow-tui/dist/border-styles.d.ts +32 -0
- package/packages/tallow-tui/dist/border-styles.d.ts.map +1 -0
- package/packages/tallow-tui/dist/border-styles.js +46 -0
- package/packages/tallow-tui/dist/border-styles.js.map +1 -0
- package/packages/tallow-tui/dist/components/bordered-box.d.ts +52 -0
- package/packages/tallow-tui/dist/components/bordered-box.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/bordered-box.js +89 -0
- package/packages/tallow-tui/dist/components/bordered-box.js.map +1 -0
- package/packages/tallow-tui/dist/components/box.d.ts +22 -0
- package/packages/tallow-tui/dist/components/box.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/box.js +104 -0
- package/packages/tallow-tui/dist/components/box.js.map +1 -0
- package/packages/tallow-tui/dist/components/cancellable-loader.d.ts +22 -0
- package/packages/tallow-tui/dist/components/cancellable-loader.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/cancellable-loader.js +35 -0
- package/packages/tallow-tui/dist/components/cancellable-loader.js.map +1 -0
- package/packages/tallow-tui/dist/components/editor.d.ts +240 -0
- package/packages/tallow-tui/dist/components/editor.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/editor.js +1766 -0
- package/packages/tallow-tui/dist/components/editor.js.map +1 -0
- package/packages/tallow-tui/dist/components/image.d.ts +126 -0
- package/packages/tallow-tui/dist/components/image.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/image.js +245 -0
- package/packages/tallow-tui/dist/components/image.js.map +1 -0
- package/packages/tallow-tui/dist/components/input.d.ts +37 -0
- package/packages/tallow-tui/dist/components/input.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/input.js +439 -0
- package/packages/tallow-tui/dist/components/input.js.map +1 -0
- package/packages/tallow-tui/dist/components/loader.d.ts +88 -0
- package/packages/tallow-tui/dist/components/loader.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/loader.js +146 -0
- package/packages/tallow-tui/dist/components/loader.js.map +1 -0
- package/packages/tallow-tui/dist/components/markdown.d.ts +95 -0
- package/packages/tallow-tui/dist/components/markdown.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/markdown.js +633 -0
- package/packages/tallow-tui/dist/components/markdown.js.map +1 -0
- package/packages/tallow-tui/dist/components/select-list.d.ts +32 -0
- package/packages/tallow-tui/dist/components/select-list.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/select-list.js +156 -0
- package/packages/tallow-tui/dist/components/select-list.js.map +1 -0
- package/packages/tallow-tui/dist/components/settings-list.d.ts +50 -0
- package/packages/tallow-tui/dist/components/settings-list.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/settings-list.js +189 -0
- package/packages/tallow-tui/dist/components/settings-list.js.map +1 -0
- package/packages/tallow-tui/dist/components/spacer.d.ts +12 -0
- package/packages/tallow-tui/dist/components/spacer.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/spacer.js +23 -0
- package/packages/tallow-tui/dist/components/spacer.js.map +1 -0
- package/packages/tallow-tui/dist/components/text.d.ts +19 -0
- package/packages/tallow-tui/dist/components/text.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/text.js +91 -0
- package/packages/tallow-tui/dist/components/text.js.map +1 -0
- package/packages/tallow-tui/dist/components/truncated-text.d.ts +13 -0
- package/packages/tallow-tui/dist/components/truncated-text.d.ts.map +1 -0
- package/packages/tallow-tui/dist/components/truncated-text.js +51 -0
- package/packages/tallow-tui/dist/components/truncated-text.js.map +1 -0
- package/packages/tallow-tui/dist/editor-component.d.ts +50 -0
- package/packages/tallow-tui/dist/editor-component.d.ts.map +1 -0
- package/packages/tallow-tui/dist/editor-component.js +2 -0
- package/packages/tallow-tui/dist/editor-component.js.map +1 -0
- package/packages/tallow-tui/dist/fuzzy.d.ts +16 -0
- package/packages/tallow-tui/dist/fuzzy.d.ts.map +1 -0
- package/packages/tallow-tui/dist/fuzzy.js +107 -0
- package/packages/tallow-tui/dist/fuzzy.js.map +1 -0
- package/packages/tallow-tui/dist/index.d.ts +25 -0
- package/packages/tallow-tui/dist/index.d.ts.map +1 -0
- package/packages/tallow-tui/dist/index.js +35 -0
- package/packages/tallow-tui/dist/index.js.map +1 -0
- package/packages/tallow-tui/dist/keybindings.d.ts +39 -0
- package/packages/tallow-tui/dist/keybindings.d.ts.map +1 -0
- package/packages/tallow-tui/dist/keybindings.js +114 -0
- package/packages/tallow-tui/dist/keybindings.js.map +1 -0
- package/packages/tallow-tui/dist/keys.d.ts +168 -0
- package/packages/tallow-tui/dist/keys.d.ts.map +1 -0
- package/packages/tallow-tui/dist/keys.js +971 -0
- package/packages/tallow-tui/dist/keys.js.map +1 -0
- package/packages/tallow-tui/dist/kill-ring.d.ts +28 -0
- package/packages/tallow-tui/dist/kill-ring.d.ts.map +1 -0
- package/packages/tallow-tui/dist/kill-ring.js +44 -0
- package/packages/tallow-tui/dist/kill-ring.js.map +1 -0
- package/packages/tallow-tui/dist/stdin-buffer.d.ts +48 -0
- package/packages/tallow-tui/dist/stdin-buffer.d.ts.map +1 -0
- package/packages/tallow-tui/dist/stdin-buffer.js +317 -0
- package/packages/tallow-tui/dist/stdin-buffer.js.map +1 -0
- package/packages/tallow-tui/dist/terminal-image.d.ts +161 -0
- package/packages/tallow-tui/dist/terminal-image.d.ts.map +1 -0
- package/packages/tallow-tui/dist/terminal-image.js +460 -0
- package/packages/tallow-tui/dist/terminal-image.js.map +1 -0
- package/packages/tallow-tui/dist/terminal.d.ts +102 -0
- package/packages/tallow-tui/dist/terminal.d.ts.map +1 -0
- package/packages/tallow-tui/dist/terminal.js +263 -0
- package/packages/tallow-tui/dist/terminal.js.map +1 -0
- package/packages/tallow-tui/dist/test-utils/capability-env.d.ts +14 -0
- package/packages/tallow-tui/dist/test-utils/capability-env.d.ts.map +1 -0
- package/packages/tallow-tui/dist/test-utils/capability-env.js +55 -0
- package/packages/tallow-tui/dist/test-utils/capability-env.js.map +1 -0
- package/packages/tallow-tui/dist/tui.d.ts +239 -0
- package/packages/tallow-tui/dist/tui.d.ts.map +1 -0
- package/packages/tallow-tui/dist/tui.js +1058 -0
- package/packages/tallow-tui/dist/tui.js.map +1 -0
- package/packages/tallow-tui/dist/undo-stack.d.ts +17 -0
- package/packages/tallow-tui/dist/undo-stack.d.ts.map +1 -0
- package/packages/tallow-tui/dist/undo-stack.js +25 -0
- package/packages/tallow-tui/dist/undo-stack.js.map +1 -0
- package/packages/tallow-tui/dist/utils.d.ts +96 -0
- package/packages/tallow-tui/dist/utils.d.ts.map +1 -0
- package/packages/tallow-tui/dist/utils.js +843 -0
- package/packages/tallow-tui/dist/utils.js.map +1 -0
- package/packages/tallow-tui/package.json +24 -0
- package/packages/tallow-tui/src/__tests__/__snapshots__/render.test.ts.snap +121 -0
- package/packages/tallow-tui/src/__tests__/editor-border.test.ts +72 -0
- package/packages/tallow-tui/src/__tests__/editor-change-listener.test.ts +121 -0
- package/packages/tallow-tui/src/__tests__/editor-ghost-text.test.ts +112 -0
- package/packages/tallow-tui/src/__tests__/fuzzy.test.ts +91 -0
- package/packages/tallow-tui/src/__tests__/image-component.test.ts +113 -0
- package/packages/tallow-tui/src/__tests__/keys.test.ts +141 -0
- package/packages/tallow-tui/src/__tests__/render.test.ts +179 -0
- package/packages/tallow-tui/src/__tests__/stdin-buffer.test.ts +82 -0
- package/packages/tallow-tui/src/__tests__/terminal-image.test.ts +363 -0
- package/packages/tallow-tui/src/__tests__/tui-diff-regression.test.ts +454 -0
- package/packages/tallow-tui/src/__tests__/tui-render-scheduling.test.ts +256 -0
- package/packages/tallow-tui/src/__tests__/utils.test.ts +259 -0
- package/packages/tallow-tui/src/autocomplete.ts +716 -0
- package/packages/tallow-tui/src/border-styles.ts +60 -0
- package/packages/tallow-tui/src/components/bordered-box.ts +113 -0
- package/packages/tallow-tui/src/components/box.ts +137 -0
- package/packages/tallow-tui/src/components/cancellable-loader.ts +40 -0
- package/packages/tallow-tui/src/components/editor.ts +2143 -0
- package/packages/tallow-tui/src/components/image.ts +315 -0
- package/packages/tallow-tui/src/components/input.ts +522 -0
- package/packages/tallow-tui/src/components/loader.ts +187 -0
- package/packages/tallow-tui/src/components/markdown.ts +780 -0
- package/packages/tallow-tui/src/components/select-list.ts +197 -0
- package/packages/tallow-tui/src/components/settings-list.ts +264 -0
- package/packages/tallow-tui/src/components/spacer.ts +28 -0
- package/packages/tallow-tui/src/components/text.ts +113 -0
- package/packages/tallow-tui/src/components/truncated-text.ts +65 -0
- package/packages/tallow-tui/src/editor-component.ts +92 -0
- package/packages/tallow-tui/src/fuzzy.ts +133 -0
- package/packages/tallow-tui/src/index.ts +118 -0
- package/packages/tallow-tui/src/keybindings.ts +183 -0
- package/packages/tallow-tui/src/keys.ts +1189 -0
- package/packages/tallow-tui/src/kill-ring.ts +46 -0
- package/packages/tallow-tui/src/stdin-buffer.ts +386 -0
- package/packages/tallow-tui/src/terminal-image.ts +619 -0
- package/packages/tallow-tui/src/terminal.ts +350 -0
- package/packages/tallow-tui/src/test-utils/capability-env.ts +56 -0
- package/packages/tallow-tui/src/tui.ts +1336 -0
- package/packages/tallow-tui/src/undo-stack.ts +28 -0
- package/packages/tallow-tui/src/utils.ts +948 -0
- package/packages/tallow-tui/tsconfig.build.json +21 -0
- package/runtime/agent-runner.ts +20 -0
- package/runtime/atomic-write.ts +8 -0
- package/runtime/otel.ts +12 -0
- package/runtime/resolve-module.ts +23 -0
- package/runtime/runtime-path-provider.ts +12 -0
- package/runtime/runtime-provenance.ts +17 -0
- package/runtime/workspace-transition-relay.ts +21 -0
- package/runtime/workspace-transition.ts +29 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image component for the TUI.
|
|
3
|
+
* Renders images via Kitty/iTerm2 protocols with optional border framing.
|
|
4
|
+
* Preserves aspect ratio and avoids small-image upscaling.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type BorderStyle, ROUNDED } from "../border-styles.js";
|
|
10
|
+
import {
|
|
11
|
+
getCapabilities,
|
|
12
|
+
getImageDimensions,
|
|
13
|
+
type ImageDimensions,
|
|
14
|
+
imageFallback,
|
|
15
|
+
renderImage,
|
|
16
|
+
} from "../terminal-image.js";
|
|
17
|
+
import type { Component } from "../tui.js";
|
|
18
|
+
import { hyperlink } from "../utils.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Pending file path for the next Image instance.
|
|
22
|
+
* Set by external code (e.g. a tool_result hook) before Image construction.
|
|
23
|
+
* Consumed once by the next Image constructor, then cleared.
|
|
24
|
+
*/
|
|
25
|
+
let pendingFilePath: string | undefined;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set the file path for the next Image instance to be constructed.
|
|
29
|
+
* Called before Image creation so the component can render a clickable link.
|
|
30
|
+
*
|
|
31
|
+
* @param path - Absolute file path, or undefined to clear
|
|
32
|
+
*/
|
|
33
|
+
export function setNextImageFilePath(path: string | undefined): void {
|
|
34
|
+
pendingFilePath = path;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ImageTheme {
|
|
38
|
+
fallbackColor: (str: string) => string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ImageOptions {
|
|
42
|
+
maxWidthCells?: number;
|
|
43
|
+
maxHeightCells?: number;
|
|
44
|
+
filename?: string;
|
|
45
|
+
/** Kitty image ID. If provided, reuses this ID (for animations/updates). */
|
|
46
|
+
imageId?: number;
|
|
47
|
+
/** Show a border around the image. Default: true. */
|
|
48
|
+
border?: boolean;
|
|
49
|
+
/** Border style. Default: ROUNDED. */
|
|
50
|
+
borderStyle?: BorderStyle;
|
|
51
|
+
/** Color function for border characters. */
|
|
52
|
+
borderColorFn?: (str: string) => string;
|
|
53
|
+
/** Absolute file path — enables a clickable OSC 8 file:// link below the image. */
|
|
54
|
+
filePath?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Default fraction of terminal rows available for images when no explicit maxHeightCells is set. */
|
|
58
|
+
const DEFAULT_MAX_HEIGHT_RATIO = 0.9;
|
|
59
|
+
|
|
60
|
+
/** Minimum dynamic max height when terminal row count is available. */
|
|
61
|
+
const MIN_DYNAMIC_MAX_HEIGHT_CELLS = 10;
|
|
62
|
+
|
|
63
|
+
/** Border overhead: │ + space on each side = 4 columns. */
|
|
64
|
+
const BORDER_OVERHEAD = 4;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Calculate a dynamic default max image height from terminal size.
|
|
68
|
+
*
|
|
69
|
+
* Uses a high percentage of the current terminal height to avoid unnecessary
|
|
70
|
+
* downscaling while still preventing images from consuming the full viewport.
|
|
71
|
+
* Returns undefined when terminal row count is unavailable, which disables the
|
|
72
|
+
* height cap unless explicitly provided via options.maxHeightCells.
|
|
73
|
+
*
|
|
74
|
+
* @returns Dynamic max height in terminal rows, or undefined
|
|
75
|
+
*/
|
|
76
|
+
function getDefaultMaxHeightCells(): number | undefined {
|
|
77
|
+
const terminalRows = process.stdout.rows;
|
|
78
|
+
if (!terminalRows || terminalRows <= 0) return undefined;
|
|
79
|
+
return Math.max(
|
|
80
|
+
MIN_DYNAMIC_MAX_HEIGHT_CELLS,
|
|
81
|
+
Math.floor(terminalRows * DEFAULT_MAX_HEIGHT_RATIO)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* TUI component that renders an inline image using terminal graphics protocols.
|
|
87
|
+
* Falls back to a text placeholder when the terminal lacks image support.
|
|
88
|
+
*
|
|
89
|
+
* Features:
|
|
90
|
+
* - Dynamic height cap (~90% of terminal rows) by default (overridable via maxHeightCells)
|
|
91
|
+
* - Small images not upscaled beyond native pixel width
|
|
92
|
+
* - Rounded border frame by default (configurable or disableable)
|
|
93
|
+
* - Kitty warping fix: omits r= param so terminal auto-calculates rows
|
|
94
|
+
*/
|
|
95
|
+
export class Image implements Component {
|
|
96
|
+
private base64Data: string;
|
|
97
|
+
private mimeType: string;
|
|
98
|
+
private dimensions: ImageDimensions;
|
|
99
|
+
private theme: ImageTheme;
|
|
100
|
+
private options: ImageOptions;
|
|
101
|
+
private imageId?: number;
|
|
102
|
+
|
|
103
|
+
private cachedLines?: string[];
|
|
104
|
+
private cachedWidth?: number;
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
base64Data: string,
|
|
108
|
+
mimeType: string,
|
|
109
|
+
theme: ImageTheme,
|
|
110
|
+
options: ImageOptions = {},
|
|
111
|
+
dimensions?: ImageDimensions
|
|
112
|
+
) {
|
|
113
|
+
this.base64Data = base64Data;
|
|
114
|
+
this.mimeType = mimeType;
|
|
115
|
+
this.theme = theme;
|
|
116
|
+
this.options = options;
|
|
117
|
+
this.dimensions = dimensions ||
|
|
118
|
+
getImageDimensions(base64Data, mimeType) || { widthPx: 800, heightPx: 600 };
|
|
119
|
+
this.imageId = options.imageId;
|
|
120
|
+
|
|
121
|
+
// Auto-consume pending file path if not explicitly provided
|
|
122
|
+
if (!this.options.filePath && pendingFilePath) {
|
|
123
|
+
this.options = { ...this.options, filePath: pendingFilePath };
|
|
124
|
+
pendingFilePath = undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the Kitty image ID used by this image (if any).
|
|
130
|
+
* @returns Image ID or undefined
|
|
131
|
+
*/
|
|
132
|
+
getImageId(): number | undefined {
|
|
133
|
+
return this.imageId;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Clears cached render output so the next render() recomputes. */
|
|
137
|
+
invalidate(): void {
|
|
138
|
+
this.cachedLines = undefined;
|
|
139
|
+
this.cachedWidth = undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Render the image into terminal lines.
|
|
144
|
+
*
|
|
145
|
+
* @param width - Available terminal width in columns
|
|
146
|
+
* @returns Array of strings (lines) for the TUI to output
|
|
147
|
+
*/
|
|
148
|
+
render(width: number): string[] {
|
|
149
|
+
if (this.cachedLines && this.cachedWidth === width) {
|
|
150
|
+
return this.cachedLines;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const showBorder = this.options.border === true;
|
|
154
|
+
// Ignore maxWidthCells from upstream — it's hardcoded to 60 in
|
|
155
|
+
// pi-coding-agent and squashes landscape images. Use the full
|
|
156
|
+
// available terminal width instead; height capping and natural-width
|
|
157
|
+
// clamping in calculateImageLayout prevent oversized output.
|
|
158
|
+
const maxWidth = this.getSafeMaxWidthCells(width, showBorder);
|
|
159
|
+
const maxHeight = this.options.maxHeightCells ?? getDefaultMaxHeightCells();
|
|
160
|
+
|
|
161
|
+
const caps = getCapabilities();
|
|
162
|
+
let lines: string[];
|
|
163
|
+
|
|
164
|
+
if (caps.images) {
|
|
165
|
+
const result = renderImage(this.base64Data, this.dimensions, {
|
|
166
|
+
maxWidthCells: maxWidth,
|
|
167
|
+
maxHeightCells: maxHeight,
|
|
168
|
+
imageId: this.imageId,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (result) {
|
|
172
|
+
if (result.imageId) {
|
|
173
|
+
this.imageId = result.imageId;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
lines = showBorder
|
|
177
|
+
? this.buildBorderedImage(result.sequence, result.rows, result.columns)
|
|
178
|
+
: this.buildUnborderedImage(result.sequence, result.rows, result.columns);
|
|
179
|
+
} else {
|
|
180
|
+
lines = this.buildFallback(showBorder);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
lines = this.buildFallback(showBorder);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.cachedLines = lines;
|
|
187
|
+
this.cachedWidth = width;
|
|
188
|
+
|
|
189
|
+
return lines;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Resolve a safe image width budget from the current pane width.
|
|
194
|
+
*
|
|
195
|
+
* Keeps width math deterministic for ultra-narrow panes by enforcing a
|
|
196
|
+
* minimum of 1 cell after reserved spacing and optional border overhead.
|
|
197
|
+
*
|
|
198
|
+
* @param width - Current pane width in terminal columns
|
|
199
|
+
* @param showBorder - Whether bordered rendering is enabled
|
|
200
|
+
* @returns Positive max width in cells for `renderImage`
|
|
201
|
+
*/
|
|
202
|
+
private getSafeMaxWidthCells(width: number, showBorder: boolean): number {
|
|
203
|
+
const borderCols = showBorder ? BORDER_OVERHEAD : 0;
|
|
204
|
+
const availableWidth = Math.floor(width) - 2 - borderCols;
|
|
205
|
+
return Math.max(1, availableWidth);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Wraps visible text in an OSC 8 file:// hyperlink if filePath is set.
|
|
210
|
+
* Returns the text unchanged when no filePath is configured.
|
|
211
|
+
*
|
|
212
|
+
* @param text - Visible content to wrap (spaces, border chars, etc.)
|
|
213
|
+
* @returns Text optionally wrapped in OSC 8 escape sequences
|
|
214
|
+
*/
|
|
215
|
+
private wrapFileLink(text: string): string {
|
|
216
|
+
if (!this.options.filePath) return text;
|
|
217
|
+
return hyperlink(`file://${encodeURI(this.options.filePath)}`, text);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Builds image output without a border (original behavior).
|
|
222
|
+
* First N-1 lines are filled with OSC 8–wrapped spaces (when filePath
|
|
223
|
+
* is set) so the image area is a clickable link in the text layer.
|
|
224
|
+
* Last line moves cursor up and outputs the image escape sequence.
|
|
225
|
+
*
|
|
226
|
+
* @param sequence - Terminal escape sequence for the image
|
|
227
|
+
* @param rows - Number of terminal rows the image occupies
|
|
228
|
+
* @param columns - Number of terminal columns the image occupies
|
|
229
|
+
* @returns Lines array for the TUI
|
|
230
|
+
*/
|
|
231
|
+
private buildUnborderedImage(sequence: string, rows: number, columns: number): string[] {
|
|
232
|
+
const lines: string[] = [];
|
|
233
|
+
const filler = this.wrapFileLink(" ".repeat(columns));
|
|
234
|
+
for (let i = 0; i < rows - 1; i++) {
|
|
235
|
+
lines.push(filler);
|
|
236
|
+
}
|
|
237
|
+
const moveUp = rows > 1 ? `\x1b[${rows - 1}A` : "";
|
|
238
|
+
lines.push(`${this.wrapFileLink(" ".repeat(columns))}${moveUp}\x1b[${columns}D${sequence}`);
|
|
239
|
+
return lines;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Builds image output wrapped in a border.
|
|
244
|
+
* Border characters occupy the text layer; the image fills the inner
|
|
245
|
+
* cell range via the graphics layer (Kitty/iTerm2).
|
|
246
|
+
*
|
|
247
|
+
* The last content line outputs a full bordered row (text layer), then
|
|
248
|
+
* repositions the cursor back to the first content row and places the
|
|
249
|
+
* image sequence. The graphics layer draws over the inner spaces while
|
|
250
|
+
* border characters at the edges remain visible.
|
|
251
|
+
*
|
|
252
|
+
* @param sequence - Terminal escape sequence for the image
|
|
253
|
+
* @param rows - Number of terminal rows the image occupies
|
|
254
|
+
* @param columns - Number of terminal columns the image occupies
|
|
255
|
+
* @returns Lines array for the TUI
|
|
256
|
+
*/
|
|
257
|
+
private buildBorderedImage(sequence: string, rows: number, columns: number): string[] {
|
|
258
|
+
const style = this.options.borderStyle ?? ROUNDED;
|
|
259
|
+
const colorFn = this.options.borderColorFn ?? ((s: string) => s);
|
|
260
|
+
|
|
261
|
+
const innerWidth = columns;
|
|
262
|
+
const totalWidth = innerWidth + BORDER_OVERHEAD;
|
|
263
|
+
|
|
264
|
+
const top = colorFn(style.topLeft + style.horizontal.repeat(totalWidth - 2) + style.topRight);
|
|
265
|
+
const bottom = colorFn(
|
|
266
|
+
style.bottomLeft + style.horizontal.repeat(totalWidth - 2) + style.bottomRight
|
|
267
|
+
);
|
|
268
|
+
const leftBorder = `${colorFn(style.vertical)} `;
|
|
269
|
+
const rightBorder = ` ${colorFn(style.vertical)}`;
|
|
270
|
+
const emptyInner = this.wrapFileLink(" ".repeat(innerWidth));
|
|
271
|
+
const borderedLine = leftBorder + emptyInner + rightBorder;
|
|
272
|
+
|
|
273
|
+
const lines: string[] = [top];
|
|
274
|
+
|
|
275
|
+
// Bordered empty lines — image fills the inner area via the graphics layer
|
|
276
|
+
for (let i = 0; i < rows - 1; i++) {
|
|
277
|
+
lines.push(borderedLine);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Last content line: full bordered text, then reposition cursor for image placement.
|
|
281
|
+
// CUU (cursor up) + CUB (cursor backward) positions to column 2 of the first content row.
|
|
282
|
+
const moveUp = rows > 1 ? `\x1b[${rows - 1}A` : "";
|
|
283
|
+
const moveToImageStart = `\x1b[${totalWidth - 2}D`;
|
|
284
|
+
lines.push(borderedLine + moveUp + moveToImageStart + sequence);
|
|
285
|
+
|
|
286
|
+
lines.push(bottom);
|
|
287
|
+
|
|
288
|
+
return lines;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Builds fallback text when the terminal doesn't support images.
|
|
293
|
+
* Optionally wrapped in a border for visual consistency.
|
|
294
|
+
*
|
|
295
|
+
* @param showBorder - Whether to wrap the fallback in a border
|
|
296
|
+
* @returns Lines array for the TUI
|
|
297
|
+
*/
|
|
298
|
+
private buildFallback(showBorder: boolean): string[] {
|
|
299
|
+
const fallback = imageFallback(this.mimeType, this.dimensions, this.options.filename);
|
|
300
|
+
const text = this.theme.fallbackColor(fallback);
|
|
301
|
+
|
|
302
|
+
if (showBorder) {
|
|
303
|
+
const style = this.options.borderStyle ?? ROUNDED;
|
|
304
|
+
const colorFn = this.options.borderColorFn ?? ((s: string) => s);
|
|
305
|
+
const innerWidth = fallback.length + 2; // 1 space padding each side
|
|
306
|
+
const top = colorFn(style.topLeft + style.horizontal.repeat(innerWidth) + style.topRight);
|
|
307
|
+
const bottom = colorFn(
|
|
308
|
+
style.bottomLeft + style.horizontal.repeat(innerWidth) + style.bottomRight
|
|
309
|
+
);
|
|
310
|
+
return [top, `${colorFn(style.vertical)} ${text} ${colorFn(style.vertical)}`, bottom];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return [text];
|
|
314
|
+
}
|
|
315
|
+
}
|