toolcraft 0.0.24 → 0.0.26
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 +11 -9
- package/dist/error-report.js +14 -11
- package/dist/redaction.d.ts +4 -0
- package/dist/redaction.js +70 -0
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +33 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/json.d.ts +2 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +36 -9
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/browser.js +1 -1
- package/node_modules/@poe-code/design-system/dist/explorer/actions.js +1 -1
- package/node_modules/@poe-code/design-system/dist/explorer/keymap.js +11 -1
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +64 -8
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +9 -11
- package/node_modules/@poe-code/design-system/dist/explorer/render/footer.js +18 -8
- package/node_modules/@poe-code/design-system/dist/explorer/render/header.js +11 -18
- package/node_modules/@poe-code/design-system/dist/explorer/render/index.js +2 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/list.js +32 -22
- package/node_modules/@poe-code/design-system/dist/explorer/render/modal.js +5 -9
- package/node_modules/@poe-code/design-system/dist/explorer/render/text.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/explorer/render/text.js +81 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +2 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.js +3 -3
- package/node_modules/@poe-code/process-runner/dist/docker/args.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/docker/args.js +11 -3
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +170 -36
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +66 -9
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.d.ts +6 -0
- package/node_modules/@poe-code/process-runner/dist/docker/env-file.js +49 -0
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +5 -4
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +5 -1
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +29 -10
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +1 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +36 -2
- package/node_modules/auth-store/dist/keychain-store.js +20 -1
- package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +6 -3
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +3 -0
- package/node_modules/tiny-mcp-client/dist/internal.js +39 -14
- package/node_modules/tiny-mcp-client/src/internal.ts +45 -17
- package/node_modules/tiny-mcp-client/src/mcp-client-sdk.test.ts +32 -0
- package/node_modules/tiny-mcp-client/src/transports.test.ts +68 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getExplorerStyles } from "../theme.js";
|
|
2
|
+
import { cellWidth, fitToWidth } from "./text.js";
|
|
2
3
|
export function renderFooter(state, screen, layout) {
|
|
3
4
|
const rect = layout.footer;
|
|
4
5
|
const styles = getExplorerStyles();
|
|
@@ -9,22 +10,31 @@ export function renderFooter(state, screen, layout) {
|
|
|
9
10
|
const hints = footerHints(state);
|
|
10
11
|
let x = rect.x + 2;
|
|
11
12
|
const y = rect.y;
|
|
13
|
+
const endX = rect.x + rect.width;
|
|
12
14
|
for (const hint of hints) {
|
|
13
|
-
if (x >=
|
|
15
|
+
if (x >= endX) {
|
|
14
16
|
break;
|
|
15
17
|
}
|
|
16
18
|
if (hint.bracketed === false) {
|
|
17
19
|
const text = `${hint.key} ${hint.label}`;
|
|
18
|
-
screen
|
|
19
|
-
x += text.length + 2;
|
|
20
|
+
x += putFooterText(screen, x, y, endX, text, hint.running ? styles.muted : {}) + 2;
|
|
20
21
|
continue;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
x
|
|
24
|
-
|
|
25
|
-
x
|
|
23
|
+
const keyText = `[${hint.key}]`;
|
|
24
|
+
const keyWidth = putFooterText(screen, x, y, endX, keyText, hint.running ? styles.muted : styles.accent);
|
|
25
|
+
x += keyWidth;
|
|
26
|
+
if (keyWidth < cellWidth(keyText) || x >= endX) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
x += putFooterText(screen, x, y, endX, ` ${hint.label}`, hint.running ? styles.muted : {}) + 2;
|
|
26
30
|
}
|
|
27
31
|
}
|
|
32
|
+
function putFooterText(screen, x, y, endX, text, style = {}) {
|
|
33
|
+
const remaining = Math.max(0, endX - x);
|
|
34
|
+
const fitted = fitToWidth(text, remaining, x);
|
|
35
|
+
screen.put(x, y, fitted, style);
|
|
36
|
+
return cellWidth(fitted, x);
|
|
37
|
+
}
|
|
28
38
|
function footerHints(state) {
|
|
29
39
|
const hints = [];
|
|
30
40
|
if (state.focused === "detail") {
|
|
@@ -36,7 +46,7 @@ function footerHints(state) {
|
|
|
36
46
|
continue;
|
|
37
47
|
}
|
|
38
48
|
const key = actionKey(entry, id);
|
|
39
|
-
const label = state.selected.size > 0 && entry.source === "row"
|
|
49
|
+
const label = state.multiSelect && state.selected.size > 0 && entry.source === "row"
|
|
40
50
|
? `${entry.label} ${state.selected.size}`
|
|
41
51
|
: entry.label;
|
|
42
52
|
hints.push({ key, label, running: entry.running === true });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getExplorerStyles } from "../theme.js";
|
|
2
|
+
import { cellWidth, fitToWidth, padEndCells } from "./text.js";
|
|
2
3
|
export function renderHeader(state, screen, layout) {
|
|
3
4
|
const rect = layout.header;
|
|
4
5
|
const styles = getExplorerStyles();
|
|
@@ -7,7 +8,7 @@ export function renderHeader(state, screen, layout) {
|
|
|
7
8
|
return;
|
|
8
9
|
}
|
|
9
10
|
if (layout.mode === "too-narrow") {
|
|
10
|
-
screen.put(0, 0,
|
|
11
|
+
screen.put(0, 0, fitToWidth("Terminal too narrow", rect.width), styles.borderFocused);
|
|
11
12
|
return;
|
|
12
13
|
}
|
|
13
14
|
drawTopBorder(screen, state.title, rect.width, styles.border);
|
|
@@ -15,13 +16,15 @@ export function renderHeader(state, screen, layout) {
|
|
|
15
16
|
const prompt = `${state.title.toLocaleLowerCase()}>`;
|
|
16
17
|
const filter = state.filter.length > 0 ? ` ${state.filter}` : "";
|
|
17
18
|
const count = `${state.filtered.length}/${state.rows.length}`;
|
|
18
|
-
const selected = state.selected.size > 0 ? ` (${state.selected.size} selected)` : "";
|
|
19
|
+
const selected = state.multiSelect && state.selected.size > 0 ? ` (${state.selected.size} selected)` : "";
|
|
19
20
|
const spinner = state.detail.loading ? " *" : "";
|
|
20
21
|
const right = `${count}${selected}${spinner}`;
|
|
21
22
|
screen.put(0, 1, "│", styles.border);
|
|
22
23
|
screen.put(Math.max(0, rect.width - 1), 1, "│", styles.border);
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const rightWidth = cellWidth(right);
|
|
25
|
+
const promptWidth = Math.max(0, rect.width - rightWidth - 5);
|
|
26
|
+
screen.put(2, 1, fitToWidth(`${prompt}${filter}`, promptWidth, 2), styles.accent);
|
|
27
|
+
screen.put(Math.max(2, rect.width - rightWidth - 2), 1, right, styles.muted);
|
|
25
28
|
}
|
|
26
29
|
if (rect.height > 2) {
|
|
27
30
|
drawHorizontal(screen, 2, rect.width, styles.border);
|
|
@@ -32,11 +35,10 @@ function drawTopBorder(screen, title, width, style) {
|
|
|
32
35
|
screen.put(0, 0, "┌", style);
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
screen.put(0, 0, `┌${middle.slice(0, Math.max(0, width - 2))}┐`, style);
|
|
38
|
+
const innerWidth = Math.max(0, width - 2);
|
|
39
|
+
const label = fitToWidth(`─ ${title} `, innerWidth, 1);
|
|
40
|
+
const middle = padEndCells(label, innerWidth, "─", 1);
|
|
41
|
+
screen.put(0, 0, `┌${middle}┐`, style);
|
|
40
42
|
}
|
|
41
43
|
function drawHorizontal(screen, y, width, style) {
|
|
42
44
|
if (width === 1) {
|
|
@@ -45,12 +47,3 @@ function drawHorizontal(screen, y, width, style) {
|
|
|
45
47
|
}
|
|
46
48
|
screen.put(0, y, `├${"─".repeat(Math.max(0, width - 2))}┤`, style);
|
|
47
49
|
}
|
|
48
|
-
function fit(text, width) {
|
|
49
|
-
if (width <= 0) {
|
|
50
|
-
return "";
|
|
51
|
-
}
|
|
52
|
-
if (text.length <= width) {
|
|
53
|
-
return text;
|
|
54
|
-
}
|
|
55
|
-
return width <= 1 ? text.slice(0, width) : `${text.slice(0, width - 1)}…`;
|
|
56
|
-
}
|
|
@@ -6,6 +6,7 @@ import { renderFooter } from "./footer.js";
|
|
|
6
6
|
import { renderHeader } from "./header.js";
|
|
7
7
|
import { renderList } from "./list.js";
|
|
8
8
|
import { renderModal } from "./modal.js";
|
|
9
|
+
import { fitToWidth } from "./text.js";
|
|
9
10
|
const REGION_RENDERERS = [
|
|
10
11
|
[REGION_HEADER, renderHeader],
|
|
11
12
|
[REGION_LIST, renderList],
|
|
@@ -42,18 +43,9 @@ function renderToast(state, screen) {
|
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
const styles = getExplorerStyles();
|
|
45
|
-
const message =
|
|
46
|
+
const message = fitToWidth(` ${state.toast.message} `, screen.width);
|
|
46
47
|
screen.put(0, y, message, styles.accent);
|
|
47
48
|
}
|
|
48
|
-
function fit(text, width) {
|
|
49
|
-
if (width <= 0) {
|
|
50
|
-
return "";
|
|
51
|
-
}
|
|
52
|
-
if (text.length <= width) {
|
|
53
|
-
return text;
|
|
54
|
-
}
|
|
55
|
-
return width <= 1 ? text.slice(0, width) : `${text.slice(0, width - 1)}…`;
|
|
56
|
-
}
|
|
57
49
|
export { renderDetail } from "./detail.js";
|
|
58
50
|
export { renderFooter } from "./footer.js";
|
|
59
51
|
export { renderHeader } from "./header.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getExplorerStyles } from "../theme.js";
|
|
2
|
+
import { cellWidth, centerCells, fitToWidth, splitGraphemeCells, stripAnsi } from "./text.js";
|
|
2
3
|
const listLineCache = new WeakMap();
|
|
3
4
|
export function renderList(state, screen, layout) {
|
|
4
5
|
const styles = getExplorerStyles();
|
|
@@ -17,7 +18,7 @@ export function renderList(state, screen, layout) {
|
|
|
17
18
|
listLineCache.set(screen, { rectKey, lines: cache });
|
|
18
19
|
if (state.filtered.length === 0) {
|
|
19
20
|
const hint = state.emptyHint;
|
|
20
|
-
writeLine(screen, rect, Math.floor(rect.height / 2),
|
|
21
|
+
writeLine(screen, rect, Math.floor(rect.height / 2), centerCells(hint, rect.width, rect.x), styles.muted);
|
|
21
22
|
cache.clear();
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
@@ -43,7 +44,7 @@ export function renderList(state, screen, layout) {
|
|
|
43
44
|
if (y >= rect.height) {
|
|
44
45
|
break;
|
|
45
46
|
}
|
|
46
|
-
const selected = state.selected.has(row.id);
|
|
47
|
+
const selected = state.multiSelect && state.selected.has(row.id);
|
|
47
48
|
const cursor = rowIndex === state.filtered[state.cursor];
|
|
48
49
|
const positions = state.matchPositions.get(rowIndex) ?? [];
|
|
49
50
|
const hash = lineHash(row, selected, cursor, positions);
|
|
@@ -67,40 +68,49 @@ function renderRow(screen, rect, rowY, row, opts) {
|
|
|
67
68
|
const marker = opts.selected ? "┃" : " ";
|
|
68
69
|
const cursor = opts.cursor ? "●" : "◌";
|
|
69
70
|
const focus = opts.cursor && opts.focused ? " ▌" : "";
|
|
70
|
-
const badge = row.badge ? ` ${row.badge.text}` : "";
|
|
71
71
|
const prefix = `${marker} ${cursor} `;
|
|
72
|
-
const
|
|
73
|
-
const
|
|
72
|
+
const prefixWidth = cellWidth(prefix, rect.x);
|
|
73
|
+
const focusWidth = cellWidth(focus);
|
|
74
|
+
const badge = row.badge
|
|
75
|
+
? fitToWidth(` ${row.badge.text}`, Math.max(0, rect.width - prefixWidth - focusWidth), rect.x + prefixWidth)
|
|
76
|
+
: "";
|
|
77
|
+
const badgeWidth = cellWidth(badge);
|
|
78
|
+
const available = Math.max(0, rect.width - prefixWidth - focusWidth - badgeWidth);
|
|
79
|
+
const rawTitle = stripAnsi(row.title);
|
|
80
|
+
const titleX = rect.x + prefixWidth;
|
|
81
|
+
const title = fitToWidth(rawTitle, available, titleX);
|
|
82
|
+
const titleWasTruncated = cellWidth(rawTitle, titleX) > available;
|
|
83
|
+
const positions = new Set(opts.positions);
|
|
74
84
|
let x = rect.x;
|
|
75
85
|
const y = rect.y + rowY;
|
|
76
86
|
screen.put(x, y, prefix, opts.cursor ? styles.accent : styles.muted);
|
|
77
|
-
x +=
|
|
78
|
-
for (
|
|
79
|
-
const
|
|
80
|
-
|
|
87
|
+
x += prefixWidth;
|
|
88
|
+
for (const segment of splitGraphemeCells(title, x)) {
|
|
89
|
+
const isTruncationMarker = titleWasTruncated && segment.end === title.length && segment.value === "…";
|
|
90
|
+
const style = !isTruncationMarker && hasMatchPosition(segment.start, segment.end, positions)
|
|
91
|
+
? styles.matchHighlight
|
|
92
|
+
: {};
|
|
93
|
+
screen.put(x, y, segment.value, style);
|
|
94
|
+
x += segment.width;
|
|
81
95
|
}
|
|
82
96
|
if (row.badge) {
|
|
83
|
-
screen.put(rect.x + rect.width -
|
|
97
|
+
screen.put(rect.x + rect.width - badgeWidth - focusWidth, y, badge, styles.tones[row.badge.tone ?? "muted"]);
|
|
84
98
|
}
|
|
85
99
|
if (focus) {
|
|
86
|
-
screen.put(rect.x + rect.width -
|
|
100
|
+
screen.put(rect.x + rect.width - focusWidth, y, focus, styles.borderFocused);
|
|
87
101
|
}
|
|
88
102
|
}
|
|
89
103
|
function writeLine(screen, rect, row, text, style = {}) {
|
|
90
|
-
screen.put(rect.x, rect.y + row,
|
|
104
|
+
screen.put(rect.x, rect.y + row, fitToWidth(text, rect.width, rect.x), style);
|
|
91
105
|
}
|
|
92
106
|
function lineHash(row, selected, cursor, positions) {
|
|
93
107
|
return `${row.id}:${selected ? 1 : 0}:${cursor ? 1 : 0}:${positions.join(",")}`;
|
|
94
108
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return text;
|
|
109
|
+
function hasMatchPosition(start, end, positions) {
|
|
110
|
+
for (let position = start; position < end; position += 1) {
|
|
111
|
+
if (positions.has(position)) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
101
114
|
}
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
function stripAnsi(value) {
|
|
105
|
-
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
115
|
+
return false;
|
|
106
116
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getExplorerStyles } from "../theme.js";
|
|
2
|
+
import { fitToWidth, padEndCells } from "./text.js";
|
|
2
3
|
export function renderModal(state, screen) {
|
|
3
4
|
if (state.modal === null || screen.width <= 0 || screen.height <= 0) {
|
|
4
5
|
return;
|
|
@@ -11,7 +12,7 @@ export function renderModal(state, screen) {
|
|
|
11
12
|
drawBox(screen, x, y, width, height, title(state), styles.borderFocused);
|
|
12
13
|
const lines = modalLines(state);
|
|
13
14
|
for (let row = 0; row < Math.min(lines.length, height - 2); row += 1) {
|
|
14
|
-
screen.put(x + 2, y + 1 + row,
|
|
15
|
+
screen.put(x + 2, y + 1 + row, fitToWidth(lines[row], width - 4, x + 2), row === 1 ? styles.accent : {});
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
function modalLines(state) {
|
|
@@ -54,8 +55,9 @@ function modalHeight(state) {
|
|
|
54
55
|
}
|
|
55
56
|
function drawBox(screen, x, y, width, height, boxTitle, style) {
|
|
56
57
|
screen.clearRect({ x, y, width, height });
|
|
57
|
-
const
|
|
58
|
-
|
|
58
|
+
const innerWidth = Math.max(0, width - 2);
|
|
59
|
+
const titleSegment = fitToWidth(`─ ${boxTitle} `, innerWidth, x + 1);
|
|
60
|
+
screen.put(x, y, `╭${padEndCells(titleSegment, innerWidth, "─", x + 1)}╮`, style);
|
|
59
61
|
for (let row = 1; row < height - 1; row += 1) {
|
|
60
62
|
screen.put(x, y + row, "│", style);
|
|
61
63
|
screen.put(x + width - 1, y + row, "│", style);
|
|
@@ -83,9 +85,3 @@ function paletteLines(state) {
|
|
|
83
85
|
function labelFor(action) {
|
|
84
86
|
return typeof action.label === "function" ? action.label() : action.label;
|
|
85
87
|
}
|
|
86
|
-
function fit(text, width) {
|
|
87
|
-
if (text.length <= width) {
|
|
88
|
-
return text;
|
|
89
|
-
}
|
|
90
|
-
return width <= 1 ? text.slice(0, width) : `${text.slice(0, width - 1)}…`;
|
|
91
|
-
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GraphemeCell {
|
|
2
|
+
value: string;
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
width: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function cellWidth(value: string, startColumn?: number): number;
|
|
8
|
+
export declare function fitToWidth(text: string, width: number, startColumn?: number): string;
|
|
9
|
+
export declare function centerCells(text: string, width: number, startColumn?: number): string;
|
|
10
|
+
export declare function padEndCells(text: string, width: number, fill?: string, startColumn?: number): string;
|
|
11
|
+
export declare function splitGraphemeCells(value: string, startColumn?: number): GraphemeCell[];
|
|
12
|
+
export declare function stripAnsi(value: string): string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { displayWidth, graphemes } from "../../dashboard/terminal-width.js";
|
|
2
|
+
const ELLIPSIS = "…";
|
|
3
|
+
export function cellWidth(value, startColumn = 0) {
|
|
4
|
+
return displayWidth(value, startColumn);
|
|
5
|
+
}
|
|
6
|
+
export function fitToWidth(text, width, startColumn = 0) {
|
|
7
|
+
if (width <= 0) {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
if (cellWidth(text, startColumn) <= width) {
|
|
11
|
+
return text;
|
|
12
|
+
}
|
|
13
|
+
const ellipsisWidth = cellWidth(ELLIPSIS, startColumn);
|
|
14
|
+
if (ellipsisWidth > width) {
|
|
15
|
+
return takeCells(text, width, startColumn);
|
|
16
|
+
}
|
|
17
|
+
const prefix = takeCells(text, width - ellipsisWidth, startColumn);
|
|
18
|
+
return `${prefix}${ELLIPSIS}`;
|
|
19
|
+
}
|
|
20
|
+
export function centerCells(text, width, startColumn = 0) {
|
|
21
|
+
const fitted = fitToWidth(text, width, startColumn);
|
|
22
|
+
const padding = Math.max(0, Math.floor((width - cellWidth(fitted, startColumn)) / 2));
|
|
23
|
+
return `${" ".repeat(padding)}${fitted}`;
|
|
24
|
+
}
|
|
25
|
+
export function padEndCells(text, width, fill = " ", startColumn = 0) {
|
|
26
|
+
let output = takeCells(text, width, startColumn);
|
|
27
|
+
let used = cellWidth(output, startColumn);
|
|
28
|
+
let column = startColumn + used;
|
|
29
|
+
while (used < width) {
|
|
30
|
+
const fillWidth = cellWidth(fill, column);
|
|
31
|
+
if (fill.length === 0 || fillWidth <= 0 || used + fillWidth > width) {
|
|
32
|
+
const spaces = width - used;
|
|
33
|
+
output += " ".repeat(spaces);
|
|
34
|
+
used += spaces;
|
|
35
|
+
column += spaces;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
output += fill;
|
|
39
|
+
used += fillWidth;
|
|
40
|
+
column += fillWidth;
|
|
41
|
+
}
|
|
42
|
+
return output;
|
|
43
|
+
}
|
|
44
|
+
export function splitGraphemeCells(value, startColumn = 0) {
|
|
45
|
+
const cells = [];
|
|
46
|
+
let offset = 0;
|
|
47
|
+
let column = startColumn;
|
|
48
|
+
for (const segment of graphemes(value)) {
|
|
49
|
+
const width = cellWidth(segment, column);
|
|
50
|
+
cells.push({
|
|
51
|
+
value: segment,
|
|
52
|
+
start: offset,
|
|
53
|
+
end: offset + segment.length,
|
|
54
|
+
width
|
|
55
|
+
});
|
|
56
|
+
offset += segment.length;
|
|
57
|
+
column += width;
|
|
58
|
+
}
|
|
59
|
+
return cells;
|
|
60
|
+
}
|
|
61
|
+
export function stripAnsi(value) {
|
|
62
|
+
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
63
|
+
}
|
|
64
|
+
function takeCells(text, width, startColumn) {
|
|
65
|
+
if (width <= 0) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
let output = "";
|
|
69
|
+
let used = 0;
|
|
70
|
+
let column = startColumn;
|
|
71
|
+
for (const segment of graphemes(text)) {
|
|
72
|
+
const segmentWidth = cellWidth(segment, column);
|
|
73
|
+
if (used + segmentWidth > width) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
output += segment;
|
|
77
|
+
used += segmentWidth;
|
|
78
|
+
column += segmentWidth;
|
|
79
|
+
}
|
|
80
|
+
return output;
|
|
81
|
+
}
|
|
@@ -16,6 +16,7 @@ export function createInitialState(config, size) {
|
|
|
16
16
|
cols: normalizeSize(size.cols),
|
|
17
17
|
rows: normalizeSize(size.rows)
|
|
18
18
|
};
|
|
19
|
+
const multiSelect = config.multiSelect ?? true;
|
|
19
20
|
return {
|
|
20
21
|
title: config.title,
|
|
21
22
|
emptyHint: config.emptyHint ?? "No detail",
|
|
@@ -35,6 +36,7 @@ export function createInitialState(config, size) {
|
|
|
35
36
|
loading: false
|
|
36
37
|
},
|
|
37
38
|
selected: new Set(),
|
|
39
|
+
multiSelect,
|
|
38
40
|
modal: null,
|
|
39
41
|
toast: null,
|
|
40
42
|
dirty: REGION_ALL,
|
|
@@ -95,12 +95,12 @@ export async function withSpinner(options) {
|
|
|
95
95
|
const result = await fn();
|
|
96
96
|
const msg = stopMessage ? stopMessage(result) : undefined;
|
|
97
97
|
if (msg) {
|
|
98
|
-
process.stdout.write(
|
|
98
|
+
process.stdout.write(`${color.green("◆")} ${msg}\n`);
|
|
99
99
|
}
|
|
100
100
|
const sub = subtext ? subtext(result) : undefined;
|
|
101
101
|
if (sub) {
|
|
102
102
|
for (const line of sub.split("\n")) {
|
|
103
|
-
process.stdout.write(
|
|
103
|
+
process.stdout.write(`${color.gray("│")} ${line}\n`);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
return result;
|
|
@@ -120,7 +120,7 @@ export async function withSpinner(options) {
|
|
|
120
120
|
const sub = subtext ? subtext(result) : undefined;
|
|
121
121
|
if (sub) {
|
|
122
122
|
for (const line of sub.split("\n")) {
|
|
123
|
-
process.stdout.write(
|
|
123
|
+
process.stdout.write(`${color.gray("│")} ${line}\n`);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
return result;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
export function buildDockerEnvArgs(input) {
|
|
3
|
+
const keys = Object.keys(input.env ?? {});
|
|
4
|
+
if (keys.length === 0) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
if (input.envFilePath !== undefined) {
|
|
8
|
+
return ["--env-file", input.envFilePath];
|
|
9
|
+
}
|
|
10
|
+
return keys.flatMap((key) => ["-e", key]);
|
|
11
|
+
}
|
|
2
12
|
export function buildDockerRunArgs(input) {
|
|
3
13
|
const args = [input.engine];
|
|
4
14
|
if (input.engine === "docker" && input.context) {
|
|
@@ -21,9 +31,7 @@ export function buildDockerRunArgs(input) {
|
|
|
21
31
|
if (input.cwd !== undefined) {
|
|
22
32
|
args.push("-w", input.cwd);
|
|
23
33
|
}
|
|
24
|
-
|
|
25
|
-
args.push("-e", `${key}=${value}`);
|
|
26
|
-
}
|
|
34
|
+
args.push(...buildDockerEnvArgs(input));
|
|
27
35
|
for (const mount of input.mounts) {
|
|
28
36
|
const volume = `${path.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
|
|
29
37
|
args.push("-v", volume);
|