veryfront 0.0.82 → 0.0.84
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 +18 -17
- package/esm/deno.js +1 -1
- package/esm/proxy/cache/index.d.ts +41 -0
- package/esm/proxy/cache/index.d.ts.map +1 -0
- package/esm/proxy/cache/index.js +75 -0
- package/esm/proxy/cache/memory-cache.d.ts +18 -0
- package/esm/proxy/cache/memory-cache.d.ts.map +1 -0
- package/esm/proxy/cache/memory-cache.js +100 -0
- package/esm/proxy/cache/redis-cache.d.ts +27 -0
- package/esm/proxy/cache/redis-cache.d.ts.map +1 -0
- package/esm/proxy/cache/redis-cache.js +183 -0
- package/esm/proxy/cache/resilient-cache.d.ts +44 -0
- package/esm/proxy/cache/resilient-cache.d.ts.map +1 -0
- package/esm/proxy/cache/resilient-cache.js +178 -0
- package/esm/proxy/cache/types.d.ts +65 -0
- package/esm/proxy/cache/types.d.ts.map +1 -0
- package/esm/proxy/cache/types.js +7 -0
- package/esm/proxy/handler.d.ts +81 -0
- package/esm/proxy/handler.d.ts.map +1 -0
- package/esm/proxy/handler.js +417 -0
- package/esm/proxy/logger.d.ts +29 -0
- package/esm/proxy/logger.d.ts.map +1 -0
- package/esm/proxy/logger.js +258 -0
- package/esm/proxy/oauth-client.d.ts +15 -0
- package/esm/proxy/oauth-client.d.ts.map +1 -0
- package/esm/proxy/oauth-client.js +52 -0
- package/esm/proxy/token-manager.d.ts +59 -0
- package/esm/proxy/token-manager.d.ts.map +1 -0
- package/esm/proxy/token-manager.js +125 -0
- package/esm/proxy/tracing.d.ts +39 -0
- package/esm/proxy/tracing.d.ts.map +1 -0
- package/esm/proxy/tracing.js +194 -0
- package/esm/src/cache/backend.d.ts +2 -0
- package/esm/src/cache/backend.d.ts.map +1 -1
- package/esm/src/cache/backend.js +2 -0
- package/esm/src/cache/cache-key-builder.d.ts +0 -4
- package/esm/src/cache/cache-key-builder.d.ts.map +1 -1
- package/esm/src/cache/cache-key-builder.js +0 -6
- package/esm/src/cache/multi-tier.d.ts +0 -29
- package/esm/src/cache/multi-tier.d.ts.map +1 -1
- package/esm/src/cache/multi-tier.js +0 -26
- package/esm/src/cli/app/actions.d.ts +26 -0
- package/esm/src/cli/app/actions.d.ts.map +1 -0
- package/esm/src/cli/app/actions.js +152 -0
- package/esm/src/cli/app/components/inline-input.d.ts +35 -0
- package/esm/src/cli/app/components/inline-input.d.ts.map +1 -0
- package/esm/src/cli/app/components/inline-input.js +220 -0
- package/esm/src/cli/app/components/list-select.d.ts +69 -0
- package/esm/src/cli/app/components/list-select.d.ts.map +1 -0
- package/esm/src/cli/app/components/list-select.js +137 -0
- package/esm/src/cli/app/index.d.ts +45 -0
- package/esm/src/cli/app/index.d.ts.map +1 -0
- package/esm/src/cli/app/index.js +1252 -0
- package/esm/src/cli/app/state.d.ts +122 -0
- package/esm/src/cli/app/state.d.ts.map +1 -0
- package/esm/src/cli/app/state.js +232 -0
- package/esm/src/cli/app/views/dashboard.d.ts +19 -0
- package/esm/src/cli/app/views/dashboard.d.ts.map +1 -0
- package/esm/src/cli/app/views/dashboard.js +178 -0
- package/esm/src/cli/commands/dev.js +2 -2
- package/esm/src/cli/commands/new.js +1 -1
- package/esm/src/cli/index/command-router.d.ts.map +1 -1
- package/esm/src/cli/index/command-router.js +9 -39
- package/esm/src/cli/index/start-handler.d.ts +3 -0
- package/esm/src/cli/index/start-handler.d.ts.map +1 -0
- package/esm/src/cli/index/start-handler.js +145 -0
- package/esm/src/cli/mcp/index.d.ts +11 -0
- package/esm/src/cli/mcp/index.d.ts.map +1 -0
- package/esm/src/cli/mcp/index.js +10 -0
- package/esm/src/cli/ui/tui.js +1 -1
- package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts +2 -0
- package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts.map +1 -1
- package/esm/src/middleware/builtin/security/redis-rate-limit.js +23 -9
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts +10 -0
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.js +30 -42
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/loader.js +34 -13
- package/esm/src/platform/adapters/fs/cache/file-cache.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/cache/file-cache.js +9 -3
- package/esm/src/server/context/cache-invalidation.d.ts.map +1 -1
- package/esm/src/server/context/cache-invalidation.js +4 -0
- package/esm/src/server/handlers/dev/dashboard/api.js +4 -0
- package/esm/src/server/handlers/dev/projects/ui-handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/projects/ui-handler.js +6 -0
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +139 -64
- package/esm/src/utils/index.d.ts +1 -1
- package/esm/src/utils/index.d.ts.map +1 -1
- package/esm/src/utils/index.js +1 -1
- package/package.json +2 -1
- package/src/deno.js +1 -1
- package/src/proxy/cache/index.ts +93 -0
- package/src/proxy/cache/memory-cache.ts +120 -0
- package/src/proxy/cache/redis-cache.ts +203 -0
- package/src/proxy/cache/resilient-cache.ts +205 -0
- package/src/proxy/cache/types.ts +72 -0
- package/src/proxy/handler.ts +593 -0
- package/src/proxy/logger.ts +329 -0
- package/src/proxy/oauth-client.ts +91 -0
- package/src/proxy/token-manager.ts +174 -0
- package/src/proxy/tracing.ts +237 -0
- package/src/src/cache/backend.ts +3 -0
- package/src/src/cache/cache-key-builder.ts +0 -9
- package/src/src/cache/multi-tier.ts +0 -41
- package/src/src/cli/app/actions.ts +190 -0
- package/src/src/cli/app/components/inline-input.ts +255 -0
- package/src/src/cli/app/components/list-select.ts +215 -0
- package/src/src/cli/app/index.ts +1471 -0
- package/src/src/cli/app/state.ts +385 -0
- package/src/src/cli/app/views/dashboard.ts +212 -0
- package/src/src/cli/commands/dev.ts +2 -2
- package/src/src/cli/commands/new.ts +1 -1
- package/src/src/cli/index/command-router.ts +9 -40
- package/src/src/cli/index/start-handler.ts +195 -0
- package/src/src/cli/mcp/index.ts +11 -0
- package/src/src/cli/ui/tui.ts +1 -1
- package/src/src/middleware/builtin/security/redis-rate-limit.ts +24 -11
- package/src/src/modules/react-loader/ssr-module-loader/cache/redis.ts +36 -50
- package/src/src/modules/react-loader/ssr-module-loader/loader.ts +38 -14
- package/src/src/platform/adapters/fs/cache/file-cache.ts +9 -3
- package/src/src/server/context/cache-invalidation.ts +4 -0
- package/src/src/server/handlers/dev/dashboard/api.ts +2 -0
- package/src/src/server/handlers/dev/projects/ui-handler.ts +6 -0
- package/src/src/transforms/esm/http-cache.ts +148 -73
- package/src/src/utils/index.ts +0 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/****
|
|
2
|
+
* Inline Text Input Component
|
|
3
|
+
*
|
|
4
|
+
* Renders an input prompt at the bottom of the TUI that stays inline
|
|
5
|
+
* without exiting alternate screen mode.
|
|
6
|
+
*/
|
|
7
|
+
import { brand, dim, muted } from "../../ui/colors.js";
|
|
8
|
+
/**
|
|
9
|
+
* Render the inline input prompt
|
|
10
|
+
*/
|
|
11
|
+
export function renderInput(input, _options = {}) {
|
|
12
|
+
if (!input.active)
|
|
13
|
+
return "";
|
|
14
|
+
// Build the input line with cursor
|
|
15
|
+
const prompt = ` ${brand(">")} ${input.prompt}: `;
|
|
16
|
+
const beforeCursor = input.value.slice(0, input.cursorPos);
|
|
17
|
+
const cursorChar = input.value[input.cursorPos] ?? " ";
|
|
18
|
+
const afterCursor = input.value.slice(input.cursorPos + 1);
|
|
19
|
+
// Cursor is rendered as inverse video
|
|
20
|
+
const cursor = `\x1b[7m${cursorChar}\x1b[27m`;
|
|
21
|
+
const inputLine = `${prompt}${beforeCursor}${cursor}${afterCursor}`;
|
|
22
|
+
// Hint line
|
|
23
|
+
const hintLine = ` ${dim("Enter")} ${muted("to submit")} ${dim("Esc")} ${muted("to cancel")}`;
|
|
24
|
+
return `${inputLine}\n${hintLine}`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Render the logs area with optional scrolling
|
|
28
|
+
*/
|
|
29
|
+
export function renderLogs(logs, options = {}) {
|
|
30
|
+
const { maxLines = 5, maxWidth = 80, scroll = 0, expanded = false } = options;
|
|
31
|
+
if (logs.length === 0)
|
|
32
|
+
return "";
|
|
33
|
+
const visibleLines = expanded ? Math.max(maxLines, 15) : maxLines;
|
|
34
|
+
const end = logs.length - scroll;
|
|
35
|
+
const start = Math.max(0, end - visibleLines);
|
|
36
|
+
const visibleLogs = logs.slice(start, end);
|
|
37
|
+
const lines = [];
|
|
38
|
+
for (const log of visibleLogs) {
|
|
39
|
+
const time = formatTime(log.time);
|
|
40
|
+
const levelColor = getLevelColor(log.level);
|
|
41
|
+
const levelPrefix = getLevelPrefix(log.level);
|
|
42
|
+
if (expanded) {
|
|
43
|
+
// When expanded, show structured info if available
|
|
44
|
+
if (log.meta?.method) {
|
|
45
|
+
// Request log - show all details in clean format
|
|
46
|
+
const meta = log.meta;
|
|
47
|
+
const statusColor = getStatusColor(meta.status || 200);
|
|
48
|
+
const methodStr = (meta.method || "GET").padEnd(7);
|
|
49
|
+
const pathStr = meta.path || "/";
|
|
50
|
+
const statusStr = String(meta.status || 200);
|
|
51
|
+
const durationStr = `${meta.durationMs || 0}ms`.padStart(6);
|
|
52
|
+
// Line 1: time + method + path
|
|
53
|
+
lines.push(` ${dim(time)} ${levelColor(levelPrefix)} ${methodStr}${pathStr}`);
|
|
54
|
+
// Line 2: status + duration + project info
|
|
55
|
+
const projectInfo = [];
|
|
56
|
+
if (meta.project)
|
|
57
|
+
projectInfo.push(brand(meta.project));
|
|
58
|
+
if (meta.env)
|
|
59
|
+
projectInfo.push(dim(meta.env));
|
|
60
|
+
if (meta.releaseId)
|
|
61
|
+
projectInfo.push(dim(`#${meta.releaseId.slice(0, 8)}`));
|
|
62
|
+
lines.push(` ${"".padEnd(12)}${statusColor(statusStr)} ${dim(durationStr)}${projectInfo.length ? ` ${projectInfo.join(" ")}` : ""}`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Regular log - show full message (may wrap to multiple lines)
|
|
66
|
+
const prefix = ` ${dim(time)} ${levelColor(levelPrefix)} `;
|
|
67
|
+
const msgLines = wrapText(log.message, maxWidth - 15);
|
|
68
|
+
lines.push(`${prefix}${msgLines[0] || ""}`);
|
|
69
|
+
// Indent continuation lines
|
|
70
|
+
for (let i = 1; i < msgLines.length; i++) {
|
|
71
|
+
lines.push(` ${"".padEnd(12)}${msgLines[i]}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// When collapsed, truncate
|
|
77
|
+
const maxMsgLen = maxWidth - 15;
|
|
78
|
+
const msg = log.message.length > maxMsgLen
|
|
79
|
+
? `${log.message.slice(0, maxMsgLen - 3)}...`
|
|
80
|
+
: log.message;
|
|
81
|
+
lines.push(` ${dim(time)} ${levelColor(levelPrefix)} ${msg}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (expanded && logs.length > visibleLines) {
|
|
85
|
+
const canScrollUp = start > 0;
|
|
86
|
+
const canScrollDown = scroll > 0;
|
|
87
|
+
const scrollHint = [];
|
|
88
|
+
if (canScrollUp)
|
|
89
|
+
scrollHint.push("↑");
|
|
90
|
+
if (canScrollDown)
|
|
91
|
+
scrollHint.push("↓");
|
|
92
|
+
if (scrollHint.length > 0) {
|
|
93
|
+
lines.push(` ${dim(`[${scrollHint.join(" ")}] ${logs.length} total`)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
function wrapText(text, maxWidth) {
|
|
99
|
+
if (text.length <= maxWidth)
|
|
100
|
+
return [text];
|
|
101
|
+
const lines = [];
|
|
102
|
+
let remaining = text;
|
|
103
|
+
while (remaining.length > maxWidth) {
|
|
104
|
+
let breakPoint = remaining.lastIndexOf(" ", maxWidth);
|
|
105
|
+
if (breakPoint <= 0)
|
|
106
|
+
breakPoint = maxWidth;
|
|
107
|
+
lines.push(remaining.slice(0, breakPoint));
|
|
108
|
+
remaining = remaining.slice(breakPoint).trimStart();
|
|
109
|
+
}
|
|
110
|
+
if (remaining)
|
|
111
|
+
lines.push(remaining);
|
|
112
|
+
return lines;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format time as HH:MM:SS
|
|
116
|
+
*/
|
|
117
|
+
function formatTime(date) {
|
|
118
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
119
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
120
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
121
|
+
return `${h}:${m}:${s}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get color function for log level
|
|
125
|
+
*/
|
|
126
|
+
function getLevelColor(level) {
|
|
127
|
+
switch (level) {
|
|
128
|
+
case "error":
|
|
129
|
+
return (s) => `\x1b[31m${s}\x1b[0m`; // red
|
|
130
|
+
case "warn":
|
|
131
|
+
return (s) => `\x1b[33m${s}\x1b[0m`; // yellow
|
|
132
|
+
case "info":
|
|
133
|
+
return (s) => `\x1b[36m${s}\x1b[0m`; // cyan
|
|
134
|
+
case "debug":
|
|
135
|
+
return dim;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get color function for HTTP status code
|
|
140
|
+
*/
|
|
141
|
+
function getStatusColor(status) {
|
|
142
|
+
if (status >= 500)
|
|
143
|
+
return (s) => `\x1b[31m${s}\x1b[0m`; // red
|
|
144
|
+
if (status >= 400)
|
|
145
|
+
return (s) => `\x1b[33m${s}\x1b[0m`; // yellow
|
|
146
|
+
if (status >= 300)
|
|
147
|
+
return (s) => `\x1b[36m${s}\x1b[0m`; // cyan
|
|
148
|
+
return (s) => `\x1b[32m${s}\x1b[0m`; // green
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get prefix for log level
|
|
152
|
+
*/
|
|
153
|
+
function getLevelPrefix(level) {
|
|
154
|
+
switch (level) {
|
|
155
|
+
case "error":
|
|
156
|
+
return "ERR";
|
|
157
|
+
case "warn":
|
|
158
|
+
return "WRN";
|
|
159
|
+
case "info":
|
|
160
|
+
return "INF";
|
|
161
|
+
case "debug":
|
|
162
|
+
return "DBG";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Handle input key press
|
|
167
|
+
* Returns the new value and cursor position, or null if the key should end input
|
|
168
|
+
*/
|
|
169
|
+
export function handleInputKey(key, value, cursorPos) {
|
|
170
|
+
if (key === "\r" || key === "\n")
|
|
171
|
+
return { action: "submit" };
|
|
172
|
+
if (key === "\x1b" || key === "\x03")
|
|
173
|
+
return { action: "cancel" };
|
|
174
|
+
if (key === "\x7f" || key === "\b") {
|
|
175
|
+
if (cursorPos === 0)
|
|
176
|
+
return { value, cursorPos };
|
|
177
|
+
return {
|
|
178
|
+
value: value.slice(0, cursorPos - 1) + value.slice(cursorPos),
|
|
179
|
+
cursorPos: cursorPos - 1,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (key === "\x1b[3~") {
|
|
183
|
+
if (cursorPos >= value.length)
|
|
184
|
+
return { value, cursorPos };
|
|
185
|
+
return {
|
|
186
|
+
value: value.slice(0, cursorPos) + value.slice(cursorPos + 1),
|
|
187
|
+
cursorPos,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (key === "\x1b[D")
|
|
191
|
+
return { value, cursorPos: Math.max(0, cursorPos - 1) };
|
|
192
|
+
if (key === "\x1b[C")
|
|
193
|
+
return { value, cursorPos: Math.min(value.length, cursorPos + 1) };
|
|
194
|
+
if (key === "\x01" || key === "\x1b[H")
|
|
195
|
+
return { value, cursorPos: 0 };
|
|
196
|
+
if (key === "\x05" || key === "\x1b[F")
|
|
197
|
+
return { value, cursorPos: value.length };
|
|
198
|
+
if (key === "\x15")
|
|
199
|
+
return { value: "", cursorPos: 0 };
|
|
200
|
+
if (key === "\x17") {
|
|
201
|
+
if (cursorPos === 0)
|
|
202
|
+
return { value, cursorPos };
|
|
203
|
+
let newPos = cursorPos - 1;
|
|
204
|
+
while (newPos > 0 && value[newPos] === " ")
|
|
205
|
+
newPos--;
|
|
206
|
+
while (newPos > 0 && value[newPos - 1] !== " ")
|
|
207
|
+
newPos--;
|
|
208
|
+
return {
|
|
209
|
+
value: value.slice(0, newPos) + value.slice(cursorPos),
|
|
210
|
+
cursorPos: newPos,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (key.length === 1 && key >= " " && key <= "~") {
|
|
214
|
+
return {
|
|
215
|
+
value: value.slice(0, cursorPos) + key + value.slice(cursorPos),
|
|
216
|
+
cursorPos: cursorPos + 1,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return { value, cursorPos };
|
|
220
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive List Select Component
|
|
3
|
+
*
|
|
4
|
+
* Keyboard-navigable list with selection support.
|
|
5
|
+
* Supports arrow keys, j/k vim bindings, and number shortcuts.
|
|
6
|
+
*/
|
|
7
|
+
export interface ListItem<T = unknown> {
|
|
8
|
+
/** Unique identifier */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Display label */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Optional description */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Optional path or metadata */
|
|
15
|
+
meta?: string;
|
|
16
|
+
/** Associated data */
|
|
17
|
+
data?: T;
|
|
18
|
+
}
|
|
19
|
+
export interface ListSelectOptions {
|
|
20
|
+
/** Maximum width for the list */
|
|
21
|
+
maxWidth?: number;
|
|
22
|
+
/** Number of visible items (for scrolling) */
|
|
23
|
+
visibleCount?: number;
|
|
24
|
+
/** Show number shortcuts (1-9) */
|
|
25
|
+
showNumbers?: boolean;
|
|
26
|
+
/** Offset for number shortcuts (e.g., 1 means start at [2]) */
|
|
27
|
+
numberOffset?: number;
|
|
28
|
+
/** Empty state message */
|
|
29
|
+
emptyMessage?: string;
|
|
30
|
+
/** Show selection cursor (default true). Set false for inactive sections */
|
|
31
|
+
showSelection?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface ListSelectState<T = unknown> {
|
|
34
|
+
/** All items in the list */
|
|
35
|
+
items: ListItem<T>[];
|
|
36
|
+
/** Currently selected index */
|
|
37
|
+
selectedIndex: number;
|
|
38
|
+
/** Scroll offset for long lists */
|
|
39
|
+
scrollOffset: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create initial list state
|
|
43
|
+
*/
|
|
44
|
+
export declare function createListState<T>(items: ListItem<T>[]): ListSelectState<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Move selection up
|
|
47
|
+
*/
|
|
48
|
+
export declare function moveUp<T>(state: ListSelectState<T>): ListSelectState<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Move selection down
|
|
51
|
+
*/
|
|
52
|
+
export declare function moveDown<T>(state: ListSelectState<T>, visibleCount?: number): ListSelectState<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Select item by number (1-9)
|
|
55
|
+
*/
|
|
56
|
+
export declare function selectByNumber<T>(state: ListSelectState<T>, num: number): ListSelectState<T>;
|
|
57
|
+
/**
|
|
58
|
+
* Get currently selected item
|
|
59
|
+
*/
|
|
60
|
+
export declare function getSelectedItem<T>(state: ListSelectState<T>): ListItem<T> | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Render the list as a string
|
|
63
|
+
*/
|
|
64
|
+
export declare function renderList<T>(state: ListSelectState<T>, options?: ListSelectOptions): string;
|
|
65
|
+
/**
|
|
66
|
+
* Create a list section with title
|
|
67
|
+
*/
|
|
68
|
+
export declare function listSection<T>(title: string, state: ListSelectState<T>, options?: ListSelectOptions): string;
|
|
69
|
+
//# sourceMappingURL=list-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-select.d.ts","sourceRoot":"","sources":["../../../../../src/src/cli/app/components/list-select.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACnC,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAM3E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAQvE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,YAAY,SAAK,GAChB,eAAe,CAAC,CAAC,CAAC,CAcpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,GAAG,EAAE,MAAM,GACV,eAAe,CAAC,CAAC,CAAC,CAIpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GACxB,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,CAEzB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAkFR;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAGR"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive List Select Component
|
|
3
|
+
*
|
|
4
|
+
* Keyboard-navigable list with selection support.
|
|
5
|
+
* Supports arrow keys, j/k vim bindings, and number shortcuts.
|
|
6
|
+
*/
|
|
7
|
+
import { brand, dim } from "../../ui/colors.js";
|
|
8
|
+
import { truncate } from "../../ui/layout.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create initial list state
|
|
11
|
+
*/
|
|
12
|
+
export function createListState(items) {
|
|
13
|
+
return {
|
|
14
|
+
items,
|
|
15
|
+
selectedIndex: 0,
|
|
16
|
+
scrollOffset: 0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Move selection up
|
|
21
|
+
*/
|
|
22
|
+
export function moveUp(state) {
|
|
23
|
+
if (state.items.length === 0)
|
|
24
|
+
return state;
|
|
25
|
+
const newIndex = state.selectedIndex > 0 ? state.selectedIndex - 1 : state.items.length - 1;
|
|
26
|
+
const scrollOffset = newIndex < state.scrollOffset ? newIndex : state.scrollOffset;
|
|
27
|
+
return { ...state, selectedIndex: newIndex, scrollOffset };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Move selection down
|
|
31
|
+
*/
|
|
32
|
+
export function moveDown(state, visibleCount = 10) {
|
|
33
|
+
if (state.items.length === 0)
|
|
34
|
+
return state;
|
|
35
|
+
const newIndex = state.selectedIndex < state.items.length - 1 ? state.selectedIndex + 1 : 0;
|
|
36
|
+
let scrollOffset = state.scrollOffset;
|
|
37
|
+
if (newIndex === 0) {
|
|
38
|
+
scrollOffset = 0;
|
|
39
|
+
}
|
|
40
|
+
else if (newIndex >= scrollOffset + visibleCount) {
|
|
41
|
+
scrollOffset = newIndex - visibleCount + 1;
|
|
42
|
+
}
|
|
43
|
+
return { ...state, selectedIndex: newIndex, scrollOffset };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Select item by number (1-9)
|
|
47
|
+
*/
|
|
48
|
+
export function selectByNumber(state, num) {
|
|
49
|
+
const index = num - 1;
|
|
50
|
+
if (index < 0 || index >= state.items.length)
|
|
51
|
+
return state;
|
|
52
|
+
return { ...state, selectedIndex: index };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get currently selected item
|
|
56
|
+
*/
|
|
57
|
+
export function getSelectedItem(state) {
|
|
58
|
+
return state.items[state.selectedIndex];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Render the list as a string
|
|
62
|
+
*/
|
|
63
|
+
export function renderList(state, options = {}) {
|
|
64
|
+
const { maxWidth = 60, visibleCount = 10, showNumbers = true, numberOffset = 0, emptyMessage = "No items", showSelection = true, } = options;
|
|
65
|
+
if (state.items.length === 0)
|
|
66
|
+
return ` ${dim(emptyMessage)}`;
|
|
67
|
+
const lines = [];
|
|
68
|
+
const start = state.scrollOffset;
|
|
69
|
+
const end = Math.min(start + visibleCount, state.items.length);
|
|
70
|
+
const visibleItems = state.items.slice(start, end);
|
|
71
|
+
const numberWidth = showNumbers ? 4 : 0; // " [1] "
|
|
72
|
+
const cursorWidth = 2; // "› " or " "
|
|
73
|
+
const prefixWidth = numberWidth + cursorWidth;
|
|
74
|
+
for (let i = 0; i < visibleItems.length; i++) {
|
|
75
|
+
const item = visibleItems[i];
|
|
76
|
+
if (!item)
|
|
77
|
+
continue;
|
|
78
|
+
const actualIndex = start + i;
|
|
79
|
+
const isSelected = showSelection && actualIndex === state.selectedIndex;
|
|
80
|
+
const displayNum = actualIndex + 1 + numberOffset;
|
|
81
|
+
const parts = [];
|
|
82
|
+
parts.push(isSelected ? brand("›") : " ", " ");
|
|
83
|
+
if (showNumbers) {
|
|
84
|
+
if (displayNum <= 35) {
|
|
85
|
+
const shortcut = displayNum <= 9
|
|
86
|
+
? String(displayNum)
|
|
87
|
+
: String.fromCharCode(96 + displayNum - 9); // 10='a', 11='b', etc.
|
|
88
|
+
parts.push(isSelected ? brand(`[${shortcut}]`) : dim(`[${shortcut}]`), " ");
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
parts.push(" ");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Render label, then use remaining space for meta
|
|
95
|
+
const labelText = item.label;
|
|
96
|
+
const availableForContent = maxWidth - prefixWidth;
|
|
97
|
+
if (item.meta) {
|
|
98
|
+
// Split space between label and meta dynamically
|
|
99
|
+
const metaText = item.meta;
|
|
100
|
+
const totalNeeded = labelText.length + 1 + metaText.length; // 1 for space
|
|
101
|
+
if (totalNeeded <= availableForContent) {
|
|
102
|
+
// Both fit - no truncation needed
|
|
103
|
+
parts.push(isSelected ? labelText : dim(labelText));
|
|
104
|
+
const padding = availableForContent - labelText.length - metaText.length;
|
|
105
|
+
parts.push(" ".repeat(Math.max(1, padding)), dim(metaText));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Need to truncate - prioritize label, give rest to meta
|
|
109
|
+
const labelMax = Math.min(labelText.length, Math.floor(availableForContent * 0.4));
|
|
110
|
+
const metaMax = availableForContent - labelMax - 1;
|
|
111
|
+
const label = truncate(labelText, labelMax);
|
|
112
|
+
parts.push(isSelected ? label : dim(label));
|
|
113
|
+
parts.push(" ", dim(truncate(metaText, metaMax)));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const label = truncate(labelText, availableForContent);
|
|
118
|
+
parts.push(isSelected ? label : dim(label));
|
|
119
|
+
}
|
|
120
|
+
lines.push(parts.join(""));
|
|
121
|
+
if (isSelected && item.description) {
|
|
122
|
+
lines.push(` ${dim(truncate(item.description, maxWidth - 5))}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (start > 0)
|
|
126
|
+
lines.unshift(` ${dim("↑ more above")}`);
|
|
127
|
+
if (end < state.items.length)
|
|
128
|
+
lines.push(` ${dim("↓ more below")}`);
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a list section with title
|
|
133
|
+
*/
|
|
134
|
+
export function listSection(title, state, options = {}) {
|
|
135
|
+
const header = ` ${dim(title)} ${dim(`(${state.items.length})`)}`;
|
|
136
|
+
return `${header}\n${renderList(state, options)}`;
|
|
137
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type AppState, type StateUpdater } from "./state.js";
|
|
2
|
+
export interface AppConfig {
|
|
3
|
+
port: number;
|
|
4
|
+
projects: Map<string, string>;
|
|
5
|
+
examples?: Map<string, string>;
|
|
6
|
+
defaultProject?: string;
|
|
7
|
+
mcpPort?: number;
|
|
8
|
+
/** Force headless mode (no TUI) for coding agents */
|
|
9
|
+
headless?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface App {
|
|
12
|
+
/** Start the app */
|
|
13
|
+
start(): void;
|
|
14
|
+
/** Stop the app and restore terminal */
|
|
15
|
+
stop(): void;
|
|
16
|
+
/** Update state */
|
|
17
|
+
update(updater: StateUpdater): void;
|
|
18
|
+
/** Get current state */
|
|
19
|
+
getState(): AppState;
|
|
20
|
+
/** Render the current view */
|
|
21
|
+
render(): void;
|
|
22
|
+
/** Set server ready */
|
|
23
|
+
setServerReady(): void;
|
|
24
|
+
/** Add an error */
|
|
25
|
+
addError(): void;
|
|
26
|
+
/** Clear errors */
|
|
27
|
+
clearErrors(): void;
|
|
28
|
+
/** Add a log entry to the logs area */
|
|
29
|
+
log(level: "info" | "warn" | "error" | "debug", message: string): void;
|
|
30
|
+
/** Intercept console output and route to TUI logs */
|
|
31
|
+
interceptConsole(): () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create the CLI app
|
|
35
|
+
*/
|
|
36
|
+
export declare function createApp(config: AppConfig): App;
|
|
37
|
+
/**
|
|
38
|
+
* Show startup animation
|
|
39
|
+
*/
|
|
40
|
+
export declare function showStartup(steps: string[]): Promise<void>;
|
|
41
|
+
export type { AppState } from "./state.js";
|
|
42
|
+
export * from "./state.js";
|
|
43
|
+
export * from "./actions.js";
|
|
44
|
+
export * from "./components/list-select.js";
|
|
45
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/cli/app/index.ts"],"names":[],"mappings":"AA2BA,OAAO,EAEL,KAAK,QAAQ,EAcb,KAAK,YAAY,EAOlB,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,GAAG;IAClB,oBAAoB;IACpB,KAAK,IAAI,IAAI,CAAC;IACd,wCAAwC;IACxC,IAAI,IAAI,IAAI,CAAC;IACb,mBAAmB;IACnB,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,wBAAwB;IACxB,QAAQ,IAAI,QAAQ,CAAC;IACrB,8BAA8B;IAC9B,MAAM,IAAI,IAAI,CAAC;IACf,uBAAuB;IACvB,cAAc,IAAI,IAAI,CAAC;IACvB,mBAAmB;IACnB,QAAQ,IAAI,IAAI,CAAC;IACjB,mBAAmB;IACnB,WAAW,IAAI,IAAI,CAAC;IACpB,uCAAuC;IACvC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvE,qDAAqD;IACrD,gBAAgB,IAAI,MAAM,IAAI,CAAC;CAChC;AAuMD;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,GAAG,CAymChD;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuChE;AAED,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC"}
|