@amd-gaia/agent-ui 0.25.1-rc.6 → 0.25.1
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/assets/AdvancedSettings-D7jNZncZ.js +1 -0
- package/dist/assets/ConnectorsPane-CtDroHfp.js +1 -0
- package/dist/assets/{DocumentLibrary-UncZ9-L8.css → DocumentLibrary-CIKp6qld.css} +1 -1
- package/dist/assets/DocumentLibrary-gE-tZarr.js +4 -0
- package/dist/assets/FileBrowser-CUJCMcVf.js +4 -0
- package/dist/assets/MemoryDashboard-Cxr_1R_c.js +1 -0
- package/dist/assets/MobileAccessModal-CUqaV_OH.js +2 -0
- package/dist/assets/ScheduleManager-DgFrZse_.js +2 -0
- package/dist/assets/{apiBase-y00JgRBP.js → apiBase-BmPG2Rib.js} +1 -1
- package/dist/assets/{browser-CCrYwriZ.js → browser-KnDALPx_.js} +1 -1
- package/dist/assets/{eye-B6FejHNG.js → eye-CwjwlxR5.js} +1 -1
- package/dist/assets/index-CtsaRdvF.css +1 -0
- package/dist/assets/index-D-noqAc3.js +79 -0
- package/dist/assets/{message-square-Dwm7y3C0.js → message-square-DuVoNniw.js} +1 -1
- package/dist/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/dist/index.html +4 -4
- package/main.cjs +3 -1
- package/package.json +2 -2
- package/services/backend-installer-progress-dialog.cjs +39 -7
- package/services/backend-installer.cjs +207 -15
- package/dist/assets/AdvancedSettings-CYnPobry.js +0 -1
- package/dist/assets/ConnectorsPane-B8K0tacf.js +0 -1
- package/dist/assets/DocumentLibrary-EN6nlEF5.js +0 -1
- package/dist/assets/FileBrowser-D0-yZXPO.js +0 -4
- package/dist/assets/MemoryDashboard-CQmFGN-S.js +0 -1
- package/dist/assets/MobileAccessModal-wAxDm2-V.js +0 -2
- package/dist/assets/ScheduleManager-DIlmt8V8.js +0 -2
- package/dist/assets/index-Cp89afuH.js +0 -72
- package/dist/assets/index-DePhuNij.css +0 -1
- package/dist/assets/rolldown-runtime-hePW80VL.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./apiBase-
|
|
1
|
+
import{l as e}from"./apiBase-BmPG2Rib.js";var t={name:`message-square`,size:24,node:[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}]]};t.node;var n=e(t);export{n as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t,t.exports),e=null),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,o)=>(o=n==null?{}:e(i(n)),c(r||!n||!n.__esModule||!a.call(n,`default`)?t(o,`default`,{value:n,enumerable:!0}):o,n));export{s as n,l as r,o as t};
|
package/dist/index.html
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
<meta name="theme-color" content="#1A1917" />
|
|
9
9
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
10
10
|
<title>GAIA Agent UI</title>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="./assets/apiBase-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-D-noqAc3.js"></script>
|
|
12
|
+
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-XKyaHtl9.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="./assets/apiBase-BmPG2Rib.js">
|
|
14
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CtsaRdvF.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
package/main.cjs
CHANGED
|
@@ -826,8 +826,10 @@ async function chooseLemonadeInDialog(found, parent) {
|
|
|
826
826
|
if (owner) {
|
|
827
827
|
if (owner.isMinimized()) owner.restore();
|
|
828
828
|
owner.show();
|
|
829
|
+
owner.moveTop();
|
|
829
830
|
owner.focus();
|
|
830
|
-
|
|
831
|
+
// Windows can refuse focus to a background app; the taskbar flash is the hint.
|
|
832
|
+
if (!owner.isFocused()) owner.flashFrame(true);
|
|
831
833
|
}
|
|
832
834
|
const box = {
|
|
833
835
|
type: "question",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amd-gaia/agent-ui",
|
|
3
|
-
"version": "0.25.1
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"productName": "GAIA Agent UI",
|
|
6
6
|
"description": "Chat with the GAIA agent - local and private by default on AMD Ryzen AI, cloud models when you choose them",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"electron-updater": "^6.8.3"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@playwright/test": "1.
|
|
69
|
+
"@playwright/test": "1.64.1",
|
|
70
70
|
"@testing-library/jest-dom": "^7.0.0",
|
|
71
71
|
"@testing-library/react": "^16.1.0",
|
|
72
72
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -73,14 +73,45 @@ function registerIpcHandlers() {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// ── Stage labels ────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
// Keyed by installer.STAGES values; the ids themselves stay in the install log.
|
|
79
|
+
const STAGE_LABELS = Object.freeze({
|
|
80
|
+
"pre-checks": "Checking your PC",
|
|
81
|
+
"ensure-uv": "Getting the installer ready",
|
|
82
|
+
"create-venv": "Preparing GAIA's workspace",
|
|
83
|
+
"install-package": "Installing GAIA",
|
|
84
|
+
"gaia-init": "Getting the AI model ready",
|
|
85
|
+
verify: "Finishing up",
|
|
86
|
+
});
|
|
87
|
+
const NEUTRAL_STAGE_LABEL = "Setting up GAIA";
|
|
88
|
+
|
|
89
|
+
const unlabeledStages = new Set();
|
|
90
|
+
|
|
91
|
+
/** What the window shows for an installer stage id. */
|
|
92
|
+
function stageLabel(stage) {
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(STAGE_LABELS, stage)) return STAGE_LABELS[stage];
|
|
94
|
+
if (stage && !unlabeledStages.has(stage)) {
|
|
95
|
+
unlabeledStages.add(stage);
|
|
96
|
+
installer.log(
|
|
97
|
+
`Warning: install stage "${stage}" has no label; the window shows "${NEUTRAL_STAGE_LABEL}"`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return NEUTRAL_STAGE_LABEL;
|
|
101
|
+
}
|
|
102
|
+
|
|
76
103
|
// ── Embedded HTML ───────────────────────────────────────────────────────────
|
|
77
104
|
|
|
105
|
+
function escapeHtml(text) {
|
|
106
|
+
return String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
/**
|
|
79
110
|
* HTML payload for the progress window. Rendered via `loadURL('data:...')`.
|
|
80
111
|
* The script uses `window.gaiaInstall` exposed by preload.cjs.
|
|
81
112
|
*/
|
|
82
113
|
function buildProgressHtml({ logPath }) {
|
|
83
|
-
const escapedLog =
|
|
114
|
+
const escapedLog = escapeHtml(logPath || "");
|
|
84
115
|
return `<!DOCTYPE html>
|
|
85
116
|
<html lang="en">
|
|
86
117
|
<head>
|
|
@@ -133,8 +164,6 @@ function buildProgressHtml({ logPath }) {
|
|
|
133
164
|
.stage {
|
|
134
165
|
font-size: 12px;
|
|
135
166
|
color: var(--text-secondary);
|
|
136
|
-
text-transform: uppercase;
|
|
137
|
-
letter-spacing: 0.5px;
|
|
138
167
|
margin-bottom: 6px;
|
|
139
168
|
min-height: 15px;
|
|
140
169
|
}
|
|
@@ -202,11 +231,11 @@ function buildProgressHtml({ logPath }) {
|
|
|
202
231
|
</head>
|
|
203
232
|
<body>
|
|
204
233
|
<h1>Setting up GAIA</h1>
|
|
205
|
-
<p class="subtitle">First-
|
|
234
|
+
<p class="subtitle">First-time setup — this can take a few minutes</p>
|
|
206
235
|
<div class="stage" id="stage">Starting…</div>
|
|
207
236
|
<div class="bar"><div class="fill indeterminate" id="fill"></div></div>
|
|
208
237
|
<div class="percent" id="percent"></div>
|
|
209
|
-
<div class="message" id="message">
|
|
238
|
+
<div class="message" id="message">Checking your PC…</div>
|
|
210
239
|
<div class="footer">Log: <code>${escapedLog}</code></div>
|
|
211
240
|
<script>
|
|
212
241
|
(function() {
|
|
@@ -217,7 +246,7 @@ function buildProgressHtml({ logPath }) {
|
|
|
217
246
|
|
|
218
247
|
function onProgress(payload) {
|
|
219
248
|
if (!payload) return;
|
|
220
|
-
const stage = payload.
|
|
249
|
+
const stage = payload.label || '';
|
|
221
250
|
const percent = typeof payload.percent === 'number' ? payload.percent : null;
|
|
222
251
|
const message = payload.message || '';
|
|
223
252
|
|
|
@@ -307,7 +336,7 @@ function createProgressWindow() {
|
|
|
307
336
|
});
|
|
308
337
|
|
|
309
338
|
const onProgress = (stage, percent, message) => {
|
|
310
|
-
lastProgress = { stage, percent, message };
|
|
339
|
+
lastProgress = { stage, label: stageLabel(stage), percent, message };
|
|
311
340
|
if (window.isDestroyed()) return;
|
|
312
341
|
if (!rendererReady) return; // will be replayed on did-finish-load
|
|
313
342
|
try {
|
|
@@ -468,6 +497,9 @@ async function showPreCheckFailureDialog(parentWindow, { title, message, detail
|
|
|
468
497
|
|
|
469
498
|
module.exports = {
|
|
470
499
|
createProgressWindow,
|
|
500
|
+
buildProgressHtml,
|
|
501
|
+
stageLabel,
|
|
502
|
+
STAGE_LABELS,
|
|
471
503
|
showFailureDialog,
|
|
472
504
|
showPreCheckFailureDialog,
|
|
473
505
|
// IPC channel names for preload.cjs
|
|
@@ -55,8 +55,8 @@ const GAIA_PYTHON_BIN = IS_WINDOWS
|
|
|
55
55
|
const STATE_FILE = path.join(GAIA_HOME, "electron-install-state.json");
|
|
56
56
|
const LOG_FILE = path.join(GAIA_HOME, "electron-install.log");
|
|
57
57
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
58
|
+
// Floor for uv, Python and the GAIA package only. The models, often 30 GB,
|
|
59
|
+
// are checked by `gaia init` against Lemonade's model store, which it sizes.
|
|
60
60
|
const MIN_DISK_SPACE_BYTES = 5 * 1024 * 1024 * 1024; // 5 GB
|
|
61
61
|
const NETWORK_CHECK_HOSTS = Object.freeze([
|
|
62
62
|
"https://pypi.org/simple/",
|
|
@@ -135,6 +135,12 @@ const STAGES = Object.freeze({
|
|
|
135
135
|
VERIFY: "verify",
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
+
const GAIA_INIT_MESSAGE =
|
|
139
|
+
"Setting up Lemonade Server and downloading models (this can take several minutes)";
|
|
140
|
+
const WAITING_FOR_LEMONADE_CHOICE_MESSAGE =
|
|
141
|
+
"Waiting for your answer: GAIA found a Lemonade Server already running " +
|
|
142
|
+
"and is asking whether to use it. Setup continues once you choose.";
|
|
143
|
+
|
|
138
144
|
// Weight each stage contributes to the overall 0-100 progress.
|
|
139
145
|
// Sum must equal 100.
|
|
140
146
|
const STAGE_WEIGHTS = {
|
|
@@ -399,6 +405,148 @@ function makeProgressReporter(onProgress) {
|
|
|
399
405
|
};
|
|
400
406
|
}
|
|
401
407
|
|
|
408
|
+
// ── gaia init progress (`gaia init --progress-json`) ─────────────────────────
|
|
409
|
+
|
|
410
|
+
// Where the plan and the end of the downloads land in the GAIA_INIT slice; the
|
|
411
|
+
// rest of the slice is Lemonade setup before and verification after.
|
|
412
|
+
const INIT_PLAN_PERCENT = 5;
|
|
413
|
+
const INIT_DOWNLOADED_PERCENT = 95;
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Read one line of `gaia init --progress-json` output. Returns `{ event }` for
|
|
417
|
+
* a progress event, `{ error }` for a line that looks like one but is not, and
|
|
418
|
+
* null for human output.
|
|
419
|
+
*/
|
|
420
|
+
function parseInitEventLine(line) {
|
|
421
|
+
const text = String(line || "").trim();
|
|
422
|
+
if (!text.startsWith("{")) return null;
|
|
423
|
+
let value;
|
|
424
|
+
try {
|
|
425
|
+
value = JSON.parse(text);
|
|
426
|
+
} catch (err) {
|
|
427
|
+
return { error: err.message };
|
|
428
|
+
}
|
|
429
|
+
if (!value || typeof value !== "object" || typeof value.event !== "string") {
|
|
430
|
+
return { error: 'no "event" field' };
|
|
431
|
+
}
|
|
432
|
+
return { event: value };
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function formatGB(bytes) {
|
|
436
|
+
return `${(Math.max(0, bytes || 0) / 1e9).toFixed(1)} GB`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function formatSpeed(bytesPerSecond) {
|
|
440
|
+
return `${(bytesPerSecond / 1e6).toFixed(1)} MB/s`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function formatTimeLeft(seconds) {
|
|
444
|
+
if (seconds < 60) return "less than a minute left";
|
|
445
|
+
const minutes = Math.round(seconds / 60);
|
|
446
|
+
if (minutes < 60) return `about ${minutes} min left`;
|
|
447
|
+
return `about ${Math.floor(minutes / 60)} h ${minutes % 60} min left`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Map `gaia init --progress-json` events onto the GAIA_INIT slice of the bar.
|
|
452
|
+
* `report(withinStagePercent, message)` receives a percent that never goes
|
|
453
|
+
* back. `onLine` returns true for the lines too frequent to log.
|
|
454
|
+
*/
|
|
455
|
+
function createInitProgress(report, logFn = log) {
|
|
456
|
+
const models = new Map();
|
|
457
|
+
let percent = 0;
|
|
458
|
+
|
|
459
|
+
const advance = (within, message) => {
|
|
460
|
+
percent = Math.max(percent, Math.round(within));
|
|
461
|
+
report(percent, message);
|
|
462
|
+
};
|
|
463
|
+
const totals = () => {
|
|
464
|
+
let done = 0;
|
|
465
|
+
let total = 0;
|
|
466
|
+
for (const m of models.values()) {
|
|
467
|
+
total += m.total;
|
|
468
|
+
done += Math.min(m.done, m.total);
|
|
469
|
+
}
|
|
470
|
+
return { done, total };
|
|
471
|
+
};
|
|
472
|
+
const downloadPercent = () => {
|
|
473
|
+
const { done, total } = totals();
|
|
474
|
+
const share = total > 0 ? done / total : 0;
|
|
475
|
+
return INIT_PLAN_PERCENT + (INIT_DOWNLOADED_PERCENT - INIT_PLAN_PERCENT) * share;
|
|
476
|
+
};
|
|
477
|
+
const model = (id) => {
|
|
478
|
+
if (!models.has(id)) models.set(id, { done: 0, total: 0 });
|
|
479
|
+
return models.get(id);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
function handle(event) {
|
|
483
|
+
switch (event.event) {
|
|
484
|
+
case "step":
|
|
485
|
+
advance(percent, `Step ${event.step} of ${event.total}: ${event.message}`);
|
|
486
|
+
return true;
|
|
487
|
+
case "plan":
|
|
488
|
+
for (const m of event.models || []) {
|
|
489
|
+
if (!m.downloaded) model(m.id).total = m.bytes_total || 0;
|
|
490
|
+
}
|
|
491
|
+
advance(
|
|
492
|
+
INIT_PLAN_PERCENT,
|
|
493
|
+
event.bytes_total > 0
|
|
494
|
+
? `Downloading ${formatGB(event.bytes_total)} of models`
|
|
495
|
+
: "Models already downloaded"
|
|
496
|
+
);
|
|
497
|
+
return true;
|
|
498
|
+
case "progress": {
|
|
499
|
+
const m = model(event.model);
|
|
500
|
+
m.done = event.bytes_done || 0;
|
|
501
|
+
m.total = Math.max(event.bytes_total || 0, m.done);
|
|
502
|
+
const parts = [
|
|
503
|
+
`Downloading ${event.model} (${event.index} of ${event.count}): ` +
|
|
504
|
+
`${formatGB(m.done)} of ${formatGB(m.total)}`,
|
|
505
|
+
];
|
|
506
|
+
if (event.speed_bps > 0) {
|
|
507
|
+
const { done, total } = totals();
|
|
508
|
+
parts.push(
|
|
509
|
+
`at ${formatSpeed(event.speed_bps)}, ` +
|
|
510
|
+
formatTimeLeft((total - done) / event.speed_bps)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
advance(downloadPercent(), parts.join(" "));
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
case "model_done": {
|
|
517
|
+
const m = model(event.model);
|
|
518
|
+
m.done = m.total;
|
|
519
|
+
advance(downloadPercent(), `${event.model} ready`);
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
case "done":
|
|
523
|
+
advance(
|
|
524
|
+
event.ok ? INIT_DOWNLOADED_PERCENT : percent,
|
|
525
|
+
event.ok ? "Models downloaded, finishing setup" : "Model download did not finish"
|
|
526
|
+
);
|
|
527
|
+
return true;
|
|
528
|
+
case "error":
|
|
529
|
+
advance(percent, String(event.message || "gaia init reported an error").trim());
|
|
530
|
+
return true;
|
|
531
|
+
default:
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function onLine(line) {
|
|
537
|
+
const parsed = parseInitEventLine(line);
|
|
538
|
+
if (!parsed) return false;
|
|
539
|
+
if (parsed.error) {
|
|
540
|
+
logFn(`Malformed gaia init progress line (${parsed.error}): ${line}`);
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
handle(parsed.event);
|
|
544
|
+
return parsed.event.event === "progress";
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return { onLine, handle };
|
|
548
|
+
}
|
|
549
|
+
|
|
402
550
|
// ── Command helpers ──────────────────────────────────────────────────────────
|
|
403
551
|
|
|
404
552
|
/**
|
|
@@ -430,8 +578,11 @@ function findGaiaBin() {
|
|
|
430
578
|
/**
|
|
431
579
|
* Run a child process and stream output to the log file in real time.
|
|
432
580
|
* Returns a Promise that resolves with { code, stdout, stderr }.
|
|
581
|
+
*
|
|
582
|
+
* `onLine(line)` sees each whole stdout line as it arrives; a line it returns
|
|
583
|
+
* true for is left out of the log.
|
|
433
584
|
*/
|
|
434
|
-
function runCommand(cmd, args, { env, stageLabel } = {}) {
|
|
585
|
+
function runCommand(cmd, args, { env, stageLabel, onLine } = {}) {
|
|
435
586
|
return new Promise((resolve) => {
|
|
436
587
|
log(`$ ${cmd} ${args.join(" ")}`);
|
|
437
588
|
let proc;
|
|
@@ -451,14 +602,42 @@ function runCommand(cmd, args, { env, stageLabel } = {}) {
|
|
|
451
602
|
|
|
452
603
|
let stdout = "";
|
|
453
604
|
let stderr = "";
|
|
605
|
+
let partial = "";
|
|
606
|
+
const logLine = (line) => {
|
|
607
|
+
if (line) log(` ${stageLabel ? `[${stageLabel}] ` : ""}${line}`);
|
|
608
|
+
};
|
|
609
|
+
// An in-place progress bar redraws with \r, so each redraw is its own segment.
|
|
610
|
+
const takeLine = (line) => {
|
|
611
|
+
let quiet = true;
|
|
612
|
+
for (const segment of line.split("\r")) {
|
|
613
|
+
if (!segment) continue;
|
|
614
|
+
try {
|
|
615
|
+
quiet = onLine(segment) === true && quiet;
|
|
616
|
+
} catch (err) {
|
|
617
|
+
logError(`onLine callback threw: ${err.message}`);
|
|
618
|
+
quiet = false;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (!quiet) logLine(line);
|
|
622
|
+
};
|
|
454
623
|
|
|
455
624
|
proc.stdout.on("data", (data) => {
|
|
456
625
|
const chunk = data.toString();
|
|
457
626
|
stdout += chunk;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
627
|
+
if (!onLine) {
|
|
628
|
+
chunk.split(/\r?\n/).forEach(logLine);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const lines = (partial + chunk).split(/\r?\n/);
|
|
632
|
+
partial = lines.pop();
|
|
633
|
+
lines.forEach(takeLine);
|
|
461
634
|
});
|
|
635
|
+
if (onLine) {
|
|
636
|
+
proc.stdout.on("end", () => {
|
|
637
|
+
if (partial) takeLine(partial);
|
|
638
|
+
partial = "";
|
|
639
|
+
});
|
|
640
|
+
}
|
|
462
641
|
|
|
463
642
|
proc.stderr.on("data", (data) => {
|
|
464
643
|
const chunk = data.toString();
|
|
@@ -1397,17 +1576,16 @@ async function runGaiaInit(gaiaBin, report, version, skipGaiaInit, chooseLemonad
|
|
|
1397
1576
|
return { init: "skipped" };
|
|
1398
1577
|
}
|
|
1399
1578
|
setState(STATES.INSTALLING, { stage: STAGES.GAIA_INIT, version });
|
|
1400
|
-
report(
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1579
|
+
report(STAGES.GAIA_INIT, 0, GAIA_INIT_MESSAGE);
|
|
1580
|
+
const lemonadeArgs = await lemonadeInitArgs(gaiaBin, chooseLemonade, runCommand, report);
|
|
1581
|
+
const progress = createInitProgress((within, message) =>
|
|
1582
|
+
report(STAGES.GAIA_INIT, within, message)
|
|
1404
1583
|
);
|
|
1405
|
-
const lemonadeArgs = await lemonadeInitArgs(gaiaBin, chooseLemonade);
|
|
1406
1584
|
const initResult = await runCommand(
|
|
1407
1585
|
gaiaBin,
|
|
1408
|
-
["init", "--profile", "gaia", "--yes", "--skip-webui-build", ...lemonadeArgs],
|
|
1586
|
+
["init", "--profile", "gaia", "--yes", "--skip-webui-build", "--progress-json", ...lemonadeArgs],
|
|
1409
1587
|
// Unwrapped, so an error line reaches initFailureReason whole.
|
|
1410
|
-
{ stageLabel: "gaia-init", env: { ...process.env, COLUMNS: "1000" } }
|
|
1588
|
+
{ stageLabel: "gaia-init", env: { ...process.env, COLUMNS: "1000" }, onLine: progress.onLine }
|
|
1411
1589
|
);
|
|
1412
1590
|
if (initResult.code !== 0) {
|
|
1413
1591
|
// Non-fatal: the app opens and its start screen shows initError, and the
|
|
@@ -1427,7 +1605,8 @@ function initFailureReason({ code, stdout, stderr }) {
|
|
|
1427
1605
|
const lines = `${stdout || ""}\n${stderr || ""}`
|
|
1428
1606
|
.split(/\r?\n/)
|
|
1429
1607
|
.map((line) => line.trim())
|
|
1430
|
-
.
|
|
1608
|
+
// --progress-json event lines are for the progress bar, not the user.
|
|
1609
|
+
.filter((line) => line && !line.startsWith('{"event"'));
|
|
1431
1610
|
const marked = lines.filter((line) => line.startsWith("❌"));
|
|
1432
1611
|
const reason = marked.length
|
|
1433
1612
|
? marked[marked.length - 1].replace(/^❌\s*/, "")
|
|
@@ -1463,8 +1642,11 @@ async function readGaiaJson(run, gaiaBin, args) {
|
|
|
1463
1642
|
* `--lemonade-url <url>`, "gaia" adds `--gaia-lemonade`, null adds nothing:
|
|
1464
1643
|
* init then sets up GAIA's own and prints how to switch, as it always has.
|
|
1465
1644
|
* A server that needs a key or is too old is left to the app's setup screen.
|
|
1645
|
+
*
|
|
1646
|
+
* `report` is the progress reporter: while the question is open the progress
|
|
1647
|
+
* window says setup is waiting on the user, then goes back to GAIA_INIT_MESSAGE.
|
|
1466
1648
|
*/
|
|
1467
|
-
async function lemonadeInitArgs(gaiaBin, chooseLemonade, run = runCommand) {
|
|
1649
|
+
async function lemonadeInitArgs(gaiaBin, chooseLemonade, run = runCommand, report = () => {}) {
|
|
1468
1650
|
if (typeof chooseLemonade !== "function") return [];
|
|
1469
1651
|
const unchanged = "gaia init will set up GAIA's own Lemonade Server; to use yours later run `gaia lemonade connect <url>`.";
|
|
1470
1652
|
const status = await readGaiaJson(run, gaiaBin, ["lemonade", "status", "--json"]);
|
|
@@ -1484,11 +1666,14 @@ async function lemonadeInitArgs(gaiaBin, chooseLemonade, run = runCommand) {
|
|
|
1484
1666
|
return [];
|
|
1485
1667
|
}
|
|
1486
1668
|
let answer;
|
|
1669
|
+
report(STAGES.GAIA_INIT, 0, WAITING_FOR_LEMONADE_CHOICE_MESSAGE);
|
|
1487
1670
|
try {
|
|
1488
1671
|
answer = await chooseLemonade(found);
|
|
1489
1672
|
} catch (err) {
|
|
1490
1673
|
log(`Warning: could not ask whether to use the Lemonade Server at ${found.url}: ${err.message}; ${unchanged}`);
|
|
1491
1674
|
return [];
|
|
1675
|
+
} finally {
|
|
1676
|
+
report(STAGES.GAIA_INIT, 0, GAIA_INIT_MESSAGE);
|
|
1492
1677
|
}
|
|
1493
1678
|
log(`Lemonade Server at ${found.url}: user chose ${answer || "not to answer"}`);
|
|
1494
1679
|
if (answer === "external") return ["--lemonade-url", found.url];
|
|
@@ -2031,6 +2216,11 @@ module.exports = {
|
|
|
2031
2216
|
setState,
|
|
2032
2217
|
clearState,
|
|
2033
2218
|
|
|
2219
|
+
// gaia init progress
|
|
2220
|
+
parseInitEventLine,
|
|
2221
|
+
createInitProgress,
|
|
2222
|
+
computeOverallPercent,
|
|
2223
|
+
|
|
2034
2224
|
// Logging
|
|
2035
2225
|
openLog,
|
|
2036
2226
|
closeLog,
|
|
@@ -2042,6 +2232,8 @@ module.exports = {
|
|
|
2042
2232
|
// Constants
|
|
2043
2233
|
STATES,
|
|
2044
2234
|
STAGES,
|
|
2235
|
+
GAIA_INIT_MESSAGE,
|
|
2236
|
+
WAITING_FOR_LEMONADE_CHOICE_MESSAGE,
|
|
2045
2237
|
GAIA_HOME,
|
|
2046
2238
|
GAIA_VENV,
|
|
2047
2239
|
GAIA_BIN,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,o as n,s as r,u as i}from"./apiBase-y00JgRBP.js";import{$ as a,B as o,G as s,Gt as c,K as l,Mt as u,Q as d,et as f,ft as p,i as m,in as h,mt as g,nt as _,on as v,ot as y,pt as b,r as x,tt as S,z as C}from"./index-Cp89afuH.js";var w=e(i(),1),T=t();function E(){return window.gaiaUpdater}function D({onClose:e}){let[t,r]=(0,w.useState)({kind:`loading`}),i=(0,w.useRef)(null);(0,w.useEffect)(()=>{let e=E();if(!e){r({kind:`error`,message:`Couldn't reach GitHub to list releases — check your connection; you can still download installers from the Releases page.`});return}e.listReleases().then(e=>{r(Array.isArray(e)?{kind:`list`,releases:e}:{kind:`error`,message:e.error})}).catch(()=>{r({kind:`error`,message:`Couldn't reach GitHub to list releases — check your connection; you can still download installers from the Releases page.`})})},[]),n(!0,e),s(i,!0),(0,w.useEffect)(()=>{let e=i.current;e&&e.querySelector(`button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])`)?.focus()},[]),(0,w.useEffect)(()=>{let e=i.current;if(!e)return;function t(t){if(t.key!==`Tab`||!e)return;let n=e.querySelectorAll(`button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])`);if(n.length===0)return;let r=n[0],i=n[n.length-1];t.shiftKey?document.activeElement===r&&(t.preventDefault(),i?.focus()):document.activeElement===i&&(t.preventDefault(),r?.focus())}return e.addEventListener(`keydown`,t),()=>e.removeEventListener(`keydown`,t)},[]);function a(e,t){e.isCurrent||r({kind:`confirm`,release:e,releases:t})}async function o(t){r({kind:`installing`});let n=E();if(!n){r({kind:`error`,message:`Bridge unavailable — please restart the app.`});return}try{await n.installVersion(t.tag),e()}catch(e){let t=e instanceof Error?e.message:String(e);r({kind:`error`,message:`Failed to start rollback: ${t}`})}}function c(e){if(!e)return``;try{return new Date(e).toLocaleDateString(void 0,{year:`numeric`,month:`short`,day:`numeric`})}catch{return e}}return(0,T.jsx)(`div`,{className:`version-picker-overlay`,onClick:t=>{t.target===t.currentTarget&&e()},children:(0,T.jsxs)(`div`,{className:`version-picker-dialog`,role:`dialog`,"aria-modal":`true`,"aria-label":`Roll back to a previous version`,ref:i,children:[(0,T.jsxs)(`div`,{className:`version-picker-header`,children:[(0,T.jsx)(`h3`,{children:`Roll back to a previous version`}),(0,T.jsx)(`button`,{className:`version-picker-close`,onClick:e,"aria-label":`Close`,type:`button`,children:`×`})]}),(0,T.jsxs)(`div`,{className:`version-picker-body`,children:[t.kind===`loading`&&(0,T.jsxs)(`div`,{className:`version-picker-loading`,"aria-busy":`true`,children:[(0,T.jsx)(`span`,{className:`version-picker-spinner`}),`Loading releases…`]}),t.kind===`error`&&(0,T.jsxs)(`div`,{className:`version-picker-error`,children:[(0,T.jsx)(`p`,{children:t.message}),(0,T.jsx)(`p`,{children:(0,T.jsx)(`a`,{href:`https://github.com/amd/gaia/releases`,target:`_blank`,rel:`noreferrer`,children:`Browse releases on GitHub`})})]}),t.kind===`list`&&(0,T.jsx)(`ul`,{className:`version-picker-list`,role:`listbox`,"aria-label":`Available releases`,children:t.releases.map(e=>(0,T.jsx)(`li`,{children:(0,T.jsxs)(`button`,{type:`button`,className:`version-picker-row`,"data-version":e.version,"aria-label":`${e.version}${e.isCurrent?` — installed`:``}${e.isPinned?` — pinned`:``}`,"aria-disabled":e.isCurrent?`true`:`false`,onClick:()=>a(e,t.releases),disabled:e.isCurrent,children:[(0,T.jsx)(`span`,{className:`vp-version`,children:e.version}),(0,T.jsx)(`span`,{className:`vp-date`,children:c(e.date)}),e.isCurrent&&(0,T.jsx)(`span`,{className:`vp-badge vp-badge-current`,children:`Installed`}),e.isPinned&&!e.isCurrent&&(0,T.jsx)(`span`,{className:`vp-badge vp-badge-pinned`,children:`Pinned`}),!e.isCurrent&&(0,T.jsx)(`span`,{className:`vp-arrow`,"aria-hidden":`true`,children:`›`})]})},e.tag))}),t.kind===`list`&&(0,T.jsxs)(`p`,{className:`version-picker-more`,children:[`Showing the `,t.releases.length,` most recent.`,` `,(0,T.jsx)(`a`,{href:`https://github.com/amd/gaia/releases`,target:`_blank`,rel:`noreferrer`,children:`Browse all releases on GitHub`})]}),t.kind===`confirm`&&(0,T.jsxs)(`div`,{className:`version-picker-confirm`,children:[(0,T.jsxs)(`p`,{children:[`This will `,(0,T.jsxs)(`strong`,{children:[`downgrade to v`,t.release.version]}),` and restart the app.`]}),(0,T.jsxs)(`p`,{className:`vp-confirm-note`,children:[`Auto-update will be paused after rollback so the app stays on v`,t.release.version,`. You can resume updates from Settings → About.`]}),(0,T.jsxs)(`div`,{className:`vp-confirm-actions`,children:[(0,T.jsx)(`button`,{type:`button`,className:`vp-btn-cancel`,onClick:()=>r({kind:`list`,releases:t.releases}),children:`Back`}),(0,T.jsx)(`button`,{type:`button`,className:`vp-btn-confirm`,onClick:()=>o(t.release),children:`Confirm downgrade & restart`})]})]}),t.kind===`installing`&&(0,T.jsxs)(`div`,{className:`version-picker-loading`,"aria-busy":`true`,children:[(0,T.jsx)(`span`,{className:`version-picker-spinner`}),`Starting download…`]})]})]})})}function O(){let[e,t]=(0,w.useState)(null),[n,r]=(0,w.useState)(null),[i,a]=(0,w.useState)(``),[o,s]=(0,w.useState)(``),[l,u]=(0,w.useState)(!1),[f,m]=(0,w.useState)(null),[h,g]=(0,w.useState)(!1);(0,w.useEffect)(()=>{d().then(e=>{t(e),a(e.saved_base_url??e.base_url??``)}).catch(e=>r(e instanceof Error?e.message:String(e)))},[]);let _=(0,w.useCallback)(async e=>{u(!0),m(null);try{let n=await p(e);t(n),s(``),g(!1)}catch(e){g(C(e)),m(e instanceof Error?e.message:String(e))}finally{u(!1)}},[]),y=()=>void _({mode:`external`,base_url:i.trim(),api_key:o.trim()||void 0});if(n)return(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsxs)(`div`,{className:`model-warning`,role:`alert`,children:[(0,T.jsx)(v,{size:14}),(0,T.jsxs)(`div`,{className:`model-warning-content`,children:[(0,T.jsx)(`strong`,{children:`Could not read which model server GAIA uses`}),(0,T.jsx)(`p`,{children:n})]})]})]});if(!e)return null;let b=e.mode===`external`;return(0,T.jsxs)(`section`,{className:`settings-section`,"aria-label":`Model server`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsx)(`p`,{className:`model-override-desc`,role:`status`,children:e.source===`env`?(0,T.jsxs)(T.Fragment,{children:[`GAIA uses the Lemonade Server at `,(0,T.jsx)(`code`,{children:e.base_url}),`, set by `,(0,T.jsx)(`code`,{children:`LEMONADE_BASE_URL`}),`.`]}):b?(0,T.jsxs)(T.Fragment,{children:[`GAIA uses your Lemonade Server at `,(0,T.jsx)(`code`,{children:e.base_url}),`.`]}):(0,T.jsx)(T.Fragment,{children:`GAIA uses its own Lemonade Server.`})}),e.source===`env`?(0,T.jsxs)(`p`,{className:`model-status-hint`,children:[`Unset `,(0,T.jsx)(`code`,{children:`LEMONADE_BASE_URL`}),` and restart GAIA to change this here.`]}):(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(`p`,{className:`model-status-hint`,children:[`Already run Lemonade? Use it, so this PC keeps one server and one copy of each model.`,` `,e.what_gaia_does]}),(0,T.jsxs)(`form`,{className:`model-input-row`,onSubmit:e=>{e.preventDefault(),y()},children:[(0,T.jsx)(`input`,{type:`text`,className:`model-input`,placeholder:`http://localhost:13305`,value:i,onChange:e=>{a(e.target.value),m(null)},disabled:l,spellCheck:!1,autoCapitalize:`off`,autoCorrect:`off`,"aria-label":`Lemonade Server address`}),(0,T.jsx)(`input`,{type:`password`,className:`model-input`,autoComplete:`off`,placeholder:e.api_key_stored?`API key (blank keeps the saved one)`:`API key, if it needs one`,value:o,onChange:e=>s(e.target.value),disabled:l,"aria-label":`Lemonade API key`}),(0,T.jsx)(`div`,{className:`model-btn-group`,children:(0,T.jsx)(`button`,{type:`submit`,className:`btn-model-save`,disabled:l||!i.trim(),children:l?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(c,{size:13,className:`btn-spinner`}),` Checking…`]}):`Use this server`})})]}),b&&(0,T.jsx)(`div`,{className:`setting-actions`,children:(0,T.jsx)(`button`,{type:`button`,className:`btn-secondary`,disabled:l,onClick:()=>void _({mode:`gaia`}),children:`Use GAIA’s own`})}),f&&(0,T.jsxs)(`div`,{className:`model-warning`,role:`alert`,children:[(0,T.jsx)(v,{size:14}),(0,T.jsxs)(`div`,{className:`model-warning-content`,children:[(0,T.jsx)(`strong`,{children:h?`Needs the API key`:`Not switched`}),(0,T.jsx)(`p`,{children:f})]})]})]})]})}var k={vulkan:`Vulkan`,rocm:`ROCm`,metal:`Metal`,cpu:`CPU`,npu:`NPU`,remote:`Remote (cloud)`},A={gaia:`GAIA's own Lemonade`,user:`Your Lemonade`,remote:`Lemonade on another machine`};function j(e){return e?k[e]??e:`unknown`}function M(e){if(!e)return`unknown`;let t=j(e.backend);return e.backend_source===`lemonade_default`?`${t} (Lemonade's default)`:t}function N(e){return e?`${Math.round(e/1024)}K tokens`:`unknown`}function P({label:e,value:t,title:n}){return(0,T.jsxs)(`div`,{className:`status-row`,children:[(0,T.jsx)(`span`,{className:`status-label`,children:e}),(0,T.jsx)(`span`,{className:`status-value model-server-value`,title:n??t,children:t})]})}function ee(){let e=u(e=>e.openSettings),[t,n]=(0,w.useState)(null),[r,i]=(0,w.useState)(null),[a,o]=(0,w.useState)(null),[s,d]=(0,w.useState)(null),[p,m]=(0,w.useState)(!1);(0,w.useEffect)(()=>{f().then(n).catch(e=>i(e instanceof Error?e.message:String(e)))},[]);let h=(0,w.useCallback)(async e=>{o(e),d(null);try{let t=await b(e);n(t),d({ok:!0,text:`Saved. It applies the next time the chat model loads.`})}catch(e){d({ok:!1,text:e instanceof Error?e.message:String(e)})}finally{o(null)}},[]),g=t?.chat_model??null,_=(0,w.useCallback)(async()=>{if(g?.name){m(!0);try{if(!await y(g.name,g.ctx_size??void 0))return;d({ok:!0,text:`Reloading ${g.name}. Reopen this panel in a moment to see its backend.`})}catch(e){d({ok:!1,text:e instanceof Error?e.message:String(e)})}finally{m(!1)}}},[g]),v=(0,T.jsx)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>e(`model`),children:`Change model`});if(r)return(0,T.jsxs)(`section`,{className:`settings-section`,"aria-label":`Model server`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsx)(`p`,{className:`settings-error`,role:`alert`,children:r})]});if(!t)return(0,T.jsxs)(`section`,{className:`settings-section`,"aria-label":`Model server`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsx)(`p`,{className:`loading-text`,children:`Checking the model server…`})]});if(!t.reachable)return(0,T.jsxs)(`section`,{className:`settings-section`,"aria-label":`Model server`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsx)(`p`,{className:`settings-error`,role:`alert`,children:t.error}),v]});let x=t.available_backends??[],S=t.preferred_backend??null,C=[`auto`,...x.map(e=>e.name)];S&&!C.includes(S)&&C.push(S);let E=S??`auto`,D=x.filter(e=>e.state===`installable`).map(e=>j(e.name)),O=!!g?.name&&g.recipe===`llamacpp`;return(0,T.jsxs)(`section`,{className:`settings-section`,"aria-label":`Model server`,children:[(0,T.jsx)(`h4`,{children:`Model server`}),(0,T.jsxs)(`div`,{className:`status-grid`,children:[(0,T.jsx)(P,{label:`Server`,value:`${A[t.owner]??t.owner}${t.version?` v${t.version}`:``}`}),(0,T.jsx)(P,{label:`Address`,value:t.base_url}),(0,T.jsx)(P,{label:`Chat model`,value:g?.name?l(g.name):`None loaded`,title:g?.name??void 0}),g&&(0,T.jsx)(P,{label:`Runs on`,value:M(g)}),g&&g.backend!==`remote`&&(0,T.jsx)(P,{label:`Context window`,value:N(g.ctx_size)}),(0,T.jsx)(P,{label:`Embedding model`,value:t.embedding_model?.name?`${t.embedding_model.name} · ${M(t.embedding_model)}`:`None loaded`})]}),t.embedding_note&&(0,T.jsx)(`p`,{className:`settings-hint`,children:t.embedding_note}),t.system_info_error&&(0,T.jsx)(`p`,{className:`settings-hint`,children:t.system_info_error}),(0,T.jsxs)(`div`,{className:`settings-field model-server-backend`,children:[(0,T.jsx)(`span`,{className:`settings-label`,children:`llama.cpp backend for chat models`}),x.length===0&&!S?(0,T.jsx)(`span`,{className:`settings-hint`,children:`This Lemonade does not report which backends this PC can run, so there is nothing to choose. Update Lemonade to pick one here.`}):(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(`div`,{className:`segmented`,role:`radiogroup`,"aria-label":`llama.cpp backend`,children:C.map(e=>(0,T.jsxs)(`button`,{type:`button`,role:`radio`,"aria-checked":E===e,className:E===e?`is-active`:``,disabled:a!==null,onClick:()=>{e!==E&&h(e)},children:[a===e?(0,T.jsx)(c,{size:12,className:`spin`}):null,e===`auto`?`Auto${t.default_backend?` (${j(t.default_backend)})`:``}`:j(e)]},e))}),(0,T.jsxs)(`span`,{className:`settings-hint`,children:[`Applies the next time a chat model loads. The embedding model keeps its own backend.`,D.length>0&&` ${D.join(`, `)} downloads on first use.`]})]}),s&&(0,T.jsx)(`p`,{className:s.ok?`settings-ok`:`settings-error`,role:s.ok?`status`:`alert`,children:s.text})]}),(0,T.jsxs)(`div`,{className:`settings-row`,children:[v,O&&(0,T.jsxs)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>void _(),disabled:p,children:[p?(0,T.jsx)(c,{size:13,className:`spin`}):null,`Reload chat model now`]})]})]})}function F(){let[e,t]=(0,w.useState)(null),[n,i]=(0,w.useState)(!0),[s,l]=(0,w.useState)([]),[u,d]=(0,w.useState)(!1),f=x(),[p,y]=(0,w.useState)(``),[b,C]=(0,w.useState)(``),[E,k]=(0,w.useState)(!1),[A,j]=(0,w.useState)(!1),[M,N]=(0,w.useState)(null),[P,F]=(0,w.useState)(!1),L=(0,w.useRef)(null),[R,z]=(0,w.useState)(!1),[B,V]=(0,w.useState)(!1),[H,U]=(0,w.useState)(!1),[W,G]=(0,w.useState)(null);(0,w.useEffect)(()=>{r.system.info(`Checking system status...`);let e=r.system.time();_().then(n=>{t(n),r.system.timed(`System status received`,e,{lemonade:n.lemonade_running?`running`:`stopped`,model:n.model_loaded||`none`,embedding:n.embedding_model_loaded?`yes`:`no`,disk:`${n.disk_space_gb}GB free`,memory:n.memory_available_gb==null?`unknown`:`${n.memory_available_gb}GB available`}),n.lemonade_running||r.system.warn(`Lemonade Server is NOT running.`)}).catch(e=>{r.system.error(`Failed to get system status`,e),t(null)}).finally(()=>i(!1)),a().then(e=>l(e.servers)).catch(()=>{}),S().then(e=>{let t=e.custom_model??``;y(t),C(t),z(e.dynamic_tools),V(e.dynamic_tools_locked)}).catch(e=>{r.system.error(`Failed to load settings`,e)}).finally(()=>k(!0))},[]),(0,w.useEffect)(()=>()=>{L.current&&clearTimeout(L.current)},[]);let K=(0,w.useCallback)(async()=>{let e=p.trim(),t=e.length>0?e:``;j(!0),N(null),F(!1);try{r.system.info(`Saving custom_model override`,{custom_model:t});let e=(await g({custom_model:t})).custom_model??``;y(e),C(e),F(!0),L.current&&clearTimeout(L.current),L.current=setTimeout(()=>F(!1),2200)}catch(e){let t=e instanceof Error?e.message:String(e);r.system.error(`Failed to save custom_model`,e),N(t)}finally{j(!1)}},[p]),te=(0,w.useCallback)(async()=>{if(B||H)return;let e=R,t=!e;z(t),U(!0),G(null);try{r.system.info(`Saving dynamic_tools setting`,{dynamic_tools:t});let e=await g({dynamic_tools:t});z(e.dynamic_tools),V(e.dynamic_tools_locked)}catch(t){let n=t instanceof Error?t.message:String(t);r.system.error(`Failed to save dynamic_tools`,t),z(e),G(n)}finally{U(!1)}},[R,B,H]),q=p.trim()!==b.trim(),J=e?e.default_model_name:o,{isLoadingModel:Y,isDownloadingModel:X,loadModel:ne,downloadModel:Z}=m(J??void 0),Q=!!e?.default_model_is_cloud,$=!Q&&!!(e?.lemonade_running&&e.model_loaded&&e.expected_model_loaded===!1),re=!Q&&!!(e?.lemonade_running&&e.model_loaded&&e.context_size_sufficient===!1),ie=!Q&&!(!e?.lemonade_running||e.model_loaded||e.model_downloaded!==!1),ae=($||re)&&J!==null;return(0,T.jsxs)(`div`,{className:`settings-pane`,children:[(0,T.jsx)(`h2`,{className:`settings-pane-title`,children:`Advanced`}),(0,T.jsx)(ee,{}),(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsx)(`h4`,{children:`Local model server`}),n?(0,T.jsx)(`p`,{className:`loading-text`,children:`Checking system...`}):e?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsxs)(`div`,{className:`status-grid`,children:[(0,T.jsx)(I,{label:`Model server`,value:e.lemonade_running?`Running${e.lemonade_version?` v${e.lemonade_version}`:``}`:e.lemonade_error?`Status unavailable`:`Not Running`,ok:e.lemonade_running,hint:e.lemonade_running?void 0:e.lemonade_error?`Could not query it: ${e.lemonade_error}`:e.start_command?`Run: ${e.start_command}`:e.start_instruction??`Not running.`}),(0,T.jsx)(I,{label:`Model`,value:e.model_loaded||`None loaded`,ok:!!e.model_loaded&&(Q||e.expected_model_loaded!==!1),hint:e.model_loaded?!Q&&e.expected_model_loaded===!1?`Expected: ${J}`:void 0:`None loaded yet — it loads with the first message.`}),e.model_size_gb!=null&&(0,T.jsx)(I,{label:`Model Size`,value:`${e.model_size_gb} GB`,ok:!0}),e.model_device&&!Q&&(0,T.jsx)(I,{label:`Device`,value:e.model_device.toUpperCase(),ok:e.model_device!==`cpu`}),e.model_context_size!=null&&(0,T.jsx)(I,{label:`Context Window`,value:`${(e.model_context_size/1024).toFixed(0)}K tokens`,ok:e.context_size_sufficient}),e.model_labels&&e.model_labels.length>0&&(0,T.jsx)(I,{label:`Capabilities`,value:e.model_labels.join(`, `),ok:!0}),(0,T.jsx)(I,{label:`Embedding Model`,value:e.embedding_model_loaded?`Available`:`Not loaded`,ok:e.embedding_model_loaded}),e.gpu_name&&(0,T.jsx)(I,{label:`GPU`,value:`${e.gpu_name}${e.gpu_vram_gb?` (${e.gpu_vram_gb} GB)`:``}`,ok:!0}),(0,T.jsx)(I,{label:`Disk Space`,value:`${e.disk_space_gb} GB free`,ok:e.disk_space_gb>5,hint:!e.model_loaded&&e.disk_space_gb<30?`Local models need about 6 GB — only ${e.disk_space_gb} GB free`:void 0}),(0,T.jsx)(I,{label:`Memory`,value:e.memory_available_gb==null?`unknown`:`${e.memory_available_gb} GB available`,ok:e.memory_available_gb!=null&&e.memory_available_gb>2}),e.processor_name&&(0,T.jsx)(I,{label:`Processor`,value:e.processor_name,ok:!0})]}),ie&&(0,T.jsxs)(`div`,{className:`model-action-row model-action-row--download`,children:[(0,T.jsxs)(`div`,{className:`model-action-info`,children:[(0,T.jsx)(`span`,{className:`model-action-label`,children:`Model not downloaded.`}),(0,T.jsxs)(`span`,{className:`model-action-desc`,children:[(0,T.jsx)(`strong`,{children:J}),` is GAIA’s default local model (about 6 GB).`]})]}),(0,T.jsx)(`button`,{className:`btn-model-action btn-model-action--download`,onClick:()=>Z(!1),disabled:X,children:X?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(c,{size:13,className:`btn-spinner`}),` Downloading…`]}):`Download`})]}),ae&&(0,T.jsxs)(`div`,{className:`model-action-row model-action-row--load`,children:[(0,T.jsxs)(`div`,{className:`model-action-info`,children:[(0,T.jsx)(`span`,{className:`model-action-label`,children:$?`Wrong model loaded.`:`Context window too small.`}),(0,T.jsxs)(`span`,{className:`model-action-desc`,children:[`Load `,(0,T.jsx)(`strong`,{children:J}),` with `,32 .toFixed(0),`K token context.`]})]}),(0,T.jsx)(`button`,{className:`btn-model-action btn-model-action--load`,onClick:()=>ne(),disabled:Y,children:Y?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(c,{size:13,className:`btn-spinner`}),` Loading…`]}):`Load Model`})]}),e.lemonade_running&&J!==null&&!Q&&(0,T.jsxs)(`div`,{className:`force-redownload-row`,children:[(0,T.jsx)(`span`,{className:`force-redownload-label`,children:`If the model file is corrupted:`}),(0,T.jsx)(`button`,{className:`btn-force-redownload`,onClick:()=>Z(!0),disabled:X,children:X?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(c,{size:12,className:`btn-spinner`}),` Downloading…`]}):`Force Re-download`})]})]}):(0,T.jsx)(`div`,{className:`status-error`,children:(0,T.jsx)(`p`,{children:`Could not reach GAIA. Check that it is still running.`})})]}),(0,T.jsx)(O,{}),(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsx)(`h4`,{children:`Custom model ID`}),(0,T.jsx)(`p`,{className:`model-override-desc`,children:`Run any model the local server knows by its ID. Leave empty to use the model chosen under Model.`}),(0,T.jsxs)(`div`,{className:`model-input-row`,children:[(0,T.jsx)(`input`,{type:`text`,className:`model-input${b?` has-override`:``}`,placeholder:`Use the model chosen under Model`,value:p,onChange:e=>{y(e.target.value),N(null),F(!1)},onKeyDown:e=>{e.key===`Enter`&&!A&&q&&E&&(e.preventDefault(),K())},disabled:!E||A,spellCheck:!1,autoCapitalize:`off`,autoCorrect:`off`,"aria-label":`Custom model override`}),(0,T.jsx)(`div`,{className:`model-btn-group`,children:(0,T.jsx)(`button`,{className:`btn-model-save${P?` saved`:``}`,onClick:()=>{K()},disabled:!E||A||!q&&!P,"aria-label":`Save custom model`,children:A?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(c,{size:13,className:`btn-spinner`}),` Saving…`]}):P?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(h,{size:13}),` Saved`]}):`Save`})})]}),(0,T.jsxs)(`p`,{className:`model-status-hint`,children:[`Accepts a Lemonade model ID (e.g. `,(0,T.jsx)(`code`,{children:`Qwen3-4B-Instruct-2507-GGUF`}),`)`,` `,`or a HuggingFace ID (e.g. `,(0,T.jsx)(`code`,{children:`unsloth/Qwen3-4B-GGUF`}),`).`]}),M&&(0,T.jsxs)(`div`,{className:`model-warning`,role:`alert`,children:[(0,T.jsx)(v,{size:14}),(0,T.jsxs)(`div`,{className:`model-warning-content`,children:[(0,T.jsx)(`strong`,{children:`Could not save`}),(0,T.jsx)(`p`,{children:M})]})]})]}),(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsxs)(`h4`,{children:[`Dynamic Tools `,(0,T.jsx)(`span`,{className:`beta-badge`,children:`BETA`})]}),(0,T.jsx)(`p`,{className:`model-override-desc`,children:`Trim each turn’s tool list to a semantically-matched subset to speed up the first response. It does not apply to the GAIA agent yet.`}),(0,T.jsxs)(`label`,{className:`setting-row`,children:[(0,T.jsx)(`span`,{children:`Enable dynamic tool loading`}),(0,T.jsxs)(`span`,{className:`toggle-switch`,children:[(0,T.jsx)(`input`,{type:`checkbox`,checked:R,onChange:()=>void te(),disabled:!E||H||B,"aria-label":R?`Disable dynamic tools`:`Enable dynamic tools`}),(0,T.jsx)(`span`,{className:`toggle-track`})]})]}),B&&(0,T.jsxs)(`p`,{className:`model-status-hint`,children:[`Controlled by `,(0,T.jsx)(`code`,{children:`GAIA_DYNAMIC_TOOLS`}),` — unset that environment variable to change this here.`]}),W&&(0,T.jsxs)(`div`,{className:`model-warning`,role:`alert`,children:[(0,T.jsx)(v,{size:14}),(0,T.jsxs)(`div`,{className:`model-warning-content`,children:[(0,T.jsx)(`strong`,{children:`Could not save`}),(0,T.jsx)(`p`,{children:W})]})]})]}),s.length>0&&(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsx)(`h4`,{children:`MCP Servers`}),(0,T.jsx)(`div`,{className:`status-grid`,children:s.map(e=>(0,T.jsxs)(`div`,{className:`status-row`,children:[(0,T.jsx)(`span`,{className:`status-label`,children:e.name}),(0,T.jsx)(`div`,{className:`status-value-wrap`,children:e.connected?(0,T.jsxs)(`span`,{className:`status-value ok mcp-status-connected`,children:[(0,T.jsx)(h,{size:12}),e.tool_count,` tool`,e.tool_count===1?``:`s`]}):(0,T.jsxs)(`span`,{className:`status-value warn mcp-status-failed`,title:e.error??void 0,children:[(0,T.jsx)(v,{size:12}),`Failed`]})})]},e.name))})]}),(0,T.jsxs)(`section`,{className:`settings-section`,children:[(0,T.jsx)(`h4`,{children:`About`}),(0,T.jsxs)(`div`,{className:`about-info`,children:[(0,T.jsxs)(`p`,{children:[`GAIA v`,`0.25.1rc6`,` `,(0,T.jsx)(`span`,{className:`beta-badge`,children:`BETA`})]}),(0,T.jsx)(`p`,{className:`about-sub`,children:`Privacy-first AI chat for AMD Ryzen AI PCs.`}),f.pinnedVersion&&(0,T.jsxs)(`p`,{className:`about-pinned-notice`,children:[`Auto-update paused (pinned to `,f.pinnedVersion,`).`,` `,(0,T.jsx)(`button`,{type:`button`,className:`about-resume-btn`,onClick:()=>{window.gaiaUpdater?.resumeUpdates().catch(e=>{r.system.error(`Resume updates failed`,e)})},children:`Resume updates`})]})]}),(0,T.jsx)(`div`,{className:`setting-actions`,children:(0,T.jsx)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>d(!0),children:`Roll back to a previous version`})})]}),u&&(0,T.jsx)(D,{onClose:()=>d(!1)})]})}function I({label:e,value:t,ok:n,hint:r}){return(0,T.jsxs)(`div`,{className:`status-row${r?` status-row--has-hint`:``}`,children:[(0,T.jsx)(`span`,{className:`status-label`,children:e}),(0,T.jsxs)(`div`,{className:`status-value-wrap`,children:[(0,T.jsx)(`span`,{className:`status-value ${n?`ok`:`warn`}`,children:t}),r&&(0,T.jsx)(`span`,{className:`status-hint`,children:(0,T.jsx)(`code`,{children:r})})]})]})}export{F as AdvancedSettings};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{l as t,n,o as r,s as i,t as a,u as o}from"./apiBase-y00JgRBP.js";import{At as s,Ct as c,Dt as l,Et as u,Gt as d,Mt as f,Ot as p,St as m,Tt as h,Zt as g,_t as _,bt as v,in as y,jt as b,kt as x,ln as S,on as C,sn as w,vt as T,wt as E,xt as D,yt as O}from"./index-Cp89afuH.js";var k={name:`ellipsis`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`1`,key:`41hilf`}],[`circle`,{cx:`19`,cy:`12`,r:`1`,key:`1wjl8i`}],[`circle`,{cx:`5`,cy:`12`,r:`1`,key:`1pcz8c`}]],aliases:[`more-horizontal`]};k.node;var A=t(k),j=n(),M=e(o(),1),N=i.api;function P(e){switch(e){case`connector.configured`:return`configured`;case`connector.disconnected`:return`disconnected`;case`connection.revoked`:return`disconnected`;case`connector.oauth.completed`:return`oauth_completed`;case`connection.connected`:return`oauth_completed`;case`connector.oauth.error`:return`oauth_error`;case`connector.tested`:return`tested`;case`connector.enabled`:return`enabled`;case`connector.disabled`:return`disabled`;case`connector.grant.changed`:return`grant_changed`;case`connector.activation.changed`:return`activation_changed`;default:return null}}function F(e){let t=(0,M.useRef)(e);(0,M.useEffect)(()=>{t.current=e},[e]),(0,M.useEffect)(()=>{let e=`${a()}/connectors/events`,n=null,r=1e3,i=null,o=!1,s=()=>{o||(n=new EventSource(e),n.onopen=()=>{r=1e3},n.onmessage=e=>{try{let n=JSON.parse(e.data),r=P(n.type);if(r===null){N.debug(`connectors-sse: ignoring event`,n.type);return}let i=n.payload??{},a=i.connector_id??i.provider??null;t.current({connectorId:a,reason:r,payload:i})}catch(e){N.warn(`connectors-sse: malformed event`,e)}},n.onerror=()=>{n?.close(),n=null,!o&&(i=setTimeout(s,r),r=Math.min(r*2,3e4))})};return s(),()=>{o=!0,i&&clearTimeout(i),n?.close()}},[])}function I(e){return e.type===`mcp_server`&&e.configured}function L({connector:e,onChanged:t}){let[n,i]=(0,M.useState)(!1),[a,o]=(0,M.useState)(!1),[s,l]=(0,M.useState)(null),[u,d]=(0,M.useState)(null),f=(0,M.useRef)(null),p=(0,M.useRef)(null);(0,M.useEffect)(()=>{if(!n)return;let e=e=>{let t=e.target;f.current&&!f.current.contains(t)&&p.current&&!p.current.contains(t)&&i(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[n]);let h=(0,M.useCallback)(()=>i(!1),[]);if(r(n,h),(0,M.useEffect)(()=>{if(!n)return;let e=()=>{if(!f.current)return;let e=f.current.getBoundingClientRect();d({top:e.bottom+4,right:window.innerWidth-e.right})};return window.addEventListener(`resize`,e),window.addEventListener(`scroll`,e,!0),()=>{window.removeEventListener(`resize`,e),window.removeEventListener(`scroll`,e,!0)}},[n]),!I(e))return null;let g=e=>{e.stopPropagation(),e.preventDefault()},_=e=>{if(g(e),!n&&f.current){let e=f.current.getBoundingClientRect();d({top:e.bottom+4,right:window.innerWidth-e.right})}i(e=>!e)},v=async e=>{o(!0),l(null);try{await e(),i(!1),t()}catch(e){l(e instanceof Error?e.message:String(e))}finally{o(!1)}};return(0,j.jsxs)(`div`,{className:`connector-tile-menu`,onClick:g,onKeyDown:g,children:[(0,j.jsx)(`button`,{ref:f,type:`button`,className:`connector-tile-menu-trigger`,"aria-haspopup":`menu`,"aria-expanded":n,"aria-label":`Connector actions`,onClick:_,disabled:a,children:(0,j.jsx)(A,{size:14})}),n&&u&&(0,j.jsxs)(`div`,{ref:p,role:`menu`,className:`connector-tile-menu-popup`,style:{position:`fixed`,top:u.top,right:u.right},children:[e.enabled?(0,j.jsx)(`button`,{role:`menuitem`,type:`button`,className:`connector-tile-menu-item`,onClick:()=>void v(()=>m(e.id)),disabled:a,children:`Disable`}):(0,j.jsx)(`button`,{role:`menuitem`,type:`button`,className:`connector-tile-menu-item`,onClick:()=>void v(()=>E(e.id)),disabled:a,children:`Enable`}),(0,j.jsx)(`div`,{className:`connector-tile-menu-divider`}),(0,j.jsx)(`button`,{role:`menuitem`,type:`button`,className:`connector-tile-menu-item connector-tile-menu-item--danger`,onClick:()=>void v(()=>c(e.id)),disabled:a,children:`Disconnect`}),s&&(0,j.jsx)(`div`,{className:`connector-tile-menu-error`,children:s})]})]})}var R={"https://www.googleapis.com/auth/gmail.readonly":`Read emails`,"https://www.googleapis.com/auth/gmail.modify":`Organize emails (archive, label, trash)`,"https://www.googleapis.com/auth/gmail.send":`Send emails on your behalf`,"https://www.googleapis.com/auth/gmail.compose":`Compose emails`,"https://www.googleapis.com/auth/calendar.readonly":`View calendar events`,"https://www.googleapis.com/auth/calendar.events":`Create & respond to calendar events`,"https://www.googleapis.com/auth/drive.readonly":`Read Google Drive files`,"https://www.googleapis.com/auth/drive.file":`Manage Drive files created by this app`,"https://www.googleapis.com/auth/spreadsheets.readonly":`Read Google Sheets`,"https://www.googleapis.com/auth/spreadsheets":`Edit Google Sheets`,openid:`Identify you`,email:`See your email address`,profile:`See your basic profile info`,"https://graph.microsoft.com/Mail.ReadWrite":`Read & organize Outlook mail (archive, label, trash)`,"https://graph.microsoft.com/Mail.Send":`Send Outlook mail on your behalf`,"https://graph.microsoft.com/Calendars.ReadWrite":`Create & respond to Outlook calendar events`,"https://graph.microsoft.com/Calendars.Read":`View Outlook calendar events`,"https://graph.microsoft.com/User.Read":`Read your basic Microsoft profile`};function z(e){return R[e]??e.split(/[/.:]/).pop()??e}var B=[`use`];function V(e){return!(`revoke_supported`in e)||e.revoke_supported&&e.revoked_remotely?null:e.revoke_supported&&!e.revoked_remotely?`Disconnected locally, but the provider did not confirm the revoke (${String(e.revoke_error??`unknown error`)}). It may still show as connected in your account's app permissions.`:e.revoke_error?`Disconnected locally. ${String(e.revoke_error)}`:`Disconnected locally. This provider has no API to revoke access remotely — remove GAIA from your account's connected-apps page if you want to fully revoke it.`}function H(){let[e,t]=(0,M.useState)([]),[n,r]=(0,M.useState)([]),[i,a]=(0,M.useState)(!0),[o,c]=(0,M.useState)(null),[u,f]=(0,M.useState)(null),[p,m]=(0,M.useState)(null),g=(0,M.useCallback)(async()=>{try{let[{connectors:e},{agent_mcps:n}]=await Promise.all([s(),l()]);t(e),r(n),c(null)}catch(e){c(e instanceof Error?e.message:String(e))}finally{a(!1)}},[]);(0,M.useEffect)(()=>{g()},[g]);let _=e=>m(t=>t===e?null:e),v=(0,M.useCallback)(async e=>{try{let n=await h(e);t(t=>t.map(t=>t.id===e?n:t))}catch{g()}},[g]);return F((0,M.useCallback)(e=>{e.reason===`disconnected`&&f(V(e.payload)),e.connectorId?v(e.connectorId):g()},[v,g])),(0,j.jsxs)(`section`,{className:`connectors-section`,"aria-label":`Connectors`,children:[(0,j.jsx)(`p`,{className:`settings-help`,children:`Connect accounts and MCP servers so GAIA can use them on your behalf. Choose what GAIA may do with each one.`}),o&&(0,j.jsxs)(`div`,{className:`error-banner`,children:[(0,j.jsx)(C,{size:14}),(0,j.jsx)(`span`,{children:o})]}),u&&(0,j.jsxs)(`div`,{className:`error-banner`,role:`status`,onClick:()=>f(null),children:[(0,j.jsx)(C,{size:14}),(0,j.jsx)(`span`,{children:u})]}),i?(0,j.jsx)(`div`,{className:`connectors-loading`,children:(0,j.jsx)(d,{size:16,className:`spin`})}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`div`,{className:`connectors-list`,children:e.map(e=>(0,j.jsx)(U,{connector:e,expanded:p===e.id,onToggle:()=>_(e.id),onChanged:()=>void v(e.id)},e.id))}),n.length>0&&(0,j.jsx)(J,{servers:n,expanded:p,onToggle:_})]})]})}function U({connector:e,expanded:t,onToggle:n,onChanged:r}){return(0,j.jsxs)(`div`,{className:`connector-tile${t?` connector-tile--open`:``}`,children:[(0,j.jsxs)(`button`,{className:`connector-tile-header`,onClick:n,"aria-expanded":t,children:[(0,j.jsx)(`span`,{className:`connector-tile-name`,children:e.display_name}),(0,j.jsx)(`span`,{className:`connector-tile-type`,children:e.type===`oauth_pkce`?`OAuth`:`MCP`}),e.configured?e.type===`mcp_server`&&e.enabled===!1?(0,j.jsx)(`span`,{className:`connector-status disabled`,children:`Disabled`}):(0,j.jsxs)(`span`,{className:`connector-status ok`,children:[(0,j.jsx)(y,{size:12}),e.account_id??`Configured`]}):(0,j.jsx)(`span`,{className:`connector-status idle`,children:`Not configured`}),(0,j.jsx)(L,{connector:e,onChanged:r}),(0,j.jsx)(`span`,{className:`connector-tile-chevron`,children:t?(0,j.jsx)(w,{size:14}):(0,j.jsx)(S,{size:14})})]}),t&&(0,j.jsxs)(`div`,{className:`connector-detail`,children:[e.type===`oauth_pkce`?(0,j.jsx)(W,{connector:e,onChanged:r}):(0,j.jsx)(K,{connector:e,onChanged:r}),e.configured&&(0,j.jsx)(X,{connectorId:e.id,connectorType:e.type})]})]})}function W({connector:e,onChanged:t}){let[n,r]=(0,M.useState)(!1),[i,a]=(0,M.useState)(null),[o,s]=(0,M.useState)({}),[l,u]=(0,M.useState)(null),{agents:p}=f(),m=p.filter(t=>t.namespaced_agent_id&&t.required_connections?.some(t=>t.connector_id===e.id)),h=m.map(e=>e.namespaced_agent_id).join(`,`),[_,y]=(0,M.useState)(()=>new Set);(0,M.useEffect)(()=>{y(new Set(m.map(e=>e.namespaced_agent_id)))},[h]);let b=e=>y(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n}),x=()=>[..._];(0,M.useEffect)(()=>{let e=()=>{t()};return window.addEventListener(`focus`,e),()=>window.removeEventListener(`focus`,e)},[t]);let S=e=>{let t=window;t.gaia?.openExternal?t.gaia.openExternal(e):window.open(e,`_blank`,`noopener`)},w=async()=>{r(!0),a(null);try{let t=await T(e.id,e.available_scopes?.length?e.available_scopes:e.default_scopes,x());S(t.authorization_url)}catch(e){a(e instanceof Error?e.message:String(e))}finally{r(!1)}},E=async()=>{r(!0),a(null),u(null);try{let t=await O(e.id,e.available_scopes?.length?e.available_scopes:e.default_scopes,x());u(t)}catch(e){a(e instanceof Error?e.message:String(e))}finally{r(!1)}};(0,M.useEffect)(()=>{e.configured&&u(null)},[e.configured]),F((0,M.useCallback)(t=>{t.connectorId===e.id&&(t.reason===`oauth_error`?(a(String(t.payload.error??`Device sign-in failed.`)),u(null)):t.reason===`oauth_completed`&&u(null))},[e.id]));let D=async()=>{r(!0),a(null);try{await c(e.id),t()}catch(e){a(e instanceof Error?e.message:String(e))}finally{r(!1)}},k=async()=>{let n=(e.oauth_setup_fields??[]).filter(e=>e.required!==!1&&!o[e.key]?.trim()).map(e=>e.label);if(n.length){a(`Required: ${n.join(`, `)}`);return}r(!0),a(null);try{let n=e.available_scopes?.length?e.available_scopes:e.default_scopes,r=await v(e.id,{...o,scopes:n,grant_agents:x()}),i=typeof r.authorization_url==`string`?r.authorization_url:null;i&&S(i),t()}catch(e){a(e instanceof Error?e.message:String(e))}finally{r(!1)}},A=e.oauth_setup_fields??[],N=e.configurable===!1&&!e.configured&&A.length>0;return(0,j.jsxs)(`div`,{className:`configure-body`,children:[e.description&&(0,j.jsx)(`p`,{className:`connector-desc`,children:e.description}),N&&(0,j.jsxs)(`div`,{className:`oauth-setup-form`,children:[(0,j.jsx)(`p`,{className:`connector-desc`,children:`First-time setup — provide your OAuth client credentials below. They’re stored encrypted in your OS keyring and reused for future connections.`}),A.map(e=>(0,j.jsxs)(`label`,{className:`oauth-setup-field`,children:[(0,j.jsx)(`span`,{className:`oauth-setup-label`,children:e.label}),(0,j.jsx)(`input`,{type:e.kind===`secret`?`password`:`text`,className:`oauth-setup-input`,placeholder:e.placeholder,value:o[e.key]??``,onChange:t=>s(n=>({...n,[e.key]:t.target.value})),autoComplete:`off`,spellCheck:!1}),e.help_md&&(0,j.jsx)(`span`,{className:`oauth-setup-help`,children:e.help_md})]},e.key))]}),!e.configured&&m.length>0&&(0,j.jsxs)(`div`,{className:`oauth-grant-onconnect`,children:[(0,j.jsx)(`p`,{className:`oauth-grant-onconnect-label`,children:`Grant access to these agents when you connect:`}),m.map(e=>(0,j.jsxs)(`label`,{className:`oauth-grant-onconnect-item`,children:[(0,j.jsx)(`input`,{type:`checkbox`,checked:_.has(e.namespaced_agent_id),onChange:()=>b(e.namespaced_agent_id)}),(0,j.jsx)(`span`,{children:e.name})]},e.namespaced_agent_id)),(0,j.jsx)(`p`,{className:`oauth-grant-onconnect-help`,children:`You can change these any time under Per-agent grants below.`})]}),!N&&(0,j.jsx)(G,{connector:e,onChanged:t}),i&&(0,j.jsxs)(`div`,{className:`configure-error`,children:[(0,j.jsx)(C,{size:12}),` `,i]}),(0,j.jsxs)(`div`,{className:`configure-actions`,children:[e.configured?(0,j.jsx)(`button`,{className:`btn-secondary`,disabled:n,onClick:()=>void D(),children:n?(0,j.jsx)(d,{size:12,className:`spin`}):`Disconnect`}):N?(0,j.jsx)(`button`,{className:`btn-primary`,disabled:n,onClick:()=>void k(),children:n?(0,j.jsx)(d,{size:12,className:`spin`}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(g,{size:12}),` Save & Connect`]})}):e.configurable===!1?(0,j.jsx)(`span`,{className:`connector-setup-required`,title:e.config_error??void 0,children:`Setup required`}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`button`,{className:`btn-primary`,disabled:n||l!==null,onClick:()=>void w(),children:n?(0,j.jsx)(d,{size:12,className:`spin`}):(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(g,{size:12}),` Connect`]})}),e.supports_device_code&&(0,j.jsx)(`button`,{className:`btn-secondary`,disabled:n||l!==null,onClick:()=>void E(),title:`Enter a short code at a URL — no browser redirect or app registration needed`,children:`Sign in with a code`})]}),(e.docs_url||e.product_url)&&(0,j.jsxs)(`a`,{href:e.docs_url||e.product_url||`#`,target:`_blank`,rel:`noopener noreferrer`,className:`connector-product-link`,children:[`Learn more `,(0,j.jsx)(g,{size:11})]})]}),l&&!e.configured&&(0,j.jsxs)(`div`,{className:`device-code-block`,children:[(0,j.jsxs)(`p`,{className:`device-code-instructions`,children:[`Go to`,` `,(0,j.jsx)(`a`,{href:l.verification_uri,target:`_blank`,rel:`noopener noreferrer`,onClick:e=>{e.preventDefault(),S(l.verification_uri)},children:l.verification_uri}),` `,`and enter this code:`]}),(0,j.jsxs)(`div`,{className:`device-code-value`,children:[(0,j.jsx)(`code`,{children:l.user_code}),(0,j.jsx)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>void navigator.clipboard?.writeText(l.user_code),title:`Copy code`,children:`Copy`})]}),(0,j.jsxs)(`p`,{className:`device-code-waiting`,children:[(0,j.jsx)(d,{size:12,className:`spin`}),` Waiting for sign-in…`]}),(0,j.jsx)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>u(null),children:`Cancel`})]})]})}function G({connector:e,onChanged:t}){let[n,r]=(0,M.useState)(!1),[i,a]=(0,M.useState)({}),[o,s]=(0,M.useState)(!1),[c,l]=(0,M.useState)(!1),[u,f]=(0,M.useState)(null),p=e.oauth_setup_fields??[];if(p.length===0)return null;let m=e.oauth_client,h=m?.source===`keyring`?`Saved client: ${m.client_id}`:m?.source===`env`?`From environment: ${m.client_id}`:`No OAuth client configured`,g=async()=>{if(!i.client_id?.trim()){f(`Client ID is required.`);return}s(!0),f(null),l(!1);try{await v(e.id,{...Object.fromEntries(Object.entries(i).map(([e,t])=>[e,t.trim()])),save_only:!0}),l(!0),a({}),t(),setTimeout(()=>l(!1),2200)}catch(e){f(e instanceof Error?e.message:String(e))}finally{s(!1)}};return(0,j.jsxs)(`div`,{className:`oauth-client-editor`,children:[(0,j.jsxs)(`button`,{type:`button`,className:`oauth-client-editor__toggle`,onClick:()=>r(e=>!e),"aria-expanded":n,children:[n?(0,j.jsx)(w,{size:12}):(0,j.jsx)(S,{size:12}),(0,j.jsx)(`span`,{children:`OAuth client`}),(0,j.jsx)(`span`,{className:`oauth-client-editor__status`,children:h})]}),n&&(0,j.jsxs)(`div`,{className:`oauth-setup-form`,children:[(0,j.jsxs)(`p`,{className:`connector-desc`,children:[`Bring your own OAuth client — saved encrypted in your OS keyring (same store as`,` `,(0,j.jsx)(`code`,{children:`gaia connectors configure`}),`) and reused for future connections. Environment variables are only used when no saved client exists.`]}),p.map(e=>(0,j.jsxs)(`label`,{className:`oauth-setup-field`,children:[(0,j.jsx)(`span`,{className:`oauth-setup-label`,children:e.label}),(0,j.jsx)(`input`,{type:e.kind===`secret`?`password`:`text`,className:`oauth-setup-input`,placeholder:e.kind===`secret`&&m?.source===`keyring`&&m.has_secret?`••••••••`:e.placeholder,value:i[e.key]??``,onChange:t=>a(n=>({...n,[e.key]:t.target.value})),autoComplete:`off`,spellCheck:!1}),e.help_md&&(0,j.jsx)(`span`,{className:`oauth-setup-help`,children:e.help_md})]},e.key)),u&&(0,j.jsxs)(`div`,{className:`configure-error`,children:[(0,j.jsx)(C,{size:12}),` `,u]}),(0,j.jsx)(`div`,{className:`configure-actions`,children:(0,j.jsx)(`button`,{className:`btn-model-save${c?` saved`:``}`,disabled:o||!i.client_id?.trim(),onClick:()=>void g(),children:o?(0,j.jsx)(d,{size:12,className:`spin`}):c?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(y,{size:12}),` Saved`]}):`Save client`})})]})]})}function K({connector:e,onChanged:t}){let[n,r]=(0,M.useState)(()=>Object.fromEntries(e.mcp_env_keys.map(e=>[e,``]))),[i,a]=(0,M.useState)(!1),[o,s]=(0,M.useState)(!1),[l,u]=(0,M.useState)(null);(0,M.useEffect)(()=>{r(Object.fromEntries(e.mcp_env_keys.map(e=>[e,``])))},[e.mcp_env_keys.join(`,`)]);let f=e.mcp_env_keys.length===0,p=Object.values(n).every(e=>e.trim()===``),h=async()=>{let r=Object.fromEntries(Object.entries(n).filter(([,e])=>e.trim()!==``));if(f||Object.keys(r).length!==0){a(!0),u(null),s(!1);try{await v(e.id,r),s(!0),t(),setTimeout(()=>s(!1),2200)}catch(e){u(e instanceof Error?e.message:String(e))}finally{a(!1)}}},_=async()=>{a(!0),u(null);try{await c(e.id),r(Object.fromEntries(e.mcp_env_keys.map(e=>[e,``]))),t()}catch(e){u(e instanceof Error?e.message:String(e))}finally{a(!1)}},b=async()=>{a(!0),u(null);try{e.enabled?await m(e.id):await E(e.id),t()}catch(e){u(e instanceof Error?e.message:String(e))}finally{a(!1)}};return(0,j.jsxs)(`div`,{className:`configure-body`,children:[e.description&&(0,j.jsx)(`p`,{className:`connector-desc`,children:e.description}),l&&(0,j.jsxs)(`div`,{className:`configure-error`,children:[(0,j.jsx)(C,{size:12}),` `,l]}),e.configured&&(0,j.jsxs)(`div`,{className:`connector-toggle-row`,children:[(0,j.jsx)(`span`,{className:`connector-toggle-label`,children:`Active`}),(0,j.jsxs)(`span`,{className:`toggle-switch`,children:[(0,j.jsx)(`input`,{type:`checkbox`,checked:e.enabled!==!1,onChange:()=>void b(),disabled:i,"aria-label":e.enabled?`Disable this MCP server`:`Enable this MCP server`}),(0,j.jsx)(`span`,{className:`toggle-track`})]})]}),e.configured&&e.enabled===!1&&(0,j.jsx)(`p`,{className:`connector-toggle-help`,children:`Credentials and per-agent grants are preserved. Re-enable to make this server's tools available again.`}),e.mcp_env_keys.map(t=>(0,j.jsxs)(`div`,{className:`mcp-key-row`,children:[(0,j.jsx)(`label`,{className:`mcp-key-label`,children:t}),(0,j.jsx)(`input`,{type:`password`,className:`mcp-key-input`,placeholder:e.configured?`••••••••`:`Enter value`,value:n[t]??``,onChange:e=>r(n=>({...n,[t]:e.target.value})),spellCheck:!1,autoComplete:`off`})]},t)),(0,j.jsxs)(`div`,{className:`configure-actions`,children:[!(f&&e.configured)&&(0,j.jsx)(`button`,{className:`btn-model-save${o?` saved`:``}`,disabled:i||!f&&p,onClick:()=>void h(),children:i?(0,j.jsx)(d,{size:12,className:`spin`}):o?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(y,{size:12}),` Saved`]}):f?`Connect`:`Save`}),e.configured&&(0,j.jsx)(`button`,{className:`btn-secondary`,disabled:i,onClick:()=>void _(),children:`Disconnect`}),e.product_url&&(0,j.jsxs)(`a`,{href:e.product_url,target:`_blank`,rel:`noopener noreferrer`,className:`connector-product-link`,children:[`Docs `,(0,j.jsx)(g,{size:11})]})]})]})}function q({agent:e,connectorId:t,grantedScopes:n,onChanged:r}){let i=e.required_connections?.find(e=>e.connector_id===t),a=e.namespaced_agent_id;if(!i||!a)return null;let[o,s]=(0,M.useState)(()=>new Set(n)),[c,l]=(0,M.useState)(null),[d,f]=(0,M.useState)(null);(0,M.useEffect)(()=>{s(new Set(n))},[n.join(`,`)]);let p=async e=>{if(c!==null)return;let i=new Set(o);i.has(e)?i.delete(e):i.add(e),s(i),l(e),f(null);try{i.size===0?await b(t,a):await u(t,a,[...i]),r()}catch(e){s(new Set(n)),f(e instanceof Error?e.message:String(e))}finally{l(null)}};return(0,j.jsxs)(`div`,{className:`grant-agent-card`,children:[(0,j.jsx)(`div`,{className:`grant-agent-card-name`,children:e.name}),(0,j.jsx)(`div`,{className:`grant-scope-list`,children:i.scopes.map(e=>(0,j.jsxs)(`label`,{className:`grant-scope-item`,children:[(0,j.jsx)(`span`,{className:`grant-scope-label`,children:z(e)}),(0,j.jsxs)(`span`,{className:`toggle-switch`,children:[(0,j.jsx)(`input`,{type:`checkbox`,checked:o.has(e),onChange:()=>void p(e),disabled:c!==null}),(0,j.jsx)(`span`,{className:`toggle-track`})]})]},e))}),d&&(0,j.jsxs)(`div`,{className:`grant-scope-warning grant-scope-warning--error`,children:[(0,j.jsx)(C,{size:11}),` `,d]})]})}function J({servers:e,expanded:t,onToggle:n}){return(0,j.jsxs)(`div`,{className:`agent-mcps-section`,children:[(0,j.jsx)(`div`,{className:`agent-mcps-section-header`,children:`Custom agent servers`}),(0,j.jsx)(`div`,{className:`connectors-list`,children:e.map(e=>{let r=`${e.agent_id}::${e.server_name}`;return(0,j.jsx)(Y,{server:e,expanded:t===r,onToggle:()=>n(r)},r)})})]})}function Y({server:e,expanded:t,onToggle:n}){let r=[e.command,...e.args].join(` `);return(0,j.jsxs)(`div`,{className:`agent-mcp-tile${t?` agent-mcp-tile--open`:``}`,children:[(0,j.jsxs)(`button`,{type:`button`,className:`agent-mcp-tile-header`,onClick:n,"aria-expanded":t,children:[(0,j.jsx)(`span`,{className:`agent-mcp-server-name`,children:e.server_name}),(0,j.jsxs)(`span`,{className:`agent-mcp-agent-label`,children:[`via `,e.agent_name]}),e.disabled?(0,j.jsx)(`span`,{className:`agent-mcp-status disabled`,children:`Disabled`}):(0,j.jsxs)(`span`,{className:`agent-mcp-status enabled`,children:[(0,j.jsx)(y,{size:11}),` Active`]}),(0,j.jsx)(`span`,{className:`agent-mcp-readonly-badge`,children:`read-only`}),(0,j.jsx)(`span`,{className:`agent-mcp-chevron`,children:t?(0,j.jsx)(w,{size:14}):(0,j.jsx)(S,{size:14})})]}),t&&(0,j.jsxs)(`div`,{className:`agent-mcp-detail`,children:[r&&(0,j.jsx)(`div`,{className:`agent-mcp-command-row`,children:r}),(0,j.jsx)(`div`,{className:`agent-mcp-config-path`,children:e.config_path}),(0,j.jsxs)(`div`,{className:`agent-mcp-config-hint`,children:[`Edit the agent's `,(0,j.jsx)(`code`,{children:`mcp_servers.json`}),` to change this server.`]})]})]})}function X({connectorId:e,connectorType:t}){let{agents:n}=f(),[r,i]=(0,M.useState)({}),[a,o]=(0,M.useState)({}),[s,c]=(0,M.useState)(!0),l=(0,M.useCallback)(async()=>{try{let[{grants:t},{activations:n}]=await Promise.all([x(e),p(e)]);i(t),o(n)}catch{i({}),o({})}finally{c(!1)}},[e]);if((0,M.useEffect)(()=>{l()},[l]),F((0,M.useCallback)(t=>{t.connectorId===e&&(t.reason===`grant_changed`||t.reason===`activation_changed`||t.reason===`disconnected`)&&l()},[e,l])),s)return null;let u=n.filter(t=>t.namespaced_agent_id&&t.required_connections?.some(t=>t.connector_id===e)),d=t===`mcp_server`?n.filter(t=>t.namespaced_agent_id&&(t.consumes_mcp_servers||t.required_connections?.some(t=>t.connector_id===e))):[];return(0,j.jsxs)(`div`,{className:`connection-grants`,children:[(0,j.jsx)(`div`,{className:`grants-header`,children:`Per-agent grants`}),u.length===0?(0,j.jsx)(`div`,{className:`grants-empty`,children:`No agents require access to this connector.`}):u.map(t=>(0,j.jsx)(q,{agent:t,connectorId:e,grantedScopes:r[t.namespaced_agent_id]??[],onChanged:()=>void l()},t.namespaced_agent_id)),d.length>0&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(`div`,{className:`grants-header grants-header--activations`,children:`Active for`}),(0,j.jsx)(`div`,{className:`grants-help`,children:`Activations gate which agents see this connector's tools. Activating without a prior grant auto-creates one.`}),d.map(t=>(0,j.jsx)(Z,{agent:t,connectorId:e,active:!!a[t.namespaced_agent_id],grantedScopes:r[t.namespaced_agent_id]??[],onChanged:()=>void l()},`activation-${t.namespaced_agent_id}`))]})]})}function Z({agent:e,connectorId:t,active:n,grantedScopes:r,onChanged:i}){let a=e.required_connections?.find(e=>e.connector_id===t),o=e.namespaced_agent_id;if(!o)return null;let[s,c]=(0,M.useState)(n),[l,u]=(0,M.useState)(!1),[d,f]=(0,M.useState)(null);(0,M.useEffect)(()=>{c(n)},[n]);let p=async()=>{if(l)return;let e=!s;c(e),u(!0),f(null);try{if(e){let e=r.length===0?a?[...a.scopes]:[...B]:void 0;await _(t,o,e)}else await D(t,o);i()}catch(e){c(n),f(e instanceof Error?e.message:String(e))}finally{u(!1)}};return(0,j.jsxs)(`div`,{className:`grant-agent-card`,children:[(0,j.jsx)(`div`,{className:`grant-agent-card-name`,children:e.name}),(0,j.jsx)(`div`,{className:`grant-scope-list`,children:(0,j.jsxs)(`label`,{className:`grant-scope-item`,children:[(0,j.jsx)(`span`,{className:`grant-scope-label`,children:s?`Tools visible to this agent`:`Tools hidden from this agent`}),(0,j.jsxs)(`span`,{className:`toggle-switch`,children:[(0,j.jsx)(`input`,{type:`checkbox`,checked:s,onChange:()=>void p(),disabled:l}),(0,j.jsx)(`span`,{className:`toggle-track`})]})]})}),d&&(0,j.jsxs)(`div`,{className:`grant-scope-warning grant-scope-warning--error`,children:[(0,j.jsx)(C,{size:11}),` `,d]})]})}function Q(){return(0,j.jsxs)(`div`,{className:`settings-pane`,children:[(0,j.jsx)(`h2`,{className:`settings-pane-title`,children:`Connectors`}),(0,j.jsx)(H,{})]})}export{Q as ConnectorsPane};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{l as t,n,o as r,s as i,u as a}from"./apiBase-y00JgRBP.js";import{G as o,H as ee,It as s,Jt as c,Mt as l,Pt as te,U as u,Vt as ne,W as re,X as ie,Xt as ae,Y as d,Z as f,at as p,gt as m,ht as oe,nn as se,on as h,q as g,st as ce,t as _,ut as le,zt as ue}from"./index-Cp89afuH.js";var v={name:`circle-stop`,size:24,node:[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`rect`,{x:`9`,y:`9`,width:`6`,height:`6`,rx:`1`,key:`1ssd4o`}]],aliases:[`stop-circle`]};v.node;var y=t(v),b={name:`link-2`,size:24,node:[[`path`,{d:`M9 17H7A5 5 0 0 1 7 7h2`,key:`8i5ue5`}],[`path`,{d:`M15 7h2a5 5 0 1 1 0 10h-2`,key:`1b9ql8`}],[`line`,{x1:`8`,x2:`16`,y1:`12`,y2:`12`,key:`1jonct`}]]};b.node;var de=t(b),x={name:`loader`,size:24,node:[[`path`,{d:`M12 2v4`,key:`3427ic`}],[`path`,{d:`m16.2 7.8 2.9-2.9`,key:`r700ao`}],[`path`,{d:`M18 12h4`,key:`wj9ykh`}],[`path`,{d:`m16.2 16.2 2.9 2.9`,key:`1bxg5t`}],[`path`,{d:`M12 18v4`,key:`jadmvz`}],[`path`,{d:`m4.9 19.1 2.9-2.9`,key:`bwix9q`}],[`path`,{d:`M2 12h4`,key:`j09sii`}],[`path`,{d:`m4.9 4.9 2.9 2.9`,key:`giyufr`}]]};x.node;var S=t(x),C={name:`refresh-ccw`,size:24,node:[[`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`14sxne`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`,key:`1hlbsb`}],[`path`,{d:`M16 16h5v5`,key:`ccwih5`}]]};C.node;var fe=t(C),w=e(a(),1),T=n();function pe(e){let t=Math.floor((Date.now()-e)/1e3);return t<60?`${t}s`:`${Math.floor(t/60)}m ${t%60}s`}function me(e){try{let t=new Date(e);if(isNaN(t.getTime()))return``;let n=new Date().getTime()-t.getTime(),r=Math.floor(n/1e3),i=Math.floor(r/60),a=Math.floor(i/60),o=Math.floor(a/24);return r<60?`just now`:i<60?`${i}m ago`:a<24?`${a}h ago`:o===1?`yesterday`:o<7?`${o}d ago`:t.toLocaleDateString(void 0,{month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`})}catch{return``}}function he(e){try{let t=new Date(e);return isNaN(t.getTime())?``:t.toLocaleString(void 0,{year:`numeric`,month:`short`,day:`numeric`,hour:`numeric`,minute:`2-digit`,second:`2-digit`})}catch{return``}}function E(){let{documents:e,setDocuments:t,setShowDocLibrary:n,setShowFileBrowser:a,currentSessionId:v,sessions:b,updateSessionInList:x,draftDocumentIds:C,addDraftDocuments:E,removeDraftDocument:D}=l(),O=b.find(e=>e.id===v)??null,[k,ge]=(0,w.useState)(v?`chat`:`next`),A=k===`chat`?v:null,j=`“${O?.title||`Untitled chat`}”`,[M,N]=(0,w.useState)(``),P=(0,w.useCallback)(()=>n(!1),[n]);r(!0,P);let F=(0,w.useRef)(null);o(F,!0);let[I,L]=(0,w.useState)(!1),[R,z]=(0,w.useState)(!1),[_e,B]=(0,w.useState)(``),[V,H]=(0,w.useState)(``),[U,W]=(0,w.useState)(null),[G,K]=(0,w.useState)(new Set),q=(0,w.useRef)(new Map),[,ve]=(0,w.useState)(0);(0,w.useEffect)(()=>{i.doc.info(`Loading document library...`);let e=i.doc.time();p().then(n=>{let r=n.documents||[];t(r),i.doc.timed(`Loaded ${r.length} document(s), ${n.total_chunks||0} chunks, ${n.total_size_bytes||0} bytes total`,e)}).catch(e=>{i.doc.error(`Failed to load documents`,e),t([])})},[t]);let J=e.some(e=>e.indexing_status===`indexing`||e.indexing_status===`pending`);(0,w.useEffect)(()=>{if(!J)return;let e=l.getState().documents;for(let t of e)(t.indexing_status===`indexing`||t.indexing_status===`pending`)&&!q.current.has(t.id)&&q.current.set(t.id,Date.now());let n=!1,r=setInterval(async()=>{if(ve(e=>e+1),!n){n=!0;try{let e=l.getState().documents.filter(e=>e.indexing_status===`indexing`||e.indexing_status===`pending`);for(let n of e)try{let e=await f(n.id);if(e.indexing_status!==`indexing`&&e.indexing_status!==`pending`){q.current.delete(n.id);let e=await p();t(e.documents||[]);break}}catch{}}finally{n=!1}}},2e3);return()=>clearInterval(r)},[J,t]),(0,w.useEffect)(()=>{let t=new Set(e.filter(e=>e.indexing_status===`indexing`||e.indexing_status===`pending`).map(e=>e.id));for(let e of q.current.keys())t.has(e)||q.current.delete(e)},[e]),(0,w.useEffect)(()=>{let e=setInterval(async()=>{try{let e=(await p()).documents||[],n=l.getState().documents;(e.length!==n.length||e.some((e,t)=>{let r=n.find(t=>t.id===e.id);return!r||r.indexing_status!==e.indexing_status||r.indexed_at!==e.indexed_at||r.chunk_count!==e.chunk_count}))&&(t(e),i.doc.debug(`Document list refreshed (server-side changes detected)`))}catch{}},15e3);return()=>clearInterval(e)},[t]);let ye=e.reduce((e,t)=>e+t.file_size,0),Y=e.reduce((e,t)=>e+t.chunk_count,0),X=(0,w.useCallback)(async(e,t)=>{if(!A){E([e]),N(`${t} will be attached to your next new chat.`),i.doc.info(`"${t}" will be attached to the next new chat`);return}await g(A,e);let n=l.getState().sessions.find(e=>e.id===A)?.document_ids??[];n.includes(e)||x(A,{document_ids:[...n,e]}),N(`Attached ${t} to ${j}.`),i.doc.info(`Attached "${t}" to session ${A}`)},[A,j,x,E]),Z=(0,w.useCallback)(async(e,n)=>{let r=e.includes(`.`)?`.`+e.split(`.`).pop()?.toLowerCase():``;if(r&&!re(r)){let t=u(r),n=t?t.message:`The file type "${r}" is not supported for indexing.`;i.doc.warn(`Unsupported file type: ${e} (${r})`),W({filename:e,error:n});return}i.doc.info(`Indexing document: ${e}`);let a=i.doc.time();z(!0),B(`Indexing ${e}...`),W(null),N(``);try{let r=await n();if(i.doc.timed(`Indexed "${e}": ${r?.chunk_count||`?`} chunks`,a),r?.id)try{await X(r.id,r.filename||e)}catch(t){i.doc.error(`Could not attach "${e}" to ${j}`,t),W({filename:e,error:`Indexed, but not attached to ${j}: ${t instanceof Error?t.message:t}`})}let o=await p();t(o.documents||[]),B(``)}catch(t){i.doc.error(`Failed to index "${e}"`,t);let n=t instanceof Error?t.message:`Upload failed`,r=t?.httpStatus;W({filename:e,error:n,status:r}),B(``)}finally{z(!1)}},[X,j,t]),Q=(0,w.useCallback)(async(e,t)=>{await Z(t,()=>m(e))},[Z]),$=(0,w.useCallback)(async e=>{await Z(e.name,()=>oe(e))},[Z]),be=(0,w.useCallback)(async e=>{e.preventDefault(),L(!1);let t=Array.from(e.dataTransfer.files);i.doc.info(`Dropped ${t.length} file(s) into Document Library`);for(let e of t)await $(e)},[$]),xe=(0,w.useCallback)(async n=>{let r=e.find(e=>e.id===n);i.doc.info(`Deleting document: ${r?.filename||n}`);try{await ie(n),q.current.delete(n),D(n),t(e.filter(e=>e.id!==n)),i.doc.info(`Deleted document: ${r?.filename||n}`)}catch(e){i.doc.error(`Failed to delete document: ${r?.filename||n}`,e)}},[e,t,D]),Se=(0,w.useCallback)(async(e,t)=>{i.doc.info(`Opening folder for: ${e}`);try{await ce(e,!0)}catch(e){i.doc.error(`Failed to open folder for: ${t}`,e)}},[]),Ce=(0,w.useCallback)(async n=>{let r=e.find(e=>e.id===n);i.doc.info(`Cancelling indexing for: ${r?.filename||n}`);try{await d(n),q.current.delete(n);let e=await p();t(e.documents||[]),i.doc.info(`Cancelled indexing for: ${r?.filename||n}`)}catch(e){i.doc.error(`Failed to cancel indexing: ${r?.filename||n}`,e)}},[e,t]),we=(0,w.useCallback)(async n=>{if(G.has(n))return;let r=e.find(e=>e.id===n);i.doc.info(`Reindexing document: ${r?.filename||n}`),K(e=>new Set(e).add(n));try{await le(n),i.doc.info(`Reindex complete for: ${r?.filename||n}`)}catch(e){let t=e instanceof Error?e.message:String(e);i.doc.error(`Reindex failed for: ${r?.filename||n}`,e),W({filename:r?.filename||n,error:t})}finally{try{let e=await p();t(e.documents||[])}catch(e){i.doc.error(`Failed to refresh documents after reindex`,e)}K(e=>{let t=new Set(e);return t.delete(n),t})}},[e,t,G]),Te=(0,w.useCallback)(async t=>{let n=e.find(e=>e.id===t)?.filename||t;N(``);try{await X(t,n)}catch(e){i.doc.error(`Failed to attach ${n} to ${j}`,e),W({filename:n,error:`Not attached to ${j}: ${e instanceof Error?e.message:e}`})}},[e,X,j]),Ee=(0,w.useCallback)(async e=>{e.preventDefault(),V.trim()&&(i.doc.info(`Indexing from path input: ${V.trim()}`),await Q(V.trim(),V.trim().split(/[\\/]/).pop()||`file`),H(``))},[V,Q]),De=e=>{let t=e.indexing_status||`complete`,n=q.current.get(e.id),r=e.indexed_at?me(e.indexed_at):``,i=e.indexed_at?he(e.indexed_at):``;switch(t){case`indexing`:case`pending`:return(0,T.jsxs)(`div`,{className:`doc-indexing-status`,children:[(0,T.jsxs)(`div`,{className:`doc-indexing-bar`,children:[(0,T.jsx)(`div`,{className:`doc-indexing-bar-track`,children:(0,T.jsx)(`div`,{className:`doc-indexing-bar-fill`})}),(0,T.jsxs)(`span`,{className:`doc-indexing-label`,children:[(0,T.jsx)(S,{size:12,className:`doc-spin`}),`Indexing`,n?` (${pe(n)})`:`...`]})]}),(0,T.jsxs)(`button`,{className:`btn-cancel`,onClick:t=>{t.stopPropagation(),Ce(e.id)},title:`Cancel indexing`,"aria-label":`Cancel indexing ${e.filename}`,children:[(0,T.jsx)(y,{size:14}),`Cancel`]})]});case`failed`:return(0,T.jsxs)(`div`,{className:`doc-indexing-status`,children:[(0,T.jsxs)(`span`,{className:`doc-status-badge doc-status-failed`,title:e.last_error||void 0,"aria-label":e.last_error?`Failed: ${e.last_error}`:`Indexing failed`,children:[(0,T.jsx)(h,{size:12}),` Failed`]}),(0,T.jsxs)(`button`,{className:`btn-retry`,onClick:t=>{t.stopPropagation(),we(e.id)},title:`Retry indexing`,"aria-label":`Retry indexing ${e.filename}`,disabled:G.has(e.id),children:[(0,T.jsx)(fe,{size:14}),G.has(e.id)?`Retrying…`:`Retry`]})]});case`cancelled`:return(0,T.jsxs)(`span`,{className:`doc-status-badge doc-status-cancelled`,children:[(0,T.jsx)(y,{size:12}),` Cancelled`]});case`missing`:return(0,T.jsxs)(`span`,{className:`doc-status-badge doc-status-missing`,children:[(0,T.jsx)(h,{size:12}),` File missing`]});default:return(0,T.jsxs)(`span`,{className:`doc-meta`,children:[_(e.file_size),` · `,e.chunk_count,` chunk`,e.chunk_count===1?``:`s`,r&&(0,T.jsxs)(`span`,{className:`doc-timestamp`,title:`Indexed: ${i}`,children:[(0,T.jsx)(se,{size:10}),r]})]})}};return(0,T.jsx)(`div`,{ref:F,className:`modal-overlay`,onClick:()=>n(!1),role:`dialog`,"aria-modal":`true`,"aria-label":`Document library`,children:(0,T.jsxs)(`div`,{className:`modal-panel doc-modal`,onClick:e=>e.stopPropagation(),children:[(0,T.jsxs)(`div`,{className:`modal-header`,children:[(0,T.jsx)(`h3`,{children:`Document library`}),(0,T.jsx)(`button`,{className:`btn-icon`,onClick:()=>n(!1),"aria-label":`Close document library`,children:(0,T.jsx)(te,{size:18})})]}),(0,T.jsxs)(`div`,{className:`modal-body`,children:[(0,T.jsxs)(`div`,{className:`doc-target`,children:[v?(0,T.jsxs)(`label`,{className:`doc-target-choice`,children:[`Add documents to`,(0,T.jsxs)(`select`,{value:k,onChange:e=>{ge(e.target.value),N(``)},children:[(0,T.jsxs)(`option`,{value:`chat`,children:[`This chat: `,j]}),(0,T.jsx)(`option`,{value:`next`,children:`Your next new chat`})]})]}):(0,T.jsx)(`span`,{children:`Documents you add here are attached to your next new chat.`}),(0,T.jsx)(`span`,{className:`doc-target-hint`,children:`A chat searches only the documents attached to it.`})]}),(0,T.jsx)(`div`,{className:`drop-zone ${I?`drag-over`:``} ${R?`uploading`:``}`,onDragOver:e=>{e.preventDefault(),L(!0)},onDragLeave:()=>L(!1),onDrop:be,children:R?(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(`div`,{className:`upload-spinner`}),(0,T.jsx)(`p`,{children:_e})]}):(0,T.jsxs)(T.Fragment,{children:[(0,T.jsx)(s,{size:32,strokeWidth:1.5}),(0,T.jsx)(`p`,{children:`Drop files here to index`}),(0,T.jsx)(`span`,{className:`drop-hint`,children:`PDF, TXT, MD, code files, and 50+ formats`})]})}),(0,T.jsxs)(`form`,{className:`path-input-form`,onSubmit:Ee,children:[(0,T.jsx)(c,{size:16,className:`path-icon`}),(0,T.jsx)(`input`,{type:`text`,className:`path-input`,value:V,onChange:e=>H(e.target.value),placeholder:`Or enter file path: C:\\docs\\manual.pdf`,"aria-label":`File path to index`}),(0,T.jsx)(`button`,{type:`submit`,className:`btn-secondary`,disabled:!V.trim()||R,children:`Index`}),(0,T.jsxs)(`button`,{type:`button`,className:`btn-secondary`,onClick:()=>{n(!1),a(!0)},title:`Browse files on this computer`,children:[(0,T.jsx)(ne,{size:14}),`Browse files`]})]}),M&&(0,T.jsx)(`p`,{className:`doc-target-notice`,role:`status`,children:M}),U&&(0,T.jsx)(ee,{filename:U.filename,error:U.error,status:U.status,onDismiss:()=>W(null)}),e.length>0&&(0,T.jsxs)(`div`,{className:`doc-stats`,children:[(0,T.jsxs)(`span`,{children:[e.length,` document`,e.length===1?``:`s`]}),(0,T.jsx)(`span`,{children:`·`}),(0,T.jsxs)(`span`,{children:[Y,` chunk`,Y===1?``:`s`]}),(0,T.jsx)(`span`,{children:`·`}),(0,T.jsx)(`span`,{children:_(ye)})]}),(0,T.jsxs)(`div`,{className:`doc-list`,children:[e.length===0&&!R&&(0,T.jsxs)(`div`,{className:`empty-docs`,children:[(0,T.jsx)(ae,{size:32,strokeWidth:1}),(0,T.jsx)(`p`,{children:`No documents indexed yet`}),(0,T.jsx)(`span`,{children:`Drop files above or enter a file path`})]}),e.map(e=>(0,T.jsxs)(`div`,{className:`doc-row ${e.indexing_status===`indexing`?`doc-row-indexing`:``}`,children:[(0,T.jsxs)(`div`,{className:`doc-info`,children:[(0,T.jsx)(`span`,{className:`doc-name`,children:e.filename}),De(e)]}),(0,T.jsxs)(`div`,{className:`doc-row-actions`,children:[e.filepath&&(0,T.jsx)(`button`,{className:`btn-icon-sm doc-open-folder`,onClick:()=>Se(e.filepath,e.filename),title:`Open folder: ${e.filepath}`,"aria-label":`Open containing folder for ${e.filename}`,children:(0,T.jsx)(c,{size:14})}),e.indexing_status===`complete`&&!(A?O?.document_ids?.includes(e.id):C.includes(e.id))&&(0,T.jsx)(`button`,{className:`btn-icon-sm doc-attach`,onClick:()=>Te(e.id),title:A?`Use in ${j}`:`Use in your next chat`,"aria-label":A?`Attach ${e.filename} to ${j}`:`Attach ${e.filename} to your next chat`,children:(0,T.jsx)(de,{size:14})}),e.indexing_status!==`indexing`&&e.indexing_status!==`pending`&&(0,T.jsx)(`button`,{className:`btn-icon-sm doc-delete`,onClick:()=>xe(e.id),title:`Remove`,"aria-label":`Remove ${e.filename}`,children:(0,T.jsx)(ue,{size:14})})]})]},e.id))]})]})]})})}export{E as DocumentLibrary};
|