clay-server 2.39.0-beta.2 → 2.39.0-beta.4
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/lib/daemon.js +103 -0
- package/lib/project-connection.js +15 -0
- package/lib/project-sessions.js +54 -112
- package/lib/public/app.js +14 -3
- package/lib/public/css/tui-attention.css +317 -0
- package/lib/public/index.html +21 -20
- package/lib/public/modules/app-home-hub.js +41 -0
- package/lib/public/modules/app-messages.js +16 -5
- package/lib/public/modules/app-notifications.js +11 -2
- package/lib/public/modules/app-projects.js +2 -0
- package/lib/public/modules/session-tui-view.js +126 -68
- package/lib/public/modules/sidebar-mobile.js +1 -11
- package/lib/public/modules/sidebar-sessions.js +5 -97
- package/lib/public/modules/whats-new-article.js +70 -0
- package/lib/public/modules/whats-new.js +264 -0
- package/lib/sessions.js +137 -1
- package/lib/users-preferences.js +74 -10
- package/lib/users.js +4 -0
- package/lib/whats-new-content.js +68 -0
- package/lib/whats-new.js +54 -0
- package/lib/ws-schema.js +7 -3
- package/lib/yoke/adapters/claude-worker.js +19 -0
- package/lib/yoke/adapters/claude.js +18 -0
- package/package.json +1 -1
package/lib/daemon.js
CHANGED
|
@@ -1248,11 +1248,114 @@ var ipc = createIPCServer(socketPath(), function (msg) {
|
|
|
1248
1248
|
}
|
|
1249
1249
|
});
|
|
1250
1250
|
|
|
1251
|
+
// --- Cleanup stale SDK warmup CLI session files ---
|
|
1252
|
+
//
|
|
1253
|
+
// On adapter init the SDK is given a dummy "hi" prompt to discover models,
|
|
1254
|
+
// skills, and capabilities. Even though we abort before any response
|
|
1255
|
+
// arrives, the SDK creates a real CLI session jsonl on disk. Going forward
|
|
1256
|
+
// we delete those inline (see lib/yoke/adapters/claude.js and
|
|
1257
|
+
// claude-worker.js), but installs from before that fix can have a long
|
|
1258
|
+
// tail of "hi"-only sessions polluting the Resume CLI session dialog.
|
|
1259
|
+
//
|
|
1260
|
+
// On every daemon startup we scan REAL_HOME/.claude/projects/*/ and unlink
|
|
1261
|
+
// files that match a strict warmup signature so legitimate "hi" sessions
|
|
1262
|
+
// from real users are never touched. All checks must pass:
|
|
1263
|
+
//
|
|
1264
|
+
// 1. file size < 64 KB (real sessions grow past this fast;
|
|
1265
|
+
// threshold is generous because warmup files include the SDK's
|
|
1266
|
+
// skill_listing / deferred_tools attachments which can run into the
|
|
1267
|
+
// tens of KB on its own)
|
|
1268
|
+
// 2. mtime older than 60 seconds (avoid racing an in-flight warmup)
|
|
1269
|
+
// 3. exactly one user message, content text equals literal "hi"
|
|
1270
|
+
// 4. zero assistant messages (real users virtually always get a reply)
|
|
1271
|
+
//
|
|
1272
|
+
// Multi-user OS-isolated installs have per-user ~/.claude trees that the
|
|
1273
|
+
// daemon process can't traverse; the inline cleanup in claude-worker.js
|
|
1274
|
+
// covers those.
|
|
1275
|
+
function cleanupSdkWarmupSessions() {
|
|
1276
|
+
var projectsRoot = path.join(REAL_HOME, ".claude", "projects");
|
|
1277
|
+
var projectDirs;
|
|
1278
|
+
try { projectDirs = fs.readdirSync(projectsRoot, { withFileTypes: true }); }
|
|
1279
|
+
catch (e) { return; }
|
|
1280
|
+
|
|
1281
|
+
var deleted = 0;
|
|
1282
|
+
var nowMs = Date.now();
|
|
1283
|
+
var MAX_BYTES = 64 * 1024;
|
|
1284
|
+
var MIN_AGE_MS = 60 * 1000;
|
|
1285
|
+
|
|
1286
|
+
for (var di = 0; di < projectDirs.length; di++) {
|
|
1287
|
+
if (!projectDirs[di].isDirectory()) continue;
|
|
1288
|
+
var projDir = path.join(projectsRoot, projectDirs[di].name);
|
|
1289
|
+
var files;
|
|
1290
|
+
try { files = fs.readdirSync(projDir); } catch (e) { continue; }
|
|
1291
|
+
|
|
1292
|
+
for (var fi = 0; fi < files.length; fi++) {
|
|
1293
|
+
var f = files[fi];
|
|
1294
|
+
if (!f.endsWith(".jsonl")) continue;
|
|
1295
|
+
var fp = path.join(projDir, f);
|
|
1296
|
+
|
|
1297
|
+
var st;
|
|
1298
|
+
try { st = fs.statSync(fp); } catch (e) { continue; }
|
|
1299
|
+
if (st.size >= MAX_BYTES) continue;
|
|
1300
|
+
if (nowMs - st.mtimeMs < MIN_AGE_MS) continue;
|
|
1301
|
+
|
|
1302
|
+
var raw;
|
|
1303
|
+
try { raw = fs.readFileSync(fp, "utf8"); } catch (e) { continue; }
|
|
1304
|
+
var lines = raw.split("\n");
|
|
1305
|
+
var userCount = 0;
|
|
1306
|
+
var assistantCount = 0;
|
|
1307
|
+
var userText = null;
|
|
1308
|
+
var parseOk = true;
|
|
1309
|
+
for (var li = 0; li < lines.length; li++) {
|
|
1310
|
+
var line = lines[li];
|
|
1311
|
+
if (!line) continue;
|
|
1312
|
+
var ev;
|
|
1313
|
+
try { ev = JSON.parse(line); } catch (e) { parseOk = false; break; }
|
|
1314
|
+
if (!ev || typeof ev !== "object") continue;
|
|
1315
|
+
if (ev.type === "user") {
|
|
1316
|
+
userCount++;
|
|
1317
|
+
if (userCount > 1) break;
|
|
1318
|
+
var msg = ev.message;
|
|
1319
|
+
var content = msg && msg.content;
|
|
1320
|
+
if (typeof content === "string") {
|
|
1321
|
+
userText = content;
|
|
1322
|
+
} else if (Array.isArray(content)) {
|
|
1323
|
+
var parts = [];
|
|
1324
|
+
for (var ci = 0; ci < content.length; ci++) {
|
|
1325
|
+
if (content[ci] && content[ci].type === "text" && typeof content[ci].text === "string") {
|
|
1326
|
+
parts.push(content[ci].text);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
userText = parts.join("");
|
|
1330
|
+
}
|
|
1331
|
+
} else if (ev.type === "assistant") {
|
|
1332
|
+
assistantCount++;
|
|
1333
|
+
if (assistantCount > 0) break;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
if (!parseOk) continue;
|
|
1337
|
+
if (userCount !== 1) continue;
|
|
1338
|
+
if (assistantCount !== 0) continue;
|
|
1339
|
+
if (userText !== "hi") continue;
|
|
1340
|
+
|
|
1341
|
+
try { fs.unlinkSync(fp); deleted++; } catch (e) {}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
if (deleted > 0) {
|
|
1346
|
+
console.log("[daemon] Removed " + deleted + " stale SDK warmup session file(s)");
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1251
1350
|
// --- Start listening (with retry for port-in-use during update handoff) ---
|
|
1252
1351
|
var listenRetries = 0;
|
|
1253
1352
|
var MAX_LISTEN_RETRIES = 15;
|
|
1254
1353
|
|
|
1255
1354
|
function startListening() {
|
|
1355
|
+
// One-shot cleanup of stale SDK warmup CLI session files from previous
|
|
1356
|
+
// daemon runs (best-effort, never fatal).
|
|
1357
|
+
try { cleanupSdkWarmupSessions(); } catch (e) {}
|
|
1358
|
+
|
|
1256
1359
|
relay.server.listen(config.port, listenHost, function () {
|
|
1257
1360
|
var protocol = tlsOptions ? "https" : "http";
|
|
1258
1361
|
console.log("[daemon] Listening on " + protocol + "://" + listenHost + ":" + config.port);
|
|
@@ -157,6 +157,21 @@ function attachConnection(ctx) {
|
|
|
157
157
|
var _comVal = _comUid ? usersModule.getClaudeOpenMode(_comUid) : "tui";
|
|
158
158
|
sendTo(ws, { type: "claude_open_mode_changed", claudeOpenMode: _comVal || "tui" });
|
|
159
159
|
}
|
|
160
|
+
|
|
161
|
+
// What's New: push the full entries list (for the home-page feed)
|
|
162
|
+
// plus the subset of unseen ids (for the auto-pop carousel). Content
|
|
163
|
+
// lives in lib/whats-new-content.js so adding an entry doesn't touch
|
|
164
|
+
// this file.
|
|
165
|
+
try {
|
|
166
|
+
var _wn = require("./whats-new");
|
|
167
|
+
var _wnUid = (wsUser && wsUser.id) || null;
|
|
168
|
+
var _wnState = _wnUid ? _wn.getStateForUser(_wnUid) : { entries: _wn.listEntries(), unseenIds: [] };
|
|
169
|
+
if (_wnState.entries.length > 0) {
|
|
170
|
+
sendTo(ws, { type: "whats_new_state", entries: _wnState.entries, unseenIds: _wnState.unseenIds });
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {
|
|
173
|
+
if (debug) console.error("[project] whats_new send failed:", e && e.message);
|
|
174
|
+
}
|
|
160
175
|
_loop.sendConnectionState(ws);
|
|
161
176
|
if (_mcp) _mcp.sendConnectionState(ws);
|
|
162
177
|
if (_notifications) _notifications.sendConnectionState(ws, sendTo);
|
package/lib/project-sessions.js
CHANGED
|
@@ -265,12 +265,21 @@ function attachSessions(ctx) {
|
|
|
265
265
|
return null;
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
|
|
268
|
+
// Prepare a born-TUI session to be rendered via the SDK GUI chat for the
|
|
269
|
+
// current click. Reads the CLI jsonl transcript (cli-sessions.js),
|
|
270
|
+
// populates session.history, and tears
|
|
271
|
+
// down the PTY. The born-TUI marker (session.mode === 'tui') is kept on
|
|
272
|
+
// disk so that flipping claudeOpenMode back to 'tui' later restores the
|
|
273
|
+
// embedded-terminal experience instead of locking the session as GUI.
|
|
274
|
+
//
|
|
275
|
+
// Idempotent: if the PTY is already gone and history is populated, this
|
|
276
|
+
// is a no-op (avoids re-reading jsonl and re-writing the session file on
|
|
277
|
+
// every click while the user is reading the session in GUI mode).
|
|
278
|
+
function prepareTuiSessionForGuiView(session) {
|
|
273
279
|
if (!session || session.cliSessionId == null) return;
|
|
280
|
+
var alreadyHydrated = (typeof session.terminalId !== "number") &&
|
|
281
|
+
Array.isArray(session.history) && session.history.length > 0;
|
|
282
|
+
if (alreadyHydrated) return;
|
|
274
283
|
var cliSess;
|
|
275
284
|
try { cliSess = require("./cli-sessions"); } catch (e) { return; }
|
|
276
285
|
var history = null;
|
|
@@ -278,7 +287,6 @@ function attachSessions(ctx) {
|
|
|
278
287
|
if (Array.isArray(history)) {
|
|
279
288
|
session.history = history;
|
|
280
289
|
}
|
|
281
|
-
session.mode = "gui";
|
|
282
290
|
if (typeof session.terminalId === "number" && tm) {
|
|
283
291
|
try { tm.close(session.terminalId); } catch (e) {}
|
|
284
292
|
}
|
|
@@ -494,101 +502,10 @@ function attachSessions(ctx) {
|
|
|
494
502
|
return true;
|
|
495
503
|
}
|
|
496
504
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// If Clay already has a persisted meta file for this cliSessionId, read
|
|
502
|
-
// its vendor so resumeSession doesn't silently default to the project's
|
|
503
|
-
// primary vendor (which would break codex sessions after server restart).
|
|
504
|
-
var persistedVendor = null;
|
|
505
|
-
try {
|
|
506
|
-
var _fsResume = require("fs");
|
|
507
|
-
var _pathResume = require("path");
|
|
508
|
-
var metaPath = _pathResume.join(sm.sessionsDir, msg.cliSessionId + ".jsonl");
|
|
509
|
-
if (_fsResume.existsSync(metaPath)) {
|
|
510
|
-
var firstLine = _fsResume.readFileSync(metaPath, "utf8").split("\n", 1)[0];
|
|
511
|
-
try {
|
|
512
|
-
var metaObj = JSON.parse(firstLine);
|
|
513
|
-
if (metaObj && metaObj.type === "meta" && metaObj.vendor) persistedVendor = metaObj.vendor;
|
|
514
|
-
} catch (e) {}
|
|
515
|
-
}
|
|
516
|
-
} catch (e) {}
|
|
517
|
-
|
|
518
|
-
// Try SDK for title first, then fall back to manual parsing
|
|
519
|
-
var titlePromise = adapter.getSessionInfo(msg.cliSessionId, { dir: cwd }).then(function(info) {
|
|
520
|
-
return (info && info.summary) ? info.summary.substring(0, 100) : null;
|
|
521
|
-
}).catch(function() { return null; });
|
|
522
|
-
|
|
523
|
-
Promise.all([
|
|
524
|
-
cliSess.readCliSessionHistory(cwd, msg.cliSessionId),
|
|
525
|
-
titlePromise
|
|
526
|
-
]).then(function(results) {
|
|
527
|
-
var history = results[0];
|
|
528
|
-
var sdkTitle = results[1];
|
|
529
|
-
var title = sdkTitle || "Resumed session";
|
|
530
|
-
if (!sdkTitle) {
|
|
531
|
-
for (var i = 0; i < history.length; i++) {
|
|
532
|
-
if (history[i].type === "user_message" && history[i].text) {
|
|
533
|
-
title = history[i].text.substring(0, 50);
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
var resumed = sm.resumeSession(msg.cliSessionId, { history: history, title: title, vendor: persistedVendor || undefined }, ws);
|
|
539
|
-
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
540
|
-
}).catch(function() {
|
|
541
|
-
var resumed = sm.resumeSession(msg.cliSessionId, persistedVendor ? { vendor: persistedVendor } : undefined, ws);
|
|
542
|
-
if (resumed) ws._clayActiveSession = resumed.localId;
|
|
543
|
-
});
|
|
544
|
-
return true;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (msg.type === "list_cli_sessions") {
|
|
548
|
-
var _fs = require("fs");
|
|
549
|
-
// Collect session IDs already in relay (in-memory + persisted on disk)
|
|
550
|
-
var relayIds = {};
|
|
551
|
-
sm.sessions.forEach(function (s) {
|
|
552
|
-
if (s.cliSessionId) relayIds[s.cliSessionId] = true;
|
|
553
|
-
});
|
|
554
|
-
try {
|
|
555
|
-
var sessDir = sm.sessionsDir;
|
|
556
|
-
var diskFiles = _fs.readdirSync(sessDir);
|
|
557
|
-
for (var fi = 0; fi < diskFiles.length; fi++) {
|
|
558
|
-
if (diskFiles[fi].endsWith(".jsonl")) {
|
|
559
|
-
relayIds[diskFiles[fi].replace(".jsonl", "")] = true;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
} catch (e) {}
|
|
563
|
-
|
|
564
|
-
adapter.listSessions({ dir: cwd }).then(function(sdkSessions) {
|
|
565
|
-
var filtered = sdkSessions.filter(function(s) {
|
|
566
|
-
return !relayIds[s.sessionId];
|
|
567
|
-
}).map(function(s) {
|
|
568
|
-
return {
|
|
569
|
-
sessionId: s.sessionId,
|
|
570
|
-
firstPrompt: s.summary || s.firstPrompt || "",
|
|
571
|
-
model: null,
|
|
572
|
-
gitBranch: s.gitBranch || null,
|
|
573
|
-
startTime: s.createdAt ? new Date(s.createdAt).toISOString() : null,
|
|
574
|
-
lastActivity: s.lastModified ? new Date(s.lastModified).toISOString() : null,
|
|
575
|
-
};
|
|
576
|
-
});
|
|
577
|
-
sendTo(ws, { type: "cli_session_list", sessions: filtered });
|
|
578
|
-
}).catch(function() {
|
|
579
|
-
// Fallback to manual parsing if SDK fails
|
|
580
|
-
var cliSessions = require("./cli-sessions");
|
|
581
|
-
cliSessions.listCliSessions(cwd).then(function(sessions) {
|
|
582
|
-
var filtered = sessions.filter(function(s) {
|
|
583
|
-
return !relayIds[s.sessionId];
|
|
584
|
-
});
|
|
585
|
-
sendTo(ws, { type: "cli_session_list", sessions: filtered });
|
|
586
|
-
}).catch(function() {
|
|
587
|
-
sendTo(ws, { type: "cli_session_list", sessions: [] });
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
return true;
|
|
591
|
-
}
|
|
505
|
+
// Note: the old `resume_session` and `list_cli_sessions` WS handlers
|
|
506
|
+
// were removed when CLI sessions started being auto-adopted into the
|
|
507
|
+
// unified session list on server start (see sessions.js
|
|
508
|
+
// adoptOrphanedCliSessions). Click a session in the sidebar instead.
|
|
592
509
|
|
|
593
510
|
if (msg.type === "switch_session") {
|
|
594
511
|
if (msg.id && sm.sessions.has(msg.id)) {
|
|
@@ -598,21 +515,24 @@ function attachSessions(ctx) {
|
|
|
598
515
|
// - born-GUI viewed under TUI pref: spawn a transient PTY running
|
|
599
516
|
// `claude --resume <cliSessionId>`; the session record stays GUI
|
|
600
517
|
// so a later pref flip back to GUI just hides the runtime link.
|
|
601
|
-
// - born-TUI viewed under GUI pref:
|
|
602
|
-
//
|
|
603
|
-
//
|
|
604
|
-
//
|
|
518
|
+
// - born-TUI viewed under GUI pref: hydrate session.history from
|
|
519
|
+
// the jsonl transcript and tear down the PTY so the session
|
|
520
|
+
// renders via the SDK chat. The born-TUI marker stays on the
|
|
521
|
+
// record so a later pref flip back to TUI restores the
|
|
522
|
+
// embedded-terminal experience.
|
|
605
523
|
//
|
|
606
524
|
// runtimeMode / runtimeTerminalId are set on the session record so
|
|
607
525
|
// the session_switched and session_list broadcasts surface them to
|
|
608
526
|
// the client without sessions.js needing to know about the pref.
|
|
609
527
|
var xmTarget = sm.sessions.get(msg.id);
|
|
610
528
|
if (xmTarget && (xmTarget.vendor === "claude" || !xmTarget.vendor)) {
|
|
529
|
+
var xmPref = getClaudeOpenModeForWs(ws);
|
|
611
530
|
// Born-TUI session whose PTY is gone (typically after a daemon
|
|
612
|
-
// restart)
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
|
|
531
|
+
// restart) AND the user wants TUI rendering this click. Respawn
|
|
532
|
+
// via `claude --resume <cliSessionId>` so the rest of the pipeline
|
|
533
|
+
// has a live PTY to point at. Skipped when the user's pref is GUI
|
|
534
|
+
// - we'd just tear it back down two lines later.
|
|
535
|
+
if (xmTarget.mode === "tui" && xmTarget.cliSessionId && tm && xmPref === "tui" &&
|
|
616
536
|
(typeof xmTarget.terminalId !== "number" || !tm.has(xmTarget.terminalId))) {
|
|
617
537
|
var rsSid = xmTarget.cliSessionId;
|
|
618
538
|
var rsLocalId = xmTarget.localId;
|
|
@@ -632,11 +552,15 @@ function attachSessions(ctx) {
|
|
|
632
552
|
if (rsTerm) xmTarget.terminalId = rsTerm.id;
|
|
633
553
|
startTitleWatcher(xmTarget);
|
|
634
554
|
}
|
|
635
|
-
var xmPref = getClaudeOpenModeForWs(ws);
|
|
636
555
|
var xmRuntime = computeRuntimeMode(xmTarget, xmPref);
|
|
637
556
|
if (xmRuntime === "gui" && xmTarget.mode === "tui") {
|
|
638
|
-
|
|
639
|
-
|
|
557
|
+
// Born-TUI session under GUI pref. Hydrate history + drop PTY
|
|
558
|
+
// without flipping session.mode, so flipping back to TUI later
|
|
559
|
+
// restores the embedded-terminal rendering. runtimeMode is set
|
|
560
|
+
// to 'gui' explicitly so the client doesn't fall back to
|
|
561
|
+
// session.mode (which is still 'tui').
|
|
562
|
+
prepareTuiSessionForGuiView(xmTarget);
|
|
563
|
+
xmTarget.runtimeMode = "gui";
|
|
640
564
|
xmTarget.runtimeTerminalId = null;
|
|
641
565
|
} else if (xmRuntime === "tui" && xmTarget.mode === "gui" && xmTarget.cliSessionId) {
|
|
642
566
|
var xmRid = spawnRuntimeTuiPty(xmTarget, ws);
|
|
@@ -1754,6 +1678,24 @@ function attachSessions(ctx) {
|
|
|
1754
1678
|
return true;
|
|
1755
1679
|
}
|
|
1756
1680
|
|
|
1681
|
+
if (msg.type === "whats_new_seen") {
|
|
1682
|
+
// Persist that the current user dismissed a What's New entry so it
|
|
1683
|
+
// is not shown again on future connects.
|
|
1684
|
+
var wnUserId = ws._clayUser ? ws._clayUser.id : null;
|
|
1685
|
+
if (!wnUserId) {
|
|
1686
|
+
sendTo(ws, { type: "whats_new_seen_result", ok: false, error: "no_user" });
|
|
1687
|
+
return true;
|
|
1688
|
+
}
|
|
1689
|
+
var wnSvc = require("./whats-new");
|
|
1690
|
+
var wnResult = wnSvc.markSeen(wnUserId, msg.id);
|
|
1691
|
+
if (wnResult && wnResult.ok) {
|
|
1692
|
+
sendTo(ws, { type: "whats_new_seen_result", ok: true, id: msg.id });
|
|
1693
|
+
} else {
|
|
1694
|
+
sendTo(ws, { type: "whats_new_seen_result", ok: false, error: (wnResult && wnResult.error) || "unknown" });
|
|
1695
|
+
}
|
|
1696
|
+
return true;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1757
1699
|
if (msg.type === "set_claude_open_mode") {
|
|
1758
1700
|
// Per-user preference: when Clay opens a Claude session, render it as
|
|
1759
1701
|
// the SDK-driven custom chat ("gui") or as an embedded `claude` TUI
|
package/lib/public/app.js
CHANGED
|
@@ -5,7 +5,7 @@ import { renderMarkdown, highlightCodeBlocks, renderMermaidBlocks, closeMermaidM
|
|
|
5
5
|
import { initSidebar, updatePageTitle, spawnDustParticles } from './modules/sidebar.js';
|
|
6
6
|
import {
|
|
7
7
|
renderSessionList, handleSearchResults, updateSessionPresence,
|
|
8
|
-
updateSessionBadge
|
|
8
|
+
updateSessionBadge
|
|
9
9
|
} from './modules/sidebar-sessions.js';
|
|
10
10
|
import {
|
|
11
11
|
renderIconStrip, getEmojiCategories, updateProjectBadge
|
|
@@ -63,6 +63,8 @@ import { initDebateUi, showDebateConcludeConfirm as _debShowDebateConcludeConfir
|
|
|
63
63
|
import { initLoopUi, updateLoopInputVisibility as _loopUpdateLoopInputVisibility, updateLoopButton as _loopUpdateLoopButton, showLoopBanner as _loopShowLoopBanner, updateLoopBanner as _loopUpdateLoopBanner, updateRalphBars as _loopUpdateRalphBars, showRalphCraftingBar as _loopShowRalphCraftingBar, showRalphApprovalBar as _loopShowRalphApprovalBar, updateRalphApprovalStatus as _loopUpdateRalphApprovalStatus, openRalphPreviewModal as _loopOpenRalphPreviewModal, showExecModal as _loopShowExecModal, closeExecModal as _loopCloseExecModal, updateExecModalStatus as _loopUpdateExecModalStatus } from './modules/app-loop-ui.js';
|
|
64
64
|
import { initLoopWizard, openRalphWizard as _loopOpenRalphWizard, closeRalphWizard as _loopCloseRalphWizard, getWizardSource as _loopGetWizardSource } from './modules/app-loop-wizard.js';
|
|
65
65
|
import { initAppNotifications, handleNotificationsState as _notifHandleState, handleNotificationCreated as _notifHandleCreated, handleNotificationDismissed as _notifHandleDismissed, handleNotificationDismissedAll as _notifHandleDismissedAll } from './modules/app-notifications.js';
|
|
66
|
+
import { initWhatsNew, handleWhatsNewState as _wnHandleState, handleWhatsNewSeenResult as _wnHandleSeenResult } from './modules/whats-new.js';
|
|
67
|
+
import { initWhatsNewArticle, openArticle as openWhatsNewArticle } from './modules/whats-new-article.js';
|
|
66
68
|
import { createStore, store } from './modules/store.js';
|
|
67
69
|
import { initPanels, updateConfigChip as _panUpdateConfigChip, getModelEffortLevels as _panGetModelEffortLevels, accumulateUsage as _panAccumulateUsage, updateUsagePanel as _panUpdateUsagePanel, resetUsage as _panResetUsage, toggleUsagePanel as _panToggleUsagePanel, formatTokens as _panFormatTokens, updateStatusPanel as _panUpdateStatusPanel, requestProcessStats as _panRequestProcessStats, toggleStatusPanel as _panToggleStatusPanel, accumulateContext as _panAccumulateContext, updateContextPanel as _panUpdateContextPanel, resetContext as _panResetContext, resetContextData as _panResetContextData, minimizeContext as _panMinimizeContext, expandContext as _panExpandContext, toggleContextPanel as _panToggleContextPanel, getContextView as _panGetContextView, renderCtxPopover as _panRenderCtxPopover, hideCtxPopover as _panHideCtxPopover, formatBytes as _panFormatBytes, formatUptime as _panFormatUptime, getModelSupportsEffort as _panGetModelSupportsEffort, getSessionUsage, setSessionUsage, getContextData, setContextData, setContextView as _panSetContextView, applyContextView as _panApplyContextView } from './modules/app-panels.js';
|
|
68
70
|
import { initProjects, updateProjectList as _projUpdateProjectList, renderProjectList as _projRenderProjectList, renderTopbarPresence as _projRenderTopbarPresence, switchProject as _projSwitchProject, resetClientState as _projResetClientState, confirmRemoveProject as _projConfirmRemoveProject, handleRemoveProjectCheckResult as _projHandleRemoveProjectCheckResult, handleRemoveProjectResult as _projHandleRemoveProjectResult, openAddProjectModal as _projOpenAddProjectModal, closeAddProjectModal as _projCloseAddProjectModal, handleBrowseDirResult as _projHandleBrowseDirResult, handleAddProjectResult as _projHandleAddProjectResult, handleCloneProgress as _projHandleCloneProgress, showUpdateAvailable as _projShowUpdateAvailable, getCachedProjects, setCachedProjects, getCachedProjectCount, getCachedRemovedProjects, setCachedRemovedProjects } from './modules/app-projects.js';
|
|
@@ -97,7 +99,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
97
99
|
var hamburgerBtn = $("hamburger-btn");
|
|
98
100
|
var sidebarToggleBtn = $("sidebar-toggle-btn");
|
|
99
101
|
var sidebarExpandBtn = $("sidebar-expand-btn");
|
|
100
|
-
var resumeSessionBtn = $("resume-session-btn");
|
|
101
102
|
var imagePreviewBar = $("image-preview-bar");
|
|
102
103
|
var connectOverlay = $("connect-overlay");
|
|
103
104
|
|
|
@@ -376,7 +377,6 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
376
377
|
sidebarExpandBtn: sidebarExpandBtn,
|
|
377
378
|
hamburgerBtn: hamburgerBtn,
|
|
378
379
|
newSessionBtn: newSessionBtn,
|
|
379
|
-
resumeSessionBtn: resumeSessionBtn,
|
|
380
380
|
headerTitleEl: headerTitleEl,
|
|
381
381
|
showConfirm: showConfirm,
|
|
382
382
|
onFilesTabOpen: function () { loadRootDirectory(); },
|
|
@@ -590,6 +590,17 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
590
590
|
// --- Notifications module ---
|
|
591
591
|
initAppNotifications();
|
|
592
592
|
|
|
593
|
+
// --- What's New viewer ---
|
|
594
|
+
initWhatsNewArticle();
|
|
595
|
+
initWhatsNew({
|
|
596
|
+
// "Read more" in the carousel opens the dedicated article viewer
|
|
597
|
+
// for the chosen entry, jumping straight to the body content
|
|
598
|
+
// (skipping the home list).
|
|
599
|
+
onReadMore: function (entryId) {
|
|
600
|
+
if (entryId) openWhatsNewArticle(entryId);
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
|
|
593
604
|
// --- Panels module ---
|
|
594
605
|
initPanels();
|
|
595
606
|
|