toolcraft-openapi 0.0.23 → 0.0.25
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/README.md +2 -3
- package/dist/auth/bearer-token-auth.js +12 -3
- package/dist/auth/types.d.ts +1 -1
- package/dist/bin/generate.d.ts +5 -4
- package/dist/bin/generate.js +57 -23
- package/dist/generate.js +6 -2
- package/dist/http.js +29 -17
- package/dist/interpreter.js +12 -3
- package/dist/mock/fetch.js +22 -5
- package/dist/network-error.js +5 -3
- package/dist/redaction.d.ts +3 -0
- package/dist/redaction.js +38 -0
- package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
- package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
- package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
- package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
- package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
- 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 +12 -15
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
- package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
- package/node_modules/@poe-code/design-system/package.json +2 -1
- package/node_modules/auth-store/dist/create-secret-store.js +4 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +8 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +104 -8
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
- package/node_modules/auth-store/dist/keychain-store.js +18 -16
- package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
- package/node_modules/auth-store/dist/provider-store.js +55 -7
- package/node_modules/auth-store/dist/types.d.ts +3 -1
- package/node_modules/auth-store/package.json +2 -1
- package/package.json +3 -3
- package/dist/lock.d.ts +0 -14
- package/dist/lock.js +0 -152
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { color } from "../components/color.js";
|
|
2
|
+
import { expandTabs, graphemes, graphemeWidth } from "./terminal-width.js";
|
|
2
3
|
const EMPTY_CELL = { ch: " ", style: {} };
|
|
3
4
|
export class ScreenBuffer {
|
|
4
5
|
_width;
|
|
@@ -21,13 +22,19 @@ export class ScreenBuffer {
|
|
|
21
22
|
}
|
|
22
23
|
const normalizedStyle = normalizeStyle(style);
|
|
23
24
|
let offset = 0;
|
|
24
|
-
for (const ch of text) {
|
|
25
|
+
for (const ch of graphemes(expandTabs(text, Math.max(0, x)))) {
|
|
25
26
|
const targetX = x + offset;
|
|
26
|
-
|
|
27
|
+
const width = graphemeWidth(ch);
|
|
28
|
+
offset += width;
|
|
27
29
|
if (!this.isInBoundsX(targetX)) {
|
|
28
30
|
continue;
|
|
29
31
|
}
|
|
30
32
|
this._cells[this.index(targetX, y)] = { ch, style: normalizedStyle };
|
|
33
|
+
for (let continuation = 1; continuation < width; continuation += 1) {
|
|
34
|
+
if (this.isInBoundsX(targetX + continuation)) {
|
|
35
|
+
this._cells[this.index(targetX + continuation, y)] = { ch: "", style: normalizedStyle };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
get(x, y) {
|
|
@@ -77,16 +84,22 @@ export class ScreenBuffer {
|
|
|
77
84
|
const normalizedStyle = normalizeStyle(style);
|
|
78
85
|
const rectEndX = rect.x + rect.width;
|
|
79
86
|
let offset = 0;
|
|
80
|
-
for (const ch of text) {
|
|
87
|
+
for (const ch of graphemes(expandTabs(text))) {
|
|
81
88
|
const targetX = rect.x + offset;
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
const width = graphemeWidth(ch);
|
|
90
|
+
offset += width;
|
|
91
|
+
if (targetX + width > rectEndX) {
|
|
84
92
|
break;
|
|
85
93
|
}
|
|
86
94
|
if (!this.isInBoundsX(targetX)) {
|
|
87
95
|
continue;
|
|
88
96
|
}
|
|
89
97
|
this._cells[this.index(targetX, y)] = { ch, style: normalizedStyle };
|
|
98
|
+
for (let continuation = 1; continuation < width; continuation += 1) {
|
|
99
|
+
if (this.isInBoundsX(targetX + continuation)) {
|
|
100
|
+
this._cells[this.index(targetX + continuation, y)] = { ch: "", style: normalizedStyle };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
index(x, y) {
|
|
@@ -118,6 +131,9 @@ export function diff(prev, next) {
|
|
|
118
131
|
return changes;
|
|
119
132
|
}
|
|
120
133
|
export function cellToAnsi(cell) {
|
|
134
|
+
if (cell.ch.length === 0) {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
121
137
|
const style = cell.style ?? {};
|
|
122
138
|
let painter = color;
|
|
123
139
|
if (style.bold) {
|
|
@@ -126,6 +142,9 @@ export function cellToAnsi(cell) {
|
|
|
126
142
|
if (style.dim) {
|
|
127
143
|
painter = painter.dim;
|
|
128
144
|
}
|
|
145
|
+
if (style.inverse) {
|
|
146
|
+
painter = painter.inverse;
|
|
147
|
+
}
|
|
129
148
|
if (style.underline) {
|
|
130
149
|
painter = painter.underline;
|
|
131
150
|
}
|
|
@@ -161,6 +180,9 @@ function normalizeStyle(style) {
|
|
|
161
180
|
if (style?.dim !== undefined) {
|
|
162
181
|
next.dim = style.dim;
|
|
163
182
|
}
|
|
183
|
+
if (style?.inverse !== undefined) {
|
|
184
|
+
next.inverse = style.inverse;
|
|
185
|
+
}
|
|
164
186
|
if (style?.underline !== undefined) {
|
|
165
187
|
next.underline = style.underline;
|
|
166
188
|
}
|
|
@@ -175,6 +197,7 @@ function cellsEqual(left, right) {
|
|
|
175
197
|
&& left.style.bg === right.style.bg
|
|
176
198
|
&& left.style.bold === right.style.bold
|
|
177
199
|
&& left.style.dim === right.style.dim
|
|
200
|
+
&& left.style.inverse === right.style.inverse
|
|
178
201
|
&& left.style.underline === right.style.underline;
|
|
179
202
|
}
|
|
180
203
|
function applyForegroundColor(instance, ansiColor) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveThemeName } from "../../internal/theme-detect.js";
|
|
2
2
|
import { hasAnsi, parseAnsi } from "../ansi.js";
|
|
3
|
+
import { displayWidth, expandTabs, graphemes, graphemeWidth } from "../terminal-width.js";
|
|
3
4
|
const TEXT_OFFSET = 3;
|
|
4
5
|
const CONTINUATION_PREFIX = "│";
|
|
5
6
|
export function renderOutputPane(buffer, rect, items) {
|
|
@@ -37,7 +38,7 @@ export function renderOutputPane(buffer, rect, items) {
|
|
|
37
38
|
width: remaining,
|
|
38
39
|
height: textRect.height
|
|
39
40
|
}, row, segment.text, segment.style);
|
|
40
|
-
offsetX +=
|
|
41
|
+
offsetX += displayWidth(segment.text, offsetX);
|
|
41
42
|
}
|
|
42
43
|
continue;
|
|
43
44
|
}
|
|
@@ -54,8 +55,8 @@ export function computeVisualLines(items, width) {
|
|
|
54
55
|
const visualLines = [];
|
|
55
56
|
for (const item of items) {
|
|
56
57
|
const itemStyle = getItemStyle(item.kind, themeName);
|
|
57
|
-
if (hasAnsi(item.text)) {
|
|
58
|
-
const styledLines = parseAnsi(item.text, {});
|
|
58
|
+
if (hasAnsi(item.text) || hasCursorControls(item.text)) {
|
|
59
|
+
const styledLines = parseAnsi(item.text, hasAnsi(item.text) ? {} : itemStyle);
|
|
59
60
|
let firstRow = true;
|
|
60
61
|
for (const styledLine of styledLines) {
|
|
61
62
|
const rows = hardWrapSegments(styledLine.segments, textWidth);
|
|
@@ -84,6 +85,9 @@ export function computeVisualLines(items, width) {
|
|
|
84
85
|
}
|
|
85
86
|
return visualLines;
|
|
86
87
|
}
|
|
88
|
+
function hasCursorControls(text) {
|
|
89
|
+
return text.includes("\r") || text.includes("\b");
|
|
90
|
+
}
|
|
87
91
|
function hardWrapSegments(segments, width) {
|
|
88
92
|
if (width <= 0) {
|
|
89
93
|
return [[]];
|
|
@@ -91,33 +95,34 @@ function hardWrapSegments(segments, width) {
|
|
|
91
95
|
const rows = [[]];
|
|
92
96
|
let rowWidth = 0;
|
|
93
97
|
for (const segment of segments) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const chars = [...segment.text];
|
|
98
|
-
let cursor = 0;
|
|
99
|
-
while (cursor < chars.length) {
|
|
100
|
-
const space = width - rowWidth;
|
|
101
|
-
if (space <= 0) {
|
|
102
|
-
rows.push([]);
|
|
103
|
-
rowWidth = 0;
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const take = chars.slice(cursor, cursor + space).join("");
|
|
107
|
-
const currentRow = rows[rows.length - 1];
|
|
108
|
-
currentRow.push({ text: take, style: { ...segment.style } });
|
|
109
|
-
rowWidth += Math.min(space, chars.length - cursor);
|
|
110
|
-
cursor += space;
|
|
111
|
-
if (cursor < chars.length) {
|
|
98
|
+
for (const grapheme of graphemes(expandTabs(segment.text, rowWidth))) {
|
|
99
|
+
const graphemeCells = graphemeWidth(grapheme);
|
|
100
|
+
if (rowWidth > 0 && rowWidth + graphemeCells > width) {
|
|
112
101
|
rows.push([]);
|
|
113
102
|
rowWidth = 0;
|
|
114
103
|
}
|
|
104
|
+
appendSegment(rows[rows.length - 1], grapheme, segment.style);
|
|
105
|
+
rowWidth += graphemeCells;
|
|
115
106
|
}
|
|
116
107
|
}
|
|
117
108
|
return rows;
|
|
118
109
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
110
|
+
function appendSegment(segments, text, style) {
|
|
111
|
+
const last = segments[segments.length - 1];
|
|
112
|
+
if (last && stylesEqual(last.style, style)) {
|
|
113
|
+
last.text += text;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
segments.push({ text, style: { ...style } });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function stylesEqual(left, right) {
|
|
120
|
+
return left.fg === right.fg
|
|
121
|
+
&& left.bg === right.bg
|
|
122
|
+
&& left.bold === right.bold
|
|
123
|
+
&& left.dim === right.dim
|
|
124
|
+
&& left.inverse === right.inverse
|
|
125
|
+
&& left.underline === right.underline;
|
|
121
126
|
}
|
|
122
127
|
function getPrefix(kind) {
|
|
123
128
|
if (kind === "success") {
|
|
@@ -160,7 +165,7 @@ function wrapText(value, width) {
|
|
|
160
165
|
if (width <= 0) {
|
|
161
166
|
return logicalLines.map(() => "");
|
|
162
167
|
}
|
|
163
|
-
return logicalLines.flatMap((line) => wrapParagraph(line, width));
|
|
168
|
+
return logicalLines.flatMap((line) => wrapParagraph(expandTabs(line), width));
|
|
164
169
|
}
|
|
165
170
|
function wrapParagraph(value, width) {
|
|
166
171
|
if (value.length === 0) {
|
|
@@ -186,7 +191,7 @@ function wrapParagraph(value, width) {
|
|
|
186
191
|
for (let index = 0; index < chunks.length; index += 1) {
|
|
187
192
|
const chunk = chunks[index] ?? "";
|
|
188
193
|
const gap = index === 0 ? pendingSpace : "";
|
|
189
|
-
if (currentLine.length > 0 && currentLine
|
|
194
|
+
if (currentLine.length > 0 && displayWidth(`${currentLine}${gap}${chunk}`) > width) {
|
|
190
195
|
flushLine();
|
|
191
196
|
}
|
|
192
197
|
if (currentLine.length > 0 && gap.length > 0) {
|
|
@@ -205,12 +210,24 @@ function wrapParagraph(value, width) {
|
|
|
205
210
|
return lines;
|
|
206
211
|
}
|
|
207
212
|
function splitWord(value, width) {
|
|
208
|
-
if (value
|
|
213
|
+
if (displayWidth(value) <= width) {
|
|
209
214
|
return [value];
|
|
210
215
|
}
|
|
211
216
|
const chunks = [];
|
|
212
|
-
|
|
213
|
-
|
|
217
|
+
let chunk = "";
|
|
218
|
+
let chunkWidth = 0;
|
|
219
|
+
for (const grapheme of graphemes(value)) {
|
|
220
|
+
const graphemeCells = graphemeWidth(grapheme);
|
|
221
|
+
if (chunk.length > 0 && chunkWidth + graphemeCells > width) {
|
|
222
|
+
chunks.push(chunk);
|
|
223
|
+
chunk = "";
|
|
224
|
+
chunkWidth = 0;
|
|
225
|
+
}
|
|
226
|
+
chunk += grapheme;
|
|
227
|
+
chunkWidth += graphemeCells;
|
|
228
|
+
}
|
|
229
|
+
if (chunk.length > 0) {
|
|
230
|
+
chunks.push(chunk);
|
|
214
231
|
}
|
|
215
232
|
return chunks;
|
|
216
233
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function graphemes(value: string): string[];
|
|
2
|
+
export declare function displayWidth(value: string, startColumn?: number): number;
|
|
3
|
+
export declare function expandTabs(value: string, startColumn?: number): string;
|
|
4
|
+
export declare function graphemeWidth(segment: string): number;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
2
|
+
export function graphemes(value) {
|
|
3
|
+
return Array.from(graphemeSegmenter.segment(value), ({ segment }) => segment);
|
|
4
|
+
}
|
|
5
|
+
export function displayWidth(value, startColumn = 0) {
|
|
6
|
+
let column = startColumn;
|
|
7
|
+
for (const segment of graphemes(value)) {
|
|
8
|
+
if (segment === "\t") {
|
|
9
|
+
column += 8 - (column % 8);
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
column += graphemeWidth(segment);
|
|
13
|
+
}
|
|
14
|
+
return column - startColumn;
|
|
15
|
+
}
|
|
16
|
+
export function expandTabs(value, startColumn = 0) {
|
|
17
|
+
let column = startColumn;
|
|
18
|
+
let expanded = "";
|
|
19
|
+
for (const segment of graphemes(value)) {
|
|
20
|
+
if (segment === "\t") {
|
|
21
|
+
const spaces = 8 - (column % 8);
|
|
22
|
+
expanded += " ".repeat(spaces);
|
|
23
|
+
column += spaces;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
expanded += segment;
|
|
27
|
+
column += graphemeWidth(segment);
|
|
28
|
+
}
|
|
29
|
+
return expanded;
|
|
30
|
+
}
|
|
31
|
+
export function graphemeWidth(segment) {
|
|
32
|
+
const codePoint = segment.codePointAt(0);
|
|
33
|
+
if (codePoint === undefined || isZeroWidthCodePoint(codePoint)) {
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
return isWideCodePoint(codePoint) || isFlagSegment(segment) ? 2 : 1;
|
|
37
|
+
}
|
|
38
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
39
|
+
return (codePoint >= 0x0300 && codePoint <= 0x036f)
|
|
40
|
+
|| (codePoint >= 0x1ab0 && codePoint <= 0x1aff)
|
|
41
|
+
|| (codePoint >= 0x1dc0 && codePoint <= 0x1dff)
|
|
42
|
+
|| (codePoint >= 0x20d0 && codePoint <= 0x20ff)
|
|
43
|
+
|| (codePoint >= 0xfe20 && codePoint <= 0xfe2f);
|
|
44
|
+
}
|
|
45
|
+
function isFlagSegment(segment) {
|
|
46
|
+
const codePoints = [...segment].map((character) => character.codePointAt(0));
|
|
47
|
+
return codePoints.length === 2
|
|
48
|
+
&& codePoints.every((codePoint) => codePoint !== undefined && codePoint >= 0x1f1e6 && codePoint <= 0x1f1ff);
|
|
49
|
+
}
|
|
50
|
+
function isWideCodePoint(codePoint) {
|
|
51
|
+
return ((codePoint >= 0x1100 && codePoint <= 0x115f)
|
|
52
|
+
|| codePoint === 0x2329
|
|
53
|
+
|| codePoint === 0x232a
|
|
54
|
+
|| (codePoint >= 0x2e80 && codePoint <= 0x303e)
|
|
55
|
+
|| (codePoint >= 0x3041 && codePoint <= 0x33bf)
|
|
56
|
+
|| (codePoint >= 0x3400 && codePoint <= 0x4dbf)
|
|
57
|
+
|| (codePoint >= 0x4e00 && codePoint <= 0xa4cf)
|
|
58
|
+
|| (codePoint >= 0xa960 && codePoint <= 0xa97f)
|
|
59
|
+
|| (codePoint >= 0xac00 && codePoint <= 0xd7af)
|
|
60
|
+
|| (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
61
|
+
|| (codePoint >= 0xfe10 && codePoint <= 0xfe19)
|
|
62
|
+
|| (codePoint >= 0xfe30 && codePoint <= 0xfe6f)
|
|
63
|
+
|| (codePoint >= 0xff00 && codePoint <= 0xff60)
|
|
64
|
+
|| (codePoint >= 0xffe0 && codePoint <= 0xffe6)
|
|
65
|
+
|| (codePoint >= 0x1b000 && codePoint <= 0x1b0ff)
|
|
66
|
+
|| codePoint === 0x1f004
|
|
67
|
+
|| codePoint === 0x1f0cf
|
|
68
|
+
|| (codePoint >= 0x1f200 && codePoint <= 0x1fffd)
|
|
69
|
+
|| (codePoint >= 0x20000 && codePoint <= 0x2fffd)
|
|
70
|
+
|| (codePoint >= 0x30000 && codePoint <= 0x3fffd));
|
|
71
|
+
}
|
|
@@ -35,6 +35,12 @@ export type ExplorerEvent = {
|
|
|
35
35
|
rowId: string;
|
|
36
36
|
token: number;
|
|
37
37
|
items: DetailItem[];
|
|
38
|
+
} | {
|
|
39
|
+
type: "detailItemRendered";
|
|
40
|
+
rowId: string;
|
|
41
|
+
token: number;
|
|
42
|
+
itemIndex: number;
|
|
43
|
+
content: string;
|
|
38
44
|
} | {
|
|
39
45
|
type: "detailError";
|
|
40
46
|
rowId: string;
|
|
@@ -21,6 +21,8 @@ export function step(state, event, runtimeHandles = DEFAULT_ACTION_HANDLES) {
|
|
|
21
21
|
return detailLoading(state, event.rowId, event.token);
|
|
22
22
|
case "detailLoaded":
|
|
23
23
|
return detailLoaded(state, event.rowId, event.token, event.items);
|
|
24
|
+
case "detailItemRendered":
|
|
25
|
+
return detailItemRendered(state, event.rowId, event.token, event.itemIndex, event.content);
|
|
24
26
|
case "detailError":
|
|
25
27
|
return detailError(state, event.rowId, event.token, event.error);
|
|
26
28
|
case "actionResolved":
|
|
@@ -42,6 +44,9 @@ function stepKey(state, key, runtimeHandles) {
|
|
|
42
44
|
return stepFilterKey(state, key, target, runtimeHandles);
|
|
43
45
|
}
|
|
44
46
|
if (target?.type === "action") {
|
|
47
|
+
if (state.actionState.get(target.id)?.source === "detail" && state.focused !== "detail") {
|
|
48
|
+
return mark(state, 0);
|
|
49
|
+
}
|
|
45
50
|
const action = resolveAction(state, key);
|
|
46
51
|
return action === null ? mark(state, 0) : dispatchAction(state, action, false, runtimeHandles);
|
|
47
52
|
}
|
|
@@ -170,20 +175,27 @@ function resize(state, cols, rows) {
|
|
|
170
175
|
};
|
|
171
176
|
}
|
|
172
177
|
function rowsLoaded(state, rows) {
|
|
178
|
+
const rowIds = new Set();
|
|
179
|
+
for (const row of rows) {
|
|
180
|
+
if (rowIds.has(row.id)) {
|
|
181
|
+
throw new Error(`Duplicate explorer row id: ${row.id}`);
|
|
182
|
+
}
|
|
183
|
+
rowIds.add(row.id);
|
|
184
|
+
}
|
|
173
185
|
const matches = filterRows(state.filter, rows);
|
|
174
186
|
const filtered = matches.map((match) => match.index);
|
|
175
187
|
const matchPositions = createMatchPositions(matches);
|
|
176
188
|
const cursor = clamp(state.cursor, 0, Math.max(0, filtered.length - 1));
|
|
189
|
+
const selected = pruneSelection(state.selected, rows);
|
|
190
|
+
const detail = resetDetailForCursor(state, rows, filtered, cursor);
|
|
191
|
+
const modal = modalStillValid(state.modal, rows);
|
|
192
|
+
if (state.modal?.kind === "confirm" && modal === null) {
|
|
193
|
+
state.modal.resolver(false);
|
|
194
|
+
}
|
|
195
|
+
const nextView = { ...state, rows, filtered, matchPositions, cursor, selected, detail, modal };
|
|
177
196
|
const next = {
|
|
178
|
-
...
|
|
179
|
-
|
|
180
|
-
filtered,
|
|
181
|
-
matchPositions,
|
|
182
|
-
cursor,
|
|
183
|
-
selected: pruneSelection(state.selected, rows),
|
|
184
|
-
detail: resetDetailForCursor(state, rows, filtered, cursor),
|
|
185
|
-
modal: modalStillValid(state.modal, rows),
|
|
186
|
-
actionState: recomputeActionState({ ...state, rows, filtered, matchPositions, cursor }),
|
|
197
|
+
...nextView,
|
|
198
|
+
actionState: recomputeActionState(nextView),
|
|
187
199
|
dirty: REGION_HEADER | REGION_LIST | REGION_DETAIL | REGION_FOOTER | REGION_MODAL
|
|
188
200
|
};
|
|
189
201
|
const effect = detailEffect(next);
|
|
@@ -222,6 +234,16 @@ function detailLoaded(state, rowId, token, items) {
|
|
|
222
234
|
effects: NO_EFFECTS
|
|
223
235
|
};
|
|
224
236
|
}
|
|
237
|
+
function detailItemRendered(state, rowId, token, itemIndex, content) {
|
|
238
|
+
if (state.detail.rowId !== rowId || state.detail.token !== token || state.detail.items?.[itemIndex] === undefined) {
|
|
239
|
+
return mark(state, 0);
|
|
240
|
+
}
|
|
241
|
+
const items = state.detail.items.map((item, index) => index === itemIndex ? { ...item, renderedContent: content } : item);
|
|
242
|
+
return {
|
|
243
|
+
state: { ...state, detail: { ...state.detail, items }, dirty: REGION_DETAIL },
|
|
244
|
+
effects: NO_EFFECTS
|
|
245
|
+
};
|
|
246
|
+
}
|
|
225
247
|
function detailError(state, rowId, token, error) {
|
|
226
248
|
if (state.detail.rowId !== rowId || state.detail.token !== token) {
|
|
227
249
|
return mark(state, 0);
|
|
@@ -530,7 +552,7 @@ function dispatchPaletteAction(state, runtimeHandles) {
|
|
|
530
552
|
}
|
|
531
553
|
function dispatchPrimary(state, runtimeHandles) {
|
|
532
554
|
for (const [id, entry] of state.actionState.entries()) {
|
|
533
|
-
if (entry.action?.primary === true) {
|
|
555
|
+
if (entry.action?.primary === true && entry.available === true && entry.running !== true) {
|
|
534
556
|
return dispatchActionById(state, id, false, runtimeHandles);
|
|
535
557
|
}
|
|
536
558
|
}
|
|
@@ -25,6 +25,8 @@ class ExplorerRuntime {
|
|
|
25
25
|
unsubscribeKeypress;
|
|
26
26
|
unsubscribeResize;
|
|
27
27
|
toastTimer;
|
|
28
|
+
rowsRequestToken = 0;
|
|
29
|
+
reorderToken = 0;
|
|
28
30
|
stopped = false;
|
|
29
31
|
settle;
|
|
30
32
|
constructor(config, driver) {
|
|
@@ -32,6 +34,10 @@ class ExplorerRuntime {
|
|
|
32
34
|
this.driver = driver;
|
|
33
35
|
this.state = createInitialState(config, driver.getSize());
|
|
34
36
|
this.detailJobs = createDetailJobs((event) => {
|
|
37
|
+
if (event.type === "detailLoaded") {
|
|
38
|
+
this.loadDetailContent(event.rowId, event.token, event.items);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
35
41
|
this.dispatch(event);
|
|
36
42
|
});
|
|
37
43
|
this.runtimeHandles = {
|
|
@@ -76,13 +82,16 @@ class ExplorerRuntime {
|
|
|
76
82
|
this.dispatch({ type: "resize", cols: size.cols, rows: size.rows });
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
|
-
async loadRows() {
|
|
85
|
+
async loadRows(requestToken = ++this.rowsRequestToken) {
|
|
80
86
|
const rows = await this.config.rows();
|
|
81
|
-
|
|
87
|
+
if (requestToken === this.rowsRequestToken) {
|
|
88
|
+
this.dispatch({ type: "rowsLoaded", rows });
|
|
89
|
+
}
|
|
82
90
|
}
|
|
83
91
|
async refreshRowsFromSource() {
|
|
92
|
+
const requestToken = ++this.rowsRequestToken;
|
|
84
93
|
await this.config.refresh?.();
|
|
85
|
-
await this.loadRows();
|
|
94
|
+
await this.loadRows(requestToken);
|
|
86
95
|
}
|
|
87
96
|
dispatch(event) {
|
|
88
97
|
if (this.stopped) {
|
|
@@ -101,7 +110,7 @@ class ExplorerRuntime {
|
|
|
101
110
|
continue;
|
|
102
111
|
}
|
|
103
112
|
if (effect.type === "persistOrder") {
|
|
104
|
-
this.track(this.persistOrder(effect.orderedIds, previousState.rows));
|
|
113
|
+
this.track(this.persistOrder(effect.orderedIds, previousState.rows, ++this.reorderToken));
|
|
105
114
|
continue;
|
|
106
115
|
}
|
|
107
116
|
if (effect.type === "suspend") {
|
|
@@ -130,7 +139,47 @@ class ExplorerRuntime {
|
|
|
130
139
|
signal: new AbortController().signal
|
|
131
140
|
});
|
|
132
141
|
}
|
|
133
|
-
|
|
142
|
+
loadDetailContent(rowId, token, items) {
|
|
143
|
+
const row = this.state.rows.find((candidate) => candidate.id === rowId);
|
|
144
|
+
if (row === undefined) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const layout = computeExplorerLayout({
|
|
148
|
+
cols: this.state.size.cols,
|
|
149
|
+
rows: this.state.size.rows,
|
|
150
|
+
detailHidden: this.state.layout === "narrow-list-only" || this.state.layout === "too-narrow"
|
|
151
|
+
});
|
|
152
|
+
const context = {
|
|
153
|
+
width: layout.detail.width,
|
|
154
|
+
height: layout.detail.height,
|
|
155
|
+
row,
|
|
156
|
+
signal: new AbortController().signal
|
|
157
|
+
};
|
|
158
|
+
const preparedItems = items.map((item, itemIndex) => {
|
|
159
|
+
try {
|
|
160
|
+
const content = item.render(context);
|
|
161
|
+
if (typeof content === "string") {
|
|
162
|
+
return { ...item, renderedContent: content };
|
|
163
|
+
}
|
|
164
|
+
this.track(content.then((resolved) => this.dispatch({ type: "detailItemRendered", rowId, token, itemIndex, content: resolved }), (error) => this.dispatch({
|
|
165
|
+
type: "detailItemRendered",
|
|
166
|
+
rowId,
|
|
167
|
+
token,
|
|
168
|
+
itemIndex,
|
|
169
|
+
content: error instanceof Error ? `Error: ${error.message}` : "Error: detail failed"
|
|
170
|
+
})));
|
|
171
|
+
return { ...item, renderedContent: "Loading detail..." };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return {
|
|
175
|
+
...item,
|
|
176
|
+
renderedContent: error instanceof Error ? `Error: ${error.message}` : "Error: detail failed"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
this.dispatch({ type: "detailLoaded", rowId, token, items: preparedItems });
|
|
181
|
+
}
|
|
182
|
+
async persistOrder(orderedIds, previousRows, token) {
|
|
134
183
|
try {
|
|
135
184
|
await this.config.reorder?.onReorder(orderedIds, {
|
|
136
185
|
refresh: this.runtimeHandles.refresh,
|
|
@@ -139,7 +188,9 @@ class ExplorerRuntime {
|
|
|
139
188
|
}
|
|
140
189
|
catch (error) {
|
|
141
190
|
this.showToast(error instanceof Error ? error.message : "Could not persist order", "error");
|
|
142
|
-
|
|
191
|
+
if (token === this.reorderToken) {
|
|
192
|
+
this.dispatch({ type: "rowsLoaded", rows: previousRows });
|
|
193
|
+
}
|
|
143
194
|
}
|
|
144
195
|
}
|
|
145
196
|
async runActionEffect(effect) {
|
|
@@ -46,21 +46,18 @@ export function createInitialState(config, size) {
|
|
|
46
46
|
}
|
|
47
47
|
function createInitialActionState(config) {
|
|
48
48
|
const state = new Map();
|
|
49
|
-
for (const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
action: action,
|
|
62
|
-
source: "detail"
|
|
63
|
-
});
|
|
49
|
+
for (const [source, actions] of [["row", config.actions], ["detail", config.detail.actions ?? []]]) {
|
|
50
|
+
for (const action of actions) {
|
|
51
|
+
if (state.has(action.id)) {
|
|
52
|
+
throw new Error(`Duplicate explorer action id: ${action.id}`);
|
|
53
|
+
}
|
|
54
|
+
state.set(action.id, {
|
|
55
|
+
available: true,
|
|
56
|
+
label: typeof action.label === "function" ? action.id : action.label,
|
|
57
|
+
action: action,
|
|
58
|
+
source
|
|
59
|
+
});
|
|
60
|
+
}
|
|
64
61
|
}
|
|
65
62
|
return state;
|
|
66
63
|
}
|
|
@@ -17,7 +17,9 @@ export { formatCommandNotFound } from "./components/command-errors.js";
|
|
|
17
17
|
export { formatCommandNotFoundPanel } from "./components/command-errors.js";
|
|
18
18
|
export { renderTable } from "./components/table.js";
|
|
19
19
|
export type { TableColumn, RenderTableOptions } from "./components/table.js";
|
|
20
|
-
export {
|
|
20
|
+
export { renderDetailCard } from "./components/detail-card.js";
|
|
21
|
+
export type { DetailCardRow, DetailCardSection, RenderDetailCardOptions } from "./components/detail-card.js";
|
|
22
|
+
export { getTemplatePartialNames, renderTemplate, resolveTemplatePartials } from "./components/template.js";
|
|
21
23
|
export type { RenderTemplateOptions, TemplateEscape } from "./components/template.js";
|
|
22
24
|
export { openExternal } from "./components/browser.js";
|
|
23
25
|
export * as acp from "./acp/index.js";
|
|
@@ -14,7 +14,8 @@ export * as helpFormatterPlain from "./components/help-formatter-plain.js";
|
|
|
14
14
|
export { formatCommandNotFound } from "./components/command-errors.js";
|
|
15
15
|
export { formatCommandNotFoundPanel } from "./components/command-errors.js";
|
|
16
16
|
export { renderTable } from "./components/table.js";
|
|
17
|
-
export {
|
|
17
|
+
export { renderDetailCard } from "./components/detail-card.js";
|
|
18
|
+
export { getTemplatePartialNames, renderTemplate, resolveTemplatePartials } from "./components/template.js";
|
|
18
19
|
export { openExternal } from "./components/browser.js";
|
|
19
20
|
// ACP rendering
|
|
20
21
|
export * as acp from "./acp/index.js";
|
|
@@ -8,7 +8,8 @@ export function intro(title) {
|
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
10
|
if (format === "markdown") {
|
|
11
|
-
|
|
11
|
+
const safeTitle = stripAnsi(title).replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
12
|
+
process.stdout.write(`# ${safeTitle}\n\n`);
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
14
15
|
process.stdout.write(`${color.gray("┌")} ${text.intro(title)}\n`);
|
|
@@ -2,6 +2,9 @@ import { color } from "../../components/color.js";
|
|
|
2
2
|
import { symbols } from "../../components/symbols.js";
|
|
3
3
|
import { resolveOutputFormat } from "../../internal/output-format.js";
|
|
4
4
|
import { stripAnsi } from "../../internal/strip-ansi.js";
|
|
5
|
+
function renderMarkdownInline(value) {
|
|
6
|
+
return stripAnsi(value).replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
7
|
+
}
|
|
5
8
|
function writeTerminalMessage(msg, { symbol = color.gray("│"), secondarySymbol = color.gray("│"), spacing = 1, withGuide = true } = {}) {
|
|
6
9
|
const lines = [];
|
|
7
10
|
const showGuide = withGuide !== false;
|
|
@@ -35,7 +38,7 @@ function writeTerminalMessage(msg, { symbol = color.gray("│"), secondarySymbol
|
|
|
35
38
|
export function message(msg, options) {
|
|
36
39
|
const format = resolveOutputFormat();
|
|
37
40
|
if (format === "markdown") {
|
|
38
|
-
process.stdout.write(`- ${
|
|
41
|
+
process.stdout.write(`- ${renderMarkdownInline(msg)}\n`);
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
44
|
if (format === "json") {
|
|
@@ -47,7 +50,7 @@ export function message(msg, options) {
|
|
|
47
50
|
export function info(msg) {
|
|
48
51
|
const format = resolveOutputFormat();
|
|
49
52
|
if (format === "markdown") {
|
|
50
|
-
process.stdout.write(`- **info:** ${
|
|
53
|
+
process.stdout.write(`- **info:** ${renderMarkdownInline(msg)}\n`);
|
|
51
54
|
return;
|
|
52
55
|
}
|
|
53
56
|
if (format === "json") {
|
|
@@ -59,7 +62,7 @@ export function info(msg) {
|
|
|
59
62
|
export function success(msg) {
|
|
60
63
|
const format = resolveOutputFormat();
|
|
61
64
|
if (format === "markdown") {
|
|
62
|
-
process.stdout.write(`- **success:** ${
|
|
65
|
+
process.stdout.write(`- **success:** ${renderMarkdownInline(msg)}\n`);
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
68
|
if (format === "json") {
|
|
@@ -71,7 +74,7 @@ export function success(msg) {
|
|
|
71
74
|
export function warn(msg) {
|
|
72
75
|
const format = resolveOutputFormat();
|
|
73
76
|
if (format === "markdown") {
|
|
74
|
-
process.stdout.write(`- **warning:** ${
|
|
77
|
+
process.stdout.write(`- **warning:** ${renderMarkdownInline(msg)}\n`);
|
|
75
78
|
return;
|
|
76
79
|
}
|
|
77
80
|
if (format === "json") {
|
|
@@ -83,7 +86,7 @@ export function warn(msg) {
|
|
|
83
86
|
export function error(msg) {
|
|
84
87
|
const format = resolveOutputFormat();
|
|
85
88
|
if (format === "markdown") {
|
|
86
|
-
process.stdout.write(`- **error:** ${
|
|
89
|
+
process.stdout.write(`- **error:** ${renderMarkdownInline(msg)}\n`);
|
|
87
90
|
return;
|
|
88
91
|
}
|
|
89
92
|
if (format === "json") {
|
|
@@ -19,7 +19,7 @@ function renderTerminalNote(message, title) {
|
|
|
19
19
|
export function note(message, title) {
|
|
20
20
|
const format = resolveOutputFormat();
|
|
21
21
|
const strippedMessage = stripAnsi(message);
|
|
22
|
-
const strippedTitle = stripAnsi(title ?? "");
|
|
22
|
+
const strippedTitle = stripAnsi(title ?? "").replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
23
23
|
if (format === "markdown") {
|
|
24
24
|
const lines = strippedMessage.split("\n");
|
|
25
25
|
const heading = strippedTitle ? `> **${strippedTitle}**\n` : "";
|