@opengeni/react 0.2.0 → 0.3.0
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/index.d.ts +86 -4
- package/dist/index.js +330 -153
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/src/client.ts +2 -0
- package/src/components/chat-composer.tsx +28 -2
- package/src/components/markdown.tsx +170 -0
- package/src/components/message-timeline.tsx +21 -5
- package/src/components/model-picker.tsx +87 -0
- package/src/hooks/use-available-models.ts +39 -0
- package/src/index.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -1566,6 +1566,21 @@ function useBillingUsage(options = {}) {
|
|
|
1566
1566
|
};
|
|
1567
1567
|
}
|
|
1568
1568
|
|
|
1569
|
+
// src/hooks/use-available-models.ts
|
|
1570
|
+
import { useCallback as useCallback14 } from "react";
|
|
1571
|
+
function useAvailableModels(options = {}) {
|
|
1572
|
+
const client = useOpenGeniClient(options);
|
|
1573
|
+
const load = useCallback14(async () => await client.getClientConfig(), [client]);
|
|
1574
|
+
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled: options.enabled });
|
|
1575
|
+
return {
|
|
1576
|
+
models: state.data?.models ?? [],
|
|
1577
|
+
defaultModel: state.data?.defaultModel ?? null,
|
|
1578
|
+
loading: state.loading,
|
|
1579
|
+
error: state.error,
|
|
1580
|
+
refresh: state.refresh
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1569
1584
|
// src/approvals.ts
|
|
1570
1585
|
function approvalsFromRequiresAction(payload) {
|
|
1571
1586
|
const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
|
|
@@ -1798,7 +1813,7 @@ function clearErrorMessage(cause) {
|
|
|
1798
1813
|
}
|
|
1799
1814
|
|
|
1800
1815
|
// src/hooks/use-slash-commands.ts
|
|
1801
|
-
import { useCallback as
|
|
1816
|
+
import { useCallback as useCallback15, useMemo as useMemo3, useRef as useRef6, useState as useState7 } from "react";
|
|
1802
1817
|
function useSlashCommands(options) {
|
|
1803
1818
|
const { commands, context, handlers, value, setValue } = options;
|
|
1804
1819
|
const [highlight, setHighlight] = useState7(0);
|
|
@@ -1838,7 +1853,7 @@ function useSlashCommands(options) {
|
|
|
1838
1853
|
const isCommandDraft = parsed !== null && items.length > 0;
|
|
1839
1854
|
const clampedHighlight = items.length === 0 ? 0 : Math.min(highlight, items.length - 1);
|
|
1840
1855
|
const activeArgHint = activeCommand ? argHint(activeCommand.args) : "";
|
|
1841
|
-
const buildContext =
|
|
1856
|
+
const buildContext = useCallback15(
|
|
1842
1857
|
(command) => {
|
|
1843
1858
|
if (!context) {
|
|
1844
1859
|
return null;
|
|
@@ -1847,7 +1862,7 @@ function useSlashCommands(options) {
|
|
|
1847
1862
|
},
|
|
1848
1863
|
[context, handlers]
|
|
1849
1864
|
);
|
|
1850
|
-
const execute =
|
|
1865
|
+
const execute = useCallback15(
|
|
1851
1866
|
async (command, args) => {
|
|
1852
1867
|
const ctx = buildContext(command);
|
|
1853
1868
|
if (!ctx) {
|
|
@@ -1867,20 +1882,20 @@ function useSlashCommands(options) {
|
|
|
1867
1882
|
},
|
|
1868
1883
|
[buildContext, setValue]
|
|
1869
1884
|
);
|
|
1870
|
-
const autocomplete =
|
|
1885
|
+
const autocomplete = useCallback15(
|
|
1871
1886
|
(command) => {
|
|
1872
1887
|
setValue(`/${command.name} `);
|
|
1873
1888
|
setHighlight(0);
|
|
1874
1889
|
},
|
|
1875
1890
|
[setValue]
|
|
1876
1891
|
);
|
|
1877
|
-
const autocompleteHighlighted =
|
|
1892
|
+
const autocompleteHighlighted = useCallback15(() => {
|
|
1878
1893
|
const command = items[clampedHighlight];
|
|
1879
1894
|
if (command) {
|
|
1880
1895
|
autocomplete(command);
|
|
1881
1896
|
}
|
|
1882
1897
|
}, [items, clampedHighlight, autocomplete]);
|
|
1883
|
-
const runResolved =
|
|
1898
|
+
const runResolved = useCallback15(
|
|
1884
1899
|
async (command, options2) => {
|
|
1885
1900
|
if (!parsed) {
|
|
1886
1901
|
return;
|
|
@@ -1903,7 +1918,7 @@ function useSlashCommands(options) {
|
|
|
1903
1918
|
},
|
|
1904
1919
|
[parsed, activeCommand, autocomplete, execute]
|
|
1905
1920
|
);
|
|
1906
|
-
const runHighlighted =
|
|
1921
|
+
const runHighlighted = useCallback15(async () => {
|
|
1907
1922
|
if (!parsed) {
|
|
1908
1923
|
return;
|
|
1909
1924
|
}
|
|
@@ -1922,7 +1937,7 @@ function useSlashCommands(options) {
|
|
|
1922
1937
|
}
|
|
1923
1938
|
await runResolved(command);
|
|
1924
1939
|
}, [parsed, activeCommand, items, clampedHighlight, runResolved]);
|
|
1925
|
-
const runAt =
|
|
1940
|
+
const runAt = useCallback15(
|
|
1926
1941
|
async (index) => {
|
|
1927
1942
|
const command = items[index];
|
|
1928
1943
|
if (!command) {
|
|
@@ -1933,7 +1948,7 @@ function useSlashCommands(options) {
|
|
|
1933
1948
|
[items, runResolved]
|
|
1934
1949
|
);
|
|
1935
1950
|
const runningRef = useRef6(false);
|
|
1936
|
-
const onKeyDown =
|
|
1951
|
+
const onKeyDown = useCallback15(
|
|
1937
1952
|
(event) => {
|
|
1938
1953
|
if (!open) {
|
|
1939
1954
|
return false;
|
|
@@ -2084,8 +2099,63 @@ function CommandPalette({ open, items, highlight, onHighlight, onRun, argHintTex
|
|
|
2084
2099
|
// src/components/chat-composer.tsx
|
|
2085
2100
|
import { ArrowUpIcon, FileIcon, ImageIcon, LoaderCircleIcon, PaperclipIcon, SquareIcon, XIcon } from "lucide-react";
|
|
2086
2101
|
import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion/react";
|
|
2087
|
-
import { useCallback as
|
|
2088
|
-
|
|
2102
|
+
import { useCallback as useCallback16, useEffect as useEffect6, useId as useId2, useMemo as useMemo5, useRef as useRef7, useState as useState8 } from "react";
|
|
2103
|
+
|
|
2104
|
+
// src/components/model-picker.tsx
|
|
2105
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
2106
|
+
import { useId, useMemo as useMemo4 } from "react";
|
|
2107
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
2108
|
+
function ModelPicker({ models, value, onChange, disabled, className }) {
|
|
2109
|
+
const selectId = useId();
|
|
2110
|
+
const groups = useMemo4(() => {
|
|
2111
|
+
const byProvider = /* @__PURE__ */ new Map();
|
|
2112
|
+
for (const model of models) {
|
|
2113
|
+
let group = byProvider.get(model.provider);
|
|
2114
|
+
if (!group) {
|
|
2115
|
+
group = { label: model.providerLabel, models: [] };
|
|
2116
|
+
byProvider.set(model.provider, group);
|
|
2117
|
+
}
|
|
2118
|
+
group.models.push(model);
|
|
2119
|
+
}
|
|
2120
|
+
return [...byProvider.values()];
|
|
2121
|
+
}, [models]);
|
|
2122
|
+
if (models.length === 0) {
|
|
2123
|
+
return null;
|
|
2124
|
+
}
|
|
2125
|
+
return /* @__PURE__ */ jsxs2("span", { className: cn("relative inline-flex items-center", className), children: [
|
|
2126
|
+
/* @__PURE__ */ jsx3("label", { htmlFor: selectId, className: "sr-only", children: "Model" }),
|
|
2127
|
+
/* @__PURE__ */ jsx3(
|
|
2128
|
+
"select",
|
|
2129
|
+
{
|
|
2130
|
+
id: selectId,
|
|
2131
|
+
value: value ?? "",
|
|
2132
|
+
onChange: (event) => onChange(event.target.value),
|
|
2133
|
+
disabled: disabled === true,
|
|
2134
|
+
"aria-label": "Model",
|
|
2135
|
+
className: cn(
|
|
2136
|
+
// Sized like the other footer controls; the chevron overlay needs the
|
|
2137
|
+
// right padding so the value never collides with it.
|
|
2138
|
+
"h-8 max-w-[180px] cursor-pointer appearance-none truncate rounded-og-md bg-transparent",
|
|
2139
|
+
"py-0 pl-2 pr-6 text-[13px] text-og-fg-muted",
|
|
2140
|
+
"transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
|
|
2141
|
+
"focus:outline-none focus-visible:outline-none",
|
|
2142
|
+
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
2143
|
+
),
|
|
2144
|
+
children: groups.map((group) => /* @__PURE__ */ jsx3("optgroup", { label: group.label, children: group.models.map((model) => /* @__PURE__ */ jsx3("option", { value: model.id, children: model.label }, model.id)) }, group.label))
|
|
2145
|
+
}
|
|
2146
|
+
),
|
|
2147
|
+
/* @__PURE__ */ jsx3(
|
|
2148
|
+
ChevronDownIcon,
|
|
2149
|
+
{
|
|
2150
|
+
"aria-hidden": true,
|
|
2151
|
+
className: "pointer-events-none absolute right-1.5 size-3.5 text-og-fg-subtle"
|
|
2152
|
+
}
|
|
2153
|
+
)
|
|
2154
|
+
] });
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/components/chat-composer.tsx
|
|
2158
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
2089
2159
|
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
2090
2160
|
function ChatComposer({
|
|
2091
2161
|
composer,
|
|
@@ -2098,6 +2168,9 @@ function ChatComposer({
|
|
|
2098
2168
|
header,
|
|
2099
2169
|
onPaste,
|
|
2100
2170
|
attachments,
|
|
2171
|
+
models,
|
|
2172
|
+
selectedModel,
|
|
2173
|
+
onSelectModel,
|
|
2101
2174
|
className,
|
|
2102
2175
|
commands = defaultCommands,
|
|
2103
2176
|
commandContext,
|
|
@@ -2111,7 +2184,7 @@ function ChatComposer({
|
|
|
2111
2184
|
const canSend = (composer.canSend || hasReadyAttachment) && !blockedByUpload && !composer.sending;
|
|
2112
2185
|
const [dragging, setDragging] = useState8(false);
|
|
2113
2186
|
const dragCarriesFiles = (event) => event.dataTransfer != null && [...event.dataTransfer.types].includes("Files");
|
|
2114
|
-
const handleDragOver =
|
|
2187
|
+
const handleDragOver = useCallback16(
|
|
2115
2188
|
(event) => {
|
|
2116
2189
|
if (!attachments || !dragCarriesFiles(event)) {
|
|
2117
2190
|
return;
|
|
@@ -2121,7 +2194,7 @@ function ChatComposer({
|
|
|
2121
2194
|
},
|
|
2122
2195
|
[attachments]
|
|
2123
2196
|
);
|
|
2124
|
-
const handleDragLeave =
|
|
2197
|
+
const handleDragLeave = useCallback16(
|
|
2125
2198
|
(event) => {
|
|
2126
2199
|
if (!attachments) {
|
|
2127
2200
|
return;
|
|
@@ -2133,7 +2206,7 @@ function ChatComposer({
|
|
|
2133
2206
|
},
|
|
2134
2207
|
[attachments]
|
|
2135
2208
|
);
|
|
2136
|
-
const handleDrop =
|
|
2209
|
+
const handleDrop = useCallback16(
|
|
2137
2210
|
(event) => {
|
|
2138
2211
|
if (!attachments || !dragCarriesFiles(event)) {
|
|
2139
2212
|
return;
|
|
@@ -2149,8 +2222,8 @@ function ChatComposer({
|
|
|
2149
2222
|
const [notice, setNotice] = useState8(null);
|
|
2150
2223
|
const [helpOpen, setHelpOpen] = useState8(false);
|
|
2151
2224
|
const [confirmState, setConfirmState] = useState8(null);
|
|
2152
|
-
const listboxId =
|
|
2153
|
-
const resize =
|
|
2225
|
+
const listboxId = useId2();
|
|
2226
|
+
const resize = useCallback16(() => {
|
|
2154
2227
|
const textarea = textareaRef.current;
|
|
2155
2228
|
if (!textarea) {
|
|
2156
2229
|
return;
|
|
@@ -2161,7 +2234,7 @@ function ChatComposer({
|
|
|
2161
2234
|
useEffect6(() => {
|
|
2162
2235
|
resize();
|
|
2163
2236
|
}, [composer.value, resize]);
|
|
2164
|
-
const handlers =
|
|
2237
|
+
const handlers = useMemo5(
|
|
2165
2238
|
() => ({
|
|
2166
2239
|
notice: (next) => {
|
|
2167
2240
|
setNotice(next);
|
|
@@ -2219,14 +2292,14 @@ function ChatComposer({
|
|
|
2219
2292
|
void composer.send();
|
|
2220
2293
|
}
|
|
2221
2294
|
};
|
|
2222
|
-
const handlePaste =
|
|
2295
|
+
const handlePaste = useCallback16(
|
|
2223
2296
|
(event) => {
|
|
2224
2297
|
onPaste?.(event);
|
|
2225
2298
|
attachments?.addFromPaste(event);
|
|
2226
2299
|
},
|
|
2227
2300
|
[onPaste, attachments]
|
|
2228
2301
|
);
|
|
2229
|
-
const handleFileChange =
|
|
2302
|
+
const handleFileChange = useCallback16(
|
|
2230
2303
|
(event) => {
|
|
2231
2304
|
if (event.target.files) {
|
|
2232
2305
|
attachments?.addFiles(event.target.files);
|
|
@@ -2235,7 +2308,7 @@ function ChatComposer({
|
|
|
2235
2308
|
},
|
|
2236
2309
|
[attachments]
|
|
2237
2310
|
);
|
|
2238
|
-
const helpCommands =
|
|
2311
|
+
const helpCommands = useMemo5(
|
|
2239
2312
|
() => commands.filter((command) => {
|
|
2240
2313
|
if (command.permission && commandContext) {
|
|
2241
2314
|
const perms = commandContext.permissions;
|
|
@@ -2246,9 +2319,9 @@ function ChatComposer({
|
|
|
2246
2319
|
[commands, commandContext]
|
|
2247
2320
|
);
|
|
2248
2321
|
const activeNotice = notice ?? (composer.error ? { tone: "error", message: composer.error.message || "Sending failed \u2014 your draft is still here. Try again." } : null);
|
|
2249
|
-
return /* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
paletteEnabled ? /* @__PURE__ */
|
|
2322
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn("og-root", className), children: [
|
|
2323
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative", children: [
|
|
2324
|
+
paletteEnabled ? /* @__PURE__ */ jsx4(
|
|
2252
2325
|
CommandPalette,
|
|
2253
2326
|
{
|
|
2254
2327
|
open: palette.open && confirmState === null,
|
|
@@ -2263,7 +2336,7 @@ function ChatComposer({
|
|
|
2263
2336
|
listboxId
|
|
2264
2337
|
}
|
|
2265
2338
|
) : null,
|
|
2266
|
-
/* @__PURE__ */
|
|
2339
|
+
/* @__PURE__ */ jsxs3(
|
|
2267
2340
|
"div",
|
|
2268
2341
|
{
|
|
2269
2342
|
onDragOver: attachments ? handleDragOver : void 0,
|
|
@@ -2278,7 +2351,7 @@ function ChatComposer({
|
|
|
2278
2351
|
dragging && "border-dashed border-og-accent"
|
|
2279
2352
|
),
|
|
2280
2353
|
children: [
|
|
2281
|
-
dragging ? /* @__PURE__ */
|
|
2354
|
+
dragging ? /* @__PURE__ */ jsx4(
|
|
2282
2355
|
"div",
|
|
2283
2356
|
{
|
|
2284
2357
|
"aria-hidden": true,
|
|
@@ -2286,15 +2359,15 @@ function ChatComposer({
|
|
|
2286
2359
|
"pointer-events-none absolute inset-0 z-10 flex items-center justify-center",
|
|
2287
2360
|
"rounded-og-lg bg-og-surface-1/85 text-sm font-medium text-og-accent backdrop-blur-[1px]"
|
|
2288
2361
|
),
|
|
2289
|
-
children: /* @__PURE__ */
|
|
2290
|
-
/* @__PURE__ */
|
|
2362
|
+
children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-2", children: [
|
|
2363
|
+
/* @__PURE__ */ jsx4(PaperclipIcon, { className: "size-4" }),
|
|
2291
2364
|
"Drop files to attach"
|
|
2292
2365
|
] })
|
|
2293
2366
|
}
|
|
2294
2367
|
) : null,
|
|
2295
|
-
attachments && attachments.attachments.length > 0 ? /* @__PURE__ */
|
|
2368
|
+
attachments && attachments.attachments.length > 0 ? /* @__PURE__ */ jsx4(AttachmentChips, { attachments: attachments.attachments, onRemove: attachments.remove }) : null,
|
|
2296
2369
|
header,
|
|
2297
|
-
/* @__PURE__ */
|
|
2370
|
+
/* @__PURE__ */ jsx4(
|
|
2298
2371
|
"textarea",
|
|
2299
2372
|
{
|
|
2300
2373
|
ref: textareaRef,
|
|
@@ -2326,17 +2399,17 @@ function ChatComposer({
|
|
|
2326
2399
|
)
|
|
2327
2400
|
}
|
|
2328
2401
|
),
|
|
2329
|
-
confirmState && pendingDangerCommand ? /* @__PURE__ */
|
|
2402
|
+
confirmState && pendingDangerCommand ? /* @__PURE__ */ jsx4(
|
|
2330
2403
|
ConfirmBar,
|
|
2331
2404
|
{
|
|
2332
2405
|
command: pendingDangerCommand,
|
|
2333
2406
|
onCancel: () => confirmState.resolve(false),
|
|
2334
2407
|
onConfirm: () => confirmState.resolve(true)
|
|
2335
2408
|
}
|
|
2336
|
-
) : /* @__PURE__ */
|
|
2337
|
-
attachments || controlsStart ? /* @__PURE__ */
|
|
2338
|
-
attachments ? /* @__PURE__ */
|
|
2339
|
-
/* @__PURE__ */
|
|
2409
|
+
) : /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2 px-2.5 pb-2.5 pt-1", children: [
|
|
2410
|
+
attachments || models || controlsStart ? /* @__PURE__ */ jsxs3("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
2411
|
+
attachments ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2412
|
+
/* @__PURE__ */ jsx4(
|
|
2340
2413
|
"input",
|
|
2341
2414
|
{
|
|
2342
2415
|
ref: fileInputRef,
|
|
@@ -2346,7 +2419,7 @@ function ChatComposer({
|
|
|
2346
2419
|
onChange: handleFileChange
|
|
2347
2420
|
}
|
|
2348
2421
|
),
|
|
2349
|
-
/* @__PURE__ */
|
|
2422
|
+
/* @__PURE__ */ jsx4(
|
|
2350
2423
|
"button",
|
|
2351
2424
|
{
|
|
2352
2425
|
type: "button",
|
|
@@ -2359,14 +2432,23 @@ function ChatComposer({
|
|
|
2359
2432
|
"text-og-fg-muted transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
|
|
2360
2433
|
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
2361
2434
|
),
|
|
2362
|
-
children: /* @__PURE__ */
|
|
2435
|
+
children: /* @__PURE__ */ jsx4(PaperclipIcon, { className: "size-4" })
|
|
2363
2436
|
}
|
|
2364
2437
|
)
|
|
2365
2438
|
] }) : null,
|
|
2439
|
+
models ? /* @__PURE__ */ jsx4(
|
|
2440
|
+
ModelPicker,
|
|
2441
|
+
{
|
|
2442
|
+
models,
|
|
2443
|
+
value: selectedModel,
|
|
2444
|
+
onChange: (modelId) => onSelectModel?.(modelId),
|
|
2445
|
+
disabled: disabled === true
|
|
2446
|
+
}
|
|
2447
|
+
) : null,
|
|
2366
2448
|
controlsStart
|
|
2367
|
-
] }) : /* @__PURE__ */
|
|
2368
|
-
/* @__PURE__ */
|
|
2369
|
-
/* @__PURE__ */
|
|
2449
|
+
] }) : /* @__PURE__ */ jsx4("span", { className: "px-1.5 text-[11px] text-og-fg-subtle max-sm:hidden", children: hint ?? "Enter to send \xB7 Shift+Enter for a new line \xB7 / for commands" }),
|
|
2450
|
+
/* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
|
|
2451
|
+
/* @__PURE__ */ jsx4(AnimatePresence2, { initial: false, children: active ? /* @__PURE__ */ jsx4(
|
|
2370
2452
|
motion2.button,
|
|
2371
2453
|
{
|
|
2372
2454
|
type: "button",
|
|
@@ -2384,11 +2466,11 @@ function ChatComposer({
|
|
|
2384
2466
|
"hover:border-og-status-failed/50 hover:text-og-status-failed",
|
|
2385
2467
|
"disabled:opacity-50"
|
|
2386
2468
|
),
|
|
2387
|
-
children: composer.interrupting ? /* @__PURE__ */
|
|
2469
|
+
children: composer.interrupting ? /* @__PURE__ */ jsx4(LoaderCircleIcon, { className: "size-3.5 animate-og-spin" }) : /* @__PURE__ */ jsx4(SquareIcon, { className: "size-3 fill-current" })
|
|
2388
2470
|
},
|
|
2389
2471
|
"stop"
|
|
2390
2472
|
) : null }),
|
|
2391
|
-
/* @__PURE__ */
|
|
2473
|
+
/* @__PURE__ */ jsx4(
|
|
2392
2474
|
"button",
|
|
2393
2475
|
{
|
|
2394
2476
|
type: "button",
|
|
@@ -2407,7 +2489,7 @@ function ChatComposer({
|
|
|
2407
2489
|
"hover:bg-og-accent-strong active:scale-95",
|
|
2408
2490
|
"disabled:cursor-not-allowed disabled:bg-og-surface-3 disabled:text-og-fg-subtle disabled:shadow-none"
|
|
2409
2491
|
),
|
|
2410
|
-
children: composer.sending ? /* @__PURE__ */
|
|
2492
|
+
children: composer.sending ? /* @__PURE__ */ jsx4(LoaderCircleIcon, { className: "size-4 animate-og-spin" }) : /* @__PURE__ */ jsx4(ArrowUpIcon, { className: "size-4" })
|
|
2411
2493
|
}
|
|
2412
2494
|
)
|
|
2413
2495
|
] })
|
|
@@ -2416,8 +2498,8 @@ function ChatComposer({
|
|
|
2416
2498
|
}
|
|
2417
2499
|
)
|
|
2418
2500
|
] }),
|
|
2419
|
-
/* @__PURE__ */
|
|
2420
|
-
/* @__PURE__ */
|
|
2501
|
+
/* @__PURE__ */ jsx4(AnimatePresence2, { children: helpOpen ? /* @__PURE__ */ jsx4(HelpPanel, { commands: helpCommands, onClose: () => setHelpOpen(false) }) : null }),
|
|
2502
|
+
/* @__PURE__ */ jsx4(AnimatePresence2, { children: activeNotice ? /* @__PURE__ */ jsx4(
|
|
2421
2503
|
motion2.p,
|
|
2422
2504
|
{
|
|
2423
2505
|
initial: { opacity: 0, height: 0 },
|
|
@@ -2439,7 +2521,7 @@ function ChatComposer({
|
|
|
2439
2521
|
] });
|
|
2440
2522
|
}
|
|
2441
2523
|
function ConfirmBar({ command, onCancel, onConfirm }) {
|
|
2442
|
-
return /* @__PURE__ */
|
|
2524
|
+
return /* @__PURE__ */ jsxs3(
|
|
2443
2525
|
"div",
|
|
2444
2526
|
{
|
|
2445
2527
|
role: "alertdialog",
|
|
@@ -2447,17 +2529,17 @@ function ConfirmBar({ command, onCancel, onConfirm }) {
|
|
|
2447
2529
|
"data-testid": "danger-confirm",
|
|
2448
2530
|
className: "flex items-center justify-between gap-2 px-2.5 pb-2.5 pt-1",
|
|
2449
2531
|
children: [
|
|
2450
|
-
/* @__PURE__ */
|
|
2532
|
+
/* @__PURE__ */ jsxs3("span", { className: "px-1.5 text-[12px] text-og-status-failed", children: [
|
|
2451
2533
|
"Run ",
|
|
2452
|
-
/* @__PURE__ */
|
|
2534
|
+
/* @__PURE__ */ jsxs3("span", { className: "font-mono", children: [
|
|
2453
2535
|
"/",
|
|
2454
2536
|
command.name
|
|
2455
2537
|
] }),
|
|
2456
2538
|
"? ",
|
|
2457
2539
|
command.description
|
|
2458
2540
|
] }),
|
|
2459
|
-
/* @__PURE__ */
|
|
2460
|
-
/* @__PURE__ */
|
|
2541
|
+
/* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
|
|
2542
|
+
/* @__PURE__ */ jsx4(
|
|
2461
2543
|
"button",
|
|
2462
2544
|
{
|
|
2463
2545
|
type: "button",
|
|
@@ -2466,7 +2548,7 @@ function ConfirmBar({ command, onCancel, onConfirm }) {
|
|
|
2466
2548
|
children: "Cancel"
|
|
2467
2549
|
}
|
|
2468
2550
|
),
|
|
2469
|
-
/* @__PURE__ */
|
|
2551
|
+
/* @__PURE__ */ jsx4(
|
|
2470
2552
|
"button",
|
|
2471
2553
|
{
|
|
2472
2554
|
type: "button",
|
|
@@ -2482,7 +2564,7 @@ function ConfirmBar({ command, onCancel, onConfirm }) {
|
|
|
2482
2564
|
);
|
|
2483
2565
|
}
|
|
2484
2566
|
function AttachmentChips({ attachments, onRemove }) {
|
|
2485
|
-
return /* @__PURE__ */
|
|
2567
|
+
return /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-2 border-b border-og-border px-3 py-2", children: attachments.map((attachment) => /* @__PURE__ */ jsxs3(
|
|
2486
2568
|
"div",
|
|
2487
2569
|
{
|
|
2488
2570
|
className: cn(
|
|
@@ -2490,10 +2572,10 @@ function AttachmentChips({ attachments, onRemove }) {
|
|
|
2490
2572
|
"border-og-border bg-og-surface-2 text-xs"
|
|
2491
2573
|
),
|
|
2492
2574
|
children: [
|
|
2493
|
-
attachment.previewUrl ? /* @__PURE__ */
|
|
2494
|
-
/* @__PURE__ */
|
|
2495
|
-
/* @__PURE__ */
|
|
2496
|
-
/* @__PURE__ */
|
|
2575
|
+
attachment.previewUrl ? /* @__PURE__ */ jsx4("img", { src: attachment.previewUrl, alt: "", className: "size-8 shrink-0 rounded object-cover" }) : attachment.contentType.startsWith("image/") ? /* @__PURE__ */ jsx4(ImageIcon, { className: "size-4 shrink-0 text-og-fg-muted" }) : /* @__PURE__ */ jsx4(FileIcon, { className: "size-4 shrink-0 text-og-fg-muted" }),
|
|
2576
|
+
/* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
|
|
2577
|
+
/* @__PURE__ */ jsx4("div", { className: "truncate font-medium text-og-fg", children: attachment.name }),
|
|
2578
|
+
/* @__PURE__ */ jsx4(
|
|
2497
2579
|
"div",
|
|
2498
2580
|
{
|
|
2499
2581
|
className: cn(
|
|
@@ -2504,15 +2586,15 @@ function AttachmentChips({ attachments, onRemove }) {
|
|
|
2504
2586
|
}
|
|
2505
2587
|
)
|
|
2506
2588
|
] }),
|
|
2507
|
-
attachment.status === "uploading" ? /* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
2589
|
+
attachment.status === "uploading" ? /* @__PURE__ */ jsx4(LoaderCircleIcon, { className: "size-3.5 shrink-0 animate-og-spin" }) : null,
|
|
2590
|
+
/* @__PURE__ */ jsx4(
|
|
2509
2591
|
"button",
|
|
2510
2592
|
{
|
|
2511
2593
|
type: "button",
|
|
2512
2594
|
onClick: () => onRemove(attachment.id),
|
|
2513
2595
|
className: "shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg",
|
|
2514
2596
|
"aria-label": `Remove ${attachment.name}`,
|
|
2515
|
-
children: /* @__PURE__ */
|
|
2597
|
+
children: /* @__PURE__ */ jsx4(XIcon, { className: "size-3.5" })
|
|
2516
2598
|
}
|
|
2517
2599
|
)
|
|
2518
2600
|
]
|
|
@@ -2521,7 +2603,7 @@ function AttachmentChips({ attachments, onRemove }) {
|
|
|
2521
2603
|
)) });
|
|
2522
2604
|
}
|
|
2523
2605
|
function HelpPanel({ commands, onClose }) {
|
|
2524
|
-
return /* @__PURE__ */
|
|
2606
|
+
return /* @__PURE__ */ jsxs3(
|
|
2525
2607
|
motion2.div,
|
|
2526
2608
|
{
|
|
2527
2609
|
initial: { opacity: 0, height: 0 },
|
|
@@ -2529,20 +2611,20 @@ function HelpPanel({ commands, onClose }) {
|
|
|
2529
2611
|
exit: { opacity: 0, height: 0 },
|
|
2530
2612
|
className: "mt-2 overflow-hidden rounded-og-lg border border-og-border bg-og-surface-2",
|
|
2531
2613
|
children: [
|
|
2532
|
-
/* @__PURE__ */
|
|
2533
|
-
/* @__PURE__ */
|
|
2534
|
-
/* @__PURE__ */
|
|
2614
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between border-b border-og-border px-3 py-1.5", children: [
|
|
2615
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[12px] font-medium text-og-fg", children: "Commands" }),
|
|
2616
|
+
/* @__PURE__ */ jsx4("button", { type: "button", onClick: onClose, className: "text-[11px] text-og-fg-subtle hover:text-og-fg", children: "Close" })
|
|
2535
2617
|
] }),
|
|
2536
|
-
/* @__PURE__ */
|
|
2618
|
+
/* @__PURE__ */ jsx4("ul", { className: "py-1", children: commands.map((command) => {
|
|
2537
2619
|
const hint = argHint(command.args);
|
|
2538
|
-
return /* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
2620
|
+
return /* @__PURE__ */ jsxs3("li", { className: "flex items-baseline gap-2 px-3 py-1", children: [
|
|
2621
|
+
/* @__PURE__ */ jsxs3("span", { className: "font-mono text-[12px] text-og-accent", children: [
|
|
2540
2622
|
"/",
|
|
2541
2623
|
command.name,
|
|
2542
|
-
hint ? /* @__PURE__ */
|
|
2624
|
+
hint ? /* @__PURE__ */ jsx4("span", { className: "ml-1 text-og-fg-subtle", children: hint }) : null
|
|
2543
2625
|
] }),
|
|
2544
|
-
/* @__PURE__ */
|
|
2545
|
-
command.danger ? /* @__PURE__ */
|
|
2626
|
+
/* @__PURE__ */ jsx4("span", { className: "text-[12px] text-og-fg-muted", children: command.description }),
|
|
2627
|
+
command.danger ? /* @__PURE__ */ jsx4("span", { className: "ml-auto rounded-og-xs bg-og-status-failed/15 px-1 text-[10px] uppercase tracking-wide text-og-status-failed", children: "danger" }) : null
|
|
2546
2628
|
] }, command.name);
|
|
2547
2629
|
}) })
|
|
2548
2630
|
]
|
|
@@ -2562,11 +2644,94 @@ import {
|
|
|
2562
2644
|
WrenchIcon
|
|
2563
2645
|
} from "lucide-react";
|
|
2564
2646
|
import { AnimatePresence as AnimatePresence3, motion as motion3 } from "motion/react";
|
|
2565
|
-
import { useEffect as useEffect7, useMemo as
|
|
2647
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState9 } from "react";
|
|
2566
2648
|
import { Collapsible } from "radix-ui";
|
|
2567
2649
|
|
|
2650
|
+
// src/components/markdown.tsx
|
|
2651
|
+
import { memo } from "react";
|
|
2652
|
+
import ReactMarkdown from "react-markdown";
|
|
2653
|
+
import remarkGfm from "remark-gfm";
|
|
2654
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
2655
|
+
var components = {
|
|
2656
|
+
h1: ({ children, ...props }) => /* @__PURE__ */ jsx5("h1", { className: "mt-5 mb-2.5 text-xl font-semibold tracking-tight text-og-fg first:mt-0", ...props, children }),
|
|
2657
|
+
h2: ({ children, ...props }) => /* @__PURE__ */ jsx5("h2", { className: "mt-5 mb-2 text-lg font-semibold tracking-tight text-og-fg first:mt-0", ...props, children }),
|
|
2658
|
+
h3: ({ children, ...props }) => /* @__PURE__ */ jsx5("h3", { className: "mt-4 mb-1.5 text-[15px] font-semibold tracking-tight text-og-fg first:mt-0", ...props, children }),
|
|
2659
|
+
h4: ({ children, ...props }) => /* @__PURE__ */ jsx5("h4", { className: "mt-4 mb-1.5 text-sm font-semibold uppercase tracking-[0.04em] text-og-fg-muted first:mt-0", ...props, children }),
|
|
2660
|
+
p: ({ children, ...props }) => /* @__PURE__ */ jsx5("p", { className: "my-2.5 leading-7 first:mt-0 last:mb-0", ...props, children }),
|
|
2661
|
+
strong: ({ children, ...props }) => /* @__PURE__ */ jsx5("strong", { className: "font-semibold text-og-fg", ...props, children }),
|
|
2662
|
+
em: ({ children, ...props }) => /* @__PURE__ */ jsx5("em", { className: "italic", ...props, children }),
|
|
2663
|
+
a: ({ children, ...props }) => /* @__PURE__ */ jsx5(
|
|
2664
|
+
"a",
|
|
2665
|
+
{
|
|
2666
|
+
className: "break-words font-medium text-og-accent underline-offset-2 hover:underline",
|
|
2667
|
+
target: "_blank",
|
|
2668
|
+
rel: "noreferrer noopener",
|
|
2669
|
+
...props,
|
|
2670
|
+
children
|
|
2671
|
+
}
|
|
2672
|
+
),
|
|
2673
|
+
ul: ({ children, ...props }) => /* @__PURE__ */ jsx5("ul", { className: "my-2.5 ml-5 flex list-disc flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0", ...props, children }),
|
|
2674
|
+
ol: ({ children, ...props }) => /* @__PURE__ */ jsx5("ol", { className: "my-2.5 ml-5 flex list-decimal flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0", ...props, children }),
|
|
2675
|
+
// GFM task-list items carry a leading checkbox <input>; `list-none` + a
|
|
2676
|
+
// negative margin pull the checkbox back to the bullet column so it aligns
|
|
2677
|
+
// with the text.
|
|
2678
|
+
li: ({ children, ...props }) => /* @__PURE__ */ jsx5("li", { className: "leading-7 marker:text-og-fg-subtle [&>ul]:my-1 [&>ol]:my-1 [&:has(>input)]:list-none [&:has(>input)]:-ml-5", ...props, children }),
|
|
2679
|
+
input: ({ type, ...props }) => type === "checkbox" ? /* @__PURE__ */ jsx5(
|
|
2680
|
+
"input",
|
|
2681
|
+
{
|
|
2682
|
+
...props,
|
|
2683
|
+
type: "checkbox",
|
|
2684
|
+
disabled: true,
|
|
2685
|
+
className: "mr-2 size-3.5 translate-y-[2px] cursor-default appearance-none rounded-[3px] border border-og-border bg-og-surface-1 align-baseline checked:border-og-accent checked:bg-og-accent checked:[background-image:url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2012%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22white%22%20stroke-width%3D%221.6%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M2.5%206.2l2.2%202.2%204.6-4.8%22%2F%3E%3C%2Fsvg%3E')] checked:bg-[length:11px_11px] checked:bg-center checked:bg-no-repeat"
|
|
2686
|
+
}
|
|
2687
|
+
) : /* @__PURE__ */ jsx5("input", { type, ...props }),
|
|
2688
|
+
blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx5(
|
|
2689
|
+
"blockquote",
|
|
2690
|
+
{
|
|
2691
|
+
className: "my-3 border-l-2 border-og-border-strong pl-3.5 text-og-fg-muted [&>p]:my-1.5 first:mt-0 last:mb-0",
|
|
2692
|
+
...props,
|
|
2693
|
+
children
|
|
2694
|
+
}
|
|
2695
|
+
),
|
|
2696
|
+
hr: (props) => /* @__PURE__ */ jsx5("hr", { className: "my-4 border-0 border-t border-og-border", ...props }),
|
|
2697
|
+
// Inline `code` vs fenced code blocks. react-markdown v10 no longer passes an
|
|
2698
|
+
// `inline` flag; a fenced block is a <code> whose parent is <pre> (styled by
|
|
2699
|
+
// the `pre` renderer), so a `code` reaching here is treated as inline.
|
|
2700
|
+
code: ({ children, className: _className, ...props }) => /* @__PURE__ */ jsx5(
|
|
2701
|
+
"code",
|
|
2702
|
+
{
|
|
2703
|
+
className: "rounded-og-xs border border-og-border bg-og-surface-1 px-1 py-0.5 font-og-mono text-[0.85em] text-og-fg",
|
|
2704
|
+
...props,
|
|
2705
|
+
children
|
|
2706
|
+
}
|
|
2707
|
+
),
|
|
2708
|
+
// Fenced code blocks — mirror the timeline's PayloadBlock <pre> styling for
|
|
2709
|
+
// visual consistency (bordered, scrollable, mono, surface background).
|
|
2710
|
+
pre: ({ children, ...props }) => /* @__PURE__ */ jsx5(
|
|
2711
|
+
"pre",
|
|
2712
|
+
{
|
|
2713
|
+
className: "my-3 max-h-96 overflow-auto rounded-og-md border border-og-border bg-og-bg/60 p-3 font-og-mono text-[12.5px] leading-5 text-og-fg-muted [&>code]:border-0 [&>code]:bg-transparent [&>code]:p-0 [&>code]:text-inherit first:mt-0 last:mb-0",
|
|
2714
|
+
...props,
|
|
2715
|
+
children
|
|
2716
|
+
}
|
|
2717
|
+
),
|
|
2718
|
+
table: ({ children, ...props }) => /* @__PURE__ */ jsx5("div", { className: "my-3 max-w-full overflow-x-auto rounded-og-md border border-og-border first:mt-0 last:mb-0", children: /* @__PURE__ */ jsx5("table", { className: "w-full border-collapse text-[13px]", ...props, children }) }),
|
|
2719
|
+
thead: ({ children, ...props }) => /* @__PURE__ */ jsx5("thead", { className: "bg-og-surface-1", ...props, children }),
|
|
2720
|
+
th: ({ children, ...props }) => /* @__PURE__ */ jsx5("th", { className: "border-b border-og-border px-3 py-1.5 text-left font-medium text-og-fg", ...props, children }),
|
|
2721
|
+
td: ({ children, ...props }) => /* @__PURE__ */ jsx5("td", { className: "border-b border-og-border px-3 py-1.5 align-top text-og-fg-muted [tr:last-child>&]:border-b-0", ...props, children }),
|
|
2722
|
+
img: ({ alt, ...props }) => /* @__PURE__ */ jsx5("img", { alt: alt ?? "", className: "my-3 max-w-full rounded-og-md border border-og-border", ...props })
|
|
2723
|
+
};
|
|
2724
|
+
function MarkdownImpl({ children, className }) {
|
|
2725
|
+
return (
|
|
2726
|
+
// `min-w-0` lets the prose shrink inside flex parents (message bubbles) so
|
|
2727
|
+
// long links and code blocks wrap/scroll instead of forcing overflow.
|
|
2728
|
+
/* @__PURE__ */ jsx5("div", { className: cn("min-w-0 break-words", className), children: /* @__PURE__ */ jsx5(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children }) })
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2731
|
+
var Markdown = memo(MarkdownImpl);
|
|
2732
|
+
|
|
2568
2733
|
// src/components/session-status.tsx
|
|
2569
|
-
import { jsx as
|
|
2734
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2570
2735
|
var SESSION_STATUS_META = {
|
|
2571
2736
|
queued: {
|
|
2572
2737
|
label: "Queued",
|
|
@@ -2607,7 +2772,7 @@ var SESSION_STATUS_META = {
|
|
|
2607
2772
|
};
|
|
2608
2773
|
function SessionStatus({ status, label, size = "md", className }) {
|
|
2609
2774
|
const meta = SESSION_STATUS_META[status];
|
|
2610
|
-
return /* @__PURE__ */
|
|
2775
|
+
return /* @__PURE__ */ jsxs4(
|
|
2611
2776
|
"span",
|
|
2612
2777
|
{
|
|
2613
2778
|
"data-status": status,
|
|
@@ -2618,7 +2783,7 @@ function SessionStatus({ status, label, size = "md", className }) {
|
|
|
2618
2783
|
className
|
|
2619
2784
|
),
|
|
2620
2785
|
children: [
|
|
2621
|
-
/* @__PURE__ */
|
|
2786
|
+
/* @__PURE__ */ jsx6(StatusDot, { status, className: size === "sm" ? "size-1" : "size-1.5" }),
|
|
2622
2787
|
label ?? meta.label
|
|
2623
2788
|
]
|
|
2624
2789
|
}
|
|
@@ -2626,11 +2791,11 @@ function SessionStatus({ status, label, size = "md", className }) {
|
|
|
2626
2791
|
}
|
|
2627
2792
|
function StatusDot({ status, className }) {
|
|
2628
2793
|
const meta = SESSION_STATUS_META[status];
|
|
2629
|
-
return /* @__PURE__ */
|
|
2794
|
+
return /* @__PURE__ */ jsx6("span", { className: cn("relative inline-flex size-1.5 shrink-0 rounded-full", meta.dotClassName, className), children: meta.pulse ? /* @__PURE__ */ jsx6("span", { className: cn("absolute inset-0 animate-og-pulse rounded-full", meta.dotClassName) }) : null });
|
|
2630
2795
|
}
|
|
2631
2796
|
|
|
2632
2797
|
// src/components/message-timeline.tsx
|
|
2633
|
-
import { jsx as
|
|
2798
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2634
2799
|
function MessageTimeline({
|
|
2635
2800
|
events,
|
|
2636
2801
|
items,
|
|
@@ -2641,8 +2806,8 @@ function MessageTimeline({
|
|
|
2641
2806
|
emptyState,
|
|
2642
2807
|
className
|
|
2643
2808
|
}) {
|
|
2644
|
-
const resolvedItems =
|
|
2645
|
-
const groups =
|
|
2809
|
+
const resolvedItems = useMemo6(() => items ?? buildTimeline(events ?? []), [items, events]);
|
|
2810
|
+
const groups = useMemo6(() => groupTimeline(resolvedItems), [resolvedItems]);
|
|
2646
2811
|
const scrollRef = useRef8(null);
|
|
2647
2812
|
const [pinned, setPinned] = useState9(true);
|
|
2648
2813
|
const lastItem = resolvedItems[resolvedItems.length - 1];
|
|
@@ -2661,15 +2826,15 @@ function MessageTimeline({
|
|
|
2661
2826
|
}
|
|
2662
2827
|
setPinned(node.scrollHeight - node.scrollTop - node.clientHeight < 48);
|
|
2663
2828
|
};
|
|
2664
|
-
return /* @__PURE__ */
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
groups.length === 0 && !working ? emptyState ?? /* @__PURE__ */
|
|
2829
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn("og-root relative min-h-0", className), children: [
|
|
2830
|
+
/* @__PURE__ */ jsx7("div", { ref: scrollRef, onScroll, className: "h-full overflow-y-auto overscroll-contain px-4 py-6 sm:px-6", children: /* @__PURE__ */ jsxs5("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: [
|
|
2831
|
+
groups.length === 0 && !working ? emptyState ?? /* @__PURE__ */ jsx7("p", { className: "py-10 text-center text-sm text-og-fg-subtle", children: "No activity yet." }) : null,
|
|
2667
2832
|
groups.map(
|
|
2668
|
-
(group) => group.kind === "activity" ? /* @__PURE__ */
|
|
2833
|
+
(group) => group.kind === "activity" ? /* @__PURE__ */ jsx7(ActivityCluster, { items: group.items, onOpenSession }, group.id) : /* @__PURE__ */ jsx7(TimelineRow, { item: group.item, renderMessageText }, group.item.id)
|
|
2669
2834
|
),
|
|
2670
|
-
working ? /* @__PURE__ */
|
|
2835
|
+
working ? /* @__PURE__ */ jsx7("div", { className: "animate-og-enter flex items-center gap-2 text-sm", children: /* @__PURE__ */ jsx7("span", { className: "og-shimmer-text font-medium", children: "Working\u2026" }) }) : null
|
|
2671
2836
|
] }) }),
|
|
2672
|
-
/* @__PURE__ */
|
|
2837
|
+
/* @__PURE__ */ jsx7(AnimatePresence3, { children: !pinned && autoFollow ? /* @__PURE__ */ jsxs5(
|
|
2673
2838
|
motion3.button,
|
|
2674
2839
|
{
|
|
2675
2840
|
type: "button",
|
|
@@ -2691,7 +2856,7 @@ function MessageTimeline({
|
|
|
2691
2856
|
"hover:border-og-border-strong"
|
|
2692
2857
|
),
|
|
2693
2858
|
children: [
|
|
2694
|
-
/* @__PURE__ */
|
|
2859
|
+
/* @__PURE__ */ jsx7(ArrowDownIcon, { className: "size-3.5" }),
|
|
2695
2860
|
"Jump to latest"
|
|
2696
2861
|
]
|
|
2697
2862
|
}
|
|
@@ -2704,15 +2869,15 @@ function TimelineRow({
|
|
|
2704
2869
|
}) {
|
|
2705
2870
|
switch (item.kind) {
|
|
2706
2871
|
case "user-message":
|
|
2707
|
-
return /* @__PURE__ */
|
|
2872
|
+
return /* @__PURE__ */ jsx7(UserMessageRow, { item, renderMessageText });
|
|
2708
2873
|
case "agent-message":
|
|
2709
|
-
return /* @__PURE__ */
|
|
2874
|
+
return /* @__PURE__ */ jsx7(AgentMessageRow, { item, renderMessageText });
|
|
2710
2875
|
case "session-status":
|
|
2711
|
-
return /* @__PURE__ */
|
|
2876
|
+
return /* @__PURE__ */ jsx7(SessionStatusRow, { item });
|
|
2712
2877
|
case "goal":
|
|
2713
|
-
return /* @__PURE__ */
|
|
2878
|
+
return /* @__PURE__ */ jsx7(GoalRow, { item });
|
|
2714
2879
|
case "notice":
|
|
2715
|
-
return /* @__PURE__ */
|
|
2880
|
+
return /* @__PURE__ */ jsx7(NoticeRow, { item });
|
|
2716
2881
|
default:
|
|
2717
2882
|
return null;
|
|
2718
2883
|
}
|
|
@@ -2721,35 +2886,44 @@ function UserMessageRow({
|
|
|
2721
2886
|
item,
|
|
2722
2887
|
renderMessageText
|
|
2723
2888
|
}) {
|
|
2724
|
-
return /* @__PURE__ */
|
|
2889
|
+
return /* @__PURE__ */ jsx7("div", { className: "animate-og-enter flex justify-end", children: /* @__PURE__ */ jsx7("div", { className: "max-w-[85%] min-w-0 rounded-og-lg rounded-br-og-xs border border-og-border bg-og-surface-2 px-4 py-2.5 text-[15px] leading-6 text-og-fg", children: renderMessageText ? renderMessageText(item.text, item) : /* @__PURE__ */ jsx7(Markdown, { children: item.text }) }) });
|
|
2725
2890
|
}
|
|
2726
2891
|
function AgentMessageRow({
|
|
2727
2892
|
item,
|
|
2728
2893
|
renderMessageText
|
|
2729
2894
|
}) {
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
item.
|
|
2733
|
-
|
|
2895
|
+
const caret = item.streaming ? /* @__PURE__ */ jsx7("span", { className: "ml-0.5 inline-block h-[1.1em] w-[2px] translate-y-[3px] animate-og-blink rounded-full bg-og-accent", "aria-hidden": true }) : null;
|
|
2896
|
+
return /* @__PURE__ */ jsx7("div", { className: "animate-og-enter min-w-0 text-[15px] leading-7 text-og-fg", children: renderMessageText ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2897
|
+
renderMessageText(item.text, item),
|
|
2898
|
+
caret
|
|
2899
|
+
] }) : (
|
|
2900
|
+
// While streaming, let the caret ride the end of the last rendered line:
|
|
2901
|
+
// the trailing block (usually a <p>) flows inline so the caret sits on
|
|
2902
|
+
// its baseline instead of dropping to a new line.
|
|
2903
|
+
/* @__PURE__ */ jsxs5("div", { className: item.streaming ? "[&_>div>:last-child]:inline" : void 0, children: [
|
|
2904
|
+
/* @__PURE__ */ jsx7(Markdown, { children: item.text }),
|
|
2905
|
+
caret
|
|
2906
|
+
] })
|
|
2907
|
+
) });
|
|
2734
2908
|
}
|
|
2735
2909
|
function SessionStatusRow({ item }) {
|
|
2736
2910
|
const meta = SESSION_STATUS_META[item.status];
|
|
2737
|
-
return /* @__PURE__ */
|
|
2738
|
-
/* @__PURE__ */
|
|
2739
|
-
/* @__PURE__ */
|
|
2740
|
-
/* @__PURE__ */
|
|
2911
|
+
return /* @__PURE__ */ jsxs5("div", { className: "animate-og-enter flex items-center gap-3 text-[11px] text-og-fg-subtle", role: "status", children: [
|
|
2912
|
+
/* @__PURE__ */ jsx7("span", { className: "h-px flex-1 bg-og-border" }),
|
|
2913
|
+
/* @__PURE__ */ jsxs5("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
2914
|
+
/* @__PURE__ */ jsx7(StatusDot, { status: item.status, className: "size-1" }),
|
|
2741
2915
|
meta.label.toLowerCase(),
|
|
2742
2916
|
" \xB7 ",
|
|
2743
2917
|
formatRelativeTime(item.occurredAt)
|
|
2744
2918
|
] }),
|
|
2745
|
-
/* @__PURE__ */
|
|
2919
|
+
/* @__PURE__ */ jsx7("span", { className: "h-px flex-1 bg-og-border" })
|
|
2746
2920
|
] });
|
|
2747
2921
|
}
|
|
2748
2922
|
function GoalRow({ item }) {
|
|
2749
2923
|
const label = item.action === "set" ? "Goal set" : item.action === "updated" ? "Goal updated" : item.action === "completed" ? "Goal completed" : item.action === "paused" ? "Goal paused" : item.action === "resumed" ? "Goal resumed" : "Continuing toward the goal";
|
|
2750
|
-
return /* @__PURE__ */
|
|
2751
|
-
/* @__PURE__ */
|
|
2752
|
-
/* @__PURE__ */
|
|
2924
|
+
return /* @__PURE__ */ jsx7("div", { className: "animate-og-enter flex justify-center", children: /* @__PURE__ */ jsxs5("span", { className: "inline-flex max-w-full items-center gap-1.5 rounded-full border border-og-border bg-og-surface-1 px-3 py-1 text-xs text-og-fg-muted", children: [
|
|
2925
|
+
/* @__PURE__ */ jsx7(TargetIcon, { className: "size-3.5 shrink-0 text-og-accent" }),
|
|
2926
|
+
/* @__PURE__ */ jsxs5("span", { className: "truncate", children: [
|
|
2753
2927
|
label,
|
|
2754
2928
|
item.text ? `: ${truncate(item.text, 90)}` : ""
|
|
2755
2929
|
] })
|
|
@@ -2757,25 +2931,25 @@ function GoalRow({ item }) {
|
|
|
2757
2931
|
}
|
|
2758
2932
|
function NoticeRow({ item }) {
|
|
2759
2933
|
const tone = item.tone === "failed" ? "border-og-status-failed/35 bg-og-status-failed/10 text-og-status-failed" : item.tone === "waiting" ? "border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting" : "border-og-border bg-og-surface-1 text-og-fg-muted";
|
|
2760
|
-
return /* @__PURE__ */
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
/* @__PURE__ */
|
|
2934
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn("animate-og-enter flex items-start gap-2.5 rounded-og-md border px-3.5 py-2.5 text-sm", tone), role: "status", children: [
|
|
2935
|
+
/* @__PURE__ */ jsx7(TriangleAlertIcon, { className: cn("mt-0.5 size-4 shrink-0", item.tone === "cancelled" && "opacity-60") }),
|
|
2936
|
+
/* @__PURE__ */ jsx7("span", { className: "min-w-0 whitespace-pre-wrap break-words", children: item.text })
|
|
2763
2937
|
] });
|
|
2764
2938
|
}
|
|
2765
2939
|
function ActivityCluster({
|
|
2766
2940
|
items,
|
|
2767
2941
|
onOpenSession
|
|
2768
2942
|
}) {
|
|
2769
|
-
return /* @__PURE__ */
|
|
2943
|
+
return /* @__PURE__ */ jsx7("div", { className: "animate-og-enter flex flex-col gap-1.5 border-l-2 border-og-border pl-3 sm:pl-4", children: items.map((item) => {
|
|
2770
2944
|
switch (item.kind) {
|
|
2771
2945
|
case "reasoning":
|
|
2772
|
-
return /* @__PURE__ */
|
|
2946
|
+
return /* @__PURE__ */ jsx7(ReasoningRow, { item }, item.id);
|
|
2773
2947
|
case "tool-call":
|
|
2774
|
-
return /* @__PURE__ */
|
|
2948
|
+
return /* @__PURE__ */ jsx7(ToolCallRow, { item }, item.id);
|
|
2775
2949
|
case "worker":
|
|
2776
|
-
return /* @__PURE__ */
|
|
2950
|
+
return /* @__PURE__ */ jsx7(WorkerRow, { item, onOpenSession }, item.id);
|
|
2777
2951
|
case "sandbox":
|
|
2778
|
-
return /* @__PURE__ */
|
|
2952
|
+
return /* @__PURE__ */ jsx7(SandboxRow, { item }, item.id);
|
|
2779
2953
|
}
|
|
2780
2954
|
}) });
|
|
2781
2955
|
}
|
|
@@ -2788,8 +2962,8 @@ function ActivityDisclosure({
|
|
|
2788
2962
|
children
|
|
2789
2963
|
}) {
|
|
2790
2964
|
const [open, setOpen] = useState9(false);
|
|
2791
|
-
return /* @__PURE__ */
|
|
2792
|
-
/* @__PURE__ */
|
|
2965
|
+
return /* @__PURE__ */ jsxs5(Collapsible.Root, { open, onOpenChange: setOpen, children: [
|
|
2966
|
+
/* @__PURE__ */ jsxs5(
|
|
2793
2967
|
Collapsible.Trigger,
|
|
2794
2968
|
{
|
|
2795
2969
|
className: cn(
|
|
@@ -2797,15 +2971,15 @@ function ActivityDisclosure({
|
|
|
2797
2971
|
"text-og-fg-muted transition-colors duration-150 hover:bg-og-surface-1 hover:text-og-fg"
|
|
2798
2972
|
),
|
|
2799
2973
|
children: [
|
|
2800
|
-
/* @__PURE__ */
|
|
2801
|
-
/* @__PURE__ */
|
|
2802
|
-
/* @__PURE__ */
|
|
2803
|
-
preview ? /* @__PURE__ */
|
|
2804
|
-
running ? /* @__PURE__ */
|
|
2974
|
+
/* @__PURE__ */ jsx7(ChevronRightIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle transition-transform duration-150 group-data-[state=open]:rotate-90" }),
|
|
2975
|
+
/* @__PURE__ */ jsx7("span", { className: cn("shrink-0", failed ? "text-og-status-failed" : running ? "text-og-status-running" : "text-og-fg-subtle"), children: icon }),
|
|
2976
|
+
/* @__PURE__ */ jsx7("span", { className: cn("shrink-0 font-medium", running && "og-shimmer-text", failed && "text-og-status-failed"), children: title }),
|
|
2977
|
+
preview ? /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1 truncate font-og-mono text-xs text-og-fg-subtle", children: preview }) : null,
|
|
2978
|
+
running ? /* @__PURE__ */ jsx7("span", { className: "ml-auto size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" }) : null
|
|
2805
2979
|
]
|
|
2806
2980
|
}
|
|
2807
2981
|
),
|
|
2808
|
-
/* @__PURE__ */
|
|
2982
|
+
/* @__PURE__ */ jsx7(Collapsible.Content, { className: "overflow-hidden", children: /* @__PURE__ */ jsx7("div", { className: "mt-1 mb-1.5 ml-7 flex flex-col gap-2", children }) })
|
|
2809
2983
|
] });
|
|
2810
2984
|
}
|
|
2811
2985
|
function PayloadBlock({ label, value }) {
|
|
@@ -2813,50 +2987,50 @@ function PayloadBlock({ label, value }) {
|
|
|
2813
2987
|
if (!text) {
|
|
2814
2988
|
return null;
|
|
2815
2989
|
}
|
|
2816
|
-
return /* @__PURE__ */
|
|
2817
|
-
/* @__PURE__ */
|
|
2818
|
-
/* @__PURE__ */
|
|
2990
|
+
return /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
|
|
2991
|
+
/* @__PURE__ */ jsx7("p", { className: "mb-1 text-[10px] font-medium uppercase tracking-[0.08em] text-og-fg-subtle", children: label }),
|
|
2992
|
+
/* @__PURE__ */ jsx7("pre", { className: "max-h-64 overflow-auto rounded-og-sm border border-og-border bg-og-bg/60 p-2.5 font-og-mono text-xs leading-5 text-og-fg-muted", children: text })
|
|
2819
2993
|
] });
|
|
2820
2994
|
}
|
|
2821
2995
|
function ReasoningRow({ item }) {
|
|
2822
|
-
return /* @__PURE__ */
|
|
2996
|
+
return /* @__PURE__ */ jsx7(
|
|
2823
2997
|
ActivityDisclosure,
|
|
2824
2998
|
{
|
|
2825
|
-
icon: /* @__PURE__ */
|
|
2999
|
+
icon: /* @__PURE__ */ jsx7(BrainIcon, { className: "size-3.5" }),
|
|
2826
3000
|
title: item.streaming ? "Thinking" : "Thought",
|
|
2827
3001
|
running: item.streaming,
|
|
2828
3002
|
preview: truncate(item.text, 110),
|
|
2829
|
-
children: /* @__PURE__ */
|
|
3003
|
+
children: /* @__PURE__ */ jsx7("p", { className: "whitespace-pre-wrap text-[13px] leading-6 text-og-fg-muted", children: item.text })
|
|
2830
3004
|
}
|
|
2831
3005
|
);
|
|
2832
3006
|
}
|
|
2833
3007
|
function ToolCallRow({ item }) {
|
|
2834
|
-
return /* @__PURE__ */
|
|
3008
|
+
return /* @__PURE__ */ jsxs5(
|
|
2835
3009
|
ActivityDisclosure,
|
|
2836
3010
|
{
|
|
2837
|
-
icon: /* @__PURE__ */
|
|
3011
|
+
icon: /* @__PURE__ */ jsx7(WrenchIcon, { className: "size-3.5" }),
|
|
2838
3012
|
title: toolDisplayName(item.name),
|
|
2839
3013
|
running: item.status === "running",
|
|
2840
3014
|
preview: compactPayloadPreview(item.arguments),
|
|
2841
3015
|
children: [
|
|
2842
|
-
/* @__PURE__ */
|
|
2843
|
-
item.status === "complete" ? /* @__PURE__ */
|
|
3016
|
+
/* @__PURE__ */ jsx7(PayloadBlock, { label: "Arguments", value: item.arguments }),
|
|
3017
|
+
item.status === "complete" ? /* @__PURE__ */ jsx7(PayloadBlock, { label: "Output", value: item.output }) : null
|
|
2844
3018
|
]
|
|
2845
3019
|
}
|
|
2846
3020
|
);
|
|
2847
3021
|
}
|
|
2848
3022
|
function SandboxRow({ item }) {
|
|
2849
|
-
return /* @__PURE__ */
|
|
3023
|
+
return /* @__PURE__ */ jsxs5(
|
|
2850
3024
|
ActivityDisclosure,
|
|
2851
3025
|
{
|
|
2852
|
-
icon: /* @__PURE__ */
|
|
3026
|
+
icon: /* @__PURE__ */ jsx7(SquareTerminalIcon, { className: "size-3.5" }),
|
|
2853
3027
|
title: toolDisplayName(item.name),
|
|
2854
3028
|
running: item.status === "running",
|
|
2855
3029
|
failed: item.status === "failed",
|
|
2856
3030
|
preview: item.command ?? void 0,
|
|
2857
3031
|
children: [
|
|
2858
|
-
item.command ? /* @__PURE__ */
|
|
2859
|
-
item.output ? /* @__PURE__ */
|
|
3032
|
+
item.command ? /* @__PURE__ */ jsx7(PayloadBlock, { label: "Command", value: item.command }) : null,
|
|
3033
|
+
item.output ? /* @__PURE__ */ jsx7(PayloadBlock, { label: "Output", value: item.output }) : null
|
|
2860
3034
|
]
|
|
2861
3035
|
}
|
|
2862
3036
|
);
|
|
@@ -2864,26 +3038,26 @@ function SandboxRow({ item }) {
|
|
|
2864
3038
|
function WorkerRow({ item, onOpenSession }) {
|
|
2865
3039
|
const running = item.status === "running";
|
|
2866
3040
|
const title = item.action === "spawn" ? running ? "Spawning worker" : "Worker spawned" : running ? "Messaging worker" : "Worker messaged";
|
|
2867
|
-
return /* @__PURE__ */
|
|
2868
|
-
/* @__PURE__ */
|
|
3041
|
+
return /* @__PURE__ */ jsxs5("div", { className: "my-0.5 flex items-start gap-3 rounded-og-md border border-og-border bg-og-surface-1 p-3 shadow-og-sm", children: [
|
|
3042
|
+
/* @__PURE__ */ jsx7(
|
|
2869
3043
|
"span",
|
|
2870
3044
|
{
|
|
2871
3045
|
className: cn(
|
|
2872
3046
|
"mt-0.5 inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
|
|
2873
3047
|
"bg-og-accent-soft text-og-accent"
|
|
2874
3048
|
),
|
|
2875
|
-
children: /* @__PURE__ */
|
|
3049
|
+
children: /* @__PURE__ */ jsx7(BotIcon, { className: "size-4" })
|
|
2876
3050
|
}
|
|
2877
3051
|
),
|
|
2878
|
-
/* @__PURE__ */
|
|
2879
|
-
/* @__PURE__ */
|
|
2880
|
-
/* @__PURE__ */
|
|
2881
|
-
running ? /* @__PURE__ */
|
|
3052
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1", children: [
|
|
3053
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
3054
|
+
/* @__PURE__ */ jsx7("span", { className: cn("text-[13px] font-medium", running ? "og-shimmer-text" : "text-og-fg"), children: title }),
|
|
3055
|
+
running ? /* @__PURE__ */ jsx7("span", { className: "size-1.5 animate-og-pulse rounded-full bg-og-status-running" }) : null
|
|
2882
3056
|
] }),
|
|
2883
|
-
item.prompt ? /* @__PURE__ */
|
|
2884
|
-
item.workerSessionId ? /* @__PURE__ */
|
|
3057
|
+
item.prompt ? /* @__PURE__ */ jsx7("p", { className: "mt-0.5 truncate text-xs text-og-fg-muted", children: truncate(item.prompt, 140) }) : null,
|
|
3058
|
+
item.workerSessionId ? /* @__PURE__ */ jsx7("p", { className: "mt-1 font-og-mono text-[11px] text-og-fg-subtle", children: item.workerSessionId.slice(0, 8) }) : null
|
|
2885
3059
|
] }),
|
|
2886
|
-
item.workerSessionId && onOpenSession ? /* @__PURE__ */
|
|
3060
|
+
item.workerSessionId && onOpenSession ? /* @__PURE__ */ jsx7(
|
|
2887
3061
|
"button",
|
|
2888
3062
|
{
|
|
2889
3063
|
type: "button",
|
|
@@ -2899,7 +3073,7 @@ function WorkerRow({ item, onOpenSession }) {
|
|
|
2899
3073
|
}
|
|
2900
3074
|
|
|
2901
3075
|
// src/components/fleet-tile.tsx
|
|
2902
|
-
import { jsx as
|
|
3076
|
+
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2903
3077
|
function sessionDisplayTitle(session) {
|
|
2904
3078
|
for (const key of ["title", "name"]) {
|
|
2905
3079
|
const value = session.metadata[key];
|
|
@@ -2912,7 +3086,7 @@ function sessionDisplayTitle(session) {
|
|
|
2912
3086
|
function FleetTile({ session, title, subtitle, onOpen, className }) {
|
|
2913
3087
|
const running = session.status === "running" || session.status === "queued";
|
|
2914
3088
|
const needsYou = session.status === "requires_action";
|
|
2915
|
-
return /* @__PURE__ */
|
|
3089
|
+
return /* @__PURE__ */ jsxs6(
|
|
2916
3090
|
"button",
|
|
2917
3091
|
{
|
|
2918
3092
|
type: "button",
|
|
@@ -2928,7 +3102,7 @@ function FleetTile({ session, title, subtitle, onOpen, className }) {
|
|
|
2928
3102
|
className
|
|
2929
3103
|
),
|
|
2930
3104
|
children: [
|
|
2931
|
-
/* @__PURE__ */
|
|
3105
|
+
/* @__PURE__ */ jsx8(
|
|
2932
3106
|
"span",
|
|
2933
3107
|
{
|
|
2934
3108
|
"aria-hidden": true,
|
|
@@ -2938,16 +3112,16 @@ function FleetTile({ session, title, subtitle, onOpen, className }) {
|
|
|
2938
3112
|
)
|
|
2939
3113
|
}
|
|
2940
3114
|
),
|
|
2941
|
-
/* @__PURE__ */
|
|
2942
|
-
/* @__PURE__ */
|
|
2943
|
-
/* @__PURE__ */
|
|
3115
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex w-full items-start justify-between gap-3", children: [
|
|
3116
|
+
/* @__PURE__ */ jsx8("span", { className: "line-clamp-2 min-w-0 text-sm font-medium leading-snug text-og-fg", children: title ?? sessionDisplayTitle(session) }),
|
|
3117
|
+
/* @__PURE__ */ jsx8(SessionStatus, { status: session.status, size: "sm", className: "mt-px" })
|
|
2944
3118
|
] }),
|
|
2945
|
-
subtitle ? /* @__PURE__ */
|
|
2946
|
-
/* @__PURE__ */
|
|
2947
|
-
/* @__PURE__ */
|
|
2948
|
-
/* @__PURE__ */
|
|
2949
|
-
/* @__PURE__ */
|
|
2950
|
-
/* @__PURE__ */
|
|
3119
|
+
subtitle ? /* @__PURE__ */ jsx8("span", { className: "line-clamp-1 text-xs text-og-fg-muted", children: subtitle }) : null,
|
|
3120
|
+
/* @__PURE__ */ jsxs6("span", { className: "mt-auto flex w-full items-center gap-2 text-[11px] text-og-fg-subtle", children: [
|
|
3121
|
+
/* @__PURE__ */ jsx8("span", { className: "font-og-mono", children: session.id.slice(0, 8) }),
|
|
3122
|
+
/* @__PURE__ */ jsx8("span", { "aria-hidden": true, children: "\xB7" }),
|
|
3123
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate", children: session.model }),
|
|
3124
|
+
/* @__PURE__ */ jsx8("span", { className: "ml-auto shrink-0", title: session.updatedAt, children: formatRelativeTime(session.updatedAt) })
|
|
2951
3125
|
] })
|
|
2952
3126
|
]
|
|
2953
3127
|
}
|
|
@@ -2957,7 +3131,9 @@ export {
|
|
|
2957
3131
|
ChatComposer,
|
|
2958
3132
|
CommandPalette,
|
|
2959
3133
|
FleetTile,
|
|
3134
|
+
Markdown,
|
|
2960
3135
|
MessageTimeline,
|
|
3136
|
+
ModelPicker,
|
|
2961
3137
|
OpenGeniProvider,
|
|
2962
3138
|
SESSION_STATUS_META,
|
|
2963
3139
|
SessionStatus,
|
|
@@ -2993,6 +3169,7 @@ export {
|
|
|
2993
3169
|
toolDisplayName,
|
|
2994
3170
|
truncate,
|
|
2995
3171
|
tryParseJson,
|
|
3172
|
+
useAvailableModels,
|
|
2996
3173
|
useBillingUsage,
|
|
2997
3174
|
useComposer,
|
|
2998
3175
|
useEnvironments,
|