@oxy-hq/sdk 2.4.0 → 2.6.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/README.md +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +206 -10
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +206 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/index.mjs.map +1 -1
- package/dist/{react-DArzs6_l.mjs → react-5HGW_0oy.mjs} +46 -26
- package/dist/react-5HGW_0oy.mjs.map +1 -0
- package/dist/{react-B7Blpmt7.cjs → react-BAiiXftp.cjs} +51 -31
- package/dist/react-BAiiXftp.cjs.map +1 -0
- package/dist/{react-BLsczFL4.d.mts → react-DnBdQ8dG.d.cts} +26 -16
- package/dist/react-DnBdQ8dG.d.cts.map +1 -0
- package/dist/{react-BLsczFL4.d.cts → react-DnBdQ8dG.d.mts} +26 -16
- package/dist/react-DnBdQ8dG.d.mts.map +1 -0
- package/dist/shell.cjs +138 -54
- package/dist/shell.cjs.map +1 -1
- package/dist/shell.css +80 -4
- package/dist/shell.d.cts +15 -6
- package/dist/shell.d.cts.map +1 -1
- package/dist/shell.d.mts +15 -6
- package/dist/shell.d.mts.map +1 -1
- package/dist/shell.mjs +138 -54
- package/dist/shell.mjs.map +1 -1
- package/package.json +11 -11
- package/dist/react-B7Blpmt7.cjs.map +0 -1
- package/dist/react-BLsczFL4.d.cts.map +0 -1
- package/dist/react-BLsczFL4.d.mts.map +0 -1
- package/dist/react-DArzs6_l.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/custom-app/manifest.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Declaration of a single Oxy Function shipped in the bundle's
|
|
6
6
|
* `functions/` dir. See `internal-docs/2026-06-12-customer-apps-functions-design.md`.
|
|
@@ -136,7 +136,7 @@ interface OxyAppManifest {
|
|
|
136
136
|
* should treat this as the only source of truth for "which org/app
|
|
137
137
|
* does this bundle belong to."
|
|
138
138
|
*/
|
|
139
|
-
interface
|
|
139
|
+
interface ResolvedCustomAppManifest {
|
|
140
140
|
manifest: OxyAppManifest;
|
|
141
141
|
/**
|
|
142
142
|
* Always an empty array for v2 manifests. Kept for API compatibility;
|
|
@@ -179,13 +179,13 @@ interface LoadManifestOptions {
|
|
|
179
179
|
* Load + validate the manifest. Cached after the first call so callers
|
|
180
180
|
* can invoke this from every component without coordinating.
|
|
181
181
|
*/
|
|
182
|
-
declare function
|
|
182
|
+
declare function loadCustomAppManifest(options?: LoadManifestOptions): Promise<ResolvedCustomAppManifest>;
|
|
183
183
|
/** For tests: reset the cache between runs. */
|
|
184
|
-
declare function
|
|
184
|
+
declare function _resetCustomAppManifestCacheForTest(): void;
|
|
185
185
|
//#endregion
|
|
186
|
-
//#region src/
|
|
186
|
+
//#region src/custom-app/errors.d.ts
|
|
187
187
|
/**
|
|
188
|
-
* Error thrown by all
|
|
188
|
+
* Error thrown by all custom-app hooks when an API call returns a
|
|
189
189
|
* non-2xx response. Carries the structured `code` + `hint` the server
|
|
190
190
|
* emits so bundle UIs can render an actionable message instead of
|
|
191
191
|
* "404: { ...json... }".
|
|
@@ -208,23 +208,23 @@ declare class OxyApiError extends Error {
|
|
|
208
208
|
* dominate the bundle UI).
|
|
209
209
|
*/
|
|
210
210
|
declare function apiErrorFromResponse(resp: Response): Promise<OxyApiError>;
|
|
211
|
-
interface
|
|
211
|
+
interface CustomAppErrorReport {
|
|
212
212
|
title: string;
|
|
213
213
|
message: string;
|
|
214
214
|
hint: string;
|
|
215
215
|
docs?: string;
|
|
216
216
|
}
|
|
217
217
|
/** Interpret a thrown error as a structured report for UI display. */
|
|
218
|
-
declare function
|
|
218
|
+
declare function interpretCustomAppError(err: unknown): CustomAppErrorReport;
|
|
219
219
|
//#endregion
|
|
220
|
-
//#region src/
|
|
220
|
+
//#region src/custom-app/function-sse.d.ts
|
|
221
221
|
/** A captured `console.*` / `ctx.log` line from a function run. */
|
|
222
222
|
interface FunctionLog {
|
|
223
223
|
level: string;
|
|
224
224
|
message: string;
|
|
225
225
|
}
|
|
226
226
|
//#endregion
|
|
227
|
-
//#region src/
|
|
227
|
+
//#region src/custom-app/react.d.ts
|
|
228
228
|
/**
|
|
229
229
|
* Credentialed fetch wrapper stored in context so `useQuery` can share
|
|
230
230
|
* the same request mechanism without coupling it to the global `fetch`.
|
|
@@ -237,7 +237,7 @@ interface FunctionLog {
|
|
|
237
237
|
*/
|
|
238
238
|
type AppFetcher = typeof fetch;
|
|
239
239
|
interface OxyAppProviderProps {
|
|
240
|
-
/** Optional manifest load options. Same shape as `
|
|
240
|
+
/** Optional manifest load options. Same shape as `loadCustomAppManifest`. */
|
|
241
241
|
manifestOptions?: LoadManifestOptions;
|
|
242
242
|
/**
|
|
243
243
|
* Rendered while the manifest is loading. Defaults to nothing; pass a
|
|
@@ -249,13 +249,23 @@ interface OxyAppProviderProps {
|
|
|
249
249
|
* report so the bundle can show its own branded error card. Defaults
|
|
250
250
|
* to a minimal text-only fallback (better than a blank page).
|
|
251
251
|
*/
|
|
252
|
-
errorFallback?: (err:
|
|
252
|
+
errorFallback?: (err: CustomAppErrorReport) => React.ReactNode;
|
|
253
253
|
/**
|
|
254
254
|
* Override the fetch implementation used by all hooks (`useQuery`).
|
|
255
255
|
* Useful for test environments or proxy setups. Defaults to a wrapper
|
|
256
256
|
* that sets `credentials: "include"` on every request.
|
|
257
257
|
*/
|
|
258
258
|
fetcher?: AppFetcher;
|
|
259
|
+
/**
|
|
260
|
+
* Origin of the oxy backend to call (e.g. `https://oxy.example.com` or
|
|
261
|
+
* `http://localhost:3000`). When set, the SDK resolves its relative `/api/…`
|
|
262
|
+
* requests — `shell-context`, Ask Oxygen, events — against this origin
|
|
263
|
+
* instead of the app's own, so a standalone / cross-origin dev app can drive
|
|
264
|
+
* the wired shell without a same-origin proxy. The backend must allow the
|
|
265
|
+
* app's origin (see oxy's dev-origin CORS list). Leave unset when the app is
|
|
266
|
+
* served same-origin by oxy.
|
|
267
|
+
*/
|
|
268
|
+
backendUrl?: string;
|
|
259
269
|
children: React.ReactNode;
|
|
260
270
|
}
|
|
261
271
|
/**
|
|
@@ -268,7 +278,7 @@ declare function OxyAppProvider(props: OxyAppProviderProps): React.JSX.Element;
|
|
|
268
278
|
* `<OxyAppProvider>` — that's a programmer error worth surfacing
|
|
269
279
|
* loudly, not silently swallowing.
|
|
270
280
|
*/
|
|
271
|
-
declare function useResolvedManifest():
|
|
281
|
+
declare function useResolvedManifest(): ResolvedCustomAppManifest;
|
|
272
282
|
/**
|
|
273
283
|
* Low-level hook that returns the raw context value (including the
|
|
274
284
|
* fetcher). Prefer `useResolvedManifest` for manifest access; use
|
|
@@ -300,7 +310,7 @@ interface UseQueryResult<Row = Record<string, unknown>> {
|
|
|
300
310
|
}
|
|
301
311
|
/**
|
|
302
312
|
* Execute an ad-hoc SQL query against the project linked to this
|
|
303
|
-
*
|
|
313
|
+
* custom app. The query is specified inline by the caller; no
|
|
304
314
|
* manifest declaration is involved.
|
|
305
315
|
*
|
|
306
316
|
* Re-runs whenever `input` or enabled `params` change. Use the
|
|
@@ -642,5 +652,5 @@ interface OxyChatProps {
|
|
|
642
652
|
*/
|
|
643
653
|
declare function OxyChat(props: OxyChatProps): React.JSX.Element;
|
|
644
654
|
//#endregion
|
|
645
|
-
export { UseSemanticQueryOpts as A,
|
|
646
|
-
//# sourceMappingURL=react-
|
|
655
|
+
export { UseSemanticQueryOpts as A, CustomAppErrorReport as B, UseProcedureRunInput as C, UseQueryOpts as D, UseQueryInput as E, useProcedureRun as F, OxyAppFunctionManifest as G, apiErrorFromResponse as H, useQuery as I, _resetCustomAppManifestCacheForTest as J, OxyAppManifest as K, useResolvedManifest as L, useAgentRun as M, useFunction as N, UseQueryResult as O, useOxyApp as P, useSemanticQuery as R, UseFunctionResult as S, UseProcedureRunResult as T, interpretCustomAppError as U, OxyApiError as V, LoadManifestOptions as W, loadCustomAppManifest as Y, SemanticFilter as _, AppFetcher as a, UseAgentRunInput as b, OxyAppProvider as c, OxyChatProps as d, ProcedureProgress as f, SemanticDateRangeOp as g, SemanticArrayOp as h, AgentSqlArtifact as i, UseSemanticQueryResult as j, UseSemanticQueryInput as k, OxyAppProviderProps as l, ProcedureRunState as m, AgentRunEvent as n, OxyAnswer as o, ProcedureResult as p, ResolvedCustomAppManifest as q, AgentRunState as r, OxyAnswerProps as s, AgentArtifact as t, OxyChat as u, SemanticScalarOp as v, UseProcedureRunOpts as w, UseAgentRunResult as x, SemanticTimeDimension as y, useTrackEvent as z };
|
|
656
|
+
//# sourceMappingURL=react-DnBdQ8dG.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-DnBdQ8dG.d.mts","names":[],"sources":["../src/custom-app/manifest.ts","../src/custom-app/errors.ts","../src/custom-app/function-sse.ts","../src/custom-app/react.tsx"],"mappings":"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"}
|
package/dist/shell.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @oxy/sdk - TypeScript SDK for Oxy data platform
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
-
const require_react = require('./react-
|
|
3
|
+
const require_react = require('./react-BAiiXftp.cjs');
|
|
4
4
|
let react = require("react");
|
|
5
5
|
react = require_react.__toESM(react, 1);
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -1189,6 +1189,38 @@ function HistoryIcon() {
|
|
|
1189
1189
|
]
|
|
1190
1190
|
});
|
|
1191
1191
|
}
|
|
1192
|
+
function SearchIcon() {
|
|
1193
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1194
|
+
width: "14",
|
|
1195
|
+
height: "14",
|
|
1196
|
+
viewBox: "0 0 24 24",
|
|
1197
|
+
fill: "none",
|
|
1198
|
+
stroke: "currentColor",
|
|
1199
|
+
strokeWidth: "2",
|
|
1200
|
+
strokeLinecap: "round",
|
|
1201
|
+
strokeLinejoin: "round",
|
|
1202
|
+
"aria-hidden": "true",
|
|
1203
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1204
|
+
cx: "11",
|
|
1205
|
+
cy: "11",
|
|
1206
|
+
r: "8"
|
|
1207
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m21 21-4.3-4.3" })]
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
function ThreadIcon() {
|
|
1211
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1212
|
+
width: "15",
|
|
1213
|
+
height: "15",
|
|
1214
|
+
viewBox: "0 0 24 24",
|
|
1215
|
+
fill: "none",
|
|
1216
|
+
stroke: "currentColor",
|
|
1217
|
+
strokeWidth: "2",
|
|
1218
|
+
strokeLinecap: "round",
|
|
1219
|
+
strokeLinejoin: "round",
|
|
1220
|
+
"aria-hidden": "true",
|
|
1221
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1" })]
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1192
1224
|
function ExternalIcon() {
|
|
1193
1225
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1194
1226
|
width: "14",
|
|
@@ -1240,6 +1272,26 @@ function StopIcon() {
|
|
|
1240
1272
|
function chartsFrom(events) {
|
|
1241
1273
|
return events.filter((ev) => ev.type === "chart_rendered").map((ev) => ev.data).filter((b) => b?.config && Array.isArray(b.columns) && Array.isArray(b.rows));
|
|
1242
1274
|
}
|
|
1275
|
+
/** History label: a relative "N minutes/hours/days ago" for when the
|
|
1276
|
+
* conversation was started. */
|
|
1277
|
+
function formatHistoryTime(iso) {
|
|
1278
|
+
const d = new Date(iso);
|
|
1279
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
1280
|
+
const s = Math.max(0, Math.round((Date.now() - d.getTime()) / 1e3));
|
|
1281
|
+
if (s < 45) return "just now";
|
|
1282
|
+
const min = Math.round(s / 60);
|
|
1283
|
+
if (min < 60) return `${min} minute${min === 1 ? "" : "s"} ago`;
|
|
1284
|
+
const hr = Math.round(s / 3600);
|
|
1285
|
+
if (hr < 24) return `${hr} hour${hr === 1 ? "" : "s"} ago`;
|
|
1286
|
+
const day = Math.round(s / 86400);
|
|
1287
|
+
if (day < 30) return `${day} day${day === 1 ? "" : "s"} ago`;
|
|
1288
|
+
const mo = Math.round(day / 30);
|
|
1289
|
+
if (mo < 12) return `${mo} month${mo === 1 ? "" : "s"} ago`;
|
|
1290
|
+
const yr = Math.round(day / 365);
|
|
1291
|
+
return `${yr} year${yr === 1 ? "" : "s"} ago`;
|
|
1292
|
+
}
|
|
1293
|
+
/** How many history rows to show before the "Show more" button. */
|
|
1294
|
+
const HISTORY_PAGE_SIZE = 12;
|
|
1243
1295
|
/** Rebuild a completed turn from a transcript turn's processed events —
|
|
1244
1296
|
* identical to what the live dock accumulates during a run. */
|
|
1245
1297
|
function turnFromEvents(question, events) {
|
|
@@ -1265,9 +1317,12 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1265
1317
|
const [threadId, setThreadId] = react.useState(null);
|
|
1266
1318
|
const [history, setHistory] = react.useState([]);
|
|
1267
1319
|
const [historyOpen, setHistoryOpen] = react.useState(false);
|
|
1320
|
+
const [historyQuery, setHistoryQuery] = react.useState("");
|
|
1321
|
+
const [historyShown, setHistoryShown] = react.useState(HISTORY_PAGE_SIZE);
|
|
1268
1322
|
const [restoring, setRestoring] = react.useState(false);
|
|
1269
1323
|
const scrollRef = react.useRef(null);
|
|
1270
1324
|
const restoreGenRef = react.useRef(0);
|
|
1325
|
+
const startedAtRef = react.useRef(null);
|
|
1271
1326
|
const busy = run.state === "running";
|
|
1272
1327
|
const liveSteps = react.useMemo(() => buildTraceSteps(run.events), [run.events]);
|
|
1273
1328
|
const liveLlm = react.useMemo(() => aggregateLlmStats(run.events), [run.events]);
|
|
@@ -1291,7 +1346,8 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1291
1346
|
id: threadId ?? all[0].question,
|
|
1292
1347
|
title: all[0].question,
|
|
1293
1348
|
turns: all,
|
|
1294
|
-
threadId
|
|
1349
|
+
threadId,
|
|
1350
|
+
createdAt: startedAtRef.current ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1295
1351
|
};
|
|
1296
1352
|
};
|
|
1297
1353
|
const resetToEmpty = () => {
|
|
@@ -1301,6 +1357,7 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1301
1357
|
setPending(null);
|
|
1302
1358
|
setDraft("");
|
|
1303
1359
|
setThreadId(null);
|
|
1360
|
+
startedAtRef.current = null;
|
|
1304
1361
|
};
|
|
1305
1362
|
const newChat = () => {
|
|
1306
1363
|
const snap = snapshotCurrent();
|
|
@@ -1334,6 +1391,7 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1334
1391
|
setPending(null);
|
|
1335
1392
|
setDraft("");
|
|
1336
1393
|
setThreadId(id);
|
|
1394
|
+
startedAtRef.current = serverThreads.find((t) => t.id === id)?.created_at ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1337
1395
|
setHistoryOpen(false);
|
|
1338
1396
|
setRestoring(true);
|
|
1339
1397
|
try {
|
|
@@ -1348,6 +1406,7 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1348
1406
|
const question = raw.trim();
|
|
1349
1407
|
if (!question || busy) return;
|
|
1350
1408
|
restoreGenRef.current += 1;
|
|
1409
|
+
if (startedAtRef.current === null) startedAtRef.current = (/* @__PURE__ */ new Date()).toISOString();
|
|
1351
1410
|
if (pending !== null) {
|
|
1352
1411
|
const finalized = finalizedPendingTurn();
|
|
1353
1412
|
if (finalized) setTurns((t) => [...t, finalized]);
|
|
@@ -1363,16 +1422,31 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1363
1422
|
});
|
|
1364
1423
|
const empty = pending === null && turns.length === 0;
|
|
1365
1424
|
const canNewChat = !empty;
|
|
1366
|
-
const
|
|
1425
|
+
const currentId = threadId ?? turns[0]?.question ?? pending ?? null;
|
|
1426
|
+
const historyEntries = [...history.map((c) => ({
|
|
1367
1427
|
id: c.id,
|
|
1368
1428
|
title: c.title,
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1429
|
+
createdAt: c.createdAt,
|
|
1430
|
+
active: c.id === currentId,
|
|
1431
|
+
onOpen: c.id === currentId ? () => setHistoryOpen(false) : () => openConversation(c)
|
|
1432
|
+
})), ...serverThreads.filter((t) => !history.some((c) => c.id === t.id)).map((t) => ({
|
|
1372
1433
|
id: t.id,
|
|
1373
1434
|
title: t.title,
|
|
1374
|
-
|
|
1435
|
+
createdAt: t.created_at,
|
|
1436
|
+
active: t.id === currentId,
|
|
1437
|
+
onOpen: t.id === currentId ? () => setHistoryOpen(false) : () => void openServerThread(t.id)
|
|
1375
1438
|
}))];
|
|
1439
|
+
if (!empty && currentId != null && !historyEntries.some((e) => e.id === currentId)) historyEntries.push({
|
|
1440
|
+
id: currentId,
|
|
1441
|
+
title: turns[0]?.question ?? pending ?? currentId,
|
|
1442
|
+
createdAt: startedAtRef.current ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1443
|
+
active: true,
|
|
1444
|
+
onOpen: () => setHistoryOpen(false)
|
|
1445
|
+
});
|
|
1446
|
+
historyEntries.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
1447
|
+
const q = historyQuery.trim().toLowerCase();
|
|
1448
|
+
const filteredEntries = q ? historyEntries.filter((e) => e.title.toLowerCase().includes(q)) : historyEntries;
|
|
1449
|
+
const visibleEntries = filteredEntries.slice(0, historyShown);
|
|
1376
1450
|
const placeholder = run.state === "needs_clarification" ? "Reply…" : pending !== null ? "Ask a follow-up…" : `Ask Oxygen anything about ${workspaceName ?? "your workspace"}…`;
|
|
1377
1451
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
1378
1452
|
className: cx("oxy-shell-scope oxy-askdock", !open && "oxy-askdock--closed", className),
|
|
@@ -1405,7 +1479,11 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1405
1479
|
onClick: () => {
|
|
1406
1480
|
const next = !historyOpen;
|
|
1407
1481
|
setHistoryOpen(next);
|
|
1408
|
-
if (next)
|
|
1482
|
+
if (next) {
|
|
1483
|
+
refetchHistory();
|
|
1484
|
+
setHistoryQuery("");
|
|
1485
|
+
setHistoryShown(HISTORY_PAGE_SIZE);
|
|
1486
|
+
}
|
|
1409
1487
|
},
|
|
1410
1488
|
"data-testid": "askdock-history",
|
|
1411
1489
|
"aria-label": "Chat history",
|
|
@@ -1430,24 +1508,51 @@ function AskDock({ agentId, workspaceName, suggestions = [], threadsHref, open,
|
|
|
1430
1508
|
]
|
|
1431
1509
|
})
|
|
1432
1510
|
]
|
|
1433
|
-
}), historyOpen ? /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1511
|
+
}), historyOpen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1434
1512
|
className: "oxy-askdock__history",
|
|
1435
1513
|
"data-testid": "askdock-history-panel",
|
|
1436
|
-
children:
|
|
1514
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1515
|
+
className: "oxy-askdock__history-search",
|
|
1516
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchIcon, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1517
|
+
type: "text",
|
|
1518
|
+
className: "oxy-askdock__history-searchinput",
|
|
1519
|
+
placeholder: "Search threads…",
|
|
1520
|
+
value: historyQuery,
|
|
1521
|
+
onChange: (e) => {
|
|
1522
|
+
setHistoryQuery(e.target.value);
|
|
1523
|
+
setHistoryShown(HISTORY_PAGE_SIZE);
|
|
1524
|
+
},
|
|
1525
|
+
"aria-label": "Search threads"
|
|
1526
|
+
})]
|
|
1527
|
+
}), visibleEntries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1437
1528
|
className: "oxy-askdock__history-empty",
|
|
1438
|
-
children: "No previous chats yet."
|
|
1439
|
-
}) :
|
|
1529
|
+
children: historyQuery ? "No matching chats." : "No previous chats yet."
|
|
1530
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleEntries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1440
1531
|
type: "button",
|
|
1441
|
-
className: "oxy-askdock__history-item",
|
|
1532
|
+
className: cx("oxy-askdock__history-item", entry.active && "oxy-askdock__history-item--active"),
|
|
1442
1533
|
onClick: entry.onOpen,
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1534
|
+
title: entry.title,
|
|
1535
|
+
"aria-current": entry.active ? "true" : void 0,
|
|
1536
|
+
children: [
|
|
1537
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1538
|
+
className: "oxy-askdock__history-icon",
|
|
1539
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThreadIcon, {})
|
|
1540
|
+
}),
|
|
1541
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1542
|
+
className: "oxy-askdock__history-title",
|
|
1543
|
+
children: entry.title
|
|
1544
|
+
}),
|
|
1545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1546
|
+
className: "oxy-askdock__history-time",
|
|
1547
|
+
children: formatHistoryTime(entry.createdAt)
|
|
1548
|
+
})
|
|
1549
|
+
]
|
|
1550
|
+
}, entry.id)), filteredEntries.length > visibleEntries.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1551
|
+
type: "button",
|
|
1552
|
+
className: "oxy-askdock__history-more",
|
|
1553
|
+
onClick: () => setHistoryShown((n) => n + HISTORY_PAGE_SIZE),
|
|
1554
|
+
children: "Show more"
|
|
1555
|
+
})] })]
|
|
1451
1556
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1452
1557
|
className: "oxy-askdock__scroll",
|
|
1453
1558
|
ref: scrollRef,
|
|
@@ -1679,7 +1784,7 @@ function ShellRail({ top, groups, footerItems, bottom, className }) {
|
|
|
1679
1784
|
//#endregion
|
|
1680
1785
|
//#region src/shell/shellContext.ts
|
|
1681
1786
|
/**
|
|
1682
|
-
* Fetch the shell bootstrap payload for the current
|
|
1787
|
+
* Fetch the shell bootstrap payload for the current custom app. Must be
|
|
1683
1788
|
* called inside `<OxyAppProvider>`. Failure is non-fatal by design: the
|
|
1684
1789
|
* shell degrades to chrome-less rendering (older servers don't have the
|
|
1685
1790
|
* endpoint), so errors are surfaced on the result, never thrown.
|
|
@@ -1907,25 +2012,6 @@ function MessagesIcon() {
|
|
|
1907
2012
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M16 10a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M20 9a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1" })]
|
|
1908
2013
|
});
|
|
1909
2014
|
}
|
|
1910
|
-
/** Settings glyph (lucide "settings" outline, inlined). */
|
|
1911
|
-
function GearIcon() {
|
|
1912
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1913
|
-
width: "16",
|
|
1914
|
-
height: "16",
|
|
1915
|
-
viewBox: "0 0 24 24",
|
|
1916
|
-
fill: "none",
|
|
1917
|
-
stroke: "currentColor",
|
|
1918
|
-
strokeWidth: "2",
|
|
1919
|
-
strokeLinecap: "round",
|
|
1920
|
-
strokeLinejoin: "round",
|
|
1921
|
-
"aria-hidden": "true",
|
|
1922
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
1923
|
-
cx: "12",
|
|
1924
|
-
cy: "12",
|
|
1925
|
-
r: "3"
|
|
1926
|
-
})]
|
|
1927
|
-
});
|
|
1928
|
-
}
|
|
1929
2015
|
/** Rewrite a shell-context relative product path to an absolute URL against
|
|
1930
2016
|
* the oxy origin, when one is configured. `/api/*` paths are left relative
|
|
1931
2017
|
* — they ride the host's same-origin fetch / dev proxy (which attaches
|
|
@@ -1965,11 +2051,18 @@ function buildRailGroups(data, currentAppSlug, abs) {
|
|
|
1965
2051
|
return appItems.length ? [[hq, chat], appItems] : [[hq, chat]];
|
|
1966
2052
|
}
|
|
1967
2053
|
/**
|
|
1968
|
-
* The workspace chrome around a
|
|
2054
|
+
* The workspace chrome around a custom app: icon rail + universal top bar
|
|
1969
2055
|
* + content column — visually identical to the main web-app shell.
|
|
1970
2056
|
*/
|
|
1971
|
-
function OxyShell({ children, pageLabel, topBarLeft, topBarExtra, railBottom, hideTopBar, askHotkey = true, productBaseUrl, className }) {
|
|
2057
|
+
function OxyShell({ children, pageLabel, topBarLeft, topBarExtra, railBottom, hideTopBar, askHotkey = true, productBaseUrl, className, chromeBackground, chromeForeground }) {
|
|
1972
2058
|
const abs = makeAbs(productBaseUrl);
|
|
2059
|
+
const chromeStyle = {
|
|
2060
|
+
...chromeBackground ? { "--sidebar-background": chromeBackground } : {},
|
|
2061
|
+
...chromeForeground ? {
|
|
2062
|
+
"--foreground": chromeForeground,
|
|
2063
|
+
"--muted-foreground": `color-mix(in srgb, ${chromeForeground} 65%, transparent)`
|
|
2064
|
+
} : {}
|
|
2065
|
+
};
|
|
1973
2066
|
const { appSlug } = require_react.useOxyApp();
|
|
1974
2067
|
const { manifest } = require_react.useResolvedManifest();
|
|
1975
2068
|
const { data, loading } = useShellContext();
|
|
@@ -1992,22 +2085,13 @@ function OxyShell({ children, pageLabel, topBarLeft, topBarExtra, railBottom, hi
|
|
|
1992
2085
|
window.addEventListener("keydown", onKey);
|
|
1993
2086
|
return () => window.removeEventListener("keydown", onKey);
|
|
1994
2087
|
}, [askAgent, askHotkey]);
|
|
1995
|
-
const
|
|
1996
|
-
content: "Settings",
|
|
1997
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1998
|
-
href: abs(data.links.settings),
|
|
1999
|
-
"aria-label": "Settings",
|
|
2000
|
-
"data-testid": "rail-settings",
|
|
2001
|
-
className: "oxy-rail__item",
|
|
2002
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GearIcon, {})
|
|
2003
|
-
})
|
|
2004
|
-
}) : null;
|
|
2005
|
-
const bottom = railBottom || settingsEntry ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [railBottom, settingsEntry] }) : void 0;
|
|
2088
|
+
const bottom = railBottom;
|
|
2006
2089
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellPortalContext.Provider, {
|
|
2007
2090
|
value: container,
|
|
2008
2091
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2009
2092
|
ref: setContainer,
|
|
2010
2093
|
className: cx("oxy-shell-scope oxy-shell", degraded && "oxy-shell--degraded", className),
|
|
2094
|
+
style: chromeStyle,
|
|
2011
2095
|
children: [
|
|
2012
2096
|
!degraded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShellRail, {
|
|
2013
2097
|
top: data ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceTile, {
|