agenttop 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setNickname,
|
|
18
18
|
startMcpServer,
|
|
19
19
|
unarchiveSession
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2GBTSKHW.js";
|
|
21
21
|
|
|
22
22
|
// src/index.tsx
|
|
23
23
|
import { readFileSync as readFileSync4 } from "fs";
|
|
@@ -27,7 +27,7 @@ import React16 from "react";
|
|
|
27
27
|
import { render } from "ink";
|
|
28
28
|
|
|
29
29
|
// src/ui/App.tsx
|
|
30
|
-
import { useState as
|
|
30
|
+
import { useState as useState15, useEffect as useEffect9, useCallback as useCallback6 } from "react";
|
|
31
31
|
import { Box as Box15, Text as Text14, useApp, useStdout as useStdout3 } from "ink";
|
|
32
32
|
|
|
33
33
|
// src/config/themes.ts
|
|
@@ -347,132 +347,16 @@ var deriveSeverityColors = (c) => ({
|
|
|
347
347
|
critical: c.critical
|
|
348
348
|
});
|
|
349
349
|
|
|
350
|
-
// src/hooks/installer.ts
|
|
351
|
-
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, chmodSync } from "fs";
|
|
352
|
-
import { join, dirname } from "path";
|
|
353
|
-
import { homedir } from "os";
|
|
354
|
-
import { fileURLToPath } from "url";
|
|
355
|
-
var HOOK_FILENAME = "agenttop-guard.py";
|
|
356
|
-
var SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
|
|
357
|
-
var getHookSource = () => {
|
|
358
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
359
|
-
const srcHooksDir = join(dirname(thisFile), "..", "src", "hooks");
|
|
360
|
-
const distHooksDir = join(dirname(thisFile), "hooks");
|
|
361
|
-
for (const dir of [distHooksDir, srcHooksDir]) {
|
|
362
|
-
const path = join(dir, HOOK_FILENAME);
|
|
363
|
-
if (existsSync(path)) return path;
|
|
364
|
-
}
|
|
365
|
-
const npmGlobalPath = join(dirname(thisFile), "..", "hooks", HOOK_FILENAME);
|
|
366
|
-
if (existsSync(npmGlobalPath)) return npmGlobalPath;
|
|
367
|
-
throw new Error(`cannot find ${HOOK_FILENAME} \u2014 is agenttop installed correctly?`);
|
|
368
|
-
};
|
|
369
|
-
var getHookTarget = () => {
|
|
370
|
-
const claudeHooksDir = join(homedir(), ".claude", "hooks");
|
|
371
|
-
mkdirSync(claudeHooksDir, { recursive: true });
|
|
372
|
-
return join(claudeHooksDir, HOOK_FILENAME);
|
|
373
|
-
};
|
|
374
|
-
var readSettings = () => {
|
|
375
|
-
if (!existsSync(SETTINGS_PATH)) {
|
|
376
|
-
return {};
|
|
377
|
-
}
|
|
378
|
-
try {
|
|
379
|
-
return JSON.parse(readFileSync(SETTINGS_PATH, "utf-8"));
|
|
380
|
-
} catch {
|
|
381
|
-
return {};
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
var writeSettings = (settings) => {
|
|
385
|
-
mkdirSync(dirname(SETTINGS_PATH), { recursive: true });
|
|
386
|
-
writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
387
|
-
};
|
|
388
|
-
var installHooks = () => {
|
|
389
|
-
const source = getHookSource();
|
|
390
|
-
const target = getHookTarget();
|
|
391
|
-
copyFileSync(source, target);
|
|
392
|
-
chmodSync(target, 493);
|
|
393
|
-
const settings = readSettings();
|
|
394
|
-
const hooks = settings.hooks ?? {};
|
|
395
|
-
const postToolUse = hooks.PostToolUse ?? [];
|
|
396
|
-
const hookCommand = target;
|
|
397
|
-
const allToolsMatcher = postToolUse.find(
|
|
398
|
-
(entry) => entry.matcher === "Bash|Read|Grep|Glob|WebFetch|WebSearch"
|
|
399
|
-
);
|
|
400
|
-
if (allToolsMatcher) {
|
|
401
|
-
const alreadyInstalled = allToolsMatcher.hooks.some((h) => h.command.includes("agenttop-guard"));
|
|
402
|
-
if (alreadyInstalled) {
|
|
403
|
-
process.stdout.write("agenttop hooks already installed\n");
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
allToolsMatcher.hooks.push({ type: "command", command: hookCommand });
|
|
407
|
-
} else {
|
|
408
|
-
postToolUse.push({
|
|
409
|
-
matcher: "Bash|Read|Grep|Glob|WebFetch|WebSearch",
|
|
410
|
-
hooks: [{ type: "command", command: hookCommand }]
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
hooks.PostToolUse = postToolUse;
|
|
414
|
-
settings.hooks = hooks;
|
|
415
|
-
writeSettings(settings);
|
|
416
|
-
process.stdout.write(`agenttop hooks installed:
|
|
417
|
-
`);
|
|
418
|
-
process.stdout.write(` hook: ${target}
|
|
419
|
-
`);
|
|
420
|
-
process.stdout.write(` settings: ${SETTINGS_PATH}
|
|
421
|
-
`);
|
|
422
|
-
process.stdout.write(` matcher: PostToolUse (Bash|Read|Grep|Glob|WebFetch|WebSearch)
|
|
423
|
-
`);
|
|
424
|
-
};
|
|
425
|
-
var uninstallHooks = () => {
|
|
426
|
-
const settings = readSettings();
|
|
427
|
-
const hooks = settings.hooks ?? {};
|
|
428
|
-
const postToolUse = hooks.PostToolUse ?? [];
|
|
429
|
-
let removed = false;
|
|
430
|
-
for (const entry of postToolUse) {
|
|
431
|
-
const before = entry.hooks.length;
|
|
432
|
-
entry.hooks = entry.hooks.filter((h) => !h.command.includes("agenttop-guard"));
|
|
433
|
-
if (entry.hooks.length < before) removed = true;
|
|
434
|
-
}
|
|
435
|
-
hooks.PostToolUse = postToolUse.filter((e) => e.hooks.length > 0);
|
|
436
|
-
settings.hooks = hooks;
|
|
437
|
-
writeSettings(settings);
|
|
438
|
-
if (removed) {
|
|
439
|
-
process.stdout.write("agenttop hooks removed from Claude Code settings\n");
|
|
440
|
-
} else {
|
|
441
|
-
process.stdout.write("agenttop hooks were not installed\n");
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
// src/install-mcp.ts
|
|
446
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
447
|
-
import { join as join2 } from "path";
|
|
448
|
-
import { homedir as homedir2 } from "os";
|
|
449
|
-
var installMcpConfig = () => {
|
|
450
|
-
const settingsPath = join2(homedir2(), ".claude", "settings.json");
|
|
451
|
-
let settings = {};
|
|
452
|
-
try {
|
|
453
|
-
settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
454
|
-
} catch {
|
|
455
|
-
}
|
|
456
|
-
const mcpServers = settings.mcpServers ?? {};
|
|
457
|
-
mcpServers.agenttop = { command: "agenttop", args: ["--mcp"] };
|
|
458
|
-
settings.mcpServers = mcpServers;
|
|
459
|
-
mkdirSync2(join2(homedir2(), ".claude"), { recursive: true });
|
|
460
|
-
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
461
|
-
process.stdout.write("agenttop MCP server registered in Claude Code settings\n");
|
|
462
|
-
process.stdout.write(` settings: ${settingsPath}
|
|
463
|
-
`);
|
|
464
|
-
};
|
|
465
|
-
|
|
466
350
|
// src/updates.ts
|
|
467
351
|
import { execSync, exec } from "child_process";
|
|
468
|
-
import { readFileSync
|
|
469
|
-
import { join
|
|
470
|
-
import { fileURLToPath
|
|
352
|
+
import { readFileSync } from "fs";
|
|
353
|
+
import { join, dirname } from "path";
|
|
354
|
+
import { fileURLToPath } from "url";
|
|
471
355
|
var getPackageVersion = () => {
|
|
472
356
|
try {
|
|
473
|
-
const thisFile =
|
|
474
|
-
const pkgPath =
|
|
475
|
-
const pkg = JSON.parse(
|
|
357
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
358
|
+
const pkgPath = join(dirname(thisFile), "..", "package.json");
|
|
359
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
476
360
|
return pkg.version || "0.0.0";
|
|
477
361
|
} catch {
|
|
478
362
|
return "0.0.0";
|
|
@@ -606,30 +490,18 @@ var formatTokens = (n) => {
|
|
|
606
490
|
return String(n);
|
|
607
491
|
};
|
|
608
492
|
var truncate = (s, max) => s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
|
|
609
|
-
var getDisplayName = (session) => {
|
|
610
|
-
if (session.nickname) return session.nickname;
|
|
611
|
-
if (session.cwd) {
|
|
612
|
-
const parts = session.cwd.replace(/\/+$/, "").split("/");
|
|
613
|
-
return parts[parts.length - 1] || session.slug;
|
|
614
|
-
}
|
|
615
|
-
if (session.project) {
|
|
616
|
-
const parts = session.project.replace(/\/+$/, "").split("/");
|
|
617
|
-
return parts[parts.length - 1] || session.slug;
|
|
618
|
-
}
|
|
619
|
-
return session.slug;
|
|
620
|
-
};
|
|
621
493
|
var SIDEBAR_WIDTH = 30;
|
|
622
494
|
var INNER_WIDTH = SIDEBAR_WIDTH - 4;
|
|
623
|
-
var
|
|
495
|
+
var LINES_PER_ITEM = 3;
|
|
624
496
|
var SessionList = React2.memo(
|
|
625
|
-
({
|
|
497
|
+
({ visibleItems, selectedIndex, focused, height, filter, viewingArchive, totalSessions }) => {
|
|
626
498
|
const availableRows = height - 2;
|
|
627
|
-
const maxVisible = Math.max(1, Math.floor((availableRows + 1) /
|
|
499
|
+
const maxVisible = Math.max(1, Math.floor((availableRows + 1) / LINES_PER_ITEM));
|
|
628
500
|
const halfView = Math.floor(maxVisible / 2);
|
|
629
|
-
const scrollStart = Math.max(0, Math.min(selectedIndex - halfView,
|
|
501
|
+
const scrollStart = Math.max(0, Math.min(selectedIndex - halfView, visibleItems.length - maxVisible));
|
|
630
502
|
const start = Math.max(0, scrollStart);
|
|
631
|
-
const visible =
|
|
632
|
-
const canScroll =
|
|
503
|
+
const visible = visibleItems.slice(start, start + maxVisible);
|
|
504
|
+
const canScroll = visibleItems.length > maxVisible;
|
|
633
505
|
return /* @__PURE__ */ jsxs2(
|
|
634
506
|
Box2,
|
|
635
507
|
{
|
|
@@ -651,21 +523,104 @@ var SessionList = React2.memo(
|
|
|
651
523
|
canScroll && /* @__PURE__ */ jsxs2(Text2, { color: colors.muted, children: [
|
|
652
524
|
selectedIndex + 1,
|
|
653
525
|
"/",
|
|
654
|
-
|
|
655
|
-
] })
|
|
526
|
+
visibleItems.length
|
|
527
|
+
] }),
|
|
528
|
+
!canScroll && totalSessions > 0 && /* @__PURE__ */ jsx2(Text2, { color: colors.muted, children: totalSessions })
|
|
656
529
|
] }),
|
|
657
|
-
|
|
658
|
-
visible.map((
|
|
530
|
+
visibleItems.length === 0 && /* @__PURE__ */ jsx2(Box2, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx2(Text2, { color: colors.muted, italic: true, children: filter ? "No matches" : viewingArchive ? "No archived" : "No sessions" }) }),
|
|
531
|
+
visible.map((item, vi) => {
|
|
659
532
|
const realIdx = start + vi;
|
|
660
533
|
const isSelected = realIdx === selectedIndex;
|
|
534
|
+
if (item.type === "group") {
|
|
535
|
+
const g = item.group;
|
|
536
|
+
const arrow = g.expanded ? "\u25BE" : "\u25B8";
|
|
537
|
+
const dotColor2 = g.isActive ? colors.success : colors.muted;
|
|
538
|
+
const statusDot2 = g.isActive ? "\u25CF" : "\u25CB";
|
|
539
|
+
const nameColor2 = isSelected ? colors.bright : g.isActive ? colors.secondary : colors.text;
|
|
540
|
+
const label2 = truncate(`${g.key} (${g.sessions.length})`, INNER_WIDTH - 4);
|
|
541
|
+
const model2 = formatModel(g.latestModel);
|
|
542
|
+
return /* @__PURE__ */ jsxs2(
|
|
543
|
+
Box2,
|
|
544
|
+
{
|
|
545
|
+
flexDirection: "column",
|
|
546
|
+
paddingX: 1,
|
|
547
|
+
backgroundColor: isSelected ? colors.selected : void 0,
|
|
548
|
+
children: [
|
|
549
|
+
/* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, wrap: "truncate", children: [
|
|
550
|
+
arrow,
|
|
551
|
+
" ",
|
|
552
|
+
/* @__PURE__ */ jsx2(Text2, { color: dotColor2, children: statusDot2 }),
|
|
553
|
+
" ",
|
|
554
|
+
label2
|
|
555
|
+
] }),
|
|
556
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
557
|
+
" ",
|
|
558
|
+
model2,
|
|
559
|
+
" ",
|
|
560
|
+
formatTokens(g.totalInputTokens),
|
|
561
|
+
"in ",
|
|
562
|
+
formatTokens(g.totalOutputTokens),
|
|
563
|
+
"out"
|
|
564
|
+
] }),
|
|
565
|
+
vi < visible.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: colors.border, children: "\u2500".repeat(INNER_WIDTH) })
|
|
566
|
+
]
|
|
567
|
+
},
|
|
568
|
+
`group-${g.key}`
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const session = item.type === "session" ? item.session : item.session;
|
|
661
572
|
const isActive = session.pid !== null;
|
|
662
|
-
const indicator = isSelected ? "\u25B8" : " ";
|
|
663
573
|
const statusDot = isActive ? "\u25CF" : "\u25CB";
|
|
664
|
-
const
|
|
574
|
+
const dotColor = isActive ? colors.success : colors.muted;
|
|
665
575
|
const totalIn = session.usage.inputTokens + session.usage.cacheReadTokens;
|
|
666
576
|
const model = formatModel(session.model);
|
|
577
|
+
if (item.type === "session") {
|
|
578
|
+
const nameColor2 = isSelected ? colors.bright : isActive ? colors.secondary : colors.muted;
|
|
579
|
+
const displayName2 = truncate(session.slug, INNER_WIDTH - 6);
|
|
580
|
+
return /* @__PURE__ */ jsxs2(
|
|
581
|
+
Box2,
|
|
582
|
+
{
|
|
583
|
+
flexDirection: "column",
|
|
584
|
+
paddingX: 1,
|
|
585
|
+
backgroundColor: isSelected ? colors.selected : void 0,
|
|
586
|
+
children: [
|
|
587
|
+
/* @__PURE__ */ jsxs2(Text2, { color: nameColor2, bold: isSelected, wrap: "truncate", children: [
|
|
588
|
+
" ",
|
|
589
|
+
" ",
|
|
590
|
+
/* @__PURE__ */ jsx2(Text2, { color: dotColor, children: statusDot }),
|
|
591
|
+
" ",
|
|
592
|
+
displayName2
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsxs2(Text2, { color: colors.muted, wrap: "truncate", children: [
|
|
595
|
+
" ",
|
|
596
|
+
model,
|
|
597
|
+
" ",
|
|
598
|
+
formatTokens(totalIn),
|
|
599
|
+
"in ",
|
|
600
|
+
formatTokens(session.usage.outputTokens),
|
|
601
|
+
"out"
|
|
602
|
+
] }),
|
|
603
|
+
vi < visible.length - 1 && /* @__PURE__ */ jsx2(Text2, { color: colors.border, children: "\u2500".repeat(INNER_WIDTH) })
|
|
604
|
+
]
|
|
605
|
+
},
|
|
606
|
+
session.sessionId
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
const indicator = isSelected ? "\u25B8" : " ";
|
|
667
610
|
const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.text;
|
|
668
|
-
const
|
|
611
|
+
const getDisplayName2 = (s) => {
|
|
612
|
+
if (s.nickname) return s.nickname;
|
|
613
|
+
if (s.cwd) {
|
|
614
|
+
const parts = s.cwd.replace(/\/+$/, "").split("/");
|
|
615
|
+
return parts[parts.length - 1] || s.slug;
|
|
616
|
+
}
|
|
617
|
+
if (s.project) {
|
|
618
|
+
const parts = s.project.replace(/\/+$/, "").split("/");
|
|
619
|
+
return parts[parts.length - 1] || s.slug;
|
|
620
|
+
}
|
|
621
|
+
return s.slug;
|
|
622
|
+
};
|
|
623
|
+
const displayName = truncate(getDisplayName2(session), INNER_WIDTH - 4);
|
|
669
624
|
return /* @__PURE__ */ jsxs2(
|
|
670
625
|
Box2,
|
|
671
626
|
{
|
|
@@ -705,6 +660,16 @@ var SessionList = React2.memo(
|
|
|
705
660
|
import React3 from "react";
|
|
706
661
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
707
662
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
663
|
+
var TAG_COLORS = [
|
|
664
|
+
"#61AFEF",
|
|
665
|
+
"#98C379",
|
|
666
|
+
"#C678DD",
|
|
667
|
+
"#E5C07B",
|
|
668
|
+
"#E06C75",
|
|
669
|
+
"#56B6C2",
|
|
670
|
+
"#D19A66",
|
|
671
|
+
"#BE5046"
|
|
672
|
+
];
|
|
708
673
|
var formatTime = (ts) => {
|
|
709
674
|
const d = new Date(ts);
|
|
710
675
|
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
@@ -732,7 +697,7 @@ var summarizeInput = (call) => {
|
|
|
732
697
|
}
|
|
733
698
|
};
|
|
734
699
|
var ActivityFeed = React3.memo(
|
|
735
|
-
({ events, sessionSlug, focused, height, scrollOffset, filter }) => {
|
|
700
|
+
({ events, sessionSlug, sessionId, isActive, focused, height, scrollOffset, filter, merged, mergedSessions }) => {
|
|
736
701
|
const viewportRows = height - 2;
|
|
737
702
|
const totalEvents = events.length;
|
|
738
703
|
const start = Math.max(0, totalEvents - viewportRows - scrollOffset);
|
|
@@ -741,6 +706,12 @@ var ActivityFeed = React3.memo(
|
|
|
741
706
|
const isAtBottom = scrollOffset === 0;
|
|
742
707
|
const isAtTop = start === 0;
|
|
743
708
|
const canScroll = totalEvents > viewportRows;
|
|
709
|
+
const slugColorMap = /* @__PURE__ */ new Map();
|
|
710
|
+
if (merged && mergedSessions) {
|
|
711
|
+
mergedSessions.forEach((s, i) => {
|
|
712
|
+
slugColorMap.set(s.sessionId, TAG_COLORS[i % TAG_COLORS.length]);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
744
715
|
return /* @__PURE__ */ jsxs3(
|
|
745
716
|
Box3,
|
|
746
717
|
{
|
|
@@ -757,6 +728,7 @@ var ActivityFeed = React3.memo(
|
|
|
757
728
|
sessionSlug,
|
|
758
729
|
")"
|
|
759
730
|
] }),
|
|
731
|
+
merged && /* @__PURE__ */ jsx3(Text3, { color: colors.accent, children: " [merged]" }),
|
|
760
732
|
filter && /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
761
733
|
" [",
|
|
762
734
|
filter.length > 10 ? filter.slice(0, 9) + "\u2026" : filter,
|
|
@@ -771,19 +743,46 @@ var ActivityFeed = React3.memo(
|
|
|
771
743
|
"]"
|
|
772
744
|
] })
|
|
773
745
|
] }),
|
|
774
|
-
visible.length === 0 && /* @__PURE__ */ jsx3(Box3, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx3(Text3, { color: colors.muted, italic: true, children: sessionSlug ? "Waiting for activity..." : "Select a session" }) }),
|
|
775
|
-
visible.map((event, i) =>
|
|
746
|
+
visible.length === 0 && /* @__PURE__ */ jsx3(Box3, { paddingX: 1, paddingY: 1, children: /* @__PURE__ */ jsx3(Text3, { color: colors.muted, italic: true, children: sessionSlug || merged ? "Waiting for activity..." : "Select a session" }) }),
|
|
747
|
+
visible.map((event, i) => {
|
|
748
|
+
const tag = merged ? event.slug.slice(0, 4) : null;
|
|
749
|
+
const tagColor = merged ? slugColorMap.get(event.sessionId) || colors.muted : void 0;
|
|
750
|
+
return /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, children: [
|
|
751
|
+
tag && /* @__PURE__ */ jsx3(Text3, { color: tagColor, children: tag.padEnd(5) }),
|
|
752
|
+
/* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
753
|
+
formatTime(event.timestamp),
|
|
754
|
+
" "
|
|
755
|
+
] }),
|
|
756
|
+
/* @__PURE__ */ jsx3(Text3, { color: getToolColor(event.toolName), bold: true, children: event.toolName.padEnd(8) }),
|
|
757
|
+
/* @__PURE__ */ jsxs3(Text3, { color: colors.text, children: [
|
|
758
|
+
" ",
|
|
759
|
+
summarizeInput(event)
|
|
760
|
+
] })
|
|
761
|
+
] }, `${event.timestamp}-${i}`);
|
|
762
|
+
}),
|
|
763
|
+
focused && canScroll && !isAtTop && visible.length > 0 && /* @__PURE__ */ jsx3(Box3, { paddingX: 1, justifyContent: "flex-end", children: /* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: isAtBottom ? "" : "G:bottom " }) }),
|
|
764
|
+
!merged && sessionId && isActive === false && /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, flexDirection: "column", children: [
|
|
765
|
+
/* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: " " }),
|
|
776
766
|
/* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
" ",
|
|
783
|
-
summarizeInput(event)
|
|
767
|
+
"resume: ",
|
|
768
|
+
/* @__PURE__ */ jsxs3(Text3, { color: colors.text, children: [
|
|
769
|
+
"claude --resume ",
|
|
770
|
+
sessionId
|
|
771
|
+
] })
|
|
784
772
|
] })
|
|
785
|
-
] }
|
|
786
|
-
|
|
773
|
+
] }),
|
|
774
|
+
merged && mergedSessions && mergedSessions.some((s) => s.pid === null) && /* @__PURE__ */ jsxs3(Box3, { paddingX: 1, flexDirection: "column", children: [
|
|
775
|
+
/* @__PURE__ */ jsx3(Text3, { color: colors.muted, children: " " }),
|
|
776
|
+
mergedSessions.filter((s) => s.pid === null).map((s) => /* @__PURE__ */ jsxs3(Text3, { color: colors.muted, children: [
|
|
777
|
+
"resume ",
|
|
778
|
+
s.slug.slice(0, 8),
|
|
779
|
+
": ",
|
|
780
|
+
/* @__PURE__ */ jsxs3(Text3, { color: colors.text, children: [
|
|
781
|
+
"claude --resume ",
|
|
782
|
+
s.sessionId
|
|
783
|
+
] })
|
|
784
|
+
] }, s.sessionId))
|
|
785
|
+
] })
|
|
787
786
|
]
|
|
788
787
|
}
|
|
789
788
|
);
|
|
@@ -1898,6 +1897,8 @@ var SplitPanel = React14.memo(
|
|
|
1898
1897
|
{
|
|
1899
1898
|
events: leftEvents,
|
|
1900
1899
|
sessionSlug: leftSession?.slug ?? null,
|
|
1900
|
+
sessionId: leftSession?.sessionId,
|
|
1901
|
+
isActive: leftSession ? leftSession.pid !== null : void 0,
|
|
1901
1902
|
focused: activePanel === "left",
|
|
1902
1903
|
height,
|
|
1903
1904
|
scrollOffset: leftScroll,
|
|
@@ -1909,6 +1910,8 @@ var SplitPanel = React14.memo(
|
|
|
1909
1910
|
{
|
|
1910
1911
|
events: rightEvents,
|
|
1911
1912
|
sessionSlug: rightSession?.slug ?? null,
|
|
1913
|
+
sessionId: rightSession?.sessionId,
|
|
1914
|
+
isActive: rightSession ? rightSession.pid !== null : void 0,
|
|
1912
1915
|
focused: activePanel === "right",
|
|
1913
1916
|
height,
|
|
1914
1917
|
scrollOffset: rightScroll,
|
|
@@ -1938,9 +1941,69 @@ var SplitPanel = React14.memo(
|
|
|
1938
1941
|
import { useState as useState8, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
1939
1942
|
var ACTIVE_POLL_MS = 1e4;
|
|
1940
1943
|
var IDLE_POLL_MS = 3e4;
|
|
1944
|
+
var getDisplayName = (session) => {
|
|
1945
|
+
if (session.nickname) return session.nickname;
|
|
1946
|
+
if (session.cwd) {
|
|
1947
|
+
const parts = session.cwd.replace(/\/+$/, "").split("/");
|
|
1948
|
+
return parts[parts.length - 1] || session.slug;
|
|
1949
|
+
}
|
|
1950
|
+
if (session.project) {
|
|
1951
|
+
const parts = session.project.replace(/\/+$/, "").split("/");
|
|
1952
|
+
return parts[parts.length - 1] || session.slug;
|
|
1953
|
+
}
|
|
1954
|
+
return session.slug;
|
|
1955
|
+
};
|
|
1956
|
+
var buildGroups = (sessions, expandedKeys) => {
|
|
1957
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1958
|
+
for (const s of sessions) {
|
|
1959
|
+
const name = getDisplayName(s);
|
|
1960
|
+
const list = byName.get(name);
|
|
1961
|
+
if (list) list.push(s);
|
|
1962
|
+
else byName.set(name, [s]);
|
|
1963
|
+
}
|
|
1964
|
+
const groups = [];
|
|
1965
|
+
for (const [key, list] of byName) {
|
|
1966
|
+
list.sort((a, b) => b.lastActivity - a.lastActivity);
|
|
1967
|
+
const totalIn = list.reduce(
|
|
1968
|
+
(sum, s) => sum + s.usage.inputTokens + s.usage.cacheReadTokens,
|
|
1969
|
+
0
|
|
1970
|
+
);
|
|
1971
|
+
const totalOut = list.reduce((sum, s) => sum + s.usage.outputTokens, 0);
|
|
1972
|
+
groups.push({
|
|
1973
|
+
key,
|
|
1974
|
+
sessions: list,
|
|
1975
|
+
expanded: expandedKeys.has(key),
|
|
1976
|
+
totalInputTokens: totalIn,
|
|
1977
|
+
totalOutputTokens: totalOut,
|
|
1978
|
+
latestModel: list[0].model,
|
|
1979
|
+
isActive: list.some((s) => s.pid !== null),
|
|
1980
|
+
latestActivity: list[0].lastActivity,
|
|
1981
|
+
earliestStart: Math.min(...list.map((s) => s.startTime))
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
groups.sort((a, b) => b.latestActivity - a.latestActivity);
|
|
1985
|
+
return groups;
|
|
1986
|
+
};
|
|
1987
|
+
var buildVisibleItems = (groups) => {
|
|
1988
|
+
const items = [];
|
|
1989
|
+
for (const group of groups) {
|
|
1990
|
+
if (group.sessions.length === 1) {
|
|
1991
|
+
items.push({ type: "ungrouped", session: group.sessions[0] });
|
|
1992
|
+
} else {
|
|
1993
|
+
items.push({ type: "group", group });
|
|
1994
|
+
if (group.expanded) {
|
|
1995
|
+
for (const session of group.sessions) {
|
|
1996
|
+
items.push({ type: "session", session, groupKey: group.key });
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
return items;
|
|
2002
|
+
};
|
|
1941
2003
|
var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
1942
2004
|
const [sessions, setSessions] = useState8([]);
|
|
1943
2005
|
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
2006
|
+
const [expandedKeys, setExpandedKeys] = useState8(/* @__PURE__ */ new Set());
|
|
1944
2007
|
const usageOverrides = useRef3(/* @__PURE__ */ new Map());
|
|
1945
2008
|
const refresh = useCallback2(() => {
|
|
1946
2009
|
const found = discoverSessions(allUsers);
|
|
@@ -1982,16 +2045,28 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
|
1982
2045
|
const interval = setInterval(refresh, pollMs);
|
|
1983
2046
|
return () => clearInterval(interval);
|
|
1984
2047
|
}, [refresh, sessions.length > 0]);
|
|
1985
|
-
const
|
|
2048
|
+
const groups = buildGroups(sessions, expandedKeys);
|
|
2049
|
+
const visibleItems = buildVisibleItems(groups);
|
|
2050
|
+
const selectedItem = visibleItems[selectedIndex] ?? null;
|
|
2051
|
+
const selectedSession = selectedItem?.type === "ungrouped" ? selectedItem.session : selectedItem?.type === "session" ? selectedItem.session : null;
|
|
2052
|
+
const selectedGroup = selectedItem?.type === "group" ? selectedItem.group : null;
|
|
1986
2053
|
const selectNext = useCallback2(() => {
|
|
1987
|
-
setSelectedIndex((i) => Math.min(i + 1, Math.max(0,
|
|
1988
|
-
}, [
|
|
2054
|
+
setSelectedIndex((i) => Math.min(i + 1, Math.max(0, visibleItems.length - 1)));
|
|
2055
|
+
}, [visibleItems.length]);
|
|
1989
2056
|
const selectPrev = useCallback2(() => {
|
|
1990
2057
|
setSelectedIndex((i) => Math.max(i - 1, 0));
|
|
1991
2058
|
}, []);
|
|
1992
2059
|
const selectIndex = useCallback2((i) => {
|
|
1993
2060
|
setSelectedIndex(i);
|
|
1994
2061
|
}, []);
|
|
2062
|
+
const toggleExpand = useCallback2((groupKey) => {
|
|
2063
|
+
setExpandedKeys((prev) => {
|
|
2064
|
+
const next = new Set(prev);
|
|
2065
|
+
if (next.has(groupKey)) next.delete(groupKey);
|
|
2066
|
+
else next.add(groupKey);
|
|
2067
|
+
return next;
|
|
2068
|
+
});
|
|
2069
|
+
}, []);
|
|
1995
2070
|
const addUsage = useCallback2((sessionId, usage) => {
|
|
1996
2071
|
const existing = usageOverrides.current.get(sessionId);
|
|
1997
2072
|
if (existing) {
|
|
@@ -2005,7 +2080,20 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
|
|
|
2005
2080
|
usageOverrides.current.set(sessionId, usage);
|
|
2006
2081
|
}
|
|
2007
2082
|
}, []);
|
|
2008
|
-
return {
|
|
2083
|
+
return {
|
|
2084
|
+
sessions,
|
|
2085
|
+
groups,
|
|
2086
|
+
visibleItems,
|
|
2087
|
+
selectedSession,
|
|
2088
|
+
selectedGroup,
|
|
2089
|
+
selectedIndex,
|
|
2090
|
+
selectNext,
|
|
2091
|
+
selectPrev,
|
|
2092
|
+
selectIndex,
|
|
2093
|
+
toggleExpand,
|
|
2094
|
+
refresh,
|
|
2095
|
+
addUsage
|
|
2096
|
+
};
|
|
2009
2097
|
};
|
|
2010
2098
|
|
|
2011
2099
|
// src/ui/hooks/useActivityStream.ts
|
|
@@ -2014,20 +2102,28 @@ var MAX_EVENTS = 200;
|
|
|
2014
2102
|
var useActivityStream = (session, allUsers) => {
|
|
2015
2103
|
const [events, setEvents] = useState9([]);
|
|
2016
2104
|
const watcherRef = useRef4(null);
|
|
2105
|
+
const sessions = session === null ? [] : Array.isArray(session) ? session : [session];
|
|
2106
|
+
const sessionKey = sessions.map((s) => s.sessionId).sort().join(",");
|
|
2107
|
+
const sessionIdSet = new Set(sessions.map((s) => s.sessionId));
|
|
2017
2108
|
useEffect6(() => {
|
|
2018
2109
|
setEvents([]);
|
|
2019
|
-
if (
|
|
2110
|
+
if (sessions.length === 0) return;
|
|
2020
2111
|
const existingCalls = [];
|
|
2021
2112
|
const tempWatcher = new Watcher(() => {
|
|
2022
2113
|
}, allUsers);
|
|
2023
|
-
|
|
2114
|
+
const allFiles = sessions.flatMap((s) => s.outputFiles);
|
|
2115
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2116
|
+
for (const file of allFiles) {
|
|
2117
|
+
if (seen.has(file)) continue;
|
|
2118
|
+
seen.add(file);
|
|
2024
2119
|
existingCalls.push(...tempWatcher.readExisting(file));
|
|
2025
2120
|
}
|
|
2121
|
+
existingCalls.sort((a, b) => a.timestamp - b.timestamp);
|
|
2026
2122
|
setEvents(existingCalls.slice(-MAX_EVENTS));
|
|
2027
2123
|
const handler = (calls) => {
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2030
|
-
setEvents((prev) => [...prev, ...
|
|
2124
|
+
const matched = calls.filter((c) => sessionIdSet.has(c.sessionId));
|
|
2125
|
+
if (matched.length === 0) return;
|
|
2126
|
+
setEvents((prev) => [...prev, ...matched].slice(-MAX_EVENTS));
|
|
2031
2127
|
};
|
|
2032
2128
|
const watcher = new Watcher(handler, allUsers);
|
|
2033
2129
|
watcherRef.current = watcher;
|
|
@@ -2036,7 +2132,7 @@ var useActivityStream = (session, allUsers) => {
|
|
|
2036
2132
|
watcher.stop();
|
|
2037
2133
|
watcherRef.current = null;
|
|
2038
2134
|
};
|
|
2039
|
-
}, [
|
|
2135
|
+
}, [sessionKey, allUsers]);
|
|
2040
2136
|
return events;
|
|
2041
2137
|
};
|
|
2042
2138
|
|
|
@@ -2088,14 +2184,14 @@ var notify = (alert, config) => {
|
|
|
2088
2184
|
};
|
|
2089
2185
|
|
|
2090
2186
|
// src/alerts/logger.ts
|
|
2091
|
-
import { appendFileSync, mkdirSync
|
|
2092
|
-
import { dirname as
|
|
2187
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
2188
|
+
import { dirname as dirname2 } from "path";
|
|
2093
2189
|
var AlertLogger = class {
|
|
2094
2190
|
logPath;
|
|
2095
2191
|
writeCount = 0;
|
|
2096
2192
|
constructor(config) {
|
|
2097
2193
|
this.logPath = resolveAlertLogPath(config);
|
|
2098
|
-
|
|
2194
|
+
mkdirSync(dirname2(this.logPath), { recursive: true });
|
|
2099
2195
|
}
|
|
2100
2196
|
log(alert) {
|
|
2101
2197
|
const entry = {
|
|
@@ -2313,6 +2409,10 @@ var useKeyHandler = (deps) => {
|
|
|
2313
2409
|
}
|
|
2314
2410
|
return;
|
|
2315
2411
|
}
|
|
2412
|
+
if (matchKey(d.kb.detail, input, key) && d.activePanel === "sessions" && d.selectedGroup) {
|
|
2413
|
+
d.toggleExpand(d.selectedGroup.key);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2316
2416
|
if (matchKey(d.kb.detail, input, key) && d.selectedSession) {
|
|
2317
2417
|
if (d.splitMode) {
|
|
2318
2418
|
if (d.activePanel === "left") {
|
|
@@ -2450,98 +2550,133 @@ var useUpdateChecker = (disabled, checkOnLaunch, checkInterval) => {
|
|
|
2450
2550
|
return updateInfo;
|
|
2451
2551
|
};
|
|
2452
2552
|
|
|
2453
|
-
// src/ui/
|
|
2454
|
-
import {
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2553
|
+
// src/ui/hooks/useSetupFlow.ts
|
|
2554
|
+
import { useState as useState13, useCallback as useCallback4 } from "react";
|
|
2555
|
+
|
|
2556
|
+
// src/hooks/installer.ts
|
|
2557
|
+
import { existsSync, readFileSync as readFileSync2, writeFileSync, copyFileSync, mkdirSync as mkdirSync2, chmodSync } from "fs";
|
|
2558
|
+
import { join as join2, dirname as dirname3 } from "path";
|
|
2559
|
+
import { homedir } from "os";
|
|
2560
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2561
|
+
var HOOK_FILENAME = "agenttop-guard.py";
|
|
2562
|
+
var SETTINGS_PATH = join2(homedir(), ".claude", "settings.json");
|
|
2563
|
+
var getHookSource = () => {
|
|
2564
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
2565
|
+
const srcHooksDir = join2(dirname3(thisFile), "..", "src", "hooks");
|
|
2566
|
+
const distHooksDir = join2(dirname3(thisFile), "hooks");
|
|
2567
|
+
for (const dir of [distHooksDir, srcHooksDir]) {
|
|
2568
|
+
const path = join2(dir, HOOK_FILENAME);
|
|
2569
|
+
if (existsSync(path)) return path;
|
|
2570
|
+
}
|
|
2571
|
+
const npmGlobalPath = join2(dirname3(thisFile), "..", "hooks", HOOK_FILENAME);
|
|
2572
|
+
if (existsSync(npmGlobalPath)) return npmGlobalPath;
|
|
2573
|
+
throw new Error(`cannot find ${HOOK_FILENAME} \u2014 is agenttop installed correctly?`);
|
|
2574
|
+
};
|
|
2575
|
+
var getHookTarget = () => {
|
|
2576
|
+
const claudeHooksDir = join2(homedir(), ".claude", "hooks");
|
|
2577
|
+
mkdirSync2(claudeHooksDir, { recursive: true });
|
|
2578
|
+
return join2(claudeHooksDir, HOOK_FILENAME);
|
|
2579
|
+
};
|
|
2580
|
+
var readSettings = () => {
|
|
2581
|
+
if (!existsSync(SETTINGS_PATH)) {
|
|
2582
|
+
return {};
|
|
2583
|
+
}
|
|
2584
|
+
try {
|
|
2585
|
+
return JSON.parse(readFileSync2(SETTINGS_PATH, "utf-8"));
|
|
2586
|
+
} catch {
|
|
2587
|
+
return {};
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2590
|
+
var writeSettings = (settings) => {
|
|
2591
|
+
mkdirSync2(dirname3(SETTINGS_PATH), { recursive: true });
|
|
2592
|
+
writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
2593
|
+
};
|
|
2594
|
+
var installHooks = () => {
|
|
2595
|
+
const source = getHookSource();
|
|
2596
|
+
const target = getHookTarget();
|
|
2597
|
+
copyFileSync(source, target);
|
|
2598
|
+
chmodSync(target, 493);
|
|
2599
|
+
const settings = readSettings();
|
|
2600
|
+
const hooks = settings.hooks ?? {};
|
|
2601
|
+
const postToolUse = hooks.PostToolUse ?? [];
|
|
2602
|
+
const hookCommand = target;
|
|
2603
|
+
const allToolsMatcher = postToolUse.find(
|
|
2604
|
+
(entry) => entry.matcher === "Bash|Read|Grep|Glob|WebFetch|WebSearch"
|
|
2605
|
+
);
|
|
2606
|
+
if (allToolsMatcher) {
|
|
2607
|
+
const alreadyInstalled = allToolsMatcher.hooks.some((h) => h.command.includes("agenttop-guard"));
|
|
2608
|
+
if (alreadyInstalled) {
|
|
2609
|
+
process.stdout.write("agenttop hooks already installed\n");
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
allToolsMatcher.hooks.push({ type: "command", command: hookCommand });
|
|
2613
|
+
} else {
|
|
2614
|
+
postToolUse.push({
|
|
2615
|
+
matcher: "Bash|Read|Grep|Glob|WebFetch|WebSearch",
|
|
2616
|
+
hooks: [{ type: "command", command: hookCommand }]
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
hooks.PostToolUse = postToolUse;
|
|
2620
|
+
settings.hooks = hooks;
|
|
2621
|
+
writeSettings(settings);
|
|
2622
|
+
process.stdout.write(`agenttop hooks installed:
|
|
2623
|
+
`);
|
|
2624
|
+
process.stdout.write(` hook: ${target}
|
|
2625
|
+
`);
|
|
2626
|
+
process.stdout.write(` settings: ${SETTINGS_PATH}
|
|
2627
|
+
`);
|
|
2628
|
+
process.stdout.write(` matcher: PostToolUse (Bash|Read|Grep|Glob|WebFetch|WebSearch)
|
|
2629
|
+
`);
|
|
2630
|
+
};
|
|
2631
|
+
var uninstallHooks = () => {
|
|
2632
|
+
const settings = readSettings();
|
|
2633
|
+
const hooks = settings.hooks ?? {};
|
|
2634
|
+
const postToolUse = hooks.PostToolUse ?? [];
|
|
2635
|
+
let removed = false;
|
|
2636
|
+
for (const entry of postToolUse) {
|
|
2637
|
+
const before = entry.hooks.length;
|
|
2638
|
+
entry.hooks = entry.hooks.filter((h) => !h.command.includes("agenttop-guard"));
|
|
2639
|
+
if (entry.hooks.length < before) removed = true;
|
|
2640
|
+
}
|
|
2641
|
+
hooks.PostToolUse = postToolUse.filter((e) => e.hooks.length > 0);
|
|
2642
|
+
settings.hooks = hooks;
|
|
2643
|
+
writeSettings(settings);
|
|
2644
|
+
if (removed) {
|
|
2645
|
+
process.stdout.write("agenttop hooks removed from Claude Code settings\n");
|
|
2646
|
+
} else {
|
|
2647
|
+
process.stdout.write("agenttop hooks were not installed\n");
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
|
|
2651
|
+
// src/install-mcp.ts
|
|
2652
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
2653
|
+
import { join as join3 } from "path";
|
|
2654
|
+
import { homedir as homedir2 } from "os";
|
|
2655
|
+
var installMcpConfig = () => {
|
|
2656
|
+
const settingsPath = join3(homedir2(), ".claude", "settings.json");
|
|
2657
|
+
let settings = {};
|
|
2658
|
+
try {
|
|
2659
|
+
settings = JSON.parse(readFileSync3(settingsPath, "utf-8"));
|
|
2660
|
+
} catch {
|
|
2661
|
+
}
|
|
2662
|
+
const mcpServers = settings.mcpServers ?? {};
|
|
2663
|
+
mcpServers.agenttop = { command: "agenttop", args: ["--mcp"] };
|
|
2664
|
+
settings.mcpServers = mcpServers;
|
|
2665
|
+
mkdirSync3(join3(homedir2(), ".claude"), { recursive: true });
|
|
2666
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2667
|
+
process.stdout.write("agenttop MCP server registered in Claude Code settings\n");
|
|
2668
|
+
process.stdout.write(` settings: ${settingsPath}
|
|
2669
|
+
`);
|
|
2670
|
+
};
|
|
2671
|
+
|
|
2672
|
+
// src/ui/hooks/useSetupFlow.ts
|
|
2673
|
+
var useSetupFlow = (initialConfig, firstRun) => {
|
|
2459
2674
|
const [liveConfig, setLiveConfig] = useState13(initialConfig);
|
|
2460
|
-
const kb = liveConfig.keybindings;
|
|
2461
|
-
const [activePanel, setActivePanel] = useState13("sessions");
|
|
2462
|
-
const [activityScroll, setActivityScroll] = useState13(0);
|
|
2463
|
-
const [inputMode, setInputMode] = useState13("normal");
|
|
2464
2675
|
const [showSetup, setShowSetup] = useState13(firstRun);
|
|
2465
2676
|
const [showThemePicker, setShowThemePicker] = useState13(false);
|
|
2466
2677
|
const [showTour, setShowTour] = useState13(false);
|
|
2467
|
-
const [filter, setFilter] = useState13("");
|
|
2468
|
-
const [activityFilter, setActivityFilter] = useState13("");
|
|
2469
|
-
const [updateStatus, setUpdateStatus] = useState13("");
|
|
2470
|
-
const [showDetail, setShowDetail] = useState13(false);
|
|
2471
2678
|
const [showSettings, setShowSettings] = useState13(false);
|
|
2472
2679
|
const [showThemeMenu, setShowThemeMenu] = useState13(false);
|
|
2473
|
-
const [viewingArchive, setViewingArchive] = useState13(false);
|
|
2474
|
-
const [confirmAction, setConfirmAction] = useState13(
|
|
2475
|
-
null
|
|
2476
|
-
);
|
|
2477
|
-
const [archivedIds, setArchivedIds] = useState13(() => new Set(Object.keys(getArchived())));
|
|
2478
|
-
const [splitMode, setSplitMode] = useState13(false);
|
|
2479
|
-
const [leftSession, setLeftSession] = useState13(null);
|
|
2480
|
-
const [rightSession, setRightSession] = useState13(null);
|
|
2481
|
-
const [leftScroll, setLeftScroll] = useState13(0);
|
|
2482
|
-
const [rightScroll, setRightScroll] = useState13(0);
|
|
2483
|
-
const [leftFilter, setLeftFilter] = useState13("");
|
|
2484
|
-
const [rightFilter, setRightFilter] = useState13("");
|
|
2485
|
-
const [leftShowDetail, setLeftShowDetail] = useState13(false);
|
|
2486
|
-
const [rightShowDetail, setRightShowDetail] = useState13(false);
|
|
2487
|
-
const refreshArchived = useCallback4(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
|
|
2488
|
-
const updateInfo = useUpdateChecker(
|
|
2489
|
-
options.noUpdates,
|
|
2490
|
-
liveConfig.updates.checkOnLaunch,
|
|
2491
|
-
liveConfig.updates.checkInterval
|
|
2492
|
-
);
|
|
2493
|
-
useEffect9(() => {
|
|
2494
|
-
applyTheme(resolveTheme(liveConfig.theme, liveConfig.customThemes));
|
|
2495
|
-
}, [liveConfig.theme, liveConfig.customThemes]);
|
|
2496
|
-
const { sessions, selectedSession, selectedIndex, selectNext, selectPrev, refresh } = useSessions(
|
|
2497
|
-
options.allUsers,
|
|
2498
|
-
filter || void 0,
|
|
2499
|
-
archivedIds,
|
|
2500
|
-
viewingArchive
|
|
2501
|
-
);
|
|
2502
|
-
const rawEvents = useActivityStream(splitMode ? null : selectedSession, options.allUsers);
|
|
2503
|
-
const leftRawEvents = useActivityStream(splitMode ? leftSession : null, options.allUsers);
|
|
2504
|
-
const rightRawEvents = useActivityStream(splitMode ? rightSession : null, options.allUsers);
|
|
2505
|
-
const events = useFilteredEvents(rawEvents, activityFilter);
|
|
2506
|
-
const leftEvents = useFilteredEvents(leftRawEvents, leftFilter);
|
|
2507
|
-
const rightEvents = useFilteredEvents(rightRawEvents, rightFilter);
|
|
2508
|
-
const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, liveConfig);
|
|
2509
|
-
const nicknameInput = useTextInput(
|
|
2510
|
-
(value) => {
|
|
2511
|
-
if (selectedSession && value.trim()) {
|
|
2512
|
-
setNickname(selectedSession.sessionId, value.trim());
|
|
2513
|
-
refresh();
|
|
2514
|
-
}
|
|
2515
|
-
setInputMode("normal");
|
|
2516
|
-
},
|
|
2517
|
-
() => setInputMode("normal")
|
|
2518
|
-
);
|
|
2519
|
-
const filterInput = useTextInput(
|
|
2520
|
-
(value) => {
|
|
2521
|
-
if (activePanel === "sessions") setFilter(value);
|
|
2522
|
-
else if (activePanel === "left") setLeftFilter(value);
|
|
2523
|
-
else if (activePanel === "right") setRightFilter(value);
|
|
2524
|
-
else setActivityFilter(value);
|
|
2525
|
-
setInputMode("normal");
|
|
2526
|
-
},
|
|
2527
|
-
() => {
|
|
2528
|
-
if (activePanel === "sessions") setFilter("");
|
|
2529
|
-
else if (activePanel === "left") setLeftFilter("");
|
|
2530
|
-
else if (activePanel === "right") setRightFilter("");
|
|
2531
|
-
else setActivityFilter("");
|
|
2532
|
-
setInputMode("normal");
|
|
2533
|
-
}
|
|
2534
|
-
);
|
|
2535
|
-
useEffect9(() => {
|
|
2536
|
-
purgeExpiredArchives();
|
|
2537
|
-
refreshArchived();
|
|
2538
|
-
}, []);
|
|
2539
|
-
useEffect9(() => {
|
|
2540
|
-
setActivityScroll(0);
|
|
2541
|
-
}, [selectedSession?.sessionId]);
|
|
2542
|
-
const alertHeight = options.noSecurity ? 0 : 6;
|
|
2543
|
-
const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
|
|
2544
|
-
const viewportRows = mainHeight - 2;
|
|
2545
2680
|
const handleSettingsClose = useCallback4((c) => {
|
|
2546
2681
|
setLiveConfig(c);
|
|
2547
2682
|
saveConfig(c);
|
|
@@ -2609,28 +2744,41 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2609
2744
|
const handleTourSkip = useCallback4(() => {
|
|
2610
2745
|
setShowTour(false);
|
|
2611
2746
|
}, []);
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2747
|
+
return {
|
|
2748
|
+
liveConfig,
|
|
2749
|
+
setLiveConfig,
|
|
2750
|
+
showSetup,
|
|
2751
|
+
setShowSetup,
|
|
2752
|
+
showThemePicker,
|
|
2753
|
+
showTour,
|
|
2754
|
+
showSettings,
|
|
2755
|
+
setShowSettings,
|
|
2756
|
+
showThemeMenu,
|
|
2757
|
+
handleSettingsClose,
|
|
2758
|
+
handleThemeMenuClose,
|
|
2759
|
+
handleOpenThemeMenu,
|
|
2760
|
+
handleSetupComplete,
|
|
2761
|
+
handleThemePickerSelect,
|
|
2762
|
+
handleThemePickerSkip,
|
|
2763
|
+
handleThemePickerDismiss,
|
|
2764
|
+
handleTourComplete,
|
|
2765
|
+
handleTourSkip
|
|
2766
|
+
};
|
|
2767
|
+
};
|
|
2768
|
+
|
|
2769
|
+
// src/ui/hooks/useSplitPanel.ts
|
|
2770
|
+
import { useState as useState14, useCallback as useCallback5 } from "react";
|
|
2771
|
+
var useSplitPanel = () => {
|
|
2772
|
+
const [splitMode, setSplitMode] = useState14(false);
|
|
2773
|
+
const [leftSession, setLeftSession] = useState14(null);
|
|
2774
|
+
const [rightSession, setRightSession] = useState14(null);
|
|
2775
|
+
const [leftScroll, setLeftScroll] = useState14(0);
|
|
2776
|
+
const [rightScroll, setRightScroll] = useState14(0);
|
|
2777
|
+
const [leftFilter, setLeftFilter] = useState14("");
|
|
2778
|
+
const [rightFilter, setRightFilter] = useState14("");
|
|
2779
|
+
const [leftShowDetail, setLeftShowDetail] = useState14(false);
|
|
2780
|
+
const [rightShowDetail, setRightShowDetail] = useState14(false);
|
|
2781
|
+
const clearSplitState = useCallback5(() => {
|
|
2634
2782
|
setSplitMode(false);
|
|
2635
2783
|
setLeftSession(null);
|
|
2636
2784
|
setRightSession(null);
|
|
@@ -2640,9 +2788,8 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2640
2788
|
setRightFilter("");
|
|
2641
2789
|
setLeftShowDetail(false);
|
|
2642
2790
|
setRightShowDetail(false);
|
|
2643
|
-
setActivePanel("sessions");
|
|
2644
2791
|
}, []);
|
|
2645
|
-
const resetPanel =
|
|
2792
|
+
const resetPanel = useCallback5((side) => {
|
|
2646
2793
|
if (side === "left") {
|
|
2647
2794
|
setLeftSession(null);
|
|
2648
2795
|
setLeftScroll(0);
|
|
@@ -2655,24 +2802,146 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2655
2802
|
setRightShowDetail(false);
|
|
2656
2803
|
}
|
|
2657
2804
|
}, []);
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2805
|
+
const switchPanel = useCallback5(
|
|
2806
|
+
(dir, setActivePanel) => {
|
|
2807
|
+
if (splitMode) {
|
|
2808
|
+
const order = ["sessions", "left", "right"];
|
|
2809
|
+
setActivePanel((p) => {
|
|
2810
|
+
const idx = order.indexOf(p);
|
|
2811
|
+
if (idx === -1) return "sessions";
|
|
2812
|
+
return dir === "next" ? order[(idx + 1) % order.length] : order[(idx - 1 + order.length) % order.length];
|
|
2813
|
+
});
|
|
2814
|
+
} else {
|
|
2815
|
+
setActivePanel((p) => p === "sessions" ? "activity" : "sessions");
|
|
2816
|
+
}
|
|
2817
|
+
},
|
|
2818
|
+
[splitMode]
|
|
2819
|
+
);
|
|
2820
|
+
return {
|
|
2661
2821
|
splitMode,
|
|
2662
|
-
|
|
2663
|
-
showSetup,
|
|
2664
|
-
showSettings: showSettings || showThemeMenu || showThemePicker || showTour,
|
|
2665
|
-
showDetail,
|
|
2666
|
-
leftShowDetail,
|
|
2667
|
-
rightShowDetail,
|
|
2668
|
-
confirmAction,
|
|
2669
|
-
selectedSession,
|
|
2822
|
+
setSplitMode,
|
|
2670
2823
|
leftSession,
|
|
2824
|
+
setLeftSession,
|
|
2671
2825
|
rightSession,
|
|
2826
|
+
setRightSession,
|
|
2672
2827
|
leftScroll,
|
|
2828
|
+
setLeftScroll,
|
|
2673
2829
|
rightScroll,
|
|
2830
|
+
setRightScroll,
|
|
2674
2831
|
leftFilter,
|
|
2832
|
+
setLeftFilter,
|
|
2675
2833
|
rightFilter,
|
|
2834
|
+
setRightFilter,
|
|
2835
|
+
leftShowDetail,
|
|
2836
|
+
setLeftShowDetail,
|
|
2837
|
+
rightShowDetail,
|
|
2838
|
+
setRightShowDetail,
|
|
2839
|
+
clearSplitState,
|
|
2840
|
+
resetPanel,
|
|
2841
|
+
switchPanel
|
|
2842
|
+
};
|
|
2843
|
+
};
|
|
2844
|
+
|
|
2845
|
+
// src/ui/App.tsx
|
|
2846
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2847
|
+
var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
2848
|
+
const { exit } = useApp();
|
|
2849
|
+
const { stdout } = useStdout3();
|
|
2850
|
+
const termHeight = stdout?.rows ?? 40;
|
|
2851
|
+
const setup = useSetupFlow(initialConfig, firstRun);
|
|
2852
|
+
const split = useSplitPanel();
|
|
2853
|
+
const kb = setup.liveConfig.keybindings;
|
|
2854
|
+
const [activePanel, setActivePanel] = useState15("sessions");
|
|
2855
|
+
const [activityScroll, setActivityScroll] = useState15(0);
|
|
2856
|
+
const [inputMode, setInputMode] = useState15("normal");
|
|
2857
|
+
const [filter, setFilter] = useState15("");
|
|
2858
|
+
const [activityFilter, setActivityFilter] = useState15("");
|
|
2859
|
+
const [updateStatus, setUpdateStatus] = useState15("");
|
|
2860
|
+
const [showDetail, setShowDetail] = useState15(false);
|
|
2861
|
+
const [viewingArchive, setViewingArchive] = useState15(false);
|
|
2862
|
+
const [confirmAction, setConfirmAction] = useState15(null);
|
|
2863
|
+
const [archivedIds, setArchivedIds] = useState15(() => new Set(Object.keys(getArchived())));
|
|
2864
|
+
const refreshArchived = useCallback6(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
|
|
2865
|
+
const updateInfo = useUpdateChecker(options.noUpdates, setup.liveConfig.updates.checkOnLaunch, setup.liveConfig.updates.checkInterval);
|
|
2866
|
+
useEffect9(() => {
|
|
2867
|
+
applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
|
|
2868
|
+
}, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
|
|
2869
|
+
const { sessions, visibleItems, selectedSession, selectedGroup, selectedIndex, selectNext, selectPrev, toggleExpand, refresh } = useSessions(options.allUsers, filter || void 0, archivedIds, viewingArchive);
|
|
2870
|
+
const activityTarget = split.splitMode ? null : selectedGroup ? selectedGroup.sessions : selectedSession;
|
|
2871
|
+
const rawEvents = useActivityStream(activityTarget, options.allUsers);
|
|
2872
|
+
const leftRawEvents = useActivityStream(split.splitMode ? split.leftSession : null, options.allUsers);
|
|
2873
|
+
const rightRawEvents = useActivityStream(split.splitMode ? split.rightSession : null, options.allUsers);
|
|
2874
|
+
const events = useFilteredEvents(rawEvents, activityFilter);
|
|
2875
|
+
const leftEvents = useFilteredEvents(leftRawEvents, split.leftFilter);
|
|
2876
|
+
const rightEvents = useFilteredEvents(rightRawEvents, split.rightFilter);
|
|
2877
|
+
const { alerts } = useAlerts(!options.noSecurity, options.alertLevel, options.allUsers, setup.liveConfig);
|
|
2878
|
+
const nicknameInput = useTextInput(
|
|
2879
|
+
(value) => {
|
|
2880
|
+
if (selectedSession && value.trim()) {
|
|
2881
|
+
setNickname(selectedSession.sessionId, value.trim());
|
|
2882
|
+
refresh();
|
|
2883
|
+
}
|
|
2884
|
+
setInputMode("normal");
|
|
2885
|
+
},
|
|
2886
|
+
() => setInputMode("normal")
|
|
2887
|
+
);
|
|
2888
|
+
const filterInput = useTextInput(
|
|
2889
|
+
(value) => {
|
|
2890
|
+
if (activePanel === "sessions") setFilter(value);
|
|
2891
|
+
else if (activePanel === "left") split.setLeftFilter(value);
|
|
2892
|
+
else if (activePanel === "right") split.setRightFilter(value);
|
|
2893
|
+
else setActivityFilter(value);
|
|
2894
|
+
setInputMode("normal");
|
|
2895
|
+
},
|
|
2896
|
+
() => {
|
|
2897
|
+
if (activePanel === "sessions") setFilter("");
|
|
2898
|
+
else if (activePanel === "left") split.setLeftFilter("");
|
|
2899
|
+
else if (activePanel === "right") split.setRightFilter("");
|
|
2900
|
+
else setActivityFilter("");
|
|
2901
|
+
setInputMode("normal");
|
|
2902
|
+
}
|
|
2903
|
+
);
|
|
2904
|
+
useEffect9(() => {
|
|
2905
|
+
purgeExpiredArchives();
|
|
2906
|
+
refreshArchived();
|
|
2907
|
+
}, []);
|
|
2908
|
+
useEffect9(() => {
|
|
2909
|
+
setActivityScroll(0);
|
|
2910
|
+
}, [selectedSession?.sessionId, selectedGroup?.key]);
|
|
2911
|
+
const alertHeight = options.noSecurity ? 0 : 6;
|
|
2912
|
+
const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
|
|
2913
|
+
const viewportRows = mainHeight - 2;
|
|
2914
|
+
const switchPanel = useCallback6((dir) => split.switchPanel(dir, setActivePanel), [split.switchPanel]);
|
|
2915
|
+
const clearSplitState = useCallback6(() => {
|
|
2916
|
+
split.clearSplitState();
|
|
2917
|
+
setActivePanel("sessions");
|
|
2918
|
+
}, [split.clearSplitState]);
|
|
2919
|
+
const getActiveFilter = useCallback6(() => {
|
|
2920
|
+
if (activePanel === "sessions") return filter;
|
|
2921
|
+
if (activePanel === "left") return split.leftFilter;
|
|
2922
|
+
if (activePanel === "right") return split.rightFilter;
|
|
2923
|
+
return activityFilter;
|
|
2924
|
+
}, [activePanel, filter, split.leftFilter, split.rightFilter, activityFilter]);
|
|
2925
|
+
useKeyHandler({
|
|
2926
|
+
kb,
|
|
2927
|
+
activePanel,
|
|
2928
|
+
splitMode: split.splitMode,
|
|
2929
|
+
inputMode,
|
|
2930
|
+
showSetup: setup.showSetup,
|
|
2931
|
+
showSettings: setup.showSettings || setup.showThemeMenu || setup.showThemePicker || setup.showTour,
|
|
2932
|
+
showDetail,
|
|
2933
|
+
leftShowDetail: split.leftShowDetail,
|
|
2934
|
+
rightShowDetail: split.rightShowDetail,
|
|
2935
|
+
confirmAction,
|
|
2936
|
+
selectedSession,
|
|
2937
|
+
selectedGroup,
|
|
2938
|
+
toggleExpand,
|
|
2939
|
+
leftSession: split.leftSession,
|
|
2940
|
+
rightSession: split.rightSession,
|
|
2941
|
+
leftScroll: split.leftScroll,
|
|
2942
|
+
rightScroll: split.rightScroll,
|
|
2943
|
+
leftFilter: split.leftFilter,
|
|
2944
|
+
rightFilter: split.rightFilter,
|
|
2676
2945
|
filter,
|
|
2677
2946
|
activityFilter,
|
|
2678
2947
|
viewingArchive,
|
|
@@ -2687,24 +2956,24 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2687
2956
|
refresh,
|
|
2688
2957
|
switchPanel,
|
|
2689
2958
|
clearSplitState,
|
|
2690
|
-
resetPanel,
|
|
2959
|
+
resetPanel: split.resetPanel,
|
|
2691
2960
|
getActiveFilter,
|
|
2692
2961
|
setActivePanel,
|
|
2693
2962
|
setInputMode,
|
|
2694
2963
|
setFilter,
|
|
2695
2964
|
setActivityFilter,
|
|
2696
|
-
setLeftFilter,
|
|
2697
|
-
setRightFilter,
|
|
2965
|
+
setLeftFilter: split.setLeftFilter,
|
|
2966
|
+
setRightFilter: split.setRightFilter,
|
|
2698
2967
|
setShowDetail,
|
|
2699
|
-
setLeftShowDetail,
|
|
2700
|
-
setRightShowDetail,
|
|
2701
|
-
setShowSettings,
|
|
2968
|
+
setLeftShowDetail: split.setLeftShowDetail,
|
|
2969
|
+
setRightShowDetail: split.setRightShowDetail,
|
|
2970
|
+
setShowSettings: setup.setShowSettings,
|
|
2702
2971
|
setViewingArchive,
|
|
2703
|
-
setSplitMode,
|
|
2704
|
-
setLeftSession,
|
|
2705
|
-
setRightSession,
|
|
2706
|
-
setLeftScroll,
|
|
2707
|
-
setRightScroll,
|
|
2972
|
+
setSplitMode: split.setSplitMode,
|
|
2973
|
+
setLeftSession: split.setLeftSession,
|
|
2974
|
+
setRightSession: split.setRightSession,
|
|
2975
|
+
setLeftScroll: split.setLeftScroll,
|
|
2976
|
+
setRightScroll: split.setRightScroll,
|
|
2708
2977
|
setActivityScroll,
|
|
2709
2978
|
setConfirmAction,
|
|
2710
2979
|
setUpdateStatus,
|
|
@@ -2747,96 +3016,31 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2747
3016
|
installUpdate().then(() => setUpdateStatus(`updated to v${updateInfo?.latest} \u2014 restart to apply`)).catch(() => setUpdateStatus("update failed"));
|
|
2748
3017
|
}
|
|
2749
3018
|
});
|
|
2750
|
-
if (showSetup) {
|
|
3019
|
+
if (setup.showSetup) {
|
|
2751
3020
|
const steps = [
|
|
2752
|
-
...liveConfig.prompts.hook === "pending" ? [
|
|
2753
|
-
|
|
2754
|
-
title: "Install Claude Code hook?",
|
|
2755
|
-
description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
|
|
2756
|
-
}
|
|
2757
|
-
] : [],
|
|
2758
|
-
...liveConfig.prompts.mcp === "pending" ? [
|
|
2759
|
-
{
|
|
2760
|
-
title: "Install MCP server?",
|
|
2761
|
-
description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
|
|
2762
|
-
}
|
|
2763
|
-
] : []
|
|
3021
|
+
...setup.liveConfig.prompts.hook === "pending" ? [{ title: "Install Claude Code hook?", description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time." }] : [],
|
|
3022
|
+
...setup.liveConfig.prompts.mcp === "pending" ? [{ title: "Install MCP server?", description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts." }] : []
|
|
2764
3023
|
];
|
|
2765
3024
|
if (steps.length === 0) {
|
|
2766
|
-
setShowSetup(false);
|
|
2767
|
-
if (liveConfig.prompts.theme === "pending") setShowThemePicker(true);
|
|
2768
|
-
else if (liveConfig.prompts.tour === "pending") setShowTour(true);
|
|
3025
|
+
setup.setShowSetup(false);
|
|
2769
3026
|
return null;
|
|
2770
3027
|
}
|
|
2771
|
-
return /* @__PURE__ */ jsx15(SetupModal, { steps, onComplete: handleSetupComplete });
|
|
3028
|
+
return /* @__PURE__ */ jsx15(SetupModal, { steps, onComplete: setup.handleSetupComplete });
|
|
2772
3029
|
}
|
|
2773
|
-
if (showThemePicker) {
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
onSelect: handleThemePickerSelect,
|
|
2778
|
-
onSkip: handleThemePickerSkip,
|
|
2779
|
-
onDismiss: handleThemePickerDismiss
|
|
2780
|
-
}
|
|
2781
|
-
);
|
|
2782
|
-
}
|
|
2783
|
-
if (showTour) return /* @__PURE__ */ jsx15(GuidedTour, { onComplete: handleTourComplete, onSkip: handleTourSkip });
|
|
2784
|
-
if (showThemeMenu) return /* @__PURE__ */ jsx15(ThemeMenu, { config: liveConfig, onClose: handleThemeMenuClose });
|
|
2785
|
-
if (showSettings)
|
|
2786
|
-
return /* @__PURE__ */ jsx15(SettingsMenu, { config: liveConfig, onClose: handleSettingsClose, onOpenThemeMenu: handleOpenThemeMenu });
|
|
3030
|
+
if (setup.showThemePicker) return /* @__PURE__ */ jsx15(ThemePickerModal, { onSelect: setup.handleThemePickerSelect, onSkip: setup.handleThemePickerSkip, onDismiss: setup.handleThemePickerDismiss });
|
|
3031
|
+
if (setup.showTour) return /* @__PURE__ */ jsx15(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
|
|
3032
|
+
if (setup.showThemeMenu) return /* @__PURE__ */ jsx15(ThemeMenu, { config: setup.liveConfig, onClose: setup.handleThemeMenuClose });
|
|
3033
|
+
if (setup.showSettings) return /* @__PURE__ */ jsx15(SettingsMenu, { config: setup.liveConfig, onClose: setup.handleSettingsClose, onOpenThemeMenu: setup.handleOpenThemeMenu });
|
|
2787
3034
|
if (confirmAction) {
|
|
2788
|
-
return /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx15(
|
|
2789
|
-
ConfirmModal,
|
|
2790
|
-
{
|
|
2791
|
-
title: confirmAction.title,
|
|
2792
|
-
message: confirmAction.message,
|
|
2793
|
-
onConfirm: confirmAction.onConfirm,
|
|
2794
|
-
onCancel: () => setConfirmAction(null)
|
|
2795
|
-
}
|
|
2796
|
-
) });
|
|
3035
|
+
return /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx15(ConfirmModal, { title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onCancel: () => setConfirmAction(null) }) });
|
|
2797
3036
|
}
|
|
2798
|
-
const
|
|
2799
|
-
const
|
|
2800
|
-
|
|
2801
|
-
{
|
|
2802
|
-
activePanel,
|
|
2803
|
-
leftSession,
|
|
2804
|
-
rightSession,
|
|
2805
|
-
leftEvents,
|
|
2806
|
-
rightEvents,
|
|
2807
|
-
leftScroll,
|
|
2808
|
-
rightScroll,
|
|
2809
|
-
leftFilter,
|
|
2810
|
-
rightFilter,
|
|
2811
|
-
leftShowDetail,
|
|
2812
|
-
rightShowDetail,
|
|
2813
|
-
height: mainHeight
|
|
2814
|
-
}
|
|
2815
|
-
) : showDetail && selectedSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx15(
|
|
2816
|
-
ActivityFeed,
|
|
2817
|
-
{
|
|
2818
|
-
events,
|
|
2819
|
-
sessionSlug: selectedSession?.slug ?? null,
|
|
2820
|
-
focused: activePanel === "activity",
|
|
2821
|
-
height: mainHeight,
|
|
2822
|
-
scrollOffset: activityScroll,
|
|
2823
|
-
filter: activityFilter || void 0
|
|
2824
|
-
}
|
|
2825
|
-
);
|
|
3037
|
+
const activitySlug = selectedGroup ? selectedGroup.key : selectedSession?.slug ?? null;
|
|
3038
|
+
const isMerged = selectedGroup !== null;
|
|
3039
|
+
const rightPanel = split.splitMode ? /* @__PURE__ */ jsx15(SplitPanel, { activePanel, leftSession: split.leftSession, rightSession: split.rightSession, leftEvents, rightEvents, leftScroll: split.leftScroll, rightScroll: split.rightScroll, leftFilter: split.leftFilter, rightFilter: split.rightFilter, leftShowDetail: split.leftShowDetail, rightShowDetail: split.rightShowDetail, height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx15(ActivityFeed, { events, sessionSlug: activitySlug, sessionId: selectedSession?.sessionId, isActive: selectedGroup ? selectedGroup.isActive : selectedSession ? selectedSession.pid !== null : void 0, focused: activePanel === "activity", height: mainHeight, scrollOffset: activityScroll, filter: activityFilter || void 0, merged: isMerged, mergedSessions: selectedGroup?.sessions });
|
|
2826
3040
|
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", height: termHeight, children: [
|
|
2827
3041
|
/* @__PURE__ */ jsx15(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
|
|
2828
3042
|
/* @__PURE__ */ jsxs15(Box15, { flexGrow: 1, height: mainHeight, children: [
|
|
2829
|
-
/* @__PURE__ */ jsx15(
|
|
2830
|
-
SessionList,
|
|
2831
|
-
{
|
|
2832
|
-
sessions,
|
|
2833
|
-
selectedIndex,
|
|
2834
|
-
focused: activePanel === "sessions",
|
|
2835
|
-
height: mainHeight,
|
|
2836
|
-
filter: filter || void 0,
|
|
2837
|
-
viewingArchive
|
|
2838
|
-
}
|
|
2839
|
-
),
|
|
3043
|
+
/* @__PURE__ */ jsx15(SessionList, { visibleItems, selectedIndex, focused: activePanel === "sessions", height: mainHeight, filter: filter || void 0, viewingArchive, totalSessions: sessions.length }),
|
|
2840
3044
|
rightPanel
|
|
2841
3045
|
] }),
|
|
2842
3046
|
!options.noSecurity && /* @__PURE__ */ jsx15(AlertBar, { alerts }),
|
|
@@ -2846,12 +3050,12 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
|
|
|
2846
3050
|
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: "_" })
|
|
2847
3051
|
] }),
|
|
2848
3052
|
inputMode === "filter" && /* @__PURE__ */ jsxs15(Box15, { paddingX: 1, children: [
|
|
2849
|
-
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children:
|
|
3053
|
+
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: activePanel === "sessions" ? "sessions" : activePanel === "left" ? "left" : activePanel === "right" ? "right" : "activity" }),
|
|
2850
3054
|
/* @__PURE__ */ jsx15(Text14, { color: colors.primary, children: "/" }),
|
|
2851
3055
|
/* @__PURE__ */ jsx15(Text14, { color: colors.bright, children: filterInput.value }),
|
|
2852
3056
|
/* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: "_" })
|
|
2853
3057
|
] }),
|
|
2854
|
-
inputMode === "normal" && /* @__PURE__ */ jsx15(FooterBar, { keybindings: kb, updateStatus, viewingArchive, splitMode })
|
|
3058
|
+
inputMode === "normal" && /* @__PURE__ */ jsx15(FooterBar, { keybindings: kb, updateStatus, viewingArchive, splitMode: split.splitMode })
|
|
2855
3059
|
] });
|
|
2856
3060
|
};
|
|
2857
3061
|
|