pi-ui-extend 0.1.26 → 0.1.27
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/app/icons.d.ts +3 -0
- package/dist/app/icons.js +6 -0
- package/dist/app/rendering/conversation-tool-renderer.js +3 -3
- package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
- package/dist/app/rendering/tool-block-renderer.js +76 -21
- package/dist/app/screen/mouse-controller.d.ts +2 -0
- package/dist/app/screen/mouse-controller.js +37 -1
- package/package.json +1 -1
package/dist/app/icons.d.ts
CHANGED
|
@@ -25,6 +25,9 @@ declare const NERD_FONT_ICONS: {
|
|
|
25
25
|
readonly thinkingExpanded: "";
|
|
26
26
|
readonly stopCircle: "";
|
|
27
27
|
readonly timerSand: "";
|
|
28
|
+
readonly toolBodyEnd: "└";
|
|
29
|
+
readonly toolBodyGutter: "│";
|
|
30
|
+
readonly toolPreviewTruncated: "⊞";
|
|
28
31
|
readonly down: "";
|
|
29
32
|
};
|
|
30
33
|
export type AppIconName = keyof typeof NERD_FONT_ICONS;
|
package/dist/app/icons.js
CHANGED
|
@@ -31,6 +31,9 @@ const NERD_FONT_ICONS = {
|
|
|
31
31
|
thinkingExpanded: "\u{f0335}",
|
|
32
32
|
stopCircle: "\u{f0665}",
|
|
33
33
|
timerSand: "\u{f051f}",
|
|
34
|
+
toolBodyEnd: "└",
|
|
35
|
+
toolBodyGutter: "│",
|
|
36
|
+
toolPreviewTruncated: "⊞",
|
|
34
37
|
down: "\u{f0045}",
|
|
35
38
|
};
|
|
36
39
|
const FALLBACK_ICONS = {
|
|
@@ -58,6 +61,9 @@ const FALLBACK_ICONS = {
|
|
|
58
61
|
thinkingExpanded: ">",
|
|
59
62
|
stopCircle: "■",
|
|
60
63
|
timerSand: "⏳",
|
|
64
|
+
toolBodyEnd: "`",
|
|
65
|
+
toolBodyGutter: "|",
|
|
66
|
+
toolPreviewTruncated: "+",
|
|
61
67
|
down: "v",
|
|
62
68
|
};
|
|
63
69
|
export const APP_ICON_THEMES = {
|
|
@@ -42,7 +42,7 @@ export function renderConversationToolEntry(entry, width, options) {
|
|
|
42
42
|
collapsedBody: display.collapsedBody,
|
|
43
43
|
expandedText: display.expandedText,
|
|
44
44
|
syntaxHighlight: display.syntaxHighlight,
|
|
45
|
-
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools) });
|
|
45
|
+
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools), showGutter: true });
|
|
46
46
|
return attachImageClickTargets(lines, entry.id, entry.images, { foreground: options.colors.info, underline: true });
|
|
47
47
|
}
|
|
48
48
|
export function renderThinkingEntry(entry, width, options) {
|
|
@@ -63,7 +63,7 @@ export function renderThinkingEntry(entry, width, options) {
|
|
|
63
63
|
expandedText: compactExpandedText || "(empty)",
|
|
64
64
|
bodyWrap: "word",
|
|
65
65
|
syntaxHighlight: compactExpandedText ? markdownSyntaxHighlightsForText(compactExpandedText) : undefined,
|
|
66
|
-
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools && !forceExpanded), backgroundOverride: options.colors.thinkingMessageBackground, skipHeaderBackground: true });
|
|
66
|
+
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools && !forceExpanded), backgroundOverride: options.colors.thinkingMessageBackground, skipHeaderBackground: true, showGutter: false });
|
|
67
67
|
}
|
|
68
68
|
function trimTrailingBlankLines(text) {
|
|
69
69
|
return text.replace(/(?:\r?\n[ \t]*)+$/u, "");
|
|
@@ -88,7 +88,7 @@ function renderTodoToolEntry(entry, width, options) {
|
|
|
88
88
|
output: body,
|
|
89
89
|
collapsedBody: body,
|
|
90
90
|
expandedText: body,
|
|
91
|
-
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools) });
|
|
91
|
+
}, rule, width, options.colors, { superCompact: Boolean(options.superCompactTools), showGutter: true });
|
|
92
92
|
}
|
|
93
93
|
function todoDetailsText(details) {
|
|
94
94
|
const lines = [];
|
|
@@ -25,5 +25,6 @@ export type ToolBlockRenderOptions = {
|
|
|
25
25
|
superCompact?: boolean;
|
|
26
26
|
backgroundOverride?: string;
|
|
27
27
|
skipHeaderBackground?: boolean;
|
|
28
|
+
showGutter?: boolean;
|
|
28
29
|
};
|
|
29
30
|
export declare function renderToolBlock(entry: ToolBlockEntry, rule: ResolvedToolRule, width: number, colors: Theme["colors"], options?: ToolBlockRenderOptions): RenderedLine[];
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { resolveColor } from "../../config.js";
|
|
2
2
|
import { expandTabs, sliceByDisplayWidth, stringDisplayWidth, wrapDisplayLineByWords } from "../../terminal-width.js";
|
|
3
3
|
import { alertIconPrefixLength, hasToolLspDiagnosticsAfterMutation, lspDiagnosticSeverityForLine, sanitizeText, toolStatusIcon, toolStatusIconColor, wrapLine } from "./render-text.js";
|
|
4
|
-
|
|
4
|
+
import { APP_ICONS } from "../icons.js";
|
|
5
|
+
const TOOL_BODY_PREFIX = " ";
|
|
6
|
+
function truncatedPreviewMarker() {
|
|
7
|
+
return `${APP_ICONS.toolPreviewTruncated} `;
|
|
8
|
+
}
|
|
9
|
+
function toolBodyGutterPrefix() {
|
|
10
|
+
return `${APP_ICONS.toolBodyGutter} `;
|
|
11
|
+
}
|
|
12
|
+
function toolBodyEndPrefix() {
|
|
13
|
+
return `${APP_ICONS.toolBodyEnd} `;
|
|
14
|
+
}
|
|
5
15
|
export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
6
16
|
if (rule.hidden)
|
|
7
17
|
return [];
|
|
@@ -19,6 +29,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
19
29
|
const headerArgsWidth = width - stringDisplayWidth(headerPrefix) - 1;
|
|
20
30
|
const clippedHeaderArgs = headerArgsWidth > 0 ? sliceByDisplayWidth(headerArgs, headerArgsWidth) : "";
|
|
21
31
|
const target = { kind: "tool", id: entry.id };
|
|
32
|
+
const showGutter = options.showGutter ?? true;
|
|
22
33
|
const header = clippedHeaderArgs ? `${headerPrefix} ${clippedHeaderArgs}` : headerPrefix;
|
|
23
34
|
const headerArgsStart = clippedHeaderArgs ? headerPrefix.length + 1 : header.length;
|
|
24
35
|
const headerLine = {
|
|
@@ -34,7 +45,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
34
45
|
};
|
|
35
46
|
const headerLines = [headerLine];
|
|
36
47
|
if (expanded) {
|
|
37
|
-
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi));
|
|
48
|
+
headerLines.push(...renderToolBodyLines(entry.expandedText, width, target, toolOutputColor, entry.bodyStyle, colors, entry.syntaxHighlight, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi, showGutter));
|
|
38
49
|
if (options.skipHeaderBackground && headerLines.length > 1) {
|
|
39
50
|
applyBackground(headerLines.slice(1));
|
|
40
51
|
}
|
|
@@ -49,7 +60,7 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
49
60
|
if (!body || rule.previewLines === 0)
|
|
50
61
|
return headerLines;
|
|
51
62
|
if (!options.superCompact) {
|
|
52
|
-
headerLines.push(...renderCollapsedPreviewLines(entry, body, rule, width, target, toolOutputColor, colors, hasLspDiagnostics));
|
|
63
|
+
headerLines.push(...renderCollapsedPreviewLines(entry, body, rule, width, target, toolOutputColor, colors, hasLspDiagnostics, showGutter));
|
|
53
64
|
applyBackground(headerLines);
|
|
54
65
|
return headerLines;
|
|
55
66
|
}
|
|
@@ -60,13 +71,14 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
60
71
|
const availablePreviewWidth = width - stringDisplayWidth(header) - stringDisplayWidth(separator);
|
|
61
72
|
if (availablePreviewWidth <= 0)
|
|
62
73
|
return headerLines;
|
|
63
|
-
const
|
|
74
|
+
const markerPrefix = truncatedPreviewMarker();
|
|
75
|
+
const previewText = preview.overflow ? `${markerPrefix}${preview.text}` : preview.text;
|
|
64
76
|
const clippedPreview = sliceByDisplayWidth(previewText, availablePreviewWidth);
|
|
65
77
|
if (!clippedPreview)
|
|
66
78
|
return headerLines;
|
|
67
79
|
headerLine.text = `${header}${separator}${clippedPreview}`;
|
|
68
80
|
const previewStart = header.length + separator.length;
|
|
69
|
-
const previewTextStart = previewStart + (preview.overflow ?
|
|
81
|
+
const previewTextStart = previewStart + (preview.overflow ? markerPrefix.length : 0);
|
|
70
82
|
headerLine.segments = [
|
|
71
83
|
...(headerLine.segments ?? []),
|
|
72
84
|
...(preview.overflow ? [{ start: previewStart, end: previewStart + 1, foreground: colors.statusDotBase }] : []),
|
|
@@ -74,8 +86,8 @@ export function renderToolBlock(entry, rule, width, colors, options = {}) {
|
|
|
74
86
|
];
|
|
75
87
|
return headerLines;
|
|
76
88
|
}
|
|
77
|
-
function renderCollapsedPreviewLines(entry, body, rule, width, target, color, colors, hasLspDiagnostics) {
|
|
78
|
-
const allLines = renderToolBodyLines(body, width, target, color, entry.bodyStyle, colors, undefined, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi);
|
|
89
|
+
function renderCollapsedPreviewLines(entry, body, rule, width, target, color, colors, hasLspDiagnostics, showGutter) {
|
|
90
|
+
const allLines = renderToolBodyLines(body, width, target, color, entry.bodyStyle, colors, undefined, entry.bodyWrap, hasLspDiagnostics, entry.bodyLineStyles, entry.preserveAnsi, showGutter);
|
|
79
91
|
if (rule.previewLines >= allLines.length)
|
|
80
92
|
return allLines;
|
|
81
93
|
const previewLines = rule.direction === "tail" ? allLines.slice(-rule.previewLines) : allLines.slice(0, rule.previewLines);
|
|
@@ -92,20 +104,33 @@ function markTruncatedPreviewLine(lines, direction, markerColor) {
|
|
|
92
104
|
if (lines.length === 0)
|
|
93
105
|
return lines;
|
|
94
106
|
const markerIndex = direction === "tail" ? 0 : lines.length - 1;
|
|
107
|
+
const gutterPrefix = toolBodyGutterPrefix();
|
|
108
|
+
const markerPrefix = truncatedPreviewMarker();
|
|
95
109
|
return lines.map((line, index) => {
|
|
96
110
|
if (index !== markerIndex)
|
|
97
111
|
return line;
|
|
112
|
+
const text = line.text.startsWith(gutterPrefix)
|
|
113
|
+
? `${markerPrefix}${line.text.slice(gutterPrefix.length)}`
|
|
114
|
+
: line.text.startsWith(TOOL_BODY_PREFIX)
|
|
115
|
+
? `${markerPrefix}${line.text.slice(TOOL_BODY_PREFIX.length)}`
|
|
116
|
+
: `${markerPrefix}${line.text}`;
|
|
117
|
+
const existingSegments = (line.segments ?? []).filter((segment) => !(segment.start === 0 && segment.end === 1 && segment.foreground === markerColor));
|
|
98
118
|
return {
|
|
99
119
|
...line,
|
|
100
|
-
text
|
|
101
|
-
segments: [{ start: 0, end: 1, foreground: markerColor }, ...
|
|
120
|
+
text,
|
|
121
|
+
segments: [{ start: 0, end: 1, foreground: markerColor }, ...existingSegments],
|
|
102
122
|
};
|
|
103
123
|
});
|
|
104
124
|
}
|
|
105
|
-
function renderToolBodyLines(text, width, target, color, style, colors, syntaxHighlight, bodyWrap = "char", hasLspDiagnostics = false, bodyLineStyles, preserveAnsi = false) {
|
|
106
|
-
const
|
|
125
|
+
function renderToolBodyLines(text, width, target, color, style, colors, syntaxHighlight, bodyWrap = "char", hasLspDiagnostics = false, bodyLineStyles, preserveAnsi = false, showGutter = false) {
|
|
126
|
+
const displayPrefix = showGutter ? toolBodyGutterPrefix() : TOOL_BODY_PREFIX;
|
|
127
|
+
const prefixWidth = stringDisplayWidth(displayPrefix);
|
|
128
|
+
const bodyWidth = Math.max(1, width - prefixWidth);
|
|
107
129
|
const lines = [];
|
|
108
130
|
const wrapBodyLine = bodyWrap === "word" ? wrapDisplayLineByWords : wrapLine;
|
|
131
|
+
const gutterSegment = showGutter
|
|
132
|
+
? { start: 0, end: 1, foreground: colors.statusDotBase }
|
|
133
|
+
: undefined;
|
|
109
134
|
for (const [rawLineIndex, rawLine] of sanitizeToolBodyText(text, preserveAnsi).split("\n").entries()) {
|
|
110
135
|
const ansiLine = preserveAnsi ? ansiStyledLine(rawLine) : undefined;
|
|
111
136
|
const displayLine = ansiLine?.text ?? rawLine;
|
|
@@ -118,37 +143,67 @@ function renderToolBodyLines(text, width, target, color, style, colors, syntaxHi
|
|
|
118
143
|
: wrapBodyLine(displayLine, bodyWidth).map((wrapped) => ({ text: wrapped, segments: [] }));
|
|
119
144
|
for (const [wrapIndex, wrapped] of wrappedLines.entries()) {
|
|
120
145
|
const line = {
|
|
121
|
-
text:
|
|
122
|
-
copyText:
|
|
146
|
+
text: `${displayPrefix}${wrapped.text}`,
|
|
147
|
+
copyText: `${TOOL_BODY_PREFIX}${wrapped.text}`,
|
|
123
148
|
...(wrapIndex < wrappedLines.length - 1 ? { continuesOnNextLine: true } : {}),
|
|
124
149
|
target,
|
|
125
150
|
colorOverride: color,
|
|
126
151
|
};
|
|
127
152
|
if (diffStyle) {
|
|
128
|
-
const segment = { start:
|
|
153
|
+
const segment = { start: displayPrefix.length, end: line.text.length, foreground: diffStyle.foreground };
|
|
129
154
|
if (diffStyle.bold != null)
|
|
130
155
|
segment.bold = diffStyle.bold;
|
|
131
|
-
line.segments = [segment];
|
|
156
|
+
line.segments = gutterSegment ? [gutterSegment, segment] : [segment];
|
|
132
157
|
}
|
|
133
158
|
else if (lspDiagnosticStyle?.kind === "alert" && wrapIndex === 0) {
|
|
134
|
-
line.segments = [
|
|
159
|
+
line.segments = [
|
|
160
|
+
...(gutterSegment ? [gutterSegment] : []),
|
|
161
|
+
{ start: displayPrefix.length, end: displayPrefix.length + lspDiagnosticStyle.length, foreground: colors.warning, bold: true },
|
|
162
|
+
];
|
|
135
163
|
}
|
|
136
164
|
else if (lspDiagnosticStyle?.kind === "severity") {
|
|
137
|
-
line.segments = [
|
|
165
|
+
line.segments = [
|
|
166
|
+
...(gutterSegment ? [gutterSegment] : []),
|
|
167
|
+
{ start: displayPrefix.length, end: line.text.length, foreground: lspDiagnosticStyle.foreground },
|
|
168
|
+
];
|
|
138
169
|
}
|
|
139
|
-
else if (bodyLineStyle && line.text.length >
|
|
140
|
-
line.segments = [
|
|
170
|
+
else if (bodyLineStyle && line.text.length > displayPrefix.length) {
|
|
171
|
+
line.segments = [
|
|
172
|
+
...(gutterSegment ? [gutterSegment] : []),
|
|
173
|
+
{ start: displayPrefix.length, end: line.text.length, ...bodyLineStyle },
|
|
174
|
+
];
|
|
141
175
|
}
|
|
142
176
|
else if (lineSyntaxHighlight) {
|
|
143
177
|
const rawStart = wrapIndex === 0 ? lineSyntaxHighlight.startColumn ?? 0 : 0;
|
|
144
|
-
line.syntaxHighlight = { language: lineSyntaxHighlight.language, start: Math.min(line.text.length,
|
|
178
|
+
line.syntaxHighlight = { language: lineSyntaxHighlight.language, start: Math.min(line.text.length, displayPrefix.length + rawStart) };
|
|
179
|
+
if (gutterSegment)
|
|
180
|
+
line.segments = [gutterSegment];
|
|
145
181
|
}
|
|
146
182
|
else if (wrapped.segments.length > 0) {
|
|
147
|
-
line.segments =
|
|
183
|
+
line.segments = [
|
|
184
|
+
...(gutterSegment ? [gutterSegment] : []),
|
|
185
|
+
...wrapped.segments.map((segment) => ({
|
|
186
|
+
...segment,
|
|
187
|
+
start: segment.start + displayPrefix.length,
|
|
188
|
+
end: segment.end + displayPrefix.length,
|
|
189
|
+
})),
|
|
190
|
+
];
|
|
191
|
+
}
|
|
192
|
+
else if (gutterSegment) {
|
|
193
|
+
line.segments = [gutterSegment];
|
|
148
194
|
}
|
|
149
195
|
lines.push(line);
|
|
150
196
|
}
|
|
151
197
|
}
|
|
198
|
+
if (showGutter && lines.length > 0) {
|
|
199
|
+
const lastLine = lines.at(-1);
|
|
200
|
+
if (!lastLine)
|
|
201
|
+
return lines;
|
|
202
|
+
const gutterPrefix = toolBodyGutterPrefix();
|
|
203
|
+
if (lastLine.text.startsWith(gutterPrefix)) {
|
|
204
|
+
lastLine.text = `${toolBodyEndPrefix()}${lastLine.text.slice(gutterPrefix.length)}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
152
207
|
return lines;
|
|
153
208
|
}
|
|
154
209
|
const ANSI_STANDARD_COLORS = ["#000000", "#cd3131", "#0dbc79", "#e5e510", "#2472c8", "#bc3fbc", "#11a8cd", "#e5e5e5"];
|
|
@@ -124,6 +124,7 @@ export declare class AppMouseController {
|
|
|
124
124
|
private renderedConversationFrame;
|
|
125
125
|
constructor(host: AppMouseControllerHost, popupMenus: AppPopupMenuController, popupActions: AppPopupActionController, scrollController: AppScrollController, commandController: AppCommandController);
|
|
126
126
|
handleMouse(event: MouseEvent): void;
|
|
127
|
+
private toolTargetContainsEvent;
|
|
127
128
|
activeClickFlash(): ClickFlash | undefined;
|
|
128
129
|
consumeClickFlashDirty(): boolean;
|
|
129
130
|
private withClickFlash;
|
|
@@ -133,6 +134,7 @@ export declare class AppMouseController {
|
|
|
133
134
|
private clickFlashForEvent;
|
|
134
135
|
private clickFlashForRegion;
|
|
135
136
|
private clickFlashRegionForEvent;
|
|
137
|
+
private toolClickFlashRegionForEvent;
|
|
136
138
|
private normalizedClickFlashRegion;
|
|
137
139
|
private inputClickFlashRegionForEvent;
|
|
138
140
|
private imageTargetAt;
|
|
@@ -7,6 +7,7 @@ import { sliceByDisplayColumns, stringDisplayWidth } from "../../terminal-width.
|
|
|
7
7
|
import { formatDcpStatsToast } from "../rendering/dcp-stats.js";
|
|
8
8
|
import { detectFileLinks } from "./file-links.js";
|
|
9
9
|
import { openFileLink as openDetectedFileLink } from "./file-link-opener.js";
|
|
10
|
+
import { APP_ICONS } from "../icons.js";
|
|
10
11
|
const CLICK_FLASH_MS = 100;
|
|
11
12
|
const LOST_MOUSE_RELEASE_SETTLE_MS = 180;
|
|
12
13
|
export class AppMouseController {
|
|
@@ -151,6 +152,8 @@ export class AppMouseController {
|
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
154
|
if (target?.kind === "tool") {
|
|
155
|
+
if (!this.toolTargetContainsEvent(event))
|
|
156
|
+
return;
|
|
154
157
|
const entry = this.host.findEntry(target.id);
|
|
155
158
|
if (entry?.kind === "tool" || entry?.kind === "thinking" || entry?.kind === "shell") {
|
|
156
159
|
entry.expanded = !entry.expanded;
|
|
@@ -159,6 +162,16 @@ export class AppMouseController {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
}
|
|
165
|
+
toolTargetContainsEvent(event) {
|
|
166
|
+
const text = this.renderedRowTexts.get(event.y) ?? "";
|
|
167
|
+
const gutter = toolGutterGlyphForLine(text);
|
|
168
|
+
if (gutter) {
|
|
169
|
+
const gutterWidth = Math.max(1, stringDisplayWidth(gutter));
|
|
170
|
+
return event.x >= 1 && event.x < 1 + gutterWidth;
|
|
171
|
+
}
|
|
172
|
+
const bounds = nonBlankLineBounds(text, event.x);
|
|
173
|
+
return event.x >= bounds.startColumn && event.x < bounds.endColumn;
|
|
174
|
+
}
|
|
162
175
|
activeClickFlash() {
|
|
163
176
|
return this.clickFlash;
|
|
164
177
|
}
|
|
@@ -230,7 +243,10 @@ export class AppMouseController {
|
|
|
230
243
|
const statusTarget = this.statusTargetAt(event);
|
|
231
244
|
if (statusTarget)
|
|
232
245
|
return statusTarget;
|
|
233
|
-
const
|
|
246
|
+
const target = this.renderedTargets.get(event.y);
|
|
247
|
+
if (target?.kind === "tool")
|
|
248
|
+
return this.toolClickFlashRegionForEvent(event);
|
|
249
|
+
const toastTarget = target;
|
|
234
250
|
if (toastTarget?.kind === "toast" && toastTargetContainsEvent(toastTarget, event)) {
|
|
235
251
|
return {
|
|
236
252
|
y: event.y,
|
|
@@ -244,6 +260,19 @@ export class AppMouseController {
|
|
|
244
260
|
}
|
|
245
261
|
return undefined;
|
|
246
262
|
}
|
|
263
|
+
toolClickFlashRegionForEvent(event) {
|
|
264
|
+
const text = this.renderedRowTexts.get(event.y) ?? "";
|
|
265
|
+
const gutter = toolGutterGlyphForLine(text);
|
|
266
|
+
if (gutter) {
|
|
267
|
+
const gutterWidth = Math.max(1, stringDisplayWidth(gutter));
|
|
268
|
+
const region = { y: event.y, startColumn: 1, endColumn: 1 + gutterWidth };
|
|
269
|
+
return event.x >= region.startColumn && event.x < region.endColumn ? region : undefined;
|
|
270
|
+
}
|
|
271
|
+
const bounds = nonBlankLineBounds(text, event.x);
|
|
272
|
+
return event.x >= bounds.startColumn && event.x < bounds.endColumn
|
|
273
|
+
? { y: event.y, startColumn: bounds.startColumn, endColumn: bounds.endColumn }
|
|
274
|
+
: undefined;
|
|
275
|
+
}
|
|
247
276
|
normalizedClickFlashRegion(region) {
|
|
248
277
|
const columns = Math.max(1, this.host.terminalColumns());
|
|
249
278
|
const y = Math.max(1, region.y);
|
|
@@ -995,6 +1024,13 @@ function nonBlankLineBounds(text, fallbackColumn) {
|
|
|
995
1024
|
? { startColumn: fallbackColumn, endColumn: fallbackColumn + 1 }
|
|
996
1025
|
: { startColumn, endColumn };
|
|
997
1026
|
}
|
|
1027
|
+
function toolGutterGlyphForLine(text) {
|
|
1028
|
+
for (const glyph of [APP_ICONS.toolBodyGutter, APP_ICONS.toolBodyEnd, APP_ICONS.toolPreviewTruncated]) {
|
|
1029
|
+
if (text.startsWith(`${glyph} `))
|
|
1030
|
+
return glyph;
|
|
1031
|
+
}
|
|
1032
|
+
return undefined;
|
|
1033
|
+
}
|
|
998
1034
|
function displayCellAtColumn(text, column) {
|
|
999
1035
|
if (column < 1)
|
|
1000
1036
|
return " ";
|