@gridland/demo 0.2.27 → 0.2.29
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/run.js +3 -4
- package/package.json +2 -2
- package/dist/chunk-KTMDWS5B.js +0 -2
- package/dist/chunk-QGM4M3NI.js +0 -37
- package/dist/chunk-R3ENBVV6.js +0 -2140
- package/dist/chunk-SQ5UVJ3G.js +0 -2146
- package/dist/chunk-bdqvmfwv-A3Y47IW3.js +0 -16736
- package/dist/wrapper-SDVXX5XU.js +0 -3642
package/dist/chunk-SQ5UVJ3G.js
DELETED
|
@@ -1,2146 +0,0 @@
|
|
|
1
|
-
// ../docs/components/landing/landing-app.tsx
|
|
2
|
-
import { useState as useState10 } from "react";
|
|
3
|
-
|
|
4
|
-
// ../ui/components/theme/themes.ts
|
|
5
|
-
var darkTheme = {
|
|
6
|
-
primary: "#FF71CE",
|
|
7
|
-
accent: "#01CDFE",
|
|
8
|
-
secondary: "#B967FF",
|
|
9
|
-
muted: "#A69CBD",
|
|
10
|
-
placeholder: "#CEC7DE",
|
|
11
|
-
border: "#B967FF",
|
|
12
|
-
foreground: "#F0E6FF",
|
|
13
|
-
background: "#0D0B10",
|
|
14
|
-
success: "#05FFA1",
|
|
15
|
-
error: "#FF6B6B",
|
|
16
|
-
warning: "#FFC164"
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// ../ui/components/theme/theme-context.tsx
|
|
20
|
-
import { createContext, useContext } from "react";
|
|
21
|
-
import { jsx } from "react/jsx-runtime";
|
|
22
|
-
var ThemeContext = createContext(darkTheme);
|
|
23
|
-
function useTheme() {
|
|
24
|
-
return useContext(ThemeContext);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ../ui/components/link/link.tsx
|
|
28
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
29
|
-
var UNDERLINE = 1 << 3;
|
|
30
|
-
var UNDERLINE_DASHED = 1 << 4;
|
|
31
|
-
var UNDERLINE_DOTTED = 1 << 6;
|
|
32
|
-
function Link({ children, url, underline = "solid", color }) {
|
|
33
|
-
const theme = useTheme();
|
|
34
|
-
const resolvedColor = color ?? theme.accent;
|
|
35
|
-
let attributes = 0;
|
|
36
|
-
if (underline === "solid") {
|
|
37
|
-
attributes = UNDERLINE;
|
|
38
|
-
} else if (underline === "dashed") {
|
|
39
|
-
attributes = UNDERLINE | UNDERLINE_DASHED;
|
|
40
|
-
} else if (underline === "dotted") {
|
|
41
|
-
attributes = UNDERLINE | UNDERLINE_DOTTED;
|
|
42
|
-
}
|
|
43
|
-
return /* @__PURE__ */ jsx2("text", { children: /* @__PURE__ */ jsx2("a", { href: url, style: { attributes, fg: resolvedColor }, children }) });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ../ui/components/link/link-demo.tsx
|
|
47
|
-
import { useState } from "react";
|
|
48
|
-
|
|
49
|
-
// ../ui/components/text-style.ts
|
|
50
|
-
var BOLD = 1 << 0;
|
|
51
|
-
var DIM = 1 << 1;
|
|
52
|
-
var ITALIC = 1 << 2;
|
|
53
|
-
var UNDERLINE2 = 1 << 3;
|
|
54
|
-
var INVERSE = 1 << 5;
|
|
55
|
-
function textStyle(opts) {
|
|
56
|
-
let attributes = 0;
|
|
57
|
-
if (opts.bold) attributes |= BOLD;
|
|
58
|
-
if (opts.dim) attributes |= DIM;
|
|
59
|
-
if (opts.italic) attributes |= ITALIC;
|
|
60
|
-
if (opts.underline) attributes |= UNDERLINE2;
|
|
61
|
-
if (opts.inverse) attributes |= INVERSE;
|
|
62
|
-
const result = {};
|
|
63
|
-
if (opts.fg) result.fg = opts.fg;
|
|
64
|
-
if (opts.bg) result.bg = opts.bg;
|
|
65
|
-
if (attributes) result.attributes = attributes;
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// ../ui/components/status-bar/status-bar.tsx
|
|
70
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
71
|
-
function StatusBar({ items, extra }) {
|
|
72
|
-
const theme = useTheme();
|
|
73
|
-
const parts = [];
|
|
74
|
-
if (extra !== void 0) {
|
|
75
|
-
parts.push(
|
|
76
|
-
/* @__PURE__ */ jsx3("span", { children: extra }, "extra")
|
|
77
|
-
);
|
|
78
|
-
parts.push(
|
|
79
|
-
/* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " \u2502 " }, "pipe")
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
items.forEach((item, i) => {
|
|
83
|
-
if (i > 0) {
|
|
84
|
-
parts.push(/* @__PURE__ */ jsx3("span", { children: " " }, `gap-${i}`));
|
|
85
|
-
}
|
|
86
|
-
parts.push(
|
|
87
|
-
/* @__PURE__ */ jsx3("span", { style: textStyle({ bold: true, fg: theme.background, bg: theme.muted }), children: ` ${item.key} ` }, `key-${i}`)
|
|
88
|
-
);
|
|
89
|
-
parts.push(
|
|
90
|
-
/* @__PURE__ */ jsx3("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: ` ${item.label}` }, `label-${i}`)
|
|
91
|
-
);
|
|
92
|
-
});
|
|
93
|
-
if (parts.length === 0) {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
return /* @__PURE__ */ jsx3("text", { children: parts });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ../ui/components/provider/provider.tsx
|
|
100
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
101
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
102
|
-
var KeyboardContext = createContext2(null);
|
|
103
|
-
function useKeyboardContext(propOverride) {
|
|
104
|
-
const fromContext = useContext2(KeyboardContext);
|
|
105
|
-
return propOverride ?? fromContext ?? void 0;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ../ui/components/link/link-demo.tsx
|
|
109
|
-
import { jsx as jsx5, jsxs } from "react/jsx-runtime";
|
|
110
|
-
var MODES = ["solid", "dashed", "dotted", "none"];
|
|
111
|
-
function LinkDemo({
|
|
112
|
-
url = "https://opentui.com",
|
|
113
|
-
label = "Visit opentui.com",
|
|
114
|
-
useKeyboard: useKeyboardProp
|
|
115
|
-
}) {
|
|
116
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
117
|
-
const [modeIndex, setModeIndex] = useState(0);
|
|
118
|
-
const mode = MODES[modeIndex];
|
|
119
|
-
useKeyboard?.((event) => {
|
|
120
|
-
if (event.name === "right") {
|
|
121
|
-
setModeIndex((i) => (i + 1) % MODES.length);
|
|
122
|
-
} else if (event.name === "left") {
|
|
123
|
-
setModeIndex((i) => (i - 1 + MODES.length) % MODES.length);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
return /* @__PURE__ */ jsxs("box", { flexDirection: "column", gap: 1, children: [
|
|
127
|
-
/* @__PURE__ */ jsx5(Link, { url, underline: mode, children: label }),
|
|
128
|
-
/* @__PURE__ */ jsx5(
|
|
129
|
-
StatusBar,
|
|
130
|
-
{
|
|
131
|
-
extra: /* @__PURE__ */ jsx5("span", { style: textStyle({ bold: true }), children: mode.padEnd(6) }),
|
|
132
|
-
items: [
|
|
133
|
-
{ key: "\u2190\u2192", label: "underline style" }
|
|
134
|
-
]
|
|
135
|
-
}
|
|
136
|
-
)
|
|
137
|
-
] });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ../ui/components/ascii/ascii.tsx
|
|
141
|
-
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
142
|
-
|
|
143
|
-
// ../ui/components/spinner/spinner.tsx
|
|
144
|
-
import { useEffect, useState as useState2 } from "react";
|
|
145
|
-
import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
146
|
-
var VARIANTS = {
|
|
147
|
-
dots: { frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"], interval: 83 },
|
|
148
|
-
pulse: { frames: ["\xB7", "\u2219", "\u25CF", "\u2219", "\xB7", "\xB7", "\xB7"], interval: 180 },
|
|
149
|
-
meter: { frames: ["\u25B1\u25B1\u25B1", "\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0", "\u25B0\u25B0\u25B1", "\u25B0\u25B1\u25B1", "\u25B1\u25B1\u25B1"], interval: 143 },
|
|
150
|
-
bloom: { frames: ["\xB7", "\u2726", "\u2727", "\u2739", "\u273A", "\u274B", "\u2738", "\u2735", "\u2738", "\u274B", "\u273A", "\u2739", "\u2727", "\u2726", "\xB7", "\xB7"], interval: 100 },
|
|
151
|
-
ellipsis: { frames: [" ", ". ", ".. ", "..."], interval: 333 }
|
|
152
|
-
};
|
|
153
|
-
var VARIANT_NAMES = Object.keys(VARIANTS);
|
|
154
|
-
function Spinner({ variant = "dots", text, color }) {
|
|
155
|
-
const theme = useTheme();
|
|
156
|
-
const resolvedColor = color ?? theme.accent;
|
|
157
|
-
const { frames, interval } = VARIANTS[variant];
|
|
158
|
-
const [frame, setFrame] = useState2(0);
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
setFrame(0);
|
|
161
|
-
const timer = setInterval(() => {
|
|
162
|
-
setFrame((prev) => (prev + 1) % frames.length);
|
|
163
|
-
}, interval);
|
|
164
|
-
return () => clearInterval(timer);
|
|
165
|
-
}, [variant]);
|
|
166
|
-
return /* @__PURE__ */ jsxs2("text", { children: [
|
|
167
|
-
/* @__PURE__ */ jsx7("span", { style: { fg: resolvedColor }, children: frames[frame] }),
|
|
168
|
-
text ? /* @__PURE__ */ jsxs2("span", { style: { fg: theme.foreground }, children: [
|
|
169
|
-
" ",
|
|
170
|
-
text
|
|
171
|
-
] }) : null
|
|
172
|
-
] });
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ../ui/components/spinner/spinner-showcase.tsx
|
|
176
|
-
import { useState as useState3 } from "react";
|
|
177
|
-
import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
178
|
-
function SpinnerPicker({ useKeyboard: useKeyboardProp }) {
|
|
179
|
-
const theme = useTheme();
|
|
180
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
181
|
-
const [selected, setSelected] = useState3(0);
|
|
182
|
-
useKeyboard?.((event) => {
|
|
183
|
-
if (event.name === "left") {
|
|
184
|
-
setSelected((s) => s > 0 ? s - 1 : VARIANT_NAMES.length - 1);
|
|
185
|
-
} else if (event.name === "right") {
|
|
186
|
-
setSelected((s) => s < VARIANT_NAMES.length - 1 ? s + 1 : 0);
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
const selectedName = VARIANT_NAMES[selected];
|
|
190
|
-
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
191
|
-
/* @__PURE__ */ jsx8("box", { padding: 1, flexDirection: "column", alignItems: "center", justifyContent: "center", flexGrow: 1, children: /* @__PURE__ */ jsx8(Spinner, { variant: selectedName, color: theme.accent }) }),
|
|
192
|
-
/* @__PURE__ */ jsx8(
|
|
193
|
-
StatusBar,
|
|
194
|
-
{
|
|
195
|
-
items: [{ key: "\u2190\u2192", label: "change spinner type" }],
|
|
196
|
-
extra: /* @__PURE__ */ jsx8("span", { style: textStyle({ fg: theme.accent, bold: true }), children: selectedName.padEnd(8) })
|
|
197
|
-
}
|
|
198
|
-
)
|
|
199
|
-
] });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ../ui/components/text-input/text-input.tsx
|
|
203
|
-
import { useCallback, useRef, useState as useState4 } from "react";
|
|
204
|
-
import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
205
|
-
function TextInput({ label, description, error, required = false, disabled = false, value: controlledValue, onChange, onSubmit, placeholder, prompt, focus = true, maxLength }) {
|
|
206
|
-
const theme = useTheme();
|
|
207
|
-
const [internalValue, setInternalValue] = useState4("");
|
|
208
|
-
const isControlled = controlledValue !== void 0;
|
|
209
|
-
const controlledRef = useRef(isControlled);
|
|
210
|
-
if (controlledRef.current !== isControlled) {
|
|
211
|
-
console.warn("TextInput: switching between controlled and uncontrolled is not supported.");
|
|
212
|
-
controlledRef.current = isControlled;
|
|
213
|
-
}
|
|
214
|
-
const current = isControlled ? controlledValue : internalValue;
|
|
215
|
-
const isFocused = focus && !disabled;
|
|
216
|
-
const handleInput = useCallback(
|
|
217
|
-
(v) => {
|
|
218
|
-
if (!isControlled) setInternalValue(v);
|
|
219
|
-
onChange?.(v);
|
|
220
|
-
},
|
|
221
|
-
[isControlled, onChange]
|
|
222
|
-
);
|
|
223
|
-
const handleSubmit = useCallback(
|
|
224
|
-
(v) => {
|
|
225
|
-
onSubmit?.(v);
|
|
226
|
-
if (!isControlled) setInternalValue("");
|
|
227
|
-
},
|
|
228
|
-
[isControlled, onSubmit]
|
|
229
|
-
);
|
|
230
|
-
const empty = !current;
|
|
231
|
-
const showLabel = label != null;
|
|
232
|
-
const message = error ?? description;
|
|
233
|
-
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", children: [
|
|
234
|
-
showLabel && /* @__PURE__ */ jsxs4("text", { children: [
|
|
235
|
-
/* @__PURE__ */ jsxs4("span", { style: textStyle({ fg: error ? theme.error : isFocused ? theme.primary : disabled ? theme.muted : theme.foreground, bold: !disabled, dim: disabled }), children: [
|
|
236
|
-
isFocused ? "\u25B8 " : " ",
|
|
237
|
-
label
|
|
238
|
-
] }),
|
|
239
|
-
required && /* @__PURE__ */ jsx9("span", { style: textStyle({ fg: theme.error }), children: " *" }),
|
|
240
|
-
current && maxLength != null && /* @__PURE__ */ jsxs4("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
|
|
241
|
-
" ",
|
|
242
|
-
current.length,
|
|
243
|
-
"/",
|
|
244
|
-
maxLength
|
|
245
|
-
] }),
|
|
246
|
-
message != null && /* @__PURE__ */ jsxs4("span", { style: textStyle({ fg: error ? theme.error : theme.muted, dim: !error }), children: [
|
|
247
|
-
" | ",
|
|
248
|
-
message
|
|
249
|
-
] })
|
|
250
|
-
] }),
|
|
251
|
-
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginLeft: showLabel ? 2 : 0, children: [
|
|
252
|
-
prompt != null && /* @__PURE__ */ jsx9("text", { style: { fg: isFocused ? theme.primary : theme.muted }, children: prompt }),
|
|
253
|
-
isFocused ? /* @__PURE__ */ jsx9(
|
|
254
|
-
"input",
|
|
255
|
-
{
|
|
256
|
-
value: current,
|
|
257
|
-
placeholder,
|
|
258
|
-
maxLength,
|
|
259
|
-
focused: true,
|
|
260
|
-
onInput: handleInput,
|
|
261
|
-
onSubmit: handleSubmit,
|
|
262
|
-
cursorColor: theme.muted,
|
|
263
|
-
cursorStyle: { style: "line", blinking: empty },
|
|
264
|
-
placeholderColor: theme.placeholder,
|
|
265
|
-
textColor: theme.foreground
|
|
266
|
-
}
|
|
267
|
-
) : /* @__PURE__ */ jsx9("text", { style: { fg: empty ? theme.placeholder : disabled ? theme.muted : theme.foreground, dim: disabled }, children: empty ? placeholder : current })
|
|
268
|
-
] })
|
|
269
|
-
] });
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ../ui/components/select-input/select-input.tsx
|
|
273
|
-
import { useReducer, useMemo, useRef as useRef2 } from "react";
|
|
274
|
-
import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
275
|
-
function reducer(state, action) {
|
|
276
|
-
switch (action.type) {
|
|
277
|
-
case "MOVE": {
|
|
278
|
-
let next = state.cursor + action.direction;
|
|
279
|
-
if (next < 0) next = action.max - 1;
|
|
280
|
-
if (next >= action.max) next = 0;
|
|
281
|
-
return { ...state, cursor: next };
|
|
282
|
-
}
|
|
283
|
-
case "SUBMIT":
|
|
284
|
-
return { ...state, submitted: true };
|
|
285
|
-
default:
|
|
286
|
-
return state;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
var VISIBLE = 12;
|
|
290
|
-
var BAR = "\u2502";
|
|
291
|
-
var RADIO = "\u25CB";
|
|
292
|
-
var CURSOR = "\u25B8";
|
|
293
|
-
var SEPARATOR = "\u2500";
|
|
294
|
-
function SelectInput({
|
|
295
|
-
items = [],
|
|
296
|
-
defaultValue,
|
|
297
|
-
value: controlledValue,
|
|
298
|
-
onChange,
|
|
299
|
-
disabled = false,
|
|
300
|
-
invalid = false,
|
|
301
|
-
required = false,
|
|
302
|
-
placeholder,
|
|
303
|
-
title = "Select",
|
|
304
|
-
submittedStatus = "submitted",
|
|
305
|
-
limit,
|
|
306
|
-
highlightColor,
|
|
307
|
-
onSubmit,
|
|
308
|
-
useKeyboard: useKeyboardProp
|
|
309
|
-
}) {
|
|
310
|
-
const theme = useTheme();
|
|
311
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
312
|
-
const resolvedHighlight = highlightColor ?? theme.primary;
|
|
313
|
-
const isControlled = controlledValue !== void 0;
|
|
314
|
-
const controlledRef = useRef2(isControlled);
|
|
315
|
-
if (controlledRef.current !== isControlled) {
|
|
316
|
-
console.warn("SelectInput: switching between controlled and uncontrolled is not supported.");
|
|
317
|
-
}
|
|
318
|
-
const initialIndex = items.findIndex((i) => i.value === (isControlled ? controlledValue : defaultValue));
|
|
319
|
-
const [state, dispatch] = useReducer(reducer, {
|
|
320
|
-
cursor: Math.max(0, initialIndex),
|
|
321
|
-
submitted: false
|
|
322
|
-
});
|
|
323
|
-
const { flatRows, selectableItems } = useMemo(() => {
|
|
324
|
-
const rows = [];
|
|
325
|
-
const selectable = [];
|
|
326
|
-
let index = 0;
|
|
327
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
328
|
-
for (const item of items) {
|
|
329
|
-
const group = item.group ?? "";
|
|
330
|
-
const list = grouped.get(group) ?? [];
|
|
331
|
-
list.push(item);
|
|
332
|
-
grouped.set(group, list);
|
|
333
|
-
}
|
|
334
|
-
let first = true;
|
|
335
|
-
for (const [group, groupItems] of grouped) {
|
|
336
|
-
if (!first) rows.push({ type: "separator" });
|
|
337
|
-
first = false;
|
|
338
|
-
if (group) {
|
|
339
|
-
rows.push({ type: "group", label: group });
|
|
340
|
-
}
|
|
341
|
-
for (const item of groupItems) {
|
|
342
|
-
rows.push({ type: "item", item, index });
|
|
343
|
-
selectable.push({ item, index });
|
|
344
|
-
index++;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return { flatRows: rows, selectableItems: selectable };
|
|
348
|
-
}, [items]);
|
|
349
|
-
const visibleCount = limit ?? VISIBLE;
|
|
350
|
-
const cursorRowIndex = flatRows.findIndex((r) => r.type === "item" && r.index === state.cursor);
|
|
351
|
-
const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
|
|
352
|
-
const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
|
|
353
|
-
const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
|
|
354
|
-
useKeyboard?.((event) => {
|
|
355
|
-
if (state.submitted || disabled) return;
|
|
356
|
-
if (event.name === "up" || event.name === "k") {
|
|
357
|
-
const direction = -1;
|
|
358
|
-
let next = state.cursor + direction;
|
|
359
|
-
if (next < 0) next = selectableItems.length - 1;
|
|
360
|
-
dispatch({ type: "MOVE", direction, max: selectableItems.length });
|
|
361
|
-
const nextItem = selectableItems[next];
|
|
362
|
-
if (nextItem && !nextItem.item.disabled) {
|
|
363
|
-
onChange?.(nextItem.item.value);
|
|
364
|
-
}
|
|
365
|
-
} else if (event.name === "down" || event.name === "j") {
|
|
366
|
-
const direction = 1;
|
|
367
|
-
let next = state.cursor + direction;
|
|
368
|
-
if (next >= selectableItems.length) next = 0;
|
|
369
|
-
dispatch({ type: "MOVE", direction, max: selectableItems.length });
|
|
370
|
-
const nextItem = selectableItems[next];
|
|
371
|
-
if (nextItem && !nextItem.item.disabled) {
|
|
372
|
-
onChange?.(nextItem.item.value);
|
|
373
|
-
}
|
|
374
|
-
} else if (event.name === "return") {
|
|
375
|
-
const current = selectableItems[state.cursor];
|
|
376
|
-
if (current && !current.item.disabled) {
|
|
377
|
-
dispatch({ type: "SUBMIT" });
|
|
378
|
-
onSubmit?.(isControlled ? controlledValue : current.item.value);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
if (state.submitted) {
|
|
383
|
-
const selectedItem = isControlled ? items.find((i) => i.value === controlledValue) : selectableItems[state.cursor]?.item;
|
|
384
|
-
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
385
|
-
/* @__PURE__ */ jsxs5("text", { children: [
|
|
386
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
|
|
387
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, fg: theme.foreground }), children: title })
|
|
388
|
-
] }),
|
|
389
|
-
selectedItem && /* @__PURE__ */ jsxs5("text", { children: [
|
|
390
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.success }), children: [
|
|
391
|
-
BAR,
|
|
392
|
-
" "
|
|
393
|
-
] }),
|
|
394
|
-
/* @__PURE__ */ jsx10("span", { children: " " }),
|
|
395
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.success }), children: "\u25CF " }),
|
|
396
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.foreground }), children: selectedItem.label })
|
|
397
|
-
] }),
|
|
398
|
-
/* @__PURE__ */ jsx10("text", { children: " " }),
|
|
399
|
-
/* @__PURE__ */ jsx10("text", { children: /* @__PURE__ */ jsx10("span", { style: textStyle({ dim: true, fg: theme.muted }), children: submittedStatus }) })
|
|
400
|
-
] });
|
|
401
|
-
}
|
|
402
|
-
const hasItems = selectableItems.length > 0;
|
|
403
|
-
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
404
|
-
/* @__PURE__ */ jsxs5("text", { children: [
|
|
405
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: diamondColor, dim: disabled }), children: "\u25C6 " }),
|
|
406
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, dim: disabled, fg: theme.foreground }), children: title }),
|
|
407
|
-
required && /* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.error }), children: " *" })
|
|
408
|
-
] }),
|
|
409
|
-
invalid && /* @__PURE__ */ jsxs5("text", { children: [
|
|
410
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
411
|
-
BAR,
|
|
412
|
-
" "
|
|
413
|
-
] }),
|
|
414
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: theme.error }), children: " Please select an option" })
|
|
415
|
-
] }),
|
|
416
|
-
!hasItems && placeholder && /* @__PURE__ */ jsxs5("text", { children: [
|
|
417
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
418
|
-
BAR,
|
|
419
|
-
" "
|
|
420
|
-
] }),
|
|
421
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
|
|
422
|
-
" ",
|
|
423
|
-
placeholder
|
|
424
|
-
] })
|
|
425
|
-
] }),
|
|
426
|
-
visibleRows.map((row, i) => {
|
|
427
|
-
if (row.type === "separator") {
|
|
428
|
-
return /* @__PURE__ */ jsxs5("text", { children: [
|
|
429
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
430
|
-
BAR,
|
|
431
|
-
" "
|
|
432
|
-
] }),
|
|
433
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
434
|
-
" ",
|
|
435
|
-
SEPARATOR.repeat(20)
|
|
436
|
-
] })
|
|
437
|
-
] }, `sep-${i}`);
|
|
438
|
-
}
|
|
439
|
-
if (row.type === "group") {
|
|
440
|
-
return /* @__PURE__ */ jsxs5("text", { children: [
|
|
441
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
442
|
-
BAR,
|
|
443
|
-
" "
|
|
444
|
-
] }),
|
|
445
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ bold: true, fg: theme.muted }), children: ` ${row.label}` })
|
|
446
|
-
] }, `group-${row.label}`);
|
|
447
|
-
}
|
|
448
|
-
const { item, index: itemIndex } = row;
|
|
449
|
-
const isHighlighted = !disabled && itemIndex === state.cursor;
|
|
450
|
-
const isItemDisabled = disabled || !!item.disabled;
|
|
451
|
-
const itemColor = isItemDisabled ? theme.muted : isHighlighted ? resolvedHighlight : theme.foreground;
|
|
452
|
-
return /* @__PURE__ */ jsxs5("text", { children: [
|
|
453
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
454
|
-
BAR,
|
|
455
|
-
" "
|
|
456
|
-
] }),
|
|
457
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: isHighlighted ? resolvedHighlight : void 0 }), children: [
|
|
458
|
-
isHighlighted ? CURSOR : " ",
|
|
459
|
-
" "
|
|
460
|
-
] }),
|
|
461
|
-
/* @__PURE__ */ jsxs5("span", { style: textStyle({ fg: theme.muted, dim: isItemDisabled }), children: [
|
|
462
|
-
RADIO,
|
|
463
|
-
" "
|
|
464
|
-
] }),
|
|
465
|
-
/* @__PURE__ */ jsx10("span", { style: textStyle({ fg: itemColor, dim: isItemDisabled }), children: item.label })
|
|
466
|
-
] }, item.key ?? String(item.value));
|
|
467
|
-
})
|
|
468
|
-
] });
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// ../ui/components/multi-select/multi-select.tsx
|
|
472
|
-
import { useReducer as useReducer2, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
473
|
-
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
474
|
-
function reducer2(state, action) {
|
|
475
|
-
switch (action.type) {
|
|
476
|
-
case "MOVE": {
|
|
477
|
-
let next = state.cursor + action.direction;
|
|
478
|
-
if (next < 0) next = action.max - 1;
|
|
479
|
-
if (next >= action.max) next = 0;
|
|
480
|
-
return { ...state, cursor: next };
|
|
481
|
-
}
|
|
482
|
-
case "SET_SELECTED":
|
|
483
|
-
return { ...state, selected: new Set(action.values) };
|
|
484
|
-
case "SUBMIT":
|
|
485
|
-
return { ...state, submitted: true };
|
|
486
|
-
default:
|
|
487
|
-
return state;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
var VISIBLE2 = 12;
|
|
491
|
-
var BAR2 = "\u2502";
|
|
492
|
-
var CHECKED = "\u25CF";
|
|
493
|
-
var UNCHECKED = "\u25CB";
|
|
494
|
-
var CURSOR2 = "\u25B8";
|
|
495
|
-
var SEPARATOR2 = "\u2500";
|
|
496
|
-
function MultiSelect({
|
|
497
|
-
items = [],
|
|
498
|
-
defaultSelected = [],
|
|
499
|
-
selected: controlledSelected,
|
|
500
|
-
onChange,
|
|
501
|
-
disabled = false,
|
|
502
|
-
invalid = false,
|
|
503
|
-
required = false,
|
|
504
|
-
placeholder,
|
|
505
|
-
maxCount,
|
|
506
|
-
title = "Select",
|
|
507
|
-
submittedStatus = "submitted",
|
|
508
|
-
limit,
|
|
509
|
-
enableSelectAll = true,
|
|
510
|
-
enableClear = true,
|
|
511
|
-
highlightColor,
|
|
512
|
-
checkboxColor,
|
|
513
|
-
allowEmpty = false,
|
|
514
|
-
onSubmit,
|
|
515
|
-
useKeyboard: useKeyboardProp
|
|
516
|
-
}) {
|
|
517
|
-
const theme = useTheme();
|
|
518
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
519
|
-
const resolvedHighlight = highlightColor ?? theme.primary;
|
|
520
|
-
const resolvedCheckbox = checkboxColor ?? theme.accent;
|
|
521
|
-
const isControlled = controlledSelected !== void 0;
|
|
522
|
-
const controlledRef = useRef3(isControlled);
|
|
523
|
-
if (controlledRef.current !== isControlled) {
|
|
524
|
-
console.warn("MultiSelect: switching between controlled and uncontrolled is not supported.");
|
|
525
|
-
}
|
|
526
|
-
const [state, dispatch] = useReducer2(reducer2, {
|
|
527
|
-
cursor: 0,
|
|
528
|
-
selected: new Set(isControlled ? controlledSelected : defaultSelected),
|
|
529
|
-
submitted: false
|
|
530
|
-
});
|
|
531
|
-
const cursorRef = useRef3(0);
|
|
532
|
-
const currentSelected = isControlled ? new Set(controlledSelected) : state.selected;
|
|
533
|
-
const { flatRows, selectableItems } = useMemo2(() => {
|
|
534
|
-
const rows = [];
|
|
535
|
-
const selectable = [];
|
|
536
|
-
let index = 0;
|
|
537
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
538
|
-
for (const item of items) {
|
|
539
|
-
const group = item.group ?? "";
|
|
540
|
-
const list = grouped.get(group) ?? [];
|
|
541
|
-
list.push(item);
|
|
542
|
-
grouped.set(group, list);
|
|
543
|
-
}
|
|
544
|
-
let first = true;
|
|
545
|
-
for (const [group, groupItems] of grouped) {
|
|
546
|
-
if (!first) rows.push({ type: "separator" });
|
|
547
|
-
first = false;
|
|
548
|
-
if (group) {
|
|
549
|
-
rows.push({ type: "group", label: group });
|
|
550
|
-
}
|
|
551
|
-
for (const item of groupItems) {
|
|
552
|
-
rows.push({ type: "item", item, index });
|
|
553
|
-
selectable.push({ item, index });
|
|
554
|
-
index++;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return { flatRows: rows, selectableItems: selectable };
|
|
558
|
-
}, [items]);
|
|
559
|
-
const hasSubmitRow = allowEmpty || currentSelected.size > 0;
|
|
560
|
-
const totalPositions = selectableItems.length + (hasSubmitRow ? 1 : 0);
|
|
561
|
-
const isOnSubmit = hasSubmitRow && state.cursor === selectableItems.length;
|
|
562
|
-
const visibleCount = limit ?? VISIBLE2;
|
|
563
|
-
const cursorRowIndex = flatRows.findIndex((r) => r.type === "item" && r.index === state.cursor);
|
|
564
|
-
const scrollOffset = Math.max(0, Math.min(cursorRowIndex - Math.floor(visibleCount / 2), flatRows.length - visibleCount));
|
|
565
|
-
const visibleRows = flatRows.slice(scrollOffset, scrollOffset + visibleCount);
|
|
566
|
-
const setSelected = (values) => {
|
|
567
|
-
if (isControlled) {
|
|
568
|
-
onChange?.(values);
|
|
569
|
-
} else {
|
|
570
|
-
dispatch({ type: "SET_SELECTED", values });
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
|
-
const diamondColor = invalid ? theme.error : disabled ? theme.muted : theme.accent;
|
|
574
|
-
useKeyboard?.((event) => {
|
|
575
|
-
if (state.submitted || disabled) return;
|
|
576
|
-
const move = (direction) => {
|
|
577
|
-
let next = cursorRef.current + direction;
|
|
578
|
-
if (next < 0) next = totalPositions - 1;
|
|
579
|
-
if (next >= totalPositions) next = 0;
|
|
580
|
-
cursorRef.current = next;
|
|
581
|
-
dispatch({ type: "MOVE", direction, max: totalPositions });
|
|
582
|
-
};
|
|
583
|
-
if (event.name === "up" || event.name === "k") {
|
|
584
|
-
move(-1);
|
|
585
|
-
} else if (event.name === "down" || event.name === "j") {
|
|
586
|
-
move(1);
|
|
587
|
-
} else if (event.name === "return") {
|
|
588
|
-
const onSubmitRow = hasSubmitRow && cursorRef.current === selectableItems.length;
|
|
589
|
-
if (onSubmitRow) {
|
|
590
|
-
dispatch({ type: "SUBMIT" });
|
|
591
|
-
onSubmit?.(Array.from(currentSelected));
|
|
592
|
-
} else {
|
|
593
|
-
const current = selectableItems[cursorRef.current];
|
|
594
|
-
if (current && !current.item.disabled) {
|
|
595
|
-
const isDeselecting = currentSelected.has(current.item.value);
|
|
596
|
-
if (!isDeselecting && maxCount !== void 0 && currentSelected.size >= maxCount) return;
|
|
597
|
-
const next = new Set(currentSelected);
|
|
598
|
-
if (isDeselecting) next.delete(current.item.value);
|
|
599
|
-
else next.add(current.item.value);
|
|
600
|
-
setSelected(Array.from(next));
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
} else if (event.name === "a" && enableSelectAll) {
|
|
604
|
-
const enabledValues = items.filter((i) => !i.disabled).map((i) => i.value);
|
|
605
|
-
setSelected(maxCount !== void 0 ? enabledValues.slice(0, maxCount) : enabledValues);
|
|
606
|
-
} else if (event.name === "x" && enableClear) {
|
|
607
|
-
setSelected([]);
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
if (state.submitted) {
|
|
611
|
-
const selectedItems = items.filter((i) => currentSelected.has(i.value));
|
|
612
|
-
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
|
|
613
|
-
/* @__PURE__ */ jsxs6("text", { children: [
|
|
614
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.success }), children: "\u25C6 " }),
|
|
615
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, fg: theme.foreground }), children: title })
|
|
616
|
-
] }),
|
|
617
|
-
selectedItems.map((item) => /* @__PURE__ */ jsxs6("text", { children: [
|
|
618
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.success }), children: [
|
|
619
|
-
BAR2,
|
|
620
|
-
" "
|
|
621
|
-
] }),
|
|
622
|
-
/* @__PURE__ */ jsx11("span", { children: " " }),
|
|
623
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.success }), children: [
|
|
624
|
-
CHECKED,
|
|
625
|
-
" "
|
|
626
|
-
] }),
|
|
627
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.foreground }), children: item.label })
|
|
628
|
-
] }, item.key ?? String(item.value))),
|
|
629
|
-
/* @__PURE__ */ jsx11("text", { children: " " }),
|
|
630
|
-
/* @__PURE__ */ jsx11("text", { children: /* @__PURE__ */ jsxs6("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
|
|
631
|
-
selectedItems.length,
|
|
632
|
-
" selected \u2014 ",
|
|
633
|
-
submittedStatus
|
|
634
|
-
] }) })
|
|
635
|
-
] });
|
|
636
|
-
}
|
|
637
|
-
const hasItems = selectableItems.length > 0;
|
|
638
|
-
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", children: [
|
|
639
|
-
/* @__PURE__ */ jsxs6("text", { children: [
|
|
640
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: diamondColor, dim: disabled }), children: "\u25C6 " }),
|
|
641
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, dim: disabled, fg: theme.foreground }), children: title }),
|
|
642
|
-
required && /* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.error }), children: " *" }),
|
|
643
|
-
maxCount !== void 0 && /* @__PURE__ */ jsx11("span", { style: textStyle({ dim: true, fg: theme.muted }), children: ` (${currentSelected.size}/${maxCount})` })
|
|
644
|
-
] }),
|
|
645
|
-
invalid && /* @__PURE__ */ jsxs6("text", { children: [
|
|
646
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
647
|
-
BAR2,
|
|
648
|
-
" "
|
|
649
|
-
] }),
|
|
650
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: theme.error }), children: " Please select at least one option" })
|
|
651
|
-
] }),
|
|
652
|
-
!hasItems && placeholder && /* @__PURE__ */ jsxs6("text", { children: [
|
|
653
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
654
|
-
BAR2,
|
|
655
|
-
" "
|
|
656
|
-
] }),
|
|
657
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ dim: true, fg: theme.muted }), children: [
|
|
658
|
-
" ",
|
|
659
|
-
placeholder
|
|
660
|
-
] })
|
|
661
|
-
] }),
|
|
662
|
-
visibleRows.map((row, i) => {
|
|
663
|
-
if (row.type === "separator") {
|
|
664
|
-
return /* @__PURE__ */ jsxs6("text", { children: [
|
|
665
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
666
|
-
BAR2,
|
|
667
|
-
" "
|
|
668
|
-
] }),
|
|
669
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
670
|
-
" ",
|
|
671
|
-
SEPARATOR2.repeat(20)
|
|
672
|
-
] })
|
|
673
|
-
] }, `sep-${i}`);
|
|
674
|
-
}
|
|
675
|
-
if (row.type === "group") {
|
|
676
|
-
return /* @__PURE__ */ jsxs6("text", { children: [
|
|
677
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
678
|
-
BAR2,
|
|
679
|
-
" "
|
|
680
|
-
] }),
|
|
681
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ bold: true, fg: theme.muted }), children: ` ${row.label}` })
|
|
682
|
-
] }, `group-${row.label}`);
|
|
683
|
-
}
|
|
684
|
-
const { item, index: itemIndex } = row;
|
|
685
|
-
const isHighlighted = !disabled && itemIndex === state.cursor;
|
|
686
|
-
const isSelected = currentSelected.has(item.value);
|
|
687
|
-
const isItemDisabled = disabled || !!item.disabled;
|
|
688
|
-
const itemColor = isItemDisabled ? theme.muted : isHighlighted ? resolvedHighlight : isSelected ? resolvedCheckbox : theme.foreground;
|
|
689
|
-
return /* @__PURE__ */ jsxs6("text", { children: [
|
|
690
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
691
|
-
BAR2,
|
|
692
|
-
" "
|
|
693
|
-
] }),
|
|
694
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isHighlighted ? resolvedHighlight : void 0 }), children: [
|
|
695
|
-
isHighlighted ? CURSOR2 : " ",
|
|
696
|
-
" "
|
|
697
|
-
] }),
|
|
698
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isSelected && !isItemDisabled ? resolvedCheckbox : theme.muted, dim: isItemDisabled }), children: [
|
|
699
|
-
isSelected ? CHECKED : UNCHECKED,
|
|
700
|
-
" "
|
|
701
|
-
] }),
|
|
702
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: itemColor, dim: isItemDisabled }), children: item.label })
|
|
703
|
-
] }, item.key ?? String(item.value));
|
|
704
|
-
}),
|
|
705
|
-
hasSubmitRow && /* @__PURE__ */ jsxs6("text", { children: [
|
|
706
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: theme.muted }), children: [
|
|
707
|
-
BAR2,
|
|
708
|
-
" "
|
|
709
|
-
] }),
|
|
710
|
-
/* @__PURE__ */ jsxs6("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : void 0 }), children: [
|
|
711
|
-
isOnSubmit ? CURSOR2 : " ",
|
|
712
|
-
" "
|
|
713
|
-
] }),
|
|
714
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : theme.muted }), children: "\u21B3 " }),
|
|
715
|
-
/* @__PURE__ */ jsx11("span", { style: textStyle({ fg: isOnSubmit ? resolvedHighlight : theme.foreground }), children: "Submit" })
|
|
716
|
-
] })
|
|
717
|
-
] });
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
// ../ui/components/table/table.tsx
|
|
721
|
-
import { Fragment } from "react";
|
|
722
|
-
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
723
|
-
function getColumns(data, columnsProp) {
|
|
724
|
-
if (columnsProp) return columnsProp;
|
|
725
|
-
const keys = /* @__PURE__ */ new Set();
|
|
726
|
-
for (const row of data) {
|
|
727
|
-
for (const key in row) keys.add(key);
|
|
728
|
-
}
|
|
729
|
-
return Array.from(keys);
|
|
730
|
-
}
|
|
731
|
-
function calculateColumnWidths(columns, data, padding) {
|
|
732
|
-
return columns.map((field) => {
|
|
733
|
-
const headerWidth = String(field).length;
|
|
734
|
-
const maxDataWidth = data.reduce((max, row) => {
|
|
735
|
-
const val = row[field];
|
|
736
|
-
return Math.max(max, val == null ? 0 : String(val).length);
|
|
737
|
-
}, 0);
|
|
738
|
-
return { field, width: Math.max(headerWidth, maxDataWidth) + padding * 2 };
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
function padCell(value, width, padding) {
|
|
742
|
-
const rightPad = width - value.length - padding;
|
|
743
|
-
return " ".repeat(padding) + value + " ".repeat(Math.max(0, rightPad));
|
|
744
|
-
}
|
|
745
|
-
function Table({
|
|
746
|
-
data,
|
|
747
|
-
columns: columnsProp,
|
|
748
|
-
padding = 1,
|
|
749
|
-
headerColor,
|
|
750
|
-
borderColor
|
|
751
|
-
}) {
|
|
752
|
-
const theme = useTheme();
|
|
753
|
-
const resolvedHeaderColor = headerColor ?? theme.primary;
|
|
754
|
-
const resolvedBorderColor = borderColor ?? theme.border;
|
|
755
|
-
const columns = getColumns(data, columnsProp);
|
|
756
|
-
const colInfo = calculateColumnWidths(columns, data, padding);
|
|
757
|
-
const borderLine = (left, mid, right) => {
|
|
758
|
-
const inner = colInfo.map((c) => "\u2500".repeat(c.width)).join(mid);
|
|
759
|
-
return /* @__PURE__ */ jsx12("text", { children: /* @__PURE__ */ jsxs7("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: [
|
|
760
|
-
left,
|
|
761
|
-
inner,
|
|
762
|
-
right
|
|
763
|
-
] }) });
|
|
764
|
-
};
|
|
765
|
-
const contentRow = (rowData, isHeader) => {
|
|
766
|
-
const parts = [];
|
|
767
|
-
parts.push(
|
|
768
|
-
/* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "left-border")
|
|
769
|
-
);
|
|
770
|
-
colInfo.forEach((col, i) => {
|
|
771
|
-
const val = rowData[col.field];
|
|
772
|
-
const str = val == null ? "" : String(val);
|
|
773
|
-
const padded = padCell(str, col.width, padding);
|
|
774
|
-
if (isHeader) {
|
|
775
|
-
parts.push(
|
|
776
|
-
/* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedHeaderColor, bold: true }), children: padded }, `cell-${i}`)
|
|
777
|
-
);
|
|
778
|
-
} else {
|
|
779
|
-
parts.push(
|
|
780
|
-
/* @__PURE__ */ jsx12("span", { style: textStyle({ fg: theme.foreground }), children: padded }, `cell-${i}`)
|
|
781
|
-
);
|
|
782
|
-
}
|
|
783
|
-
if (i < colInfo.length - 1) {
|
|
784
|
-
parts.push(
|
|
785
|
-
/* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, `sep-${i}`)
|
|
786
|
-
);
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
parts.push(
|
|
790
|
-
/* @__PURE__ */ jsx12("span", { style: textStyle({ fg: resolvedBorderColor, bold: true }), children: "\u2502" }, "right-border")
|
|
791
|
-
);
|
|
792
|
-
return /* @__PURE__ */ jsx12("text", { children: parts });
|
|
793
|
-
};
|
|
794
|
-
const headerData = columns.reduce(
|
|
795
|
-
(acc, col) => ({ ...acc, [col]: col }),
|
|
796
|
-
{}
|
|
797
|
-
);
|
|
798
|
-
return /* @__PURE__ */ jsxs7("box", { children: [
|
|
799
|
-
borderLine("\u250C", "\u252C", "\u2510"),
|
|
800
|
-
contentRow(headerData, true),
|
|
801
|
-
data.map((row, index) => /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
802
|
-
borderLine("\u251C", "\u253C", "\u2524"),
|
|
803
|
-
contentRow(row, false)
|
|
804
|
-
] }, index)),
|
|
805
|
-
borderLine("\u2514", "\u2534", "\u2518")
|
|
806
|
-
] });
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// ../ui/components/gradient/gradient.tsx
|
|
810
|
-
import { Fragment as Fragment2, jsx as jsx13 } from "react/jsx-runtime";
|
|
811
|
-
var GRADIENTS = {
|
|
812
|
-
cristal: ["#bdfff3", "#4ac29a"],
|
|
813
|
-
teen: ["#77a1d3", "#79cbca", "#e684ae"],
|
|
814
|
-
mind: ["#473b7b", "#3584a7", "#30d2be"],
|
|
815
|
-
morning: ["#ff5f6d", "#ffc371"],
|
|
816
|
-
vice: ["#5ee7df", "#b490ca"],
|
|
817
|
-
passion: ["#f43b47", "#453a94"],
|
|
818
|
-
fruit: ["#ff4e50", "#f9d423"],
|
|
819
|
-
instagram: ["#833ab4", "#fd1d1d", "#fcb045"],
|
|
820
|
-
atlas: ["#feac5e", "#c779d0", "#4bc0c8"],
|
|
821
|
-
retro: ["#3f51b1", "#5a55ae", "#7b5fac", "#8f6aae", "#a86aa4", "#cc6b8e", "#f18271", "#f3a469", "#f7c978"],
|
|
822
|
-
summer: ["#fdbb2d", "#22c1c3"],
|
|
823
|
-
rainbow: ["#ff0000", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff", "#ff0000"],
|
|
824
|
-
pastel: ["#74ebd5", "#ACB6E5"]
|
|
825
|
-
};
|
|
826
|
-
function hexToRgb(hex) {
|
|
827
|
-
const normalized = hex.replace("#", "");
|
|
828
|
-
return {
|
|
829
|
-
r: parseInt(normalized.substring(0, 2), 16),
|
|
830
|
-
g: parseInt(normalized.substring(2, 4), 16),
|
|
831
|
-
b: parseInt(normalized.substring(4, 6), 16)
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
function rgbToHex(rgb) {
|
|
835
|
-
const r = rgb.r.toString(16).padStart(2, "0");
|
|
836
|
-
const g = rgb.g.toString(16).padStart(2, "0");
|
|
837
|
-
const b = rgb.b.toString(16).padStart(2, "0");
|
|
838
|
-
return `#${r}${g}${b}`;
|
|
839
|
-
}
|
|
840
|
-
function lerp(a, b, t) {
|
|
841
|
-
return a + (b - a) * t;
|
|
842
|
-
}
|
|
843
|
-
function interpolateColor(color1, color2, t) {
|
|
844
|
-
return {
|
|
845
|
-
r: Math.round(lerp(color1.r, color2.r, t)),
|
|
846
|
-
g: Math.round(lerp(color1.g, color2.g, t)),
|
|
847
|
-
b: Math.round(lerp(color1.b, color2.b, t))
|
|
848
|
-
};
|
|
849
|
-
}
|
|
850
|
-
function generateGradient(colors, steps) {
|
|
851
|
-
if (colors.length === 0) throw new Error("At least one color is required");
|
|
852
|
-
if (colors.length === 1 || steps <= 1) return Array(steps).fill(colors[0]);
|
|
853
|
-
const rgbColors = colors.map(hexToRgb);
|
|
854
|
-
const result = [];
|
|
855
|
-
const segmentLength = (steps - 1) / (rgbColors.length - 1);
|
|
856
|
-
for (let i = 0; i < steps; i++) {
|
|
857
|
-
const segmentIndex = Math.min(Math.floor(i / segmentLength), rgbColors.length - 2);
|
|
858
|
-
const segmentProgress = segmentLength > 0 ? (i - segmentIndex * segmentLength) / segmentLength : 0;
|
|
859
|
-
const color = interpolateColor(rgbColors[segmentIndex], rgbColors[segmentIndex + 1], Math.min(segmentProgress, 1));
|
|
860
|
-
result.push(rgbToHex(color));
|
|
861
|
-
}
|
|
862
|
-
return result;
|
|
863
|
-
}
|
|
864
|
-
function Gradient({ children, name, colors }) {
|
|
865
|
-
if (name && colors) throw new Error("The `name` and `colors` props are mutually exclusive");
|
|
866
|
-
if (!name && !colors) throw new Error("Either `name` or `colors` prop must be provided");
|
|
867
|
-
const gradientColors = name ? GRADIENTS[name] : colors;
|
|
868
|
-
const lines = children.split("\n");
|
|
869
|
-
const maxLength = Math.max(...lines.map((l) => l.length));
|
|
870
|
-
if (maxLength === 0) return /* @__PURE__ */ jsx13("text", { children });
|
|
871
|
-
const hexColors = generateGradient(gradientColors, maxLength);
|
|
872
|
-
return /* @__PURE__ */ jsx13(Fragment2, { children: lines.map((line, lineIndex) => /* @__PURE__ */ jsx13("text", { children: line.split("").map((char, charIndex) => /* @__PURE__ */ jsx13("span", { style: { fg: hexColors[charIndex] }, children: char }, charIndex)) }, lineIndex)) });
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// ../ui/components/tab-bar/tab-bar.tsx
|
|
876
|
-
import { createContext as createContext3, useContext as useContext3, useState as useState5, Children, isValidElement } from "react";
|
|
877
|
-
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
878
|
-
var TabsContext = createContext3(null);
|
|
879
|
-
function useTabsContext() {
|
|
880
|
-
const ctx = useContext3(TabsContext);
|
|
881
|
-
if (!ctx) throw new Error("Tabs compound components must be used within <Tabs>");
|
|
882
|
-
return ctx;
|
|
883
|
-
}
|
|
884
|
-
function Tabs({
|
|
885
|
-
value: controlledValue,
|
|
886
|
-
defaultValue = "",
|
|
887
|
-
onValueChange,
|
|
888
|
-
children
|
|
889
|
-
}) {
|
|
890
|
-
const [internalValue, setInternalValue] = useState5(defaultValue);
|
|
891
|
-
const value = controlledValue ?? internalValue;
|
|
892
|
-
const handleChange = onValueChange ?? setInternalValue;
|
|
893
|
-
return /* @__PURE__ */ jsx14(TabsContext.Provider, { value: { value, onValueChange: handleChange }, children: /* @__PURE__ */ jsx14("box", { flexDirection: "column", children }) });
|
|
894
|
-
}
|
|
895
|
-
function TabsList({
|
|
896
|
-
label,
|
|
897
|
-
focused = true,
|
|
898
|
-
activeColor,
|
|
899
|
-
separator = true,
|
|
900
|
-
children
|
|
901
|
-
}) {
|
|
902
|
-
const theme = useTheme();
|
|
903
|
-
const { value } = useTabsContext();
|
|
904
|
-
const color = activeColor ?? theme.accent;
|
|
905
|
-
const triggers = [];
|
|
906
|
-
Children.forEach(children, (child) => {
|
|
907
|
-
if (isValidElement(child) && "value" in child.props) {
|
|
908
|
-
triggers.push({ value: child.props.value, label: child.props.children });
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
const parts = [];
|
|
912
|
-
if (label !== void 0) {
|
|
913
|
-
parts.push(
|
|
914
|
-
/* @__PURE__ */ jsxs8(
|
|
915
|
-
"span",
|
|
916
|
-
{
|
|
917
|
-
style: focused ? textStyle({ bold: true, fg: theme.foreground }) : textStyle({ dim: true, fg: theme.muted }),
|
|
918
|
-
children: [
|
|
919
|
-
label,
|
|
920
|
-
" "
|
|
921
|
-
]
|
|
922
|
-
},
|
|
923
|
-
"label"
|
|
924
|
-
)
|
|
925
|
-
);
|
|
926
|
-
}
|
|
927
|
-
triggers.forEach((trigger, i) => {
|
|
928
|
-
const isSelected = trigger.value === value;
|
|
929
|
-
const padded = ` ${trigger.label} `;
|
|
930
|
-
const style = isSelected ? focused ? textStyle({ bold: true, fg: theme.background, bg: color }) : textStyle({ bold: true, fg: theme.background, bg: theme.muted, dim: true }) : focused ? textStyle({ fg: theme.foreground }) : textStyle({ dim: true, fg: theme.muted });
|
|
931
|
-
if (i > 0) {
|
|
932
|
-
parts.push(
|
|
933
|
-
/* @__PURE__ */ jsx14("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2502" }, `div-${i}`)
|
|
934
|
-
);
|
|
935
|
-
}
|
|
936
|
-
parts.push(
|
|
937
|
-
/* @__PURE__ */ jsx14("span", { style, children: padded }, `opt-${i}`)
|
|
938
|
-
);
|
|
939
|
-
});
|
|
940
|
-
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", children: [
|
|
941
|
-
/* @__PURE__ */ jsx14("text", { children: parts }),
|
|
942
|
-
separator && /* @__PURE__ */ jsx14("text", { wrapMode: "none", style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(200) })
|
|
943
|
-
] });
|
|
944
|
-
}
|
|
945
|
-
function TabsTrigger(_props) {
|
|
946
|
-
return null;
|
|
947
|
-
}
|
|
948
|
-
function TabBar({
|
|
949
|
-
label,
|
|
950
|
-
options,
|
|
951
|
-
selectedIndex,
|
|
952
|
-
focused = true,
|
|
953
|
-
activeColor,
|
|
954
|
-
separator = true
|
|
955
|
-
}) {
|
|
956
|
-
return /* @__PURE__ */ jsx14(Tabs, { value: options[selectedIndex], children: /* @__PURE__ */ jsx14(TabsList, { label, focused, activeColor, separator, children: options.map((option) => /* @__PURE__ */ jsx14(TabsTrigger, { value: option, children: option }, option)) }) });
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// ../ui/components/modal/modal.tsx
|
|
960
|
-
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
961
|
-
function Modal({
|
|
962
|
-
children,
|
|
963
|
-
title,
|
|
964
|
-
borderColor,
|
|
965
|
-
borderStyle = "rounded",
|
|
966
|
-
onClose,
|
|
967
|
-
useKeyboard: useKeyboardProp
|
|
968
|
-
}) {
|
|
969
|
-
const theme = useTheme();
|
|
970
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
971
|
-
const resolvedBorderColor = borderColor ?? theme.muted;
|
|
972
|
-
useKeyboard?.((event) => {
|
|
973
|
-
if (event.name === "escape" && onClose) {
|
|
974
|
-
onClose();
|
|
975
|
-
}
|
|
976
|
-
});
|
|
977
|
-
return /* @__PURE__ */ jsx15("box", { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx15(
|
|
978
|
-
"box",
|
|
979
|
-
{
|
|
980
|
-
flexDirection: "column",
|
|
981
|
-
flexGrow: 1,
|
|
982
|
-
border: true,
|
|
983
|
-
borderStyle,
|
|
984
|
-
borderColor: resolvedBorderColor,
|
|
985
|
-
children: title ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
986
|
-
/* @__PURE__ */ jsx15("box", { paddingX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx15("text", { style: textStyle({ bold: true, fg: theme.primary }), children: title }) }),
|
|
987
|
-
children
|
|
988
|
-
] }) : children
|
|
989
|
-
}
|
|
990
|
-
) });
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
// ../ui/components/prompt-input/prompt-input.tsx
|
|
994
|
-
import {
|
|
995
|
-
useState as useState6,
|
|
996
|
-
useRef as useRef4,
|
|
997
|
-
useCallback as useCallback2,
|
|
998
|
-
useMemo as useMemo3,
|
|
999
|
-
createContext as createContext4,
|
|
1000
|
-
useContext as useContext4
|
|
1001
|
-
} from "react";
|
|
1002
|
-
import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1003
|
-
var PromptInputControllerCtx = createContext4(null);
|
|
1004
|
-
var useOptionalController = () => useContext4(PromptInputControllerCtx);
|
|
1005
|
-
var PromptInputContext = createContext4(null);
|
|
1006
|
-
function usePromptInput() {
|
|
1007
|
-
const ctx = useContext4(PromptInputContext);
|
|
1008
|
-
if (!ctx) {
|
|
1009
|
-
throw new Error("usePromptInput must be used within a <PromptInput> component");
|
|
1010
|
-
}
|
|
1011
|
-
return ctx;
|
|
1012
|
-
}
|
|
1013
|
-
function computeDefaultSuggestions(input, commands, files) {
|
|
1014
|
-
if (input.startsWith("/") && commands.length > 0) {
|
|
1015
|
-
return commands.filter((c) => c.cmd.startsWith(input)).map((c) => ({ text: c.cmd, desc: c.desc }));
|
|
1016
|
-
}
|
|
1017
|
-
if (input.includes("@") && files.length > 0) {
|
|
1018
|
-
const query = input.split("@").pop() ?? "";
|
|
1019
|
-
return files.filter((f) => f.toLowerCase().includes(query.toLowerCase())).map((f) => ({ text: "@" + f }));
|
|
1020
|
-
}
|
|
1021
|
-
return [];
|
|
1022
|
-
}
|
|
1023
|
-
function resolveStatusHintText(status, submittedText, streamingText, errorText, disabledText) {
|
|
1024
|
-
if (status === "submitted") return submittedText;
|
|
1025
|
-
if (status === "streaming") return streamingText;
|
|
1026
|
-
if (status === "error") return errorText;
|
|
1027
|
-
return disabledText;
|
|
1028
|
-
}
|
|
1029
|
-
function PromptInputDivider() {
|
|
1030
|
-
const { theme } = usePromptInput();
|
|
1031
|
-
return /* @__PURE__ */ jsx16("text", { wrapMode: "none", children: /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.muted }), children: "\u2500".repeat(500) }) });
|
|
1032
|
-
}
|
|
1033
|
-
function PromptInputSuggestions() {
|
|
1034
|
-
const { suggestions, sugIdx, maxSuggestions, theme } = usePromptInput();
|
|
1035
|
-
const visible = suggestions.slice(0, maxSuggestions);
|
|
1036
|
-
if (visible.length === 0) return null;
|
|
1037
|
-
return /* @__PURE__ */ jsx16("box", { flexDirection: "column", marginLeft: 2, children: visible.map((sug, i) => {
|
|
1038
|
-
const active = i === sugIdx;
|
|
1039
|
-
return /* @__PURE__ */ jsxs10("text", { children: [
|
|
1040
|
-
/* @__PURE__ */ jsx16("span", { style: textStyle({ fg: active ? theme.primary : theme.muted }), children: active ? "\u25B8 " : " " }),
|
|
1041
|
-
/* @__PURE__ */ jsx16("span", { style: textStyle({ fg: active ? theme.primary : theme.muted, bold: active }), children: sug.text }),
|
|
1042
|
-
sug.desc && /* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: " " + sug.desc })
|
|
1043
|
-
] }, sug.text);
|
|
1044
|
-
}) });
|
|
1045
|
-
}
|
|
1046
|
-
var CURSOR_CHAR = "\u258D";
|
|
1047
|
-
function PromptInputTextarea() {
|
|
1048
|
-
const { value, disabled, statusHintText, placeholder, prompt, promptColor, theme } = usePromptInput();
|
|
1049
|
-
return /* @__PURE__ */ jsxs10("text", { children: [
|
|
1050
|
-
/* @__PURE__ */ jsx16("span", { style: textStyle({ fg: promptColor }), children: prompt }),
|
|
1051
|
-
value.length === 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
1052
|
-
!disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR }),
|
|
1053
|
-
/* @__PURE__ */ jsx16("span", { style: textStyle({ dim: true, fg: theme.placeholder }), children: disabled ? statusHintText : " " + placeholder })
|
|
1054
|
-
] }) : /* @__PURE__ */ jsxs10(Fragment5, { children: [
|
|
1055
|
-
/* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.foreground }), children: value }),
|
|
1056
|
-
!disabled && /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.muted }), children: CURSOR_CHAR })
|
|
1057
|
-
] })
|
|
1058
|
-
] });
|
|
1059
|
-
}
|
|
1060
|
-
function PromptInputSubmit(props) {
|
|
1061
|
-
const ctx = usePromptInput();
|
|
1062
|
-
const status = props.status ?? ctx.status;
|
|
1063
|
-
const onStop = props.onStop ?? ctx.onStop;
|
|
1064
|
-
const { disabled, theme } = ctx;
|
|
1065
|
-
const isGenerating = status === "submitted" || status === "streaming";
|
|
1066
|
-
const icon = status === "submitted" ? "\u25D0" : status === "streaming" ? onStop ? "\u25A0" : "\u25D0" : status === "error" ? "\u2715" : "\u23CE";
|
|
1067
|
-
const color = status === "error" ? theme.error : isGenerating ? theme.muted : disabled ? theme.muted : theme.primary;
|
|
1068
|
-
return /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: color }), children: " " + icon }) });
|
|
1069
|
-
}
|
|
1070
|
-
function PromptInputStatusText() {
|
|
1071
|
-
const { status, errorText, theme } = usePromptInput();
|
|
1072
|
-
if (status !== "error") return null;
|
|
1073
|
-
return /* @__PURE__ */ jsx16("text", { children: /* @__PURE__ */ jsx16("span", { style: textStyle({ fg: theme.error }), children: errorText }) });
|
|
1074
|
-
}
|
|
1075
|
-
function PromptInput({
|
|
1076
|
-
value: controlledValue,
|
|
1077
|
-
defaultValue = "",
|
|
1078
|
-
onSubmit,
|
|
1079
|
-
onChange,
|
|
1080
|
-
placeholder = "Type a message...",
|
|
1081
|
-
prompt = "\u276F ",
|
|
1082
|
-
promptColor,
|
|
1083
|
-
status,
|
|
1084
|
-
onStop,
|
|
1085
|
-
submittedText = "Thinking...",
|
|
1086
|
-
streamingText: streamingLabel = "Generating...",
|
|
1087
|
-
errorText = "An error occurred. Try again.",
|
|
1088
|
-
disabled: disabledProp = false,
|
|
1089
|
-
disabledText = "Generating...",
|
|
1090
|
-
commands = [],
|
|
1091
|
-
files = [],
|
|
1092
|
-
getSuggestions: customGetSuggestions,
|
|
1093
|
-
maxSuggestions = 5,
|
|
1094
|
-
enableHistory = true,
|
|
1095
|
-
showDividers = false,
|
|
1096
|
-
useKeyboard: useKeyboardProp,
|
|
1097
|
-
children
|
|
1098
|
-
}) {
|
|
1099
|
-
const theme = useTheme();
|
|
1100
|
-
const useKeyboard = useKeyboardContext(useKeyboardProp);
|
|
1101
|
-
const resolvedPromptColor = promptColor ?? theme.muted;
|
|
1102
|
-
const disabled = status ? status === "submitted" || status === "streaming" : disabledProp;
|
|
1103
|
-
const statusHintText = resolveStatusHintText(status, submittedText, streamingLabel, errorText, disabledText);
|
|
1104
|
-
const controller = useOptionalController();
|
|
1105
|
-
const usingProvider = !!controller;
|
|
1106
|
-
const isControlled = controlledValue !== void 0;
|
|
1107
|
-
const controlledRef = useRef4(isControlled);
|
|
1108
|
-
if (controlledRef.current !== isControlled) {
|
|
1109
|
-
console.warn("PromptInput: switching between controlled and uncontrolled is not supported.");
|
|
1110
|
-
}
|
|
1111
|
-
const [localValue, setLocalValue] = useState6(defaultValue);
|
|
1112
|
-
const [localSuggestions, setLocalSuggestions] = useState6([]);
|
|
1113
|
-
const [localSugIdx, setLocalSugIdx] = useState6(0);
|
|
1114
|
-
const [history, setHistory] = useState6([]);
|
|
1115
|
-
const [histIdx, setHistIdx] = useState6(-1);
|
|
1116
|
-
const value = isControlled ? controlledValue : usingProvider ? controller.textInput.value : localValue;
|
|
1117
|
-
const suggestions = usingProvider ? controller.suggestions.suggestions : localSuggestions;
|
|
1118
|
-
const sugIdx = usingProvider ? controller.suggestions.selectedIndex : localSugIdx;
|
|
1119
|
-
const valueRef = useRef4(defaultValue);
|
|
1120
|
-
if (isControlled) valueRef.current = controlledValue;
|
|
1121
|
-
else if (usingProvider) valueRef.current = controller.textInput.value;
|
|
1122
|
-
else valueRef.current = localValue;
|
|
1123
|
-
const suggestionsRef = useRef4([]);
|
|
1124
|
-
suggestionsRef.current = suggestions;
|
|
1125
|
-
const sugIdxRef = useRef4(0);
|
|
1126
|
-
sugIdxRef.current = sugIdx;
|
|
1127
|
-
const historyRef = useRef4([]);
|
|
1128
|
-
historyRef.current = history;
|
|
1129
|
-
const histIdxRef = useRef4(-1);
|
|
1130
|
-
histIdxRef.current = histIdx;
|
|
1131
|
-
const setSug = useCallback2((next) => {
|
|
1132
|
-
suggestionsRef.current = next;
|
|
1133
|
-
if (usingProvider) {
|
|
1134
|
-
controller.suggestions.setSuggestions(next);
|
|
1135
|
-
} else {
|
|
1136
|
-
setLocalSuggestions(next);
|
|
1137
|
-
}
|
|
1138
|
-
}, [usingProvider, controller]);
|
|
1139
|
-
const setSugI = useCallback2((next) => {
|
|
1140
|
-
sugIdxRef.current = next;
|
|
1141
|
-
if (usingProvider) {
|
|
1142
|
-
controller.suggestions.setSelectedIndex(next);
|
|
1143
|
-
} else {
|
|
1144
|
-
setLocalSugIdx(next);
|
|
1145
|
-
}
|
|
1146
|
-
}, [usingProvider, controller]);
|
|
1147
|
-
const setHist = useCallback2((next) => {
|
|
1148
|
-
historyRef.current = next;
|
|
1149
|
-
setHistory(next);
|
|
1150
|
-
}, []);
|
|
1151
|
-
const setHistI = useCallback2((next) => {
|
|
1152
|
-
histIdxRef.current = next;
|
|
1153
|
-
setHistIdx(next);
|
|
1154
|
-
}, []);
|
|
1155
|
-
const computeSuggestions = useCallback2((input) => {
|
|
1156
|
-
if (customGetSuggestions) return customGetSuggestions(input);
|
|
1157
|
-
return computeDefaultSuggestions(input, commands, files);
|
|
1158
|
-
}, [customGetSuggestions, commands, files]);
|
|
1159
|
-
const updateValue = useCallback2((next) => {
|
|
1160
|
-
valueRef.current = next;
|
|
1161
|
-
if (isControlled) {
|
|
1162
|
-
} else if (usingProvider) {
|
|
1163
|
-
controller.textInput.setValue(next);
|
|
1164
|
-
} else {
|
|
1165
|
-
setLocalValue(next);
|
|
1166
|
-
}
|
|
1167
|
-
onChange?.(next);
|
|
1168
|
-
const sug = computeSuggestions(next);
|
|
1169
|
-
setSug(sug);
|
|
1170
|
-
setSugI(0);
|
|
1171
|
-
}, [isControlled, usingProvider, controller, onChange, computeSuggestions, setSug, setSugI]);
|
|
1172
|
-
const clearInput = useCallback2(() => {
|
|
1173
|
-
if (usingProvider) {
|
|
1174
|
-
controller.textInput.clear();
|
|
1175
|
-
} else if (!isControlled) {
|
|
1176
|
-
setLocalValue("");
|
|
1177
|
-
}
|
|
1178
|
-
onChange?.("");
|
|
1179
|
-
}, [usingProvider, controller, isControlled, onChange]);
|
|
1180
|
-
const handleSubmit = useCallback2((text) => {
|
|
1181
|
-
if (!onSubmit) return;
|
|
1182
|
-
const result = onSubmit({ text });
|
|
1183
|
-
if (result instanceof Promise) {
|
|
1184
|
-
result.then(
|
|
1185
|
-
() => clearInput(),
|
|
1186
|
-
() => {
|
|
1187
|
-
}
|
|
1188
|
-
);
|
|
1189
|
-
} else {
|
|
1190
|
-
clearInput();
|
|
1191
|
-
}
|
|
1192
|
-
}, [onSubmit, clearInput]);
|
|
1193
|
-
useKeyboard?.((event) => {
|
|
1194
|
-
if (event.name === "escape" && (status === "streaming" || status === "submitted") && onStop) {
|
|
1195
|
-
onStop();
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
if (disabled) return;
|
|
1199
|
-
if (event.name === "return") {
|
|
1200
|
-
if (suggestionsRef.current.length > 0) {
|
|
1201
|
-
const sel = suggestionsRef.current[sugIdxRef.current];
|
|
1202
|
-
if (sel) {
|
|
1203
|
-
if (valueRef.current.startsWith("/")) {
|
|
1204
|
-
setSug([]);
|
|
1205
|
-
updateValue("");
|
|
1206
|
-
if (enableHistory) {
|
|
1207
|
-
setHist([sel.text, ...historyRef.current]);
|
|
1208
|
-
}
|
|
1209
|
-
setHistI(-1);
|
|
1210
|
-
handleSubmit(sel.text);
|
|
1211
|
-
} else {
|
|
1212
|
-
const base = valueRef.current.slice(0, valueRef.current.lastIndexOf("@"));
|
|
1213
|
-
updateValue(base + sel.text + " ");
|
|
1214
|
-
setSug([]);
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
} else {
|
|
1218
|
-
const trimmed = valueRef.current.trim();
|
|
1219
|
-
if (!trimmed) return;
|
|
1220
|
-
if (enableHistory) {
|
|
1221
|
-
setHist([trimmed, ...historyRef.current]);
|
|
1222
|
-
}
|
|
1223
|
-
updateValue("");
|
|
1224
|
-
setHistI(-1);
|
|
1225
|
-
setSug([]);
|
|
1226
|
-
handleSubmit(trimmed);
|
|
1227
|
-
}
|
|
1228
|
-
return;
|
|
1229
|
-
}
|
|
1230
|
-
if (event.name === "tab" && suggestionsRef.current.length > 0) {
|
|
1231
|
-
setSugI((sugIdxRef.current + 1) % suggestionsRef.current.length);
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
if (event.name === "up") {
|
|
1235
|
-
if (suggestionsRef.current.length > 0) {
|
|
1236
|
-
setSugI(Math.max(0, sugIdxRef.current - 1));
|
|
1237
|
-
} else if (enableHistory && historyRef.current.length > 0) {
|
|
1238
|
-
const idx = Math.min(historyRef.current.length - 1, histIdxRef.current + 1);
|
|
1239
|
-
setHistI(idx);
|
|
1240
|
-
updateValue(historyRef.current[idx]);
|
|
1241
|
-
}
|
|
1242
|
-
return;
|
|
1243
|
-
}
|
|
1244
|
-
if (event.name === "down") {
|
|
1245
|
-
if (suggestionsRef.current.length > 0) {
|
|
1246
|
-
setSugI(Math.min(suggestionsRef.current.length - 1, sugIdxRef.current + 1));
|
|
1247
|
-
} else if (enableHistory && histIdxRef.current > 0) {
|
|
1248
|
-
const nextIdx = histIdxRef.current - 1;
|
|
1249
|
-
setHistI(nextIdx);
|
|
1250
|
-
updateValue(historyRef.current[nextIdx]);
|
|
1251
|
-
} else if (enableHistory && histIdxRef.current === 0) {
|
|
1252
|
-
setHistI(-1);
|
|
1253
|
-
updateValue("");
|
|
1254
|
-
}
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
if (event.name === "escape") {
|
|
1258
|
-
if (suggestionsRef.current.length > 0) {
|
|
1259
|
-
setSug([]);
|
|
1260
|
-
}
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
if (event.name === "backspace" || event.name === "delete") {
|
|
1264
|
-
updateValue(valueRef.current.slice(0, -1));
|
|
1265
|
-
return;
|
|
1266
|
-
}
|
|
1267
|
-
if (event.ctrl || event.meta) return;
|
|
1268
|
-
if (event.name === "space") {
|
|
1269
|
-
updateValue(valueRef.current + " ");
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
if (event.name && event.name.length === 1) {
|
|
1273
|
-
updateValue(valueRef.current + event.name);
|
|
1274
|
-
}
|
|
1275
|
-
});
|
|
1276
|
-
const visibleSuggestions = suggestions.slice(0, maxSuggestions);
|
|
1277
|
-
const ctxValue = {
|
|
1278
|
-
value,
|
|
1279
|
-
disabled,
|
|
1280
|
-
status,
|
|
1281
|
-
onStop,
|
|
1282
|
-
statusHintText,
|
|
1283
|
-
placeholder,
|
|
1284
|
-
prompt,
|
|
1285
|
-
promptColor: resolvedPromptColor,
|
|
1286
|
-
suggestions: visibleSuggestions,
|
|
1287
|
-
sugIdx,
|
|
1288
|
-
maxSuggestions,
|
|
1289
|
-
errorText,
|
|
1290
|
-
theme
|
|
1291
|
-
};
|
|
1292
|
-
if (children) {
|
|
1293
|
-
return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsx16("box", { flexDirection: "column", children }) });
|
|
1294
|
-
}
|
|
1295
|
-
return /* @__PURE__ */ jsx16(PromptInputContext.Provider, { value: ctxValue, children: /* @__PURE__ */ jsxs10("box", { flexDirection: "column", children: [
|
|
1296
|
-
showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {}),
|
|
1297
|
-
/* @__PURE__ */ jsx16(PromptInputSuggestions, {}),
|
|
1298
|
-
/* @__PURE__ */ jsx16(PromptInputTextarea, {}),
|
|
1299
|
-
/* @__PURE__ */ jsx16(PromptInputStatusText, {}),
|
|
1300
|
-
showDividers && /* @__PURE__ */ jsx16(PromptInputDivider, {})
|
|
1301
|
-
] }) });
|
|
1302
|
-
}
|
|
1303
|
-
PromptInput.Textarea = PromptInputTextarea;
|
|
1304
|
-
PromptInput.Suggestions = PromptInputSuggestions;
|
|
1305
|
-
PromptInput.Submit = PromptInputSubmit;
|
|
1306
|
-
PromptInput.Divider = PromptInputDivider;
|
|
1307
|
-
PromptInput.StatusText = PromptInputStatusText;
|
|
1308
|
-
|
|
1309
|
-
// ../ui/components/chat/chat.tsx
|
|
1310
|
-
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1311
|
-
var STATUS_ICONS = {
|
|
1312
|
-
pending: "\u2022",
|
|
1313
|
-
in_progress: "\u280B",
|
|
1314
|
-
completed: "\u2713",
|
|
1315
|
-
failed: "\u2717"
|
|
1316
|
-
};
|
|
1317
|
-
function getStatusColors(theme) {
|
|
1318
|
-
return {
|
|
1319
|
-
pending: theme.muted,
|
|
1320
|
-
in_progress: theme.warning,
|
|
1321
|
-
completed: theme.success,
|
|
1322
|
-
failed: theme.error
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
function MessageBubble({
|
|
1326
|
-
message,
|
|
1327
|
-
userColor,
|
|
1328
|
-
assistantColor,
|
|
1329
|
-
foregroundColor
|
|
1330
|
-
}) {
|
|
1331
|
-
const isUser = message.role === "user";
|
|
1332
|
-
const prefix = isUser ? "> " : "< ";
|
|
1333
|
-
const color = isUser ? userColor : assistantColor;
|
|
1334
|
-
return /* @__PURE__ */ jsxs11("text", { wrapMode: "word", children: [
|
|
1335
|
-
/* @__PURE__ */ jsx17("span", { style: textStyle({ bold: true, fg: color }), children: prefix }),
|
|
1336
|
-
/* @__PURE__ */ jsx17("span", { style: { fg: foregroundColor }, children: message.content })
|
|
1337
|
-
] });
|
|
1338
|
-
}
|
|
1339
|
-
function StreamingTextDisplay({
|
|
1340
|
-
text,
|
|
1341
|
-
assistantColor,
|
|
1342
|
-
foregroundColor,
|
|
1343
|
-
cursorChar = "_"
|
|
1344
|
-
}) {
|
|
1345
|
-
if (!text) return null;
|
|
1346
|
-
return /* @__PURE__ */ jsxs11("text", { wrapMode: "word", children: [
|
|
1347
|
-
/* @__PURE__ */ jsx17("span", { style: textStyle({ bold: true, fg: assistantColor }), children: "< " }),
|
|
1348
|
-
/* @__PURE__ */ jsx17("span", { style: { fg: foregroundColor }, children: text }),
|
|
1349
|
-
/* @__PURE__ */ jsx17("span", { style: textStyle({ dim: true, fg: foregroundColor }), children: cursorChar })
|
|
1350
|
-
] });
|
|
1351
|
-
}
|
|
1352
|
-
function ToolCallCard({ toolCall, statusColors }) {
|
|
1353
|
-
const icon = STATUS_ICONS[toolCall.status] || "\u2022";
|
|
1354
|
-
const color = statusColors[toolCall.status] || "gray";
|
|
1355
|
-
const showEllipsis = toolCall.status === "pending" || toolCall.status === "in_progress";
|
|
1356
|
-
return /* @__PURE__ */ jsxs11("text", { children: [
|
|
1357
|
-
/* @__PURE__ */ jsx17("span", { children: " " }),
|
|
1358
|
-
/* @__PURE__ */ jsx17("span", { style: textStyle({ fg: color }), children: icon }),
|
|
1359
|
-
/* @__PURE__ */ jsx17("span", { children: " " }),
|
|
1360
|
-
/* @__PURE__ */ jsx17("span", { style: textStyle({ fg: color }), children: toolCall.title }),
|
|
1361
|
-
showEllipsis && /* @__PURE__ */ jsx17("span", { style: textStyle({ dim: true, fg: color }), children: " ..." })
|
|
1362
|
-
] });
|
|
1363
|
-
}
|
|
1364
|
-
function ChatPanel({
|
|
1365
|
-
messages,
|
|
1366
|
-
streamingText = "",
|
|
1367
|
-
status,
|
|
1368
|
-
isLoading = false,
|
|
1369
|
-
activeToolCalls = [],
|
|
1370
|
-
onSendMessage,
|
|
1371
|
-
onStop,
|
|
1372
|
-
onCancel,
|
|
1373
|
-
placeholder = "Type a message...",
|
|
1374
|
-
promptChar = "> ",
|
|
1375
|
-
promptColor,
|
|
1376
|
-
userColor,
|
|
1377
|
-
assistantColor,
|
|
1378
|
-
loadingText = "Thinking...",
|
|
1379
|
-
useKeyboard: useKeyboardProp
|
|
1380
|
-
}) {
|
|
1381
|
-
const theme = useTheme();
|
|
1382
|
-
const resolvedUserColor = userColor ?? theme.secondary;
|
|
1383
|
-
const resolvedAssistantColor = assistantColor ?? theme.primary;
|
|
1384
|
-
const resolvedPromptColor = promptColor ?? theme.secondary;
|
|
1385
|
-
const statusColors = getStatusColors(theme);
|
|
1386
|
-
const chatStatus = status ? status : isLoading && !streamingText ? "submitted" : streamingText ? "streaming" : void 0;
|
|
1387
|
-
const isSubmitted = chatStatus === "submitted";
|
|
1388
|
-
const isStreaming = chatStatus === "streaming";
|
|
1389
|
-
const stopHandler = onStop ?? onCancel;
|
|
1390
|
-
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", paddingX: 1, children: [
|
|
1391
|
-
messages.map((msg) => /* @__PURE__ */ jsx17(
|
|
1392
|
-
MessageBubble,
|
|
1393
|
-
{
|
|
1394
|
-
message: msg,
|
|
1395
|
-
userColor: resolvedUserColor,
|
|
1396
|
-
assistantColor: resolvedAssistantColor,
|
|
1397
|
-
foregroundColor: theme.foreground
|
|
1398
|
-
},
|
|
1399
|
-
msg.id
|
|
1400
|
-
)),
|
|
1401
|
-
activeToolCalls.map((tc) => /* @__PURE__ */ jsx17(ToolCallCard, { toolCall: tc, statusColors }, tc.id)),
|
|
1402
|
-
isStreaming && streamingText ? /* @__PURE__ */ jsx17(
|
|
1403
|
-
StreamingTextDisplay,
|
|
1404
|
-
{
|
|
1405
|
-
text: streamingText,
|
|
1406
|
-
assistantColor: resolvedAssistantColor,
|
|
1407
|
-
foregroundColor: theme.foreground
|
|
1408
|
-
}
|
|
1409
|
-
) : isSubmitted ? /* @__PURE__ */ jsxs11("text", { style: textStyle({ dim: true, fg: theme.muted }), children: [
|
|
1410
|
-
" ",
|
|
1411
|
-
loadingText
|
|
1412
|
-
] }) : null,
|
|
1413
|
-
/* @__PURE__ */ jsx17("box", { marginTop: 1, children: /* @__PURE__ */ jsx17(
|
|
1414
|
-
PromptInput,
|
|
1415
|
-
{
|
|
1416
|
-
onSubmit: (msg) => onSendMessage(msg.text),
|
|
1417
|
-
onStop: stopHandler,
|
|
1418
|
-
status: chatStatus,
|
|
1419
|
-
placeholder,
|
|
1420
|
-
prompt: promptChar,
|
|
1421
|
-
promptColor: resolvedPromptColor,
|
|
1422
|
-
foregroundColor: theme.foreground,
|
|
1423
|
-
submittedText: loadingText,
|
|
1424
|
-
useKeyboard: useKeyboardProp
|
|
1425
|
-
}
|
|
1426
|
-
) })
|
|
1427
|
-
] });
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// ../ui/components/timeline/timeline.tsx
|
|
1431
|
-
import { useState as useState7, useEffect as useEffect2, useRef as useRef5 } from "react";
|
|
1432
|
-
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1433
|
-
var DOTS = ["\u25CB", "\u25D4", "\u25D1", "\u25D5", "\u25CF"];
|
|
1434
|
-
function getStepDot(status) {
|
|
1435
|
-
return status === "pending" ? "\u25CB" : "\u25CF";
|
|
1436
|
-
}
|
|
1437
|
-
function getStepColor(status, theme) {
|
|
1438
|
-
switch (status) {
|
|
1439
|
-
case "done":
|
|
1440
|
-
return theme.success;
|
|
1441
|
-
case "running":
|
|
1442
|
-
return theme.primary;
|
|
1443
|
-
case "pending":
|
|
1444
|
-
return theme.muted;
|
|
1445
|
-
case "error":
|
|
1446
|
-
return theme.error;
|
|
1447
|
-
default:
|
|
1448
|
-
return theme.muted;
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
function StepRow({ step, isLast, theme, frame }) {
|
|
1452
|
-
const color = getStepColor(step.status, theme);
|
|
1453
|
-
const isActive = step.status === "running";
|
|
1454
|
-
const isPending = step.status === "pending";
|
|
1455
|
-
const pipe = "\u2502";
|
|
1456
|
-
const dot = isActive ? DOTS[frame % DOTS.length] : getStepDot(step.status);
|
|
1457
|
-
const dashIdx = step.label.indexOf(" \u2014 ");
|
|
1458
|
-
const mainLabel = dashIdx >= 0 ? step.label.slice(0, dashIdx) : step.label;
|
|
1459
|
-
const detail = dashIdx >= 0 ? step.label.slice(dashIdx) : "";
|
|
1460
|
-
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginLeft: 1, children: [
|
|
1461
|
-
/* @__PURE__ */ jsxs12("text", { children: [
|
|
1462
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color }), children: dot }),
|
|
1463
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.foreground }), children: " " }),
|
|
1464
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: isPending ? theme.muted : color, dim: isPending, bold: isActive }), children: mainLabel }),
|
|
1465
|
-
detail && /* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: detail })
|
|
1466
|
-
] }),
|
|
1467
|
-
step.output && /* @__PURE__ */ jsxs12("text", { children: [
|
|
1468
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe + " " }),
|
|
1469
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: step.status === "error" ? theme.error : theme.accent }), children: step.output })
|
|
1470
|
-
] }),
|
|
1471
|
-
!isLast && /* @__PURE__ */ jsx18("text", { children: /* @__PURE__ */ jsx18("span", { style: textStyle({ fg: color, dim: true }), children: pipe }) })
|
|
1472
|
-
] });
|
|
1473
|
-
}
|
|
1474
|
-
function Timeline({
|
|
1475
|
-
steps,
|
|
1476
|
-
duration,
|
|
1477
|
-
collapsed = true,
|
|
1478
|
-
headerLabel = "Thought for"
|
|
1479
|
-
}) {
|
|
1480
|
-
const theme = useTheme();
|
|
1481
|
-
const arrow = collapsed ? "\u25B6" : "\u25BC";
|
|
1482
|
-
const durationStr = duration ?? "0ms";
|
|
1483
|
-
const hasRunning = steps?.some((s) => s.status === "running") ?? false;
|
|
1484
|
-
const [frame, setFrame] = useState7(0);
|
|
1485
|
-
const alive = useRef5(true);
|
|
1486
|
-
useEffect2(() => {
|
|
1487
|
-
alive.current = true;
|
|
1488
|
-
return () => {
|
|
1489
|
-
alive.current = false;
|
|
1490
|
-
};
|
|
1491
|
-
}, []);
|
|
1492
|
-
useEffect2(() => {
|
|
1493
|
-
if (!hasRunning) return;
|
|
1494
|
-
const id = setInterval(() => {
|
|
1495
|
-
if (alive.current) setFrame((f) => f + 1);
|
|
1496
|
-
}, 150);
|
|
1497
|
-
return () => clearInterval(id);
|
|
1498
|
-
}, [hasRunning]);
|
|
1499
|
-
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", children: [
|
|
1500
|
-
/* @__PURE__ */ jsxs12("text", { children: [
|
|
1501
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ fg: theme.muted }), children: arrow }),
|
|
1502
|
-
/* @__PURE__ */ jsx18("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " " + headerLabel + " " + durationStr })
|
|
1503
|
-
] }),
|
|
1504
|
-
!collapsed && steps && steps.map((step, i) => /* @__PURE__ */ jsx18(
|
|
1505
|
-
StepRow,
|
|
1506
|
-
{
|
|
1507
|
-
step,
|
|
1508
|
-
isLast: i === steps.length - 1,
|
|
1509
|
-
theme,
|
|
1510
|
-
frame
|
|
1511
|
-
},
|
|
1512
|
-
`step-${i}`
|
|
1513
|
-
))
|
|
1514
|
-
] });
|
|
1515
|
-
}
|
|
1516
|
-
|
|
1517
|
-
// ../ui/components/message/message.tsx
|
|
1518
|
-
import { createContext as createContext5, useContext as useContext5 } from "react";
|
|
1519
|
-
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1520
|
-
var MessageContext = createContext5(null);
|
|
1521
|
-
function useMessage() {
|
|
1522
|
-
const ctx = useContext5(MessageContext);
|
|
1523
|
-
if (!ctx) throw new Error("useMessage must be used within <Message>");
|
|
1524
|
-
return ctx;
|
|
1525
|
-
}
|
|
1526
|
-
function getBubbleColors(theme) {
|
|
1527
|
-
const isDark = theme.background !== "#FFFFFF";
|
|
1528
|
-
return isDark ? { assistantBg: "#2a2a4a", userBg: "#2a3a3a" } : { assistantBg: "#F1F5F9", userBg: "#E2E8F0" };
|
|
1529
|
-
}
|
|
1530
|
-
var TOOL_STATE_ICONS = {
|
|
1531
|
-
"partial-call": "\u2022",
|
|
1532
|
-
// •
|
|
1533
|
-
"call": "\u280B",
|
|
1534
|
-
// ⠋
|
|
1535
|
-
"result": "\u2713"
|
|
1536
|
-
// ✓
|
|
1537
|
-
};
|
|
1538
|
-
function getToolStateColor(state, theme) {
|
|
1539
|
-
switch (state) {
|
|
1540
|
-
case "partial-call":
|
|
1541
|
-
return theme.muted;
|
|
1542
|
-
case "call":
|
|
1543
|
-
return theme.warning;
|
|
1544
|
-
case "result":
|
|
1545
|
-
return theme.success;
|
|
1546
|
-
default:
|
|
1547
|
-
return theme.muted;
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
function MessageContent({ children }) {
|
|
1551
|
-
const { role, backgroundColor } = useMessage();
|
|
1552
|
-
const isUser = role === "user";
|
|
1553
|
-
return /* @__PURE__ */ jsx19(
|
|
1554
|
-
"box",
|
|
1555
|
-
{
|
|
1556
|
-
flexDirection: "column",
|
|
1557
|
-
backgroundColor,
|
|
1558
|
-
paddingX: 2,
|
|
1559
|
-
paddingY: 1,
|
|
1560
|
-
...isUser ? { maxWidth: "85%" } : { width: "85%" },
|
|
1561
|
-
children
|
|
1562
|
-
}
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
function MessageText({ children, isLast = false }) {
|
|
1566
|
-
const { isStreaming, streamingCursor, backgroundColor, textColor } = useMessage();
|
|
1567
|
-
return /* @__PURE__ */ jsxs13("text", { wrapMode: "word", children: [
|
|
1568
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children }),
|
|
1569
|
-
isLast && isStreaming && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: streamingCursor })
|
|
1570
|
-
] });
|
|
1571
|
-
}
|
|
1572
|
-
function MessageReasoning({ part }) {
|
|
1573
|
-
return /* @__PURE__ */ jsx19(
|
|
1574
|
-
Timeline,
|
|
1575
|
-
{
|
|
1576
|
-
steps: part.steps,
|
|
1577
|
-
duration: part.duration,
|
|
1578
|
-
collapsed: part.collapsed
|
|
1579
|
-
}
|
|
1580
|
-
);
|
|
1581
|
-
}
|
|
1582
|
-
function MessageToolInvocation({ part, toolColors }) {
|
|
1583
|
-
const theme = useTheme();
|
|
1584
|
-
const { backgroundColor, textColor } = useMessage();
|
|
1585
|
-
const { toolName, state, result } = part.toolInvocation;
|
|
1586
|
-
const icon = TOOL_STATE_ICONS[state] || "\u2022";
|
|
1587
|
-
const stateColor = toolColors?.[toolName] ?? getToolStateColor(state, theme);
|
|
1588
|
-
const isActive = state === "partial-call" || state === "call";
|
|
1589
|
-
return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", children: [
|
|
1590
|
-
/* @__PURE__ */ jsxs13("text", { children: [
|
|
1591
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bg: backgroundColor }), children: icon }),
|
|
1592
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, bg: backgroundColor }), children: " " }),
|
|
1593
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: stateColor, bold: isActive, bg: backgroundColor }), children: toolName }),
|
|
1594
|
-
isActive && /* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " ..." })
|
|
1595
|
-
] }),
|
|
1596
|
-
state === "result" && result !== void 0 && /* @__PURE__ */ jsxs13("text", { children: [
|
|
1597
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: " \u2514\u2500 " }),
|
|
1598
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: String(result).slice(0, 120) })
|
|
1599
|
-
] })
|
|
1600
|
-
] });
|
|
1601
|
-
}
|
|
1602
|
-
function MessageSource({ part, index }) {
|
|
1603
|
-
const theme = useTheme();
|
|
1604
|
-
const { backgroundColor, textColor } = useMessage();
|
|
1605
|
-
const title = part.source.title || part.source.url || "source";
|
|
1606
|
-
return /* @__PURE__ */ jsxs13("text", { children: [
|
|
1607
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "[" }),
|
|
1608
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: String(index + 1) }),
|
|
1609
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: textColor, dim: true, bg: backgroundColor }), children: "] " }),
|
|
1610
|
-
/* @__PURE__ */ jsx19("span", { style: textStyle({ fg: theme.accent, bg: backgroundColor }), children: title })
|
|
1611
|
-
] });
|
|
1612
|
-
}
|
|
1613
|
-
function MessageFooter({ model, timestamp }) {
|
|
1614
|
-
const theme = useTheme();
|
|
1615
|
-
if (!model && !timestamp) return null;
|
|
1616
|
-
return /* @__PURE__ */ jsxs13("text", { children: [
|
|
1617
|
-
model && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: model }),
|
|
1618
|
-
model && timestamp && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: " \xB7 " }),
|
|
1619
|
-
timestamp && /* @__PURE__ */ jsx19("span", { style: textStyle({ dim: true, fg: theme.muted }), children: timestamp })
|
|
1620
|
-
] });
|
|
1621
|
-
}
|
|
1622
|
-
function Message({
|
|
1623
|
-
role,
|
|
1624
|
-
isStreaming = false,
|
|
1625
|
-
streamingCursor = "\u258E",
|
|
1626
|
-
backgroundColor,
|
|
1627
|
-
children
|
|
1628
|
-
}) {
|
|
1629
|
-
const theme = useTheme();
|
|
1630
|
-
const { assistantBg, userBg } = getBubbleColors(theme);
|
|
1631
|
-
const isUser = role === "user";
|
|
1632
|
-
const bg = backgroundColor ?? (isUser ? userBg : assistantBg);
|
|
1633
|
-
return /* @__PURE__ */ jsx19(MessageContext.Provider, { value: { role, isStreaming, streamingCursor, backgroundColor: bg, textColor: theme.foreground }, children: /* @__PURE__ */ jsx19(
|
|
1634
|
-
"box",
|
|
1635
|
-
{
|
|
1636
|
-
flexDirection: "column",
|
|
1637
|
-
flexShrink: 0,
|
|
1638
|
-
alignItems: isUser ? "flex-end" : "flex-start",
|
|
1639
|
-
children
|
|
1640
|
-
}
|
|
1641
|
-
) });
|
|
1642
|
-
}
|
|
1643
|
-
Message.Content = MessageContent;
|
|
1644
|
-
Message.Text = MessageText;
|
|
1645
|
-
Message.Reasoning = MessageReasoning;
|
|
1646
|
-
Message.ToolInvocation = MessageToolInvocation;
|
|
1647
|
-
Message.Source = MessageSource;
|
|
1648
|
-
Message.Footer = MessageFooter;
|
|
1649
|
-
|
|
1650
|
-
// ../ui/components/terminal-window/terminal-window.tsx
|
|
1651
|
-
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1652
|
-
|
|
1653
|
-
// ../ui/components/made-with-opentui/made-with-opentui.tsx
|
|
1654
|
-
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1655
|
-
|
|
1656
|
-
// ../ui/components/made-with-gridland/made-with-gridland.tsx
|
|
1657
|
-
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1658
|
-
|
|
1659
|
-
// ../ui/components/breakpoints/use-breakpoints.ts
|
|
1660
|
-
import { useTerminalDimensions } from "@opentui/react";
|
|
1661
|
-
var BREAKPOINTS = {
|
|
1662
|
-
tiny: 40,
|
|
1663
|
-
narrow: 60,
|
|
1664
|
-
mobile: 70
|
|
1665
|
-
};
|
|
1666
|
-
function useBreakpoints() {
|
|
1667
|
-
const { width, height } = useTerminalDimensions();
|
|
1668
|
-
return {
|
|
1669
|
-
isTiny: width < BREAKPOINTS.tiny,
|
|
1670
|
-
isNarrow: width < BREAKPOINTS.narrow,
|
|
1671
|
-
isMobile: width < BREAKPOINTS.mobile,
|
|
1672
|
-
isDesktop: width >= BREAKPOINTS.mobile,
|
|
1673
|
-
width,
|
|
1674
|
-
height
|
|
1675
|
-
};
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
// ../docs/components/landing/logo.tsx
|
|
1679
|
-
import { useState as useState8, useEffect as useEffect3, useRef as useRef6, useMemo as useMemo4 } from "react";
|
|
1680
|
-
import figlet from "figlet";
|
|
1681
|
-
import ansiShadow from "figlet/importable-fonts/ANSI Shadow.js";
|
|
1682
|
-
import { Fragment as Fragment6, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1683
|
-
figlet.parseFont("ANSI Shadow", ansiShadow);
|
|
1684
|
-
function makeArt(text) {
|
|
1685
|
-
return figlet.textSync(text, { font: "ANSI Shadow" }).split("\n").filter((l) => l.trimEnd().length > 0).join("\n");
|
|
1686
|
-
}
|
|
1687
|
-
var fullArt = makeArt("gridland");
|
|
1688
|
-
var gridArt = makeArt("grid");
|
|
1689
|
-
var landArt = makeArt("land");
|
|
1690
|
-
var ART_HEIGHT = 6;
|
|
1691
|
-
function useAnimation(duration = 1e3) {
|
|
1692
|
-
const isBrowser = typeof document !== "undefined";
|
|
1693
|
-
const [progress, setProgress] = useState8(isBrowser ? 0 : 1);
|
|
1694
|
-
const startTime = useRef6(null);
|
|
1695
|
-
useEffect3(() => {
|
|
1696
|
-
if (!isBrowser) return;
|
|
1697
|
-
let raf;
|
|
1698
|
-
const tick = (time) => {
|
|
1699
|
-
if (startTime.current === null) startTime.current = time;
|
|
1700
|
-
const elapsed = time - startTime.current;
|
|
1701
|
-
const t = Math.min(1, elapsed / duration);
|
|
1702
|
-
const eased = 1 - Math.pow(1 - t, 3);
|
|
1703
|
-
setProgress(eased);
|
|
1704
|
-
if (t < 1) raf = requestAnimationFrame(tick);
|
|
1705
|
-
};
|
|
1706
|
-
raf = requestAnimationFrame(tick);
|
|
1707
|
-
return () => cancelAnimationFrame(raf);
|
|
1708
|
-
}, []);
|
|
1709
|
-
return progress;
|
|
1710
|
-
}
|
|
1711
|
-
function RevealGradient({ children, revealCol }) {
|
|
1712
|
-
const gradientColors = GRADIENTS.instagram;
|
|
1713
|
-
const lines = children.split("\n");
|
|
1714
|
-
const maxLength = Math.max(...lines.map((l) => l.length));
|
|
1715
|
-
if (maxLength === 0) return /* @__PURE__ */ jsx23("text", { children });
|
|
1716
|
-
const hexColors = useMemo4(() => generateGradient(gradientColors, maxLength), [maxLength]);
|
|
1717
|
-
return /* @__PURE__ */ jsx23("box", { position: "relative", width: maxLength, height: lines.length, shouldFill: false, children: lines.map((line, lineIndex) => {
|
|
1718
|
-
const runs = [];
|
|
1719
|
-
let current = null;
|
|
1720
|
-
for (let i = 0; i < line.length; i++) {
|
|
1721
|
-
const revealed = i <= revealCol;
|
|
1722
|
-
const char = line[i];
|
|
1723
|
-
const isVisible = revealed && char !== " ";
|
|
1724
|
-
if (isVisible) {
|
|
1725
|
-
if (!current) {
|
|
1726
|
-
current = { start: i, chars: [] };
|
|
1727
|
-
}
|
|
1728
|
-
current.chars.push(char);
|
|
1729
|
-
} else {
|
|
1730
|
-
if (current) {
|
|
1731
|
-
runs.push(current);
|
|
1732
|
-
current = null;
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
if (current) runs.push(current);
|
|
1737
|
-
return runs.map((run, runIndex) => /* @__PURE__ */ jsx23(
|
|
1738
|
-
"box",
|
|
1739
|
-
{
|
|
1740
|
-
position: "absolute",
|
|
1741
|
-
top: lineIndex,
|
|
1742
|
-
left: run.start,
|
|
1743
|
-
shouldFill: false,
|
|
1744
|
-
children: /* @__PURE__ */ jsx23("text", { shouldFill: false, children: run.chars.map((char, ci) => /* @__PURE__ */ jsx23(
|
|
1745
|
-
"span",
|
|
1746
|
-
{
|
|
1747
|
-
style: { fg: hexColors[run.start + ci] },
|
|
1748
|
-
children: char
|
|
1749
|
-
},
|
|
1750
|
-
ci
|
|
1751
|
-
)) })
|
|
1752
|
-
},
|
|
1753
|
-
`${lineIndex}-${runIndex}`
|
|
1754
|
-
));
|
|
1755
|
-
}) });
|
|
1756
|
-
}
|
|
1757
|
-
function Logo({ compact, narrow, mobile }) {
|
|
1758
|
-
const isBrowser = typeof document !== "undefined";
|
|
1759
|
-
const progress = useAnimation(900);
|
|
1760
|
-
const artHeight = compact ? 1 : narrow ? ART_HEIGHT * 2 : ART_HEIGHT;
|
|
1761
|
-
const dropOffset = Math.round((1 - progress) * -artHeight);
|
|
1762
|
-
const revealProgress = Math.max(0, Math.min(1, (progress - 0.1) / 0.7));
|
|
1763
|
-
const maxWidth = compact ? 8 : narrow ? 40 : 62;
|
|
1764
|
-
const revealCol = Math.round(revealProgress * (maxWidth + 4)) - 2;
|
|
1765
|
-
const taglineOpacity = Math.max(0, Math.min(1, (progress - 0.7) / 0.3));
|
|
1766
|
-
const subtitle = /* @__PURE__ */ jsxs17(Fragment6, { children: [
|
|
1767
|
-
/* @__PURE__ */ jsx23("text", { children: " " }),
|
|
1768
|
-
/* @__PURE__ */ jsx23("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs17("text", { style: textStyle({ fg: "#d4b0e8" }), opacity: taglineOpacity, wrapMode: "word", textAlign: "center", width: "100%", shouldFill: false, children: [
|
|
1769
|
-
"A framework for building terminal apps, built on ",
|
|
1770
|
-
/* @__PURE__ */ jsx23("a", { href: "https://opentui.com", style: { attributes: 72, fg: "#d4b0e8" }, children: "OpenTUI" }),
|
|
1771
|
-
" + React." + (mobile ? " " : "\n") + "(Gridland apps, like this website, work in the browser and terminal.)"
|
|
1772
|
-
] }) })
|
|
1773
|
-
] });
|
|
1774
|
-
if (!isBrowser) {
|
|
1775
|
-
const art = compact ? "gridland" : narrow ? gridArt + "\n" + landArt : fullArt;
|
|
1776
|
-
return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", alignItems: "center", children: [
|
|
1777
|
-
/* @__PURE__ */ jsx23(Gradient, { name: "instagram", children: art }),
|
|
1778
|
-
/* @__PURE__ */ jsx23("text", { children: " " }),
|
|
1779
|
-
/* @__PURE__ */ jsx23("box", { flexDirection: "column", alignItems: "center", width: "100%", shouldFill: false, children: /* @__PURE__ */ jsxs17("text", { style: textStyle({ fg: "#d4b0e8" }), shouldFill: false, children: [
|
|
1780
|
-
"A framework for building terminal apps, built on OpenTUI + React.",
|
|
1781
|
-
"\n",
|
|
1782
|
-
"(Gridland apps, like this website, work in the browser and terminal.)"
|
|
1783
|
-
] }) })
|
|
1784
|
-
] });
|
|
1785
|
-
}
|
|
1786
|
-
if (compact) {
|
|
1787
|
-
return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
|
|
1788
|
-
/* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx23("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: "gridland" }) }) }),
|
|
1789
|
-
subtitle
|
|
1790
|
-
] });
|
|
1791
|
-
}
|
|
1792
|
-
if (narrow) {
|
|
1793
|
-
return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
|
|
1794
|
-
/* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsxs17("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: [
|
|
1795
|
-
/* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: gridArt }),
|
|
1796
|
-
/* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: landArt })
|
|
1797
|
-
] }) }),
|
|
1798
|
-
subtitle
|
|
1799
|
-
] });
|
|
1800
|
-
}
|
|
1801
|
-
return /* @__PURE__ */ jsxs17("box", { flexDirection: "column", flexShrink: 0, width: "100%", shouldFill: false, children: [
|
|
1802
|
-
/* @__PURE__ */ jsx23("box", { height: artHeight, overflow: "hidden", position: "relative", width: "100%", flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx23("box", { position: "absolute", top: dropOffset, width: "100%", flexDirection: "column", alignItems: "center", shouldFill: false, children: /* @__PURE__ */ jsx23(RevealGradient, { revealCol, children: fullArt }) }) }),
|
|
1803
|
-
subtitle
|
|
1804
|
-
] });
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
// ../docs/components/landing/install-box.tsx
|
|
1808
|
-
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1809
|
-
function InstallBox() {
|
|
1810
|
-
const theme = useTheme();
|
|
1811
|
-
return /* @__PURE__ */ jsx24(
|
|
1812
|
-
"box",
|
|
1813
|
-
{
|
|
1814
|
-
border: true,
|
|
1815
|
-
borderStyle: "rounded",
|
|
1816
|
-
borderColor: theme.border,
|
|
1817
|
-
paddingX: 1,
|
|
1818
|
-
flexDirection: "column",
|
|
1819
|
-
flexShrink: 0,
|
|
1820
|
-
children: /* @__PURE__ */ jsxs18("text", { children: [
|
|
1821
|
-
/* @__PURE__ */ jsx24("span", { style: textStyle({ dim: true }), children: "$ " }),
|
|
1822
|
-
/* @__PURE__ */ jsx24("span", { style: textStyle({ bold: true }), children: "bun create " }),
|
|
1823
|
-
/* @__PURE__ */ jsx24("span", { style: textStyle({ fg: theme.accent }), children: "gridland" })
|
|
1824
|
-
] })
|
|
1825
|
-
}
|
|
1826
|
-
);
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
// ../docs/components/landing/links-box.tsx
|
|
1830
|
-
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1831
|
-
var UNDERLINE3 = 1 << 3;
|
|
1832
|
-
function LinksBox() {
|
|
1833
|
-
const theme = useTheme();
|
|
1834
|
-
return /* @__PURE__ */ jsx25(
|
|
1835
|
-
"box",
|
|
1836
|
-
{
|
|
1837
|
-
border: true,
|
|
1838
|
-
borderStyle: "rounded",
|
|
1839
|
-
borderColor: theme.border,
|
|
1840
|
-
paddingX: 1,
|
|
1841
|
-
flexDirection: "column",
|
|
1842
|
-
flexShrink: 0,
|
|
1843
|
-
children: /* @__PURE__ */ jsxs19("text", { children: [
|
|
1844
|
-
/* @__PURE__ */ jsx25("span", { children: "\u{1F431}" }),
|
|
1845
|
-
/* @__PURE__ */ jsx25("a", { href: "https://github.com/cjroth/gridland", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " GitHub" }),
|
|
1846
|
-
/* @__PURE__ */ jsx25("span", { children: " " }),
|
|
1847
|
-
/* @__PURE__ */ jsx25("span", { children: "\u{1F4D6}" }),
|
|
1848
|
-
/* @__PURE__ */ jsx25("a", { href: "/docs", style: { attributes: UNDERLINE3, fg: theme.accent }, children: " Docs" })
|
|
1849
|
-
] })
|
|
1850
|
-
}
|
|
1851
|
-
);
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
// ../docs/components/landing/matrix-background.tsx
|
|
1855
|
-
import { useMemo as useMemo5 } from "react";
|
|
1856
|
-
|
|
1857
|
-
// ../docs/components/landing/use-matrix.ts
|
|
1858
|
-
import { useState as useState9, useEffect as useEffect4, useRef as useRef7 } from "react";
|
|
1859
|
-
var CHARS = "abcdefghijklmnopqrstuvwxyz0123456789@#$%^&*(){}[]|;:<>,.?/~`";
|
|
1860
|
-
function randomChar() {
|
|
1861
|
-
return CHARS[Math.floor(Math.random() * CHARS.length)];
|
|
1862
|
-
}
|
|
1863
|
-
function createDrop(height, seeded = false) {
|
|
1864
|
-
const length = Math.floor(Math.random() * Math.floor(height * 0.6)) + 4;
|
|
1865
|
-
return {
|
|
1866
|
-
y: seeded ? Math.floor(Math.random() * (height + length)) : -Math.floor(Math.random() * height),
|
|
1867
|
-
speed: 1,
|
|
1868
|
-
length,
|
|
1869
|
-
chars: Array.from({ length }, randomChar)
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
function buildGrid(columns, width, height) {
|
|
1873
|
-
const grid = Array.from({ length: height }, () => Array(width).fill(" "));
|
|
1874
|
-
const brightness = Array.from({ length: height }, () => Array(width).fill(0));
|
|
1875
|
-
for (let x = 0; x < width; x++) {
|
|
1876
|
-
const drop = columns[x];
|
|
1877
|
-
if (!drop) continue;
|
|
1878
|
-
for (let i = 0; i < drop.length; i++) {
|
|
1879
|
-
const row = Math.floor(drop.y) - i;
|
|
1880
|
-
if (row < 0 || row >= height) continue;
|
|
1881
|
-
grid[row][x] = drop.chars[i];
|
|
1882
|
-
if (i === 0) {
|
|
1883
|
-
brightness[row][x] = 1;
|
|
1884
|
-
} else {
|
|
1885
|
-
brightness[row][x] = Math.max(0.15, 1 - i / drop.length);
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
return { grid, brightness };
|
|
1890
|
-
}
|
|
1891
|
-
function useMatrix(width, height) {
|
|
1892
|
-
const columnsRef = useRef7([]);
|
|
1893
|
-
const [state, setState] = useState9(() => {
|
|
1894
|
-
const columns = Array.from(
|
|
1895
|
-
{ length: width },
|
|
1896
|
-
() => Math.random() < 0.5 ? createDrop(height, true) : null
|
|
1897
|
-
);
|
|
1898
|
-
columnsRef.current = columns;
|
|
1899
|
-
return buildGrid(columns, width, height);
|
|
1900
|
-
});
|
|
1901
|
-
useEffect4(() => {
|
|
1902
|
-
if (width < 2 || height < 2) return;
|
|
1903
|
-
const id = setInterval(() => {
|
|
1904
|
-
const columns = columnsRef.current;
|
|
1905
|
-
for (let x = 0; x < width; x++) {
|
|
1906
|
-
if (columns[x] === null || columns[x] === void 0) {
|
|
1907
|
-
if (Math.random() < 0.03) {
|
|
1908
|
-
columns[x] = createDrop(height);
|
|
1909
|
-
}
|
|
1910
|
-
continue;
|
|
1911
|
-
}
|
|
1912
|
-
const drop = columns[x];
|
|
1913
|
-
drop.y += drop.speed;
|
|
1914
|
-
if (Math.random() < 0.1) {
|
|
1915
|
-
const idx = Math.floor(Math.random() * drop.chars.length);
|
|
1916
|
-
drop.chars[idx] = randomChar();
|
|
1917
|
-
}
|
|
1918
|
-
if (drop.y - drop.length > height) {
|
|
1919
|
-
columns[x] = null;
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
setState(buildGrid(columns, width, height));
|
|
1923
|
-
}, 80);
|
|
1924
|
-
return () => clearInterval(id);
|
|
1925
|
-
}, [width, height]);
|
|
1926
|
-
useEffect4(() => {
|
|
1927
|
-
columnsRef.current = Array.from(
|
|
1928
|
-
{ length: width },
|
|
1929
|
-
() => Math.random() < 0.5 ? createDrop(height, true) : null
|
|
1930
|
-
);
|
|
1931
|
-
setState(buildGrid(columnsRef.current, width, height));
|
|
1932
|
-
}, [width, height]);
|
|
1933
|
-
return state;
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
// ../docs/components/landing/matrix-background.tsx
|
|
1937
|
-
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
1938
|
-
var MUTE_LEVELS = [0.12, 0.18, 0.24, 0.3, 0.38];
|
|
1939
|
-
var BG = hexToRgb("#1a1a2e");
|
|
1940
|
-
function buildMutedColors(baseHex) {
|
|
1941
|
-
const fg = hexToRgb(baseHex);
|
|
1942
|
-
return MUTE_LEVELS.map((factor) => rgbToHex({
|
|
1943
|
-
r: Math.round(BG.r + (fg.r - BG.r) * factor),
|
|
1944
|
-
g: Math.round(BG.g + (fg.g - BG.g) * factor),
|
|
1945
|
-
b: Math.round(BG.b + (fg.b - BG.b) * factor)
|
|
1946
|
-
}));
|
|
1947
|
-
}
|
|
1948
|
-
function colorForCell(mutedColors, b) {
|
|
1949
|
-
if (b >= 1) return mutedColors[4];
|
|
1950
|
-
const idx = Math.min(Math.floor(b * (MUTE_LEVELS.length - 1)), MUTE_LEVELS.length - 2);
|
|
1951
|
-
return mutedColors[idx];
|
|
1952
|
-
}
|
|
1953
|
-
function MatrixBackground({ width, height, clearRect, clearRects }) {
|
|
1954
|
-
const { grid, brightness } = useMatrix(width, height);
|
|
1955
|
-
const theme = useTheme();
|
|
1956
|
-
const columnColors = useMemo5(
|
|
1957
|
-
() => width > 0 ? generateGradient([theme.accent, theme.secondary, theme.primary], width) : [],
|
|
1958
|
-
[width, theme.accent, theme.secondary, theme.primary]
|
|
1959
|
-
);
|
|
1960
|
-
const columnMutedColors = useMemo5(
|
|
1961
|
-
() => columnColors.map(buildMutedColors),
|
|
1962
|
-
[columnColors]
|
|
1963
|
-
);
|
|
1964
|
-
return /* @__PURE__ */ jsx26("box", { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx26("text", { children: row.map((cell, x) => {
|
|
1965
|
-
const inClearRect = clearRect && y >= clearRect.top && y < clearRect.top + clearRect.height && x >= clearRect.left && x < clearRect.left + clearRect.width || clearRects && clearRects.some(
|
|
1966
|
-
(r) => y >= r.top && y < r.top + r.height && x >= r.left && x < r.left + r.width
|
|
1967
|
-
);
|
|
1968
|
-
const mutedColors = columnMutedColors[x];
|
|
1969
|
-
if (cell === " " || inClearRect || !mutedColors) {
|
|
1970
|
-
return /* @__PURE__ */ jsx26("span", { children: " " }, x);
|
|
1971
|
-
}
|
|
1972
|
-
return /* @__PURE__ */ jsx26(
|
|
1973
|
-
"span",
|
|
1974
|
-
{
|
|
1975
|
-
style: {
|
|
1976
|
-
fg: colorForCell(mutedColors, brightness[y][x])
|
|
1977
|
-
},
|
|
1978
|
-
children: cell
|
|
1979
|
-
},
|
|
1980
|
-
x
|
|
1981
|
-
);
|
|
1982
|
-
}) }, y)) });
|
|
1983
|
-
}
|
|
1984
|
-
|
|
1985
|
-
// ../docs/components/landing/about-modal.tsx
|
|
1986
|
-
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1987
|
-
function AboutModal({ onClose, useKeyboard }) {
|
|
1988
|
-
const theme = useTheme();
|
|
1989
|
-
return /* @__PURE__ */ jsx27(Modal, { title: "About Gridland", useKeyboard, onClose, children: /* @__PURE__ */ jsxs20("box", { paddingX: 1, flexDirection: "column", gap: 1, children: [
|
|
1990
|
-
/* @__PURE__ */ jsx27("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "What is Gridland?" }),
|
|
1991
|
-
/* @__PURE__ */ jsx27("text", { children: "Gridland renders terminal UIs to HTML5 Canvas with React." }),
|
|
1992
|
-
/* @__PURE__ */ jsx27("text", { children: "No xterm.js. No terminal emulator. Just pixels." }),
|
|
1993
|
-
/* @__PURE__ */ jsx27("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Features" }),
|
|
1994
|
-
/* @__PURE__ */ jsxs20("text", { children: [
|
|
1995
|
-
/* @__PURE__ */ jsxs20("span", { style: textStyle({ dim: true }), children: [
|
|
1996
|
-
"\u2022",
|
|
1997
|
-
" "
|
|
1998
|
-
] }),
|
|
1999
|
-
"Canvas-rendered TUI components"
|
|
2000
|
-
] }),
|
|
2001
|
-
/* @__PURE__ */ jsxs20("text", { children: [
|
|
2002
|
-
/* @__PURE__ */ jsxs20("span", { style: textStyle({ dim: true }), children: [
|
|
2003
|
-
"\u2022",
|
|
2004
|
-
" "
|
|
2005
|
-
] }),
|
|
2006
|
-
"React reconciler with JSX"
|
|
2007
|
-
] }),
|
|
2008
|
-
/* @__PURE__ */ jsxs20("text", { children: [
|
|
2009
|
-
/* @__PURE__ */ jsxs20("span", { style: textStyle({ dim: true }), children: [
|
|
2010
|
-
"\u2022",
|
|
2011
|
-
" "
|
|
2012
|
-
] }),
|
|
2013
|
-
"Yoga flexbox layout engine"
|
|
2014
|
-
] }),
|
|
2015
|
-
/* @__PURE__ */ jsxs20("text", { children: [
|
|
2016
|
-
/* @__PURE__ */ jsxs20("span", { style: textStyle({ dim: true }), children: [
|
|
2017
|
-
"\u2022",
|
|
2018
|
-
" "
|
|
2019
|
-
] }),
|
|
2020
|
-
"Keyboard, mouse, and clipboard support"
|
|
2021
|
-
] }),
|
|
2022
|
-
/* @__PURE__ */ jsxs20("text", { children: [
|
|
2023
|
-
/* @__PURE__ */ jsxs20("span", { style: textStyle({ dim: true }), children: [
|
|
2024
|
-
"\u2022",
|
|
2025
|
-
" "
|
|
2026
|
-
] }),
|
|
2027
|
-
"Next.js and Vite plugins"
|
|
2028
|
-
] }),
|
|
2029
|
-
/* @__PURE__ */ jsx27("text", { style: textStyle({ bold: true, fg: theme.accent }), children: "Tech Stack" }),
|
|
2030
|
-
/* @__PURE__ */ jsx27("text", { children: "React + opentui engine + yoga-layout + HTML5 Canvas" }),
|
|
2031
|
-
/* @__PURE__ */ jsx27("text", { style: textStyle({ dim: true }), children: "Press q to close" })
|
|
2032
|
-
] }) });
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
// ../docs/components/landing/landing-app.tsx
|
|
2036
|
-
import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2037
|
-
function LandingApp({ useKeyboard }) {
|
|
2038
|
-
const theme = useTheme();
|
|
2039
|
-
const { width, height, isNarrow, isTiny, isMobile } = useBreakpoints();
|
|
2040
|
-
const [showAbout, setShowAbout] = useState10(false);
|
|
2041
|
-
useKeyboard((event) => {
|
|
2042
|
-
if (event.name === "a" && !showAbout) {
|
|
2043
|
-
setShowAbout(true);
|
|
2044
|
-
}
|
|
2045
|
-
if (event.name === "q" && showAbout) {
|
|
2046
|
-
setShowAbout(false);
|
|
2047
|
-
}
|
|
2048
|
-
});
|
|
2049
|
-
if (showAbout) {
|
|
2050
|
-
return /* @__PURE__ */ jsxs21("box", { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
2051
|
-
/* @__PURE__ */ jsx28("box", { flexGrow: 1, children: /* @__PURE__ */ jsx28(AboutModal, { onClose: () => setShowAbout(false), useKeyboard }) }),
|
|
2052
|
-
/* @__PURE__ */ jsx28(StatusBar, { items: [{ key: "q", label: "close" }] })
|
|
2053
|
-
] });
|
|
2054
|
-
}
|
|
2055
|
-
const isBrowser = typeof document !== "undefined";
|
|
2056
|
-
const logoHeight = isTiny ? 2 : isNarrow ? 13 : 7;
|
|
2057
|
-
const logoExtra = isBrowser ? 1 : 0;
|
|
2058
|
-
const gap = isMobile ? 0 : 1;
|
|
2059
|
-
const installLinksTop = 3 + logoHeight + logoExtra + gap;
|
|
2060
|
-
const installLinksHeight = 3;
|
|
2061
|
-
const boxTop = installLinksTop + installLinksHeight + gap + 1;
|
|
2062
|
-
const boxHeight = height - boxTop - 1 - 1;
|
|
2063
|
-
const clearRect = { top: boxTop, left: 1, width: width - 2, height: boxHeight };
|
|
2064
|
-
const installLinksClearRect = { top: installLinksTop, left: 1, width: width - 2, height: installLinksHeight };
|
|
2065
|
-
return /* @__PURE__ */ jsxs21("box", { width: "100%", height: "100%", position: "relative", children: [
|
|
2066
|
-
/* @__PURE__ */ jsx28(MatrixBackground, { width, height, clearRect, clearRects: [installLinksClearRect] }),
|
|
2067
|
-
/* @__PURE__ */ jsxs21(
|
|
2068
|
-
"box",
|
|
2069
|
-
{
|
|
2070
|
-
position: "absolute",
|
|
2071
|
-
top: 0,
|
|
2072
|
-
left: 0,
|
|
2073
|
-
width,
|
|
2074
|
-
height,
|
|
2075
|
-
zIndex: 1,
|
|
2076
|
-
flexDirection: "column",
|
|
2077
|
-
shouldFill: false,
|
|
2078
|
-
children: [
|
|
2079
|
-
/* @__PURE__ */ jsxs21("box", { flexGrow: 1, flexDirection: "column", paddingTop: 3, paddingLeft: 1, paddingRight: 1, paddingBottom: 1, gap: isMobile ? 0 : 1, shouldFill: false, children: [
|
|
2080
|
-
/* @__PURE__ */ jsx28("box", { flexShrink: 0, shouldFill: false, children: /* @__PURE__ */ jsx28(Logo, { compact: isTiny, narrow: isNarrow, mobile: isMobile }) }),
|
|
2081
|
-
/* @__PURE__ */ jsxs21("box", { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: isMobile ? 0 : 1, flexShrink: 0, shouldFill: false, children: [
|
|
2082
|
-
/* @__PURE__ */ jsx28(
|
|
2083
|
-
"box",
|
|
2084
|
-
{
|
|
2085
|
-
border: true,
|
|
2086
|
-
borderStyle: "rounded",
|
|
2087
|
-
borderColor: theme.border,
|
|
2088
|
-
paddingX: 1,
|
|
2089
|
-
flexDirection: "column",
|
|
2090
|
-
flexShrink: 0,
|
|
2091
|
-
children: /* @__PURE__ */ jsxs21("text", { children: [
|
|
2092
|
-
/* @__PURE__ */ jsx28("span", { style: textStyle({ dim: true }), children: "$ " }),
|
|
2093
|
-
/* @__PURE__ */ jsx28("span", { style: textStyle({ bold: true }), children: "bunx " }),
|
|
2094
|
-
/* @__PURE__ */ jsx28("span", { style: textStyle({ fg: theme.accent }), children: "@gridland/demo landing" })
|
|
2095
|
-
] })
|
|
2096
|
-
}
|
|
2097
|
-
),
|
|
2098
|
-
/* @__PURE__ */ jsx28(InstallBox, {}),
|
|
2099
|
-
/* @__PURE__ */ jsx28(LinksBox, {})
|
|
2100
|
-
] }),
|
|
2101
|
-
/* @__PURE__ */ jsx28(
|
|
2102
|
-
"box",
|
|
2103
|
-
{
|
|
2104
|
-
flexGrow: 1,
|
|
2105
|
-
border: true,
|
|
2106
|
-
borderStyle: "rounded",
|
|
2107
|
-
borderColor: theme.border
|
|
2108
|
-
}
|
|
2109
|
-
)
|
|
2110
|
-
] }),
|
|
2111
|
-
/* @__PURE__ */ jsx28(
|
|
2112
|
-
StatusBar,
|
|
2113
|
-
{
|
|
2114
|
-
items: [
|
|
2115
|
-
{ key: "a", label: "about" }
|
|
2116
|
-
]
|
|
2117
|
-
}
|
|
2118
|
-
)
|
|
2119
|
-
]
|
|
2120
|
-
}
|
|
2121
|
-
)
|
|
2122
|
-
] });
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
|
-
// ../docs/components/landing/matrix-rain.tsx
|
|
2126
|
-
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
2127
|
-
|
|
2128
|
-
export {
|
|
2129
|
-
textStyle,
|
|
2130
|
-
StatusBar,
|
|
2131
|
-
LinkDemo,
|
|
2132
|
-
SpinnerPicker,
|
|
2133
|
-
TextInput,
|
|
2134
|
-
SelectInput,
|
|
2135
|
-
MultiSelect,
|
|
2136
|
-
Table,
|
|
2137
|
-
GRADIENTS,
|
|
2138
|
-
Gradient,
|
|
2139
|
-
TabBar,
|
|
2140
|
-
Modal,
|
|
2141
|
-
PromptInput,
|
|
2142
|
-
ChatPanel,
|
|
2143
|
-
Timeline,
|
|
2144
|
-
Message,
|
|
2145
|
-
LandingApp
|
|
2146
|
-
};
|