@duckmind/dm-darwin-x64 0.35.4 → 0.35.5
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/dm +0 -0
- package/extensions/.dm-extensions.json +1 -27
- package/package.json +1 -1
- package/extensions/dm-alps/LICENSE +0 -21
- package/extensions/dm-alps/README.md +0 -22
- package/extensions/dm-alps/index.ts +0 -172
- package/extensions/dm-alps/package.json +0 -49
- package/extensions/dm-alps/src/commands.ts +0 -208
- package/extensions/dm-alps/src/features/animations/debug.ts +0 -160
- package/extensions/dm-alps/src/features/animations/index.ts +0 -33
- package/extensions/dm-alps/src/features/animations/patch.ts +0 -112
- package/extensions/dm-alps/src/features/animations/preview.ts +0 -117
- package/extensions/dm-alps/src/features/animations/registry.ts +0 -593
- package/extensions/dm-alps/src/features/animations/runtime.ts +0 -574
- package/extensions/dm-alps/src/features/animations/settings.ts +0 -69
- package/extensions/dm-alps/src/features/bottom-input/cluster.ts +0 -2
- package/extensions/dm-alps/src/features/bottom-input/compositor.ts +0 -2
- package/extensions/dm-alps/src/features/bottom-input/editor.ts +0 -148
- package/extensions/dm-alps/src/features/bottom-input/frame.ts +0 -224
- package/extensions/dm-alps/src/features/bottom-input/icons.ts +0 -36
- package/extensions/dm-alps/src/features/bottom-input/index.ts +0 -8
- package/extensions/dm-alps/src/features/bottom-input/runtime.ts +0 -1197
- package/extensions/dm-alps/src/features/bottom-input/shortcuts.ts +0 -286
- package/extensions/dm-alps/src/features/bottom-input/status.ts +0 -663
- package/extensions/dm-alps/src/features/bottom-status/index.ts +0 -2
- package/extensions/dm-alps/src/features/chrome-frame/chrome.ts +0 -222
- package/extensions/dm-alps/src/features/chrome-frame/debug.ts +0 -212
- package/extensions/dm-alps/src/features/chrome-frame/image.ts +0 -11
- package/extensions/dm-alps/src/features/chrome-frame/index.ts +0 -4
- package/extensions/dm-alps/src/features/chrome-frame/osc.ts +0 -111
- package/extensions/dm-alps/src/features/chrome-frame/patch.ts +0 -769
- package/extensions/dm-alps/src/features/chrome-frame/preview.ts +0 -67
- package/extensions/dm-alps/src/features/chrome-frame/styles.ts +0 -105
- package/extensions/dm-alps/src/features/fixed-bottom-editor/cluster.ts +0 -161
- package/extensions/dm-alps/src/features/fixed-bottom-editor/compositor.ts +0 -1149
- package/extensions/dm-alps/src/features/fixed-bottom-editor/index.ts +0 -3
- package/extensions/dm-alps/src/features/fixed-bottom-editor/runtime.ts +0 -3
- package/extensions/dm-alps/src/settings-store.ts +0 -194
- package/extensions/dm-alps/src/settings-ui.ts +0 -653
- package/extensions/dm-alps/src/settings.ts +0 -102
- package/extensions/dm-alps/src/terminal-sanitizer.ts +0 -91
- package/extensions/dm-alps/themes/LICENSE.synthwave-84 +0 -21
- package/extensions/dm-alps/themes/alps.json +0 -93
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import * as PiAgent from "@mariozechner/pi-coding-agent";
|
|
3
|
-
import { Editor, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
4
|
-
import type { ThemeLike } from "../chrome-frame/styles.ts";
|
|
5
|
-
import { MIN_FRAME_WIDTH, renderBeautifiedEditorFrame } from "./frame.ts";
|
|
6
|
-
import type { BottomInputFrameStatus } from "./status.ts";
|
|
7
|
-
|
|
8
|
-
export type BottomInputEditorState = {
|
|
9
|
-
beautifiedInputEnabled: boolean;
|
|
10
|
-
getTheme(): ThemeLike;
|
|
11
|
-
getFrameStatus(width: number): BottomInputFrameStatus;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type BottomInputEditorOptions = {
|
|
15
|
-
CustomEditor?: new (tui: any, theme: any, keybindings: any, options?: any) => any;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type SplitEditorRenderResult = {
|
|
19
|
-
editorLines: string[];
|
|
20
|
-
popupLines: string[];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export function createBottomInputEditor(tui: any, theme: any, keybindings: any, state: BottomInputEditorState, options: BottomInputEditorOptions = {}): any {
|
|
24
|
-
const BaseEditor = options.CustomEditor ?? (PiAgent as { CustomEditor?: new (tui: any, theme: any, keybindings: any, options?: any) => any }).CustomEditor;
|
|
25
|
-
const editorTheme = theme ?? createFallbackEditorTheme();
|
|
26
|
-
if (typeof BaseEditor === "function") {
|
|
27
|
-
class AlpsBeautifiedEditor extends BaseEditor {
|
|
28
|
-
private readonly stateRef: BottomInputEditorState;
|
|
29
|
-
|
|
30
|
-
constructor() {
|
|
31
|
-
super(tui, editorTheme, keybindings, { paddingX: 0 });
|
|
32
|
-
this.stateRef = state;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
render(width: number): string[] {
|
|
36
|
-
if (!this.stateRef.beautifiedInputEnabled || width < MIN_FRAME_WIDTH) return super.render(width);
|
|
37
|
-
const innerWidth = Math.max(1, Math.floor(width) - 4);
|
|
38
|
-
const base = super.render(innerWidth);
|
|
39
|
-
return renderBottomInputEditorLines({ lines: base, width, theme: this.stateRef.getTheme(), state: this.stateRef });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return new AlpsBeautifiedEditor();
|
|
43
|
-
}
|
|
44
|
-
return new FallbackBeautifiedInputEditor(tui, editorTheme, state);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function isNativeEditorRule(line: string): boolean {
|
|
48
|
-
const plain = stripAnsi(line).trim();
|
|
49
|
-
return plain.includes("─") && [...plain].every((char) => "─↑↓ 0123456789more".includes(char));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function splitNativeEditorRender(lines: readonly string[]): SplitEditorRenderResult {
|
|
53
|
-
if (lines.length === 0) return { editorLines: [], popupLines: [] };
|
|
54
|
-
const boxed = splitNativeEditorBoxRender(lines);
|
|
55
|
-
if (boxed) return boxed;
|
|
56
|
-
const withoutTop = isNativeEditorRule(lines[0] ?? "") ? lines.slice(1) : [...lines];
|
|
57
|
-
const bottomRuleIndex = withoutTop.findIndex(isNativeEditorRule);
|
|
58
|
-
if (bottomRuleIndex === -1) {
|
|
59
|
-
return { editorLines: [...withoutTop], popupLines: [] };
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
editorLines: withoutTop.slice(0, bottomRuleIndex),
|
|
63
|
-
popupLines: withoutTop.slice(bottomRuleIndex + 1),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function splitNativeEditorBoxRender(lines: readonly string[]): SplitEditorRenderResult | null {
|
|
68
|
-
if (!isNativeEditorBoxTop(lines[0] ?? "")) return null;
|
|
69
|
-
const bottomIndex = lines.findIndex((line, index) => index > 0 && isNativeEditorBoxBottom(line));
|
|
70
|
-
if (bottomIndex === -1) return null;
|
|
71
|
-
return {
|
|
72
|
-
editorLines: lines.slice(1, bottomIndex).map(stripNativeEditorBoxContentLine),
|
|
73
|
-
popupLines: lines.slice(bottomIndex + 1),
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function isNativeEditorBoxTop(line: string): boolean {
|
|
78
|
-
const plain = stripAnsi(line).trim();
|
|
79
|
-
return plain.startsWith("╭") && plain.endsWith("╮") && plain.includes("─");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function isNativeEditorBoxBottom(line: string): boolean {
|
|
83
|
-
const plain = stripAnsi(line).trim();
|
|
84
|
-
return plain.startsWith("╰") && plain.endsWith("╯") && plain.includes("─");
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function isNativeEditorBoxContent(line: string): boolean {
|
|
88
|
-
const plain = stripAnsi(line);
|
|
89
|
-
return plain.startsWith("│") && plain.endsWith("│");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function stripNativeEditorBoxContentLine(line: string): string {
|
|
93
|
-
if (!isNativeEditorBoxContent(line)) return line;
|
|
94
|
-
return line
|
|
95
|
-
.replace(/^(?:\x1b\[[0-?]*[ -/]*[@-~])*│(?:\x1b\[[0-?]*[ -/]*[@-~])*/, "")
|
|
96
|
-
.replace(/(?:\x1b\[[0-?]*[ -/]*[@-~])*│(?:\x1b\[[0-?]*[ -/]*[@-~])*$/, "");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function renderBottomInputEditorLines(input: { lines: readonly string[]; width: number; theme: ThemeLike; state: BottomInputEditorState }): string[] {
|
|
100
|
-
const width = Number.isFinite(input.width) ? Math.max(0, Math.floor(input.width)) : 0;
|
|
101
|
-
if (!input.state.beautifiedInputEnabled || width < MIN_FRAME_WIDTH) return [...input.lines];
|
|
102
|
-
const { editorLines, popupLines } = splitNativeEditorRender(input.lines);
|
|
103
|
-
return [
|
|
104
|
-
...renderBeautifiedEditorFrame({
|
|
105
|
-
editorLines,
|
|
106
|
-
width,
|
|
107
|
-
theme: input.theme,
|
|
108
|
-
status: input.state.getFrameStatus(width),
|
|
109
|
-
}),
|
|
110
|
-
...fitPopupLines(popupLines, width),
|
|
111
|
-
];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
class FallbackBeautifiedInputEditor extends Editor {
|
|
115
|
-
private readonly stateRef: BottomInputEditorState;
|
|
116
|
-
|
|
117
|
-
constructor(tui: any, editorTheme: ThemeLike, stateRef: BottomInputEditorState) {
|
|
118
|
-
super(tui, editorTheme as any, { paddingX: 0 });
|
|
119
|
-
this.stateRef = stateRef;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
render(width: number): string[] {
|
|
123
|
-
if (!this.stateRef.beautifiedInputEnabled || width < MIN_FRAME_WIDTH) return super.render(width);
|
|
124
|
-
const innerWidth = Math.max(1, Math.floor(width) - 4);
|
|
125
|
-
const base = super.render(innerWidth);
|
|
126
|
-
return renderBottomInputEditorLines({ lines: base, width, theme: this.stateRef.getTheme(), state: this.stateRef });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function fitPopupLines(lines: readonly string[], width: number): string[] {
|
|
131
|
-
return lines.map((line) => {
|
|
132
|
-
const clipped = truncateToWidth(line, Math.max(1, width), "", true);
|
|
133
|
-
const padding = " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
134
|
-
return clipped + padding;
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function createFallbackEditorTheme(): ThemeLike {
|
|
139
|
-
return {
|
|
140
|
-
fg: (_token: string, text: string) => text,
|
|
141
|
-
bg: (_token: string, text: string) => text,
|
|
142
|
-
bold: (text: string) => text,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function stripAnsi(input: string): string {
|
|
147
|
-
return input.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g, "");
|
|
148
|
-
}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
3
|
-
import { sanitizeTerminalText } from "../../terminal-sanitizer.ts";
|
|
4
|
-
import type { ThemeLike } from "../chrome-frame/styles.ts";
|
|
5
|
-
import { FIXED_EDITOR_CURSOR_MARKER } from "../fixed-bottom-editor/cluster.ts";
|
|
6
|
-
import type { BottomInputFrameStatus } from "./status.ts";
|
|
7
|
-
|
|
8
|
-
export type BeautifiedEditorFrameInput = {
|
|
9
|
-
editorLines: readonly string[];
|
|
10
|
-
width: number;
|
|
11
|
-
theme: ThemeLike;
|
|
12
|
-
status: BottomInputFrameStatus;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const MIN_FRAME_WIDTH = 8;
|
|
16
|
-
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
17
|
-
|
|
18
|
-
export function renderBeautifiedEditorFrame(input: BeautifiedEditorFrameInput): string[] {
|
|
19
|
-
const width = Number.isFinite(input.width) ? Math.max(0, Math.floor(input.width)) : 0;
|
|
20
|
-
if (width < MIN_FRAME_WIDTH) return [...input.editorLines];
|
|
21
|
-
const editorLines = input.editorLines.length > 0 ? [...input.editorLines] : [""];
|
|
22
|
-
return [
|
|
23
|
-
buildTopBorder(width, input.theme, input.status),
|
|
24
|
-
...editorLines.map((line) => renderContentLine(line, width, input.theme)),
|
|
25
|
-
buildBottomBorder(width, input.theme, input.status.elapsed),
|
|
26
|
-
];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function buildTopBorder(width: number, theme: ThemeLike, status: BottomInputFrameStatus): string {
|
|
30
|
-
const leftLabel = joinStyledSegments([status.model, status.thinking], safeFg(theme, "borderMuted", " · "));
|
|
31
|
-
const rightLabel = status.context ?? "";
|
|
32
|
-
return buildBorderLine({
|
|
33
|
-
width,
|
|
34
|
-
theme,
|
|
35
|
-
leftCorner: "╭",
|
|
36
|
-
rightCorner: "╮",
|
|
37
|
-
leftLabel,
|
|
38
|
-
rightLabel,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildBottomBorder(width: number, theme: ThemeLike, elapsed: string | null): string {
|
|
43
|
-
return buildBorderLine({
|
|
44
|
-
width,
|
|
45
|
-
theme,
|
|
46
|
-
leftCorner: "╰",
|
|
47
|
-
rightCorner: "╯",
|
|
48
|
-
rightLabel: elapsed?.trim() || "",
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function buildBorderLine(input: { width: number; theme: ThemeLike; leftCorner: string; rightCorner: string; leftLabel?: string; rightLabel?: string }): string {
|
|
53
|
-
const { width, theme, leftCorner, rightCorner } = input;
|
|
54
|
-
const safeWidth = Math.max(2, width);
|
|
55
|
-
const innerBudget = Math.max(0, safeWidth - 2);
|
|
56
|
-
let leftLabel = fitStatusLabel(input.leftLabel ?? "", Math.max(0, Math.floor(innerBudget * 0.45) - 2));
|
|
57
|
-
let rightLabel = fitStatusLabel(input.rightLabel ?? "", Math.max(0, innerBudget - visibleWidth(labelPart(leftLabel)) - 2));
|
|
58
|
-
|
|
59
|
-
for (let guard = 0; guard < 6 && visibleWidth(labelPart(leftLabel)) + visibleWidth(labelPart(rightLabel)) > innerBudget; guard += 1) {
|
|
60
|
-
const overflow = visibleWidth(labelPart(leftLabel)) + visibleWidth(labelPart(rightLabel)) - innerBudget;
|
|
61
|
-
if (rightLabel) {
|
|
62
|
-
rightLabel = fitStatusLabel(rightLabel, Math.max(0, visibleWidth(rightLabel) - overflow));
|
|
63
|
-
} else if (leftLabel) {
|
|
64
|
-
leftLabel = fitStatusLabel(leftLabel, Math.max(0, visibleWidth(leftLabel) - overflow));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (visibleWidth(labelPart(leftLabel)) + visibleWidth(labelPart(rightLabel)) > innerBudget) rightLabel = "";
|
|
68
|
-
if (visibleWidth(labelPart(leftLabel)) + visibleWidth(labelPart(rightLabel)) > innerBudget) leftLabel = "";
|
|
69
|
-
return composeBorderLine(theme, leftCorner, rightCorner, leftLabel, rightLabel, innerBudget);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function labelPart(label: string): string {
|
|
73
|
-
return label ? ` ${label} ` : "";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function composeBorderLine(theme: ThemeLike, leftCorner: string, rightCorner: string, leftLabel: string, rightLabel: string, innerBudget: number): string {
|
|
77
|
-
const leftPart = labelPart(leftLabel);
|
|
78
|
-
const rightPart = labelPart(rightLabel);
|
|
79
|
-
const dashCount = Math.max(0, innerBudget - visibleWidth(leftPart) - visibleWidth(rightPart));
|
|
80
|
-
return styleBorder(theme, leftCorner) + leftPart + styleBorder(theme, "─".repeat(dashCount)) + rightPart + styleBorder(theme, rightCorner);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function fitStatusLabel(label: string, maxWidth: number): string {
|
|
84
|
-
if (!label || maxWidth <= 0) return "";
|
|
85
|
-
if (visibleWidth(label) <= maxWidth) return label;
|
|
86
|
-
const plain = stripAnsi(label);
|
|
87
|
-
return truncateToWidth(plain, maxWidth, "…", false);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function renderContentLine(line: string, width: number, theme: ThemeLike): string {
|
|
91
|
-
const innerWidth = Math.max(0, width - 4);
|
|
92
|
-
const safeLine = sanitizeEditorLine(line);
|
|
93
|
-
const clipped = closeOpenAnsiCodes(truncateEditorLinePreservingCursor(safeLine, innerWidth));
|
|
94
|
-
const padded = padToWidth(clipped, innerWidth);
|
|
95
|
-
return styleBorder(theme, "│") + " " + padded + " " + styleBorder(theme, "│");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function sanitizeEditorLine(line: string): string {
|
|
99
|
-
const placeholder = "\uE000ALPS_CURSOR\uE000";
|
|
100
|
-
const withPlaceholder = String(line).split(FIXED_EDITOR_CURSOR_MARKER).join(placeholder);
|
|
101
|
-
return sanitizeTerminalText(withPlaceholder, { allowNewline: false, allowTab: true, preserveSgr: true }).split(placeholder).join(FIXED_EDITOR_CURSOR_MARKER);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function truncateEditorLinePreservingCursor(line: string, width: number): string {
|
|
105
|
-
if (width <= 0) return line.includes(FIXED_EDITOR_CURSOR_MARKER) ? FIXED_EDITOR_CURSOR_MARKER : "";
|
|
106
|
-
if (!line.includes(FIXED_EDITOR_CURSOR_MARKER)) return truncateToWidth(line, width, "", false);
|
|
107
|
-
const markerIndex = line.indexOf(FIXED_EDITOR_CURSOR_MARKER);
|
|
108
|
-
const beforeCursor = line.slice(0, markerIndex);
|
|
109
|
-
const afterCursor = line.slice(markerIndex + FIXED_EDITOR_CURSOR_MARKER.length);
|
|
110
|
-
const beforeWidth = visibleWidth(beforeCursor);
|
|
111
|
-
const totalWidth = beforeWidth + visibleWidth(afterCursor);
|
|
112
|
-
if (totalWidth <= width) return line;
|
|
113
|
-
|
|
114
|
-
const startCol = Math.max(0, beforeWidth - Math.max(0, width - 1));
|
|
115
|
-
const before = sliceVisibleColumns(beforeCursor, startCol, beforeWidth - startCol);
|
|
116
|
-
const remaining = Math.max(0, width - visibleWidth(before));
|
|
117
|
-
const after = sliceVisibleColumns(afterCursor, 0, remaining);
|
|
118
|
-
return `${before}${FIXED_EDITOR_CURSOR_MARKER}${after}`;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function closeOpenAnsiCodes(line: string): string {
|
|
122
|
-
return containsSgr(line) ? `${line}\x1b[0m` : line;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function containsSgr(line: string): boolean {
|
|
126
|
-
for (let index = 0; index < line.length;) {
|
|
127
|
-
const ansi = extractAnsiSequence(line, index);
|
|
128
|
-
if (!ansi) {
|
|
129
|
-
index += 1;
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (ansi.code.startsWith("\x1b[") && ansi.code.endsWith("m")) return true;
|
|
133
|
-
index += ansi.length;
|
|
134
|
-
}
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function sliceVisibleColumns(line: string, startCol: number, length: number): string {
|
|
139
|
-
if (length <= 0) return "";
|
|
140
|
-
const endCol = startCol + length;
|
|
141
|
-
let result = "";
|
|
142
|
-
let currentCol = 0;
|
|
143
|
-
let pendingAnsi = "";
|
|
144
|
-
for (let index = 0; index < line.length;) {
|
|
145
|
-
const ansi = extractAnsiSequence(line, index);
|
|
146
|
-
if (ansi) {
|
|
147
|
-
if (currentCol >= startCol && currentCol < endCol) {
|
|
148
|
-
result += ansi.code;
|
|
149
|
-
} else if (currentCol < startCol) {
|
|
150
|
-
pendingAnsi += ansi.code;
|
|
151
|
-
}
|
|
152
|
-
index += ansi.length;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let textEnd = index;
|
|
157
|
-
while (textEnd < line.length && !extractAnsiSequence(line, textEnd)) textEnd += 1;
|
|
158
|
-
for (const { segment } of segmenter.segment(line.slice(index, textEnd))) {
|
|
159
|
-
const segmentWidth = visibleWidth(segment);
|
|
160
|
-
const inRange = currentCol >= startCol && currentCol < endCol;
|
|
161
|
-
const fits = currentCol + segmentWidth <= endCol;
|
|
162
|
-
if (inRange && fits) {
|
|
163
|
-
if (pendingAnsi) {
|
|
164
|
-
result += pendingAnsi;
|
|
165
|
-
pendingAnsi = "";
|
|
166
|
-
}
|
|
167
|
-
result += segment;
|
|
168
|
-
}
|
|
169
|
-
currentCol += segmentWidth;
|
|
170
|
-
if (currentCol >= endCol) break;
|
|
171
|
-
}
|
|
172
|
-
index = textEnd;
|
|
173
|
-
if (currentCol >= endCol) break;
|
|
174
|
-
}
|
|
175
|
-
return result;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function extractAnsiSequence(line: string, index: number): { code: string; length: number } | null {
|
|
179
|
-
if (line[index] !== "\x1b") return null;
|
|
180
|
-
const next = line[index + 1];
|
|
181
|
-
if (next === "[") {
|
|
182
|
-
for (let end = index + 2; end < line.length; end += 1) {
|
|
183
|
-
const code = line.charCodeAt(end);
|
|
184
|
-
if (code >= 0x40 && code <= 0x7e) return { code: line.slice(index, end + 1), length: end + 1 - index };
|
|
185
|
-
}
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
if (next === "]" || next === "_" || next === "P" || next === "^") {
|
|
189
|
-
for (let end = index + 2; end < line.length; end += 1) {
|
|
190
|
-
if (line[end] === "\x07") return { code: line.slice(index, end + 1), length: end + 1 - index };
|
|
191
|
-
if (line[end] === "\x1b" && line[end + 1] === "\\") return { code: line.slice(index, end + 2), length: end + 2 - index };
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function padToWidth(line: string, width: number): string {
|
|
198
|
-
const current = visibleWidth(line);
|
|
199
|
-
return current >= width ? line : line + " ".repeat(width - current);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function joinStyledSegments(segments: Array<string | null>, separator: string): string {
|
|
203
|
-
return segments.filter((segment): segment is string => Boolean(segment)).join(separator);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function styleBorder(theme: ThemeLike, text: string): string {
|
|
207
|
-
return safeFg(theme, "mdCode", text, "border");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function safeFg(theme: ThemeLike, token: string, text: string, fallback = "text"): string {
|
|
211
|
-
try {
|
|
212
|
-
return theme.fg(token, text);
|
|
213
|
-
} catch {
|
|
214
|
-
try {
|
|
215
|
-
return theme.fg(fallback, text);
|
|
216
|
-
} catch {
|
|
217
|
-
return text;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function stripAnsi(input: string): string {
|
|
223
|
-
return sanitizeTerminalText(input, { allowNewline: false, allowTab: false, preserveSgr: false });
|
|
224
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
export type BottomInputIconSet = {
|
|
3
|
-
model: string;
|
|
4
|
-
time: string;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export const NERD_BOTTOM_INPUT_ICONS: BottomInputIconSet = {
|
|
8
|
-
model: "",
|
|
9
|
-
time: "",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const ASCII_BOTTOM_INPUT_ICONS: BottomInputIconSet = {
|
|
13
|
-
model: "",
|
|
14
|
-
time: "◷",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export function hasBottomInputNerdFont(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
18
|
-
const alpsOverride = parseBooleanEnv(env.ALPS_DM_NERD_FONT);
|
|
19
|
-
if (alpsOverride !== undefined) return alpsOverride;
|
|
20
|
-
const powerlineOverride = parseBooleanEnv(env.POWERLINE_NERD_FONTS);
|
|
21
|
-
if (powerlineOverride !== undefined) return powerlineOverride;
|
|
22
|
-
if (env.GHOSTTY_RESOURCES_DIR) return true;
|
|
23
|
-
|
|
24
|
-
const terminalName = `${env.TERM_PROGRAM ?? ""} ${env.TERM ?? ""}`.toLowerCase();
|
|
25
|
-
return ["iterm", "wezterm", "kitty", "ghostty", "alacritty"].some((name) => terminalName.includes(name));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function getBottomInputIcons(env: NodeJS.ProcessEnv = process.env): BottomInputIconSet {
|
|
29
|
-
return hasBottomInputNerdFont(env) ? NERD_BOTTOM_INPUT_ICONS : ASCII_BOTTOM_INPUT_ICONS;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function parseBooleanEnv(value: string | undefined): boolean | undefined {
|
|
33
|
-
if (value === "1") return true;
|
|
34
|
-
if (value === "0") return false;
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
export { createBottomInputRuntime, registerBottomInputShortcuts } from "./runtime.ts";
|
|
3
|
-
export type { BottomInputRuntime, FixedBottomEditorStatus } from "./runtime.ts";
|
|
4
|
-
export * from "./shortcuts.ts";
|
|
5
|
-
export * from "./status.ts";
|
|
6
|
-
export * from "./frame.ts";
|
|
7
|
-
export * from "./editor.ts";
|
|
8
|
-
export * from "./icons.ts";
|