@burdenoff/microfe-vibecontrols 2026.601.6 → 2026.601.7
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/components/sessions/EntityInfoPopover.js +183 -0
- package/dist/components/sessions/EntityInfoPopover.js.map +1 -0
- package/dist/components/sessions/SessionCard.js +80 -72
- package/dist/components/sessions/SessionCard.js.map +1 -1
- package/dist/components/sessions/SessionGroup.js +49 -0
- package/dist/components/sessions/SessionGroup.js.map +1 -0
- package/dist/components/sessions/SessionInfoPanel.js +128 -0
- package/dist/components/sessions/SessionInfoPanel.js.map +1 -0
- package/dist/components/sessions/SessionList.js +109 -96
- package/dist/components/sessions/SessionList.js.map +1 -1
- package/dist/components/sessions/SessionsCommandPalette.js +36 -0
- package/dist/components/sessions/SessionsCommandPalette.js.map +1 -0
- package/dist/components/sessions/ShortcutCheatsheet.js +73 -0
- package/dist/components/sessions/ShortcutCheatsheet.js.map +1 -0
- package/dist/components/sessions/TerminalPanel.js +640 -537
- package/dist/components/sessions/TerminalPanel.js.map +1 -1
- package/dist/components/sessions/groupSessions.js +46 -0
- package/dist/components/sessions/groupSessions.js.map +1 -0
- package/dist/components/sessions/index.js +7 -0
- package/dist/components/sessions/sessionShortcuts.js +149 -0
- package/dist/components/sessions/sessionShortcuts.js.map +1 -0
- package/dist/components/shared/FilterDropdown.js +14 -13
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/LinkToVibePanel.js.map +1 -1
- package/dist/components/vibe/VibeLinkedEntitiesPanel.js.map +1 -1
- package/dist/generated/wspace-operations.js +21 -0
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/pages/SessionsPage.js +1189 -920
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/ai/AgentManagerPage.js.map +1 -1
- package/dist/store/vibeControlsStore.js +16 -1
- package/dist/store/vibeControlsStore.js.map +1 -1
- package/dist/translations/en.js +97 -2
- package/dist/translations/en.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { useVibeNavigate as t } from "../../providers/VibeControlsProvider.js";
|
|
3
|
+
import { getAgentStatus as n } from "../agents/agentStatus.js";
|
|
4
|
+
import { Boxes as r, ExternalLink as i, MonitorSmartphone as a, Server as o, Terminal as s } from "lucide-react";
|
|
5
|
+
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
|
+
import { Sheet as d, SheetContent as f, SheetDescription as p, SheetHeader as m, SheetTitle as h } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/components/sessions/SessionInfoPanel.tsx
|
|
8
|
+
function g({ label: e, value: t }) {
|
|
9
|
+
return /* @__PURE__ */ u("div", {
|
|
10
|
+
className: "flex items-center justify-between gap-3 py-1 text-sm",
|
|
11
|
+
children: [/* @__PURE__ */ l("span", {
|
|
12
|
+
className: "text-text-tertiary",
|
|
13
|
+
children: e
|
|
14
|
+
}), /* @__PURE__ */ l("span", {
|
|
15
|
+
className: "font-medium text-text-primary truncate max-w-[60%]",
|
|
16
|
+
title: t,
|
|
17
|
+
children: t
|
|
18
|
+
})]
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function _({ open: _, onOpenChange: v, session: y }) {
|
|
22
|
+
let b = e(), x = t(), S = y?.agent, C = S?.target, w = y?.vibe;
|
|
23
|
+
return /* @__PURE__ */ l(d, {
|
|
24
|
+
open: _,
|
|
25
|
+
onOpenChange: v,
|
|
26
|
+
children: /* @__PURE__ */ u(f, {
|
|
27
|
+
side: "right",
|
|
28
|
+
className: "w-full sm:max-w-md overflow-y-auto",
|
|
29
|
+
"data-testid": "session-info-panel",
|
|
30
|
+
children: [/* @__PURE__ */ u(m, { children: [/* @__PURE__ */ u(h, {
|
|
31
|
+
className: "flex items-center gap-2",
|
|
32
|
+
children: [/* @__PURE__ */ l(s, { className: "size-4 text-text-secondary" }), y?.name ?? b("sessions.info.panelTitle", "Session details")]
|
|
33
|
+
}), /* @__PURE__ */ l(p, { children: b("sessions.info.panelDesc", "Where this session runs and how to reach it.") })] }), /* @__PURE__ */ l("div", {
|
|
34
|
+
className: "px-4 pb-4 space-y-5",
|
|
35
|
+
children: y ? /* @__PURE__ */ u(c, { children: [
|
|
36
|
+
/* @__PURE__ */ u("section", { children: [
|
|
37
|
+
/* @__PURE__ */ u("h3", {
|
|
38
|
+
className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2",
|
|
39
|
+
children: [/* @__PURE__ */ l(s, { className: "size-3.5" }), b("sessions.info.session", "Session")]
|
|
40
|
+
}),
|
|
41
|
+
/* @__PURE__ */ l(g, {
|
|
42
|
+
label: b("sessions.info.type", "Type"),
|
|
43
|
+
value: y.type
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ l(g, {
|
|
46
|
+
label: b("sessions.info.status", "Status"),
|
|
47
|
+
value: y.status
|
|
48
|
+
}),
|
|
49
|
+
y.workingDirectory && /* @__PURE__ */ l(g, {
|
|
50
|
+
label: b("sessions.info.cwd", "Working dir"),
|
|
51
|
+
value: y.workingDirectory
|
|
52
|
+
})
|
|
53
|
+
] }),
|
|
54
|
+
S && /* @__PURE__ */ u("section", { children: [
|
|
55
|
+
/* @__PURE__ */ u("h3", {
|
|
56
|
+
className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2",
|
|
57
|
+
children: [/* @__PURE__ */ l(o, { className: "size-3.5" }), b("sessions.info.agent", "Agent")]
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ l(g, {
|
|
60
|
+
label: b("sessions.info.name", "Name"),
|
|
61
|
+
value: S.name
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ l(g, {
|
|
64
|
+
label: b("sessions.info.status", "Status"),
|
|
65
|
+
value: n(S)
|
|
66
|
+
}),
|
|
67
|
+
S.hostname && /* @__PURE__ */ l(g, {
|
|
68
|
+
label: b("sessions.info.host", "Host"),
|
|
69
|
+
value: S.hostname
|
|
70
|
+
}),
|
|
71
|
+
S.platform && /* @__PURE__ */ l(g, {
|
|
72
|
+
label: b("sessions.info.platform", "Platform"),
|
|
73
|
+
value: S.platform
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ u("button", {
|
|
76
|
+
type: "button",
|
|
77
|
+
onClick: () => x(`/agents/${S.id}`),
|
|
78
|
+
className: "mt-2 inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline",
|
|
79
|
+
children: [/* @__PURE__ */ l(i, { className: "size-3" }), b("sessions.info.viewAgent", "View agent")]
|
|
80
|
+
})
|
|
81
|
+
] }),
|
|
82
|
+
C && /* @__PURE__ */ u("section", { children: [
|
|
83
|
+
/* @__PURE__ */ u("h3", {
|
|
84
|
+
className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2",
|
|
85
|
+
children: [/* @__PURE__ */ l(a, { className: "size-3.5" }), b("sessions.info.target", "Target")]
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ l(g, {
|
|
88
|
+
label: b("sessions.info.name", "Name"),
|
|
89
|
+
value: C.name
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ l(g, {
|
|
92
|
+
label: b("sessions.info.type", "Type"),
|
|
93
|
+
value: C.type
|
|
94
|
+
}),
|
|
95
|
+
/* @__PURE__ */ l(g, {
|
|
96
|
+
label: b("sessions.info.status", "Status"),
|
|
97
|
+
value: C.status
|
|
98
|
+
}),
|
|
99
|
+
C.host && /* @__PURE__ */ l(g, {
|
|
100
|
+
label: b("sessions.info.address", "Address"),
|
|
101
|
+
value: `${C.host}${C.port ? `:${C.port}` : ""}`
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ u("button", {
|
|
104
|
+
type: "button",
|
|
105
|
+
onClick: () => x(`/targets/${C.id}`),
|
|
106
|
+
className: "mt-2 inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline",
|
|
107
|
+
children: [/* @__PURE__ */ l(i, { className: "size-3" }), b("sessions.info.viewTarget", "View target")]
|
|
108
|
+
})
|
|
109
|
+
] }),
|
|
110
|
+
w && /* @__PURE__ */ u("section", { children: [/* @__PURE__ */ u("h3", {
|
|
111
|
+
className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2",
|
|
112
|
+
children: [/* @__PURE__ */ l(r, { className: "size-3.5" }), b("sessions.info.vibe", "Vibe")]
|
|
113
|
+
}), /* @__PURE__ */ l(g, {
|
|
114
|
+
label: b("sessions.info.name", "Name"),
|
|
115
|
+
value: w.name
|
|
116
|
+
})] })
|
|
117
|
+
] }) : /* @__PURE__ */ l("p", {
|
|
118
|
+
className: "text-sm text-text-tertiary",
|
|
119
|
+
children: b("sessions.info.noActive", "No active terminal session.")
|
|
120
|
+
})
|
|
121
|
+
})]
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
export { _ as SessionInfoPanel };
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=SessionInfoPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionInfoPanel.js","names":[],"sources":["../../../src/components/sessions/SessionInfoPanel.tsx"],"sourcesContent":["/**\n * SessionInfoPanel — a side sheet summarising the active terminal tab's\n * session: which agent it runs on, which target machine that agent is\n * installed on, and the optional vibe. Each entity links to its full\n * detail page. Opened from the terminal status bar or the `i` shortcut.\n */\nimport {\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n} from '@burdenoff/fe-libs/ui';\nimport { Server, MonitorSmartphone, Boxes, ExternalLink, Terminal } from 'lucide-react';\nimport { getAgentStatus } from '@/components/agents/agentStatus';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type { Session } from './types';\n\nfunction Field({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex items-center justify-between gap-3 py-1 text-sm\">\n <span className=\"text-text-tertiary\">{label}</span>\n <span className=\"font-medium text-text-primary truncate max-w-[60%]\" title={value}>\n {value}\n </span>\n </div>\n );\n}\n\nexport interface SessionInfoPanelProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n session: Session | null;\n}\n\nexport function SessionInfoPanel({ open, onOpenChange, session }: SessionInfoPanelProps) {\n const tr = useTr();\n const navigate = useVibeNavigate();\n const agent = session?.agent;\n const target = agent?.target;\n const vibe = session?.vibe;\n\n return (\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent\n side=\"right\"\n className=\"w-full sm:max-w-md overflow-y-auto\"\n data-testid=\"session-info-panel\"\n >\n <SheetHeader>\n <SheetTitle className=\"flex items-center gap-2\">\n <Terminal className=\"size-4 text-text-secondary\" />\n {session?.name ?? tr('sessions.info.panelTitle', 'Session details')}\n </SheetTitle>\n <SheetDescription>\n {tr('sessions.info.panelDesc', 'Where this session runs and how to reach it.')}\n </SheetDescription>\n </SheetHeader>\n\n <div className=\"px-4 pb-4 space-y-5\">\n {!session ? (\n <p className=\"text-sm text-text-tertiary\">\n {tr('sessions.info.noActive', 'No active terminal session.')}\n </p>\n ) : (\n <>\n <section>\n <h3 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2\">\n <Terminal className=\"size-3.5\" />\n {tr('sessions.info.session', 'Session')}\n </h3>\n <Field label={tr('sessions.info.type', 'Type')} value={session.type} />\n <Field label={tr('sessions.info.status', 'Status')} value={session.status} />\n {session.workingDirectory && (\n <Field\n label={tr('sessions.info.cwd', 'Working dir')}\n value={session.workingDirectory}\n />\n )}\n </section>\n\n {agent && (\n <section>\n <h3 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2\">\n <Server className=\"size-3.5\" />\n {tr('sessions.info.agent', 'Agent')}\n </h3>\n <Field label={tr('sessions.info.name', 'Name')} value={agent.name} />\n <Field\n label={tr('sessions.info.status', 'Status')}\n value={getAgentStatus(agent)}\n />\n {agent.hostname && (\n <Field label={tr('sessions.info.host', 'Host')} value={agent.hostname} />\n )}\n {agent.platform && (\n <Field\n label={tr('sessions.info.platform', 'Platform')}\n value={agent.platform}\n />\n )}\n <button\n type=\"button\"\n onClick={() => navigate(`/agents/${agent.id}`)}\n className=\"mt-2 inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline\"\n >\n <ExternalLink className=\"size-3\" />\n {tr('sessions.info.viewAgent', 'View agent')}\n </button>\n </section>\n )}\n\n {target && (\n <section>\n <h3 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2\">\n <MonitorSmartphone className=\"size-3.5\" />\n {tr('sessions.info.target', 'Target')}\n </h3>\n <Field label={tr('sessions.info.name', 'Name')} value={target.name} />\n <Field label={tr('sessions.info.type', 'Type')} value={target.type} />\n <Field label={tr('sessions.info.status', 'Status')} value={target.status} />\n {target.host && (\n <Field\n label={tr('sessions.info.address', 'Address')}\n value={`${target.host}${target.port ? `:${target.port}` : ''}`}\n />\n )}\n <button\n type=\"button\"\n onClick={() => navigate(`/targets/${target.id}`)}\n className=\"mt-2 inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline\"\n >\n <ExternalLink className=\"size-3\" />\n {tr('sessions.info.viewTarget', 'View target')}\n </button>\n </section>\n )}\n\n {vibe && (\n <section>\n <h3 className=\"flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-tertiary mb-2\">\n <Boxes className=\"size-3.5\" />\n {tr('sessions.info.vibe', 'Vibe')}\n </h3>\n <Field label={tr('sessions.info.name', 'Name')} value={vibe.name} />\n </section>\n )}\n </>\n )}\n </div>\n </SheetContent>\n </Sheet>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,162 +1,170 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { EmptyState as t } from "../shared/EmptyState.js";
|
|
3
3
|
import { SessionStatusBadge as n } from "./SessionStatusBadge.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { AgentInfoPopover as r, TargetInfoPopover as i } from "./EntityInfoPopover.js";
|
|
5
|
+
import { useState as a } from "react";
|
|
6
|
+
import { Clock as o, MoreVertical as s, Play as c, Plus as l, RotateCcw as u, Share2 as d, Square as f, Terminal as p } from "lucide-react";
|
|
7
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/sessions/SessionList.tsx
|
|
8
|
-
var
|
|
9
|
+
var g = {
|
|
9
10
|
TMUX: "🖥️",
|
|
10
11
|
SSH: "🔐",
|
|
11
12
|
TERMINAL: "💻",
|
|
12
13
|
SCRIPT: "📜",
|
|
13
14
|
CUSTOM: "⚙️"
|
|
14
15
|
};
|
|
15
|
-
function
|
|
16
|
-
let
|
|
17
|
-
return /* @__PURE__ */
|
|
18
|
-
className: `border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${
|
|
19
|
-
onClick: () =>
|
|
16
|
+
function _({ session: t, onOpen: l, onStart: _, onStop: v, onRestart: y, onTerminate: b, onOpenTerminal: x, onShare: S, selected: C, onToggleSelect: w }) {
|
|
17
|
+
let T = e(), [E, D] = a(!1), O = t.status === "RUNNING", k = t.status === "STOPPED" || t.status === "TERMINATED";
|
|
18
|
+
return /* @__PURE__ */ h("tr", {
|
|
19
|
+
className: `border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${C ? "bg-action-primary-bg/5" : ""}`,
|
|
20
|
+
onClick: () => l?.(t),
|
|
20
21
|
children: [
|
|
21
|
-
|
|
22
|
+
w && /* @__PURE__ */ m("td", {
|
|
22
23
|
className: "p-3 w-10",
|
|
23
|
-
children: /* @__PURE__ */
|
|
24
|
+
children: /* @__PURE__ */ m("input", {
|
|
24
25
|
"aria-label": "control",
|
|
25
26
|
type: "checkbox",
|
|
26
|
-
checked:
|
|
27
|
+
checked: C ?? !1,
|
|
27
28
|
onChange: (e) => {
|
|
28
|
-
e.stopPropagation(),
|
|
29
|
+
e.stopPropagation(), w();
|
|
29
30
|
},
|
|
30
31
|
onClick: (e) => e.stopPropagation(),
|
|
31
32
|
className: "size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg"
|
|
32
33
|
})
|
|
33
34
|
}),
|
|
34
|
-
/* @__PURE__ */
|
|
35
|
+
/* @__PURE__ */ m("td", {
|
|
35
36
|
className: "px-4 py-3",
|
|
36
|
-
children: /* @__PURE__ */
|
|
37
|
+
children: /* @__PURE__ */ h("div", {
|
|
37
38
|
className: "flex items-center gap-3 min-w-0",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
39
|
+
children: [/* @__PURE__ */ m(p, { className: "size-4 text-text-secondary shrink-0" }), /* @__PURE__ */ h("div", {
|
|
39
40
|
className: "min-w-0",
|
|
40
|
-
children: [/* @__PURE__ */
|
|
41
|
+
children: [/* @__PURE__ */ m("p", {
|
|
41
42
|
className: "text-sm font-medium text-text-primary truncate",
|
|
42
43
|
children: t.name
|
|
43
|
-
}), t.command && /* @__PURE__ */
|
|
44
|
+
}), t.command && /* @__PURE__ */ h("p", {
|
|
44
45
|
className: "text-xs font-mono text-text-muted truncate max-w-[200px]",
|
|
45
46
|
children: ["$ ", t.command]
|
|
46
47
|
})]
|
|
47
48
|
})]
|
|
48
49
|
})
|
|
49
50
|
}),
|
|
50
|
-
/* @__PURE__ */
|
|
51
|
+
/* @__PURE__ */ m("td", {
|
|
51
52
|
className: "px-4 py-3",
|
|
52
|
-
children: /* @__PURE__ */
|
|
53
|
+
children: /* @__PURE__ */ h("div", {
|
|
53
54
|
className: "flex items-center gap-1.5",
|
|
54
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ m("span", {
|
|
55
56
|
className: "text-sm",
|
|
56
|
-
children:
|
|
57
|
-
}), /* @__PURE__ */
|
|
57
|
+
children: g[t.type] || "💻"
|
|
58
|
+
}), /* @__PURE__ */ m("span", {
|
|
58
59
|
className: "text-sm text-text-secondary",
|
|
59
60
|
children: t.type
|
|
60
61
|
})]
|
|
61
62
|
})
|
|
62
63
|
}),
|
|
63
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ m("td", {
|
|
64
65
|
className: "px-4 py-3",
|
|
65
|
-
children: /* @__PURE__ */
|
|
66
|
+
children: /* @__PURE__ */ m(n, { status: t.status })
|
|
66
67
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ m("td", {
|
|
68
69
|
className: "px-4 py-3",
|
|
69
|
-
children: /* @__PURE__ */
|
|
70
|
-
className: "text-sm text-text-secondary
|
|
71
|
-
children:
|
|
70
|
+
children: t.agent ? /* @__PURE__ */ m(r, { agent: t.agent }) : /* @__PURE__ */ m("span", {
|
|
71
|
+
className: "text-sm text-text-secondary",
|
|
72
|
+
children: "-"
|
|
72
73
|
})
|
|
73
74
|
}),
|
|
74
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ m("td", {
|
|
75
76
|
className: "px-4 py-3",
|
|
76
|
-
children: /* @__PURE__ */
|
|
77
|
+
children: t.agent?.target ? /* @__PURE__ */ m(i, { target: t.agent.target }) : /* @__PURE__ */ m("span", {
|
|
78
|
+
className: "text-sm text-text-secondary",
|
|
79
|
+
children: "-"
|
|
80
|
+
})
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ m("td", {
|
|
83
|
+
className: "px-4 py-3",
|
|
84
|
+
children: /* @__PURE__ */ m("span", {
|
|
77
85
|
className: "text-sm text-text-secondary truncate block max-w-[120px]",
|
|
78
86
|
children: t.vibe?.name || "-"
|
|
79
87
|
})
|
|
80
88
|
}),
|
|
81
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ m("td", {
|
|
82
90
|
className: "px-4 py-3",
|
|
83
|
-
children: /* @__PURE__ */
|
|
91
|
+
children: /* @__PURE__ */ h("div", {
|
|
84
92
|
className: "flex items-center gap-1.5 text-xs text-text-muted",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ m(o, { className: "size-3" }), ((e) => {
|
|
86
94
|
if (!e) return "Never";
|
|
87
95
|
let t = Date.now() - new Date(e).getTime();
|
|
88
96
|
return t < 6e4 ? "Just now" : t < 36e5 ? `${Math.floor(t / 6e4)}m ago` : t < 864e5 ? `${Math.floor(t / 36e5)}h ago` : new Date(e).toLocaleDateString();
|
|
89
97
|
})(t.updatedAt)]
|
|
90
98
|
})
|
|
91
99
|
}),
|
|
92
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ m("td", {
|
|
93
101
|
className: "px-4 py-3",
|
|
94
|
-
children: /* @__PURE__ */
|
|
102
|
+
children: /* @__PURE__ */ h("div", {
|
|
95
103
|
className: "flex items-center gap-1 justify-end",
|
|
96
104
|
children: [
|
|
97
|
-
|
|
105
|
+
k && _ && /* @__PURE__ */ m("button", {
|
|
98
106
|
type: "button",
|
|
99
107
|
onClick: (e) => {
|
|
100
|
-
e.stopPropagation(),
|
|
108
|
+
e.stopPropagation(), _(t);
|
|
101
109
|
},
|
|
102
110
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
103
111
|
title: "Start",
|
|
104
|
-
children: /* @__PURE__ */
|
|
112
|
+
children: /* @__PURE__ */ m(c, { className: "size-3.5 text-text-secondary" })
|
|
105
113
|
}),
|
|
106
|
-
|
|
114
|
+
O && v && /* @__PURE__ */ m("button", {
|
|
107
115
|
type: "button",
|
|
108
116
|
onClick: (e) => {
|
|
109
|
-
e.stopPropagation(),
|
|
117
|
+
e.stopPropagation(), v(t);
|
|
110
118
|
},
|
|
111
119
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
112
120
|
title: "Stop",
|
|
113
|
-
children: /* @__PURE__ */ f
|
|
121
|
+
children: /* @__PURE__ */ m(f, { className: "size-3.5 text-text-secondary" })
|
|
114
122
|
}),
|
|
115
|
-
|
|
123
|
+
O && y && /* @__PURE__ */ m("button", {
|
|
116
124
|
type: "button",
|
|
117
125
|
onClick: (e) => {
|
|
118
|
-
e.stopPropagation(),
|
|
126
|
+
e.stopPropagation(), y(t);
|
|
119
127
|
},
|
|
120
128
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
121
129
|
title: "Restart",
|
|
122
|
-
children: /* @__PURE__ */
|
|
130
|
+
children: /* @__PURE__ */ m(u, { className: "size-3.5 text-text-secondary" })
|
|
123
131
|
}),
|
|
124
|
-
|
|
132
|
+
O && x && /* @__PURE__ */ m("button", {
|
|
125
133
|
type: "button",
|
|
126
134
|
onClick: (e) => {
|
|
127
|
-
e.stopPropagation(),
|
|
135
|
+
e.stopPropagation(), x(t);
|
|
128
136
|
},
|
|
129
137
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
130
138
|
title: "Open Terminal",
|
|
131
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ m(p, { className: "size-3.5 text-text-secondary" })
|
|
132
140
|
}),
|
|
133
|
-
|
|
141
|
+
O && S && /* @__PURE__ */ m("button", {
|
|
134
142
|
type: "button",
|
|
135
143
|
onClick: (e) => {
|
|
136
|
-
e.stopPropagation(),
|
|
144
|
+
e.stopPropagation(), S(t);
|
|
137
145
|
},
|
|
138
146
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
139
147
|
title: "Share Session",
|
|
140
|
-
children: /* @__PURE__ */
|
|
148
|
+
children: /* @__PURE__ */ m(d, { className: "size-3.5 text-text-secondary" })
|
|
141
149
|
}),
|
|
142
|
-
|
|
150
|
+
b && /* @__PURE__ */ h("div", {
|
|
143
151
|
className: "relative",
|
|
144
|
-
children: [/* @__PURE__ */
|
|
152
|
+
children: [/* @__PURE__ */ m("button", {
|
|
145
153
|
type: "button",
|
|
146
154
|
onClick: (e) => {
|
|
147
|
-
e.stopPropagation(),
|
|
155
|
+
e.stopPropagation(), D(!E);
|
|
148
156
|
},
|
|
149
157
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
150
|
-
children: /* @__PURE__ */
|
|
151
|
-
}),
|
|
158
|
+
children: /* @__PURE__ */ m(s, { className: "size-3.5 text-text-secondary" })
|
|
159
|
+
}), E && /* @__PURE__ */ m("div", {
|
|
152
160
|
className: "absolute right-0 mt-1 w-36 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10",
|
|
153
|
-
children:
|
|
161
|
+
children: b && /* @__PURE__ */ m("button", {
|
|
154
162
|
type: "button",
|
|
155
163
|
onClick: (e) => {
|
|
156
|
-
e.stopPropagation(),
|
|
164
|
+
e.stopPropagation(), b(t), D(!1);
|
|
157
165
|
},
|
|
158
166
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-lg",
|
|
159
|
-
children:
|
|
167
|
+
children: T("vibecontrols.sessions.list.terminate", "Terminate")
|
|
160
168
|
})
|
|
161
169
|
})]
|
|
162
170
|
})
|
|
@@ -166,79 +174,84 @@ function h({ session: t, onOpen: s, onStart: h, onStop: g, onRestart: _, onTermi
|
|
|
166
174
|
]
|
|
167
175
|
});
|
|
168
176
|
}
|
|
169
|
-
function
|
|
170
|
-
let
|
|
171
|
-
return !
|
|
172
|
-
icon: /* @__PURE__ */
|
|
173
|
-
title:
|
|
174
|
-
description:
|
|
175
|
-
action:
|
|
177
|
+
function v({ sessions: n, onOpen: r, onStart: i, onStop: a, onRestart: o, onTerminate: s, onOpenTerminal: c, onShare: u, onCreateNew: d, isLoading: f, selectedIds: g, onToggleSelect: v, onSelectAll: y, onDeselectAll: b, hideHeaderSelectAll: x = !1 }) {
|
|
178
|
+
let S = e(), C = g && n.length > 0 && g.size === n.length;
|
|
179
|
+
return !f && n.length === 0 ? /* @__PURE__ */ m(t, {
|
|
180
|
+
icon: /* @__PURE__ */ m(p, { className: "size-8 text-text-secondary" }),
|
|
181
|
+
title: S("vibecontrols.sessions.list.noSessionsTitle", "No sessions yet"),
|
|
182
|
+
description: S("vibecontrols.sessions.list.noSessionsDescription", "Sessions are terminal processes running on your agents. Start a session to run commands, scripts, or interactive shells."),
|
|
183
|
+
action: d && /* @__PURE__ */ h("button", {
|
|
176
184
|
type: "button",
|
|
177
|
-
onClick:
|
|
185
|
+
onClick: d,
|
|
178
186
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
179
|
-
children: [/* @__PURE__ */
|
|
187
|
+
children: [/* @__PURE__ */ m(l, { className: "size-4" }), S("vibecontrols.sessions.list.startSession", "Start Session")]
|
|
180
188
|
})
|
|
181
|
-
}) : /* @__PURE__ */
|
|
189
|
+
}) : /* @__PURE__ */ m("div", {
|
|
182
190
|
className: "border border-border-default rounded-lg overflow-visible",
|
|
183
|
-
children: /* @__PURE__ */
|
|
191
|
+
children: /* @__PURE__ */ m("div", {
|
|
184
192
|
className: "overflow-x-auto",
|
|
185
|
-
children: /* @__PURE__ */
|
|
193
|
+
children: /* @__PURE__ */ h("table", {
|
|
186
194
|
className: "w-full",
|
|
187
|
-
children: [/* @__PURE__ */
|
|
195
|
+
children: [/* @__PURE__ */ m("thead", { children: /* @__PURE__ */ h("tr", {
|
|
188
196
|
className: "bg-bg-sunken border-b border-border-default",
|
|
189
197
|
children: [
|
|
190
|
-
v && /* @__PURE__ */
|
|
198
|
+
v && x && /* @__PURE__ */ m("th", { className: "px-3 py-2.5 w-10" }),
|
|
199
|
+
v && !x && /* @__PURE__ */ m("th", {
|
|
191
200
|
className: "px-3 py-2.5 w-10",
|
|
192
|
-
children: /* @__PURE__ */
|
|
201
|
+
children: /* @__PURE__ */ m("input", {
|
|
193
202
|
"aria-label": "control",
|
|
194
203
|
type: "checkbox",
|
|
195
|
-
checked:
|
|
204
|
+
checked: C ?? !1,
|
|
196
205
|
onChange: () => {
|
|
197
|
-
|
|
206
|
+
C && b ? b() : y && y();
|
|
198
207
|
},
|
|
199
208
|
className: "size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg",
|
|
200
|
-
title:
|
|
209
|
+
title: C ? "Deselect all" : "Select all"
|
|
201
210
|
})
|
|
202
211
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
212
|
+
/* @__PURE__ */ m("th", {
|
|
213
|
+
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
214
|
+
children: S("vibecontrols.sessions.list.colName", "Name")
|
|
215
|
+
}),
|
|
216
|
+
/* @__PURE__ */ m("th", {
|
|
204
217
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
205
|
-
children:
|
|
218
|
+
children: S("vibecontrols.sessions.list.colType", "Type")
|
|
206
219
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ m("th", {
|
|
208
221
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
209
|
-
children:
|
|
222
|
+
children: S("vibecontrols.sessions.list.colStatus", "Status")
|
|
210
223
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
224
|
+
/* @__PURE__ */ m("th", {
|
|
212
225
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
213
|
-
children:
|
|
226
|
+
children: S("vibecontrols.sessions.list.colAgent", "Agent")
|
|
214
227
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ m("th", {
|
|
216
229
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
217
|
-
children:
|
|
230
|
+
children: S("vibecontrols.sessions.list.colTarget", "Target")
|
|
218
231
|
}),
|
|
219
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ m("th", {
|
|
220
233
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
221
|
-
children:
|
|
234
|
+
children: S("vibecontrols.sessions.list.colVibe", "Vibe")
|
|
222
235
|
}),
|
|
223
|
-
/* @__PURE__ */
|
|
236
|
+
/* @__PURE__ */ m("th", {
|
|
224
237
|
className: "px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
225
|
-
children:
|
|
238
|
+
children: S("vibecontrols.sessions.list.colUpdated", "Updated")
|
|
226
239
|
}),
|
|
227
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ m("th", {
|
|
228
241
|
className: "px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
229
|
-
children:
|
|
242
|
+
children: S("vibecontrols.sessions.list.colActions", "Actions")
|
|
230
243
|
})
|
|
231
244
|
]
|
|
232
|
-
}) }), /* @__PURE__ */
|
|
245
|
+
}) }), /* @__PURE__ */ m("tbody", { children: n.map((e) => /* @__PURE__ */ m(_, {
|
|
233
246
|
session: e,
|
|
234
247
|
onOpen: r,
|
|
235
248
|
onStart: i,
|
|
236
249
|
onStop: a,
|
|
237
250
|
onRestart: o,
|
|
238
|
-
onTerminate:
|
|
239
|
-
onOpenTerminal:
|
|
251
|
+
onTerminate: s,
|
|
252
|
+
onOpenTerminal: c,
|
|
240
253
|
onShare: u,
|
|
241
|
-
selected:
|
|
254
|
+
selected: g?.has(e.id),
|
|
242
255
|
onToggleSelect: v ? () => v(e.id) : void 0
|
|
243
256
|
}, e.id)) })]
|
|
244
257
|
})
|
|
@@ -246,6 +259,6 @@ function g({ sessions: n, onOpen: r, onStart: i, onStop: a, onRestart: o, onTerm
|
|
|
246
259
|
});
|
|
247
260
|
}
|
|
248
261
|
//#endregion
|
|
249
|
-
export {
|
|
262
|
+
export { v as SessionList };
|
|
250
263
|
|
|
251
264
|
//# sourceMappingURL=SessionList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionList.js","names":[],"sources":["../../../src/components/sessions/SessionList.tsx"],"sourcesContent":["import type { Session } from './types';\nimport { SessionStatusBadge } from './SessionStatusBadge';\nimport { EmptyState } from '../shared/EmptyState';\nimport { Terminal, Plus, Play, Square, RotateCcw, MoreVertical, Clock, Share2 } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface SessionListProps {\n sessions: Session[];\n onOpen?: (session: Session) => void;\n onStart?: (session: Session) => void;\n onStop?: (session: Session) => void;\n onRestart?: (session: Session) => void;\n onTerminate?: (session: Session) => void;\n onOpenTerminal?: (session: Session) => void;\n onShare?: (session: Session) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n /** Set of selected session IDs for bulk operations */\n selectedIds?: Set<string>;\n /** Toggle selection for a single session */\n onToggleSelect?: (sessionId: string) => void;\n /** Select all visible sessions */\n onSelectAll?: () => void;\n /** Deselect all sessions */\n onDeselectAll?: () => void;\n}\n\nconst sessionTypeIcons: Record<string, string> = {\n TMUX: '🖥️',\n SSH: '🔐',\n TERMINAL: '💻',\n SCRIPT: '📜',\n CUSTOM: '⚙️',\n};\n\nfunction SessionRow({\n session,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n selected,\n onToggleSelect,\n}: {\n session: Session;\n onOpen?: (session: Session) => void;\n onStart?: (session: Session) => void;\n onStop?: (session: Session) => void;\n onRestart?: (session: Session) => void;\n onTerminate?: (session: Session) => void;\n onOpenTerminal?: (session: Session) => void;\n onShare?: (session: Session) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n const tr = useTr();\n const [showMenu, setShowMenu] = useState(false);\n const isRunning = session.status === 'RUNNING';\n const isStopped = session.status === 'STOPPED' || session.status === 'TERMINATED';\n\n const formatTime = (timestamp: string | undefined | null) => {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return new Date(timestamp).toLocaleDateString();\n };\n\n return (\n <tr\n className={`border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${selected ? 'bg-action-primary-bg/5' : ''}`}\n onClick={() => onOpen?.(session)}\n >\n {/* Selection checkbox */}\n {onToggleSelect && (\n <td className=\"p-3 w-10\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selected ?? false}\n onChange={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n </td>\n )}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <Terminal className=\"size-4 text-text-secondary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{session.name}</p>\n {session.command && (\n <p className=\"text-xs font-mono text-text-muted truncate max-w-[200px]\">\n $ {session.command}\n </p>\n )}\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-sm\">{sessionTypeIcons[session.type] || '💻'}</span>\n <span className=\"text-sm text-text-secondary\">{session.type}</span>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <SessionStatusBadge status={session.status} />\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary truncate block max-w-[120px]\">\n {session.agent?.name || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary truncate block max-w-[120px]\">\n {session.vibe?.name || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Clock className=\"size-3\" />\n {formatTime(session.updatedAt)}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {isStopped && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onRestart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRestart(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Restart\"\n >\n <RotateCcw className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onOpenTerminal && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onOpenTerminal(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Open Terminal\"\n >\n <Terminal className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onShare && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onShare(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Share Session\"\n >\n <Share2 className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {onTerminate && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-3.5 text-text-secondary\" />\n </button>\n {showMenu && (\n <div className=\"absolute right-0 mt-1 w-36 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\">\n {onTerminate && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onTerminate(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-lg\"\n >\n {tr('vibecontrols.sessions.list.terminate', 'Terminate')}\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function SessionList({\n sessions,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n onCreateNew,\n isLoading,\n selectedIds,\n onToggleSelect,\n onSelectAll,\n onDeselectAll,\n}: SessionListProps) {\n const tr = useTr();\n const isAllSelected = selectedIds && sessions.length > 0 && selectedIds.size === sessions.length;\n\n if (!isLoading && sessions.length === 0) {\n return (\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.sessions.list.noSessionsTitle', 'No sessions yet')}\n description={tr(\n 'vibecontrols.sessions.list.noSessionsDescription',\n 'Sessions are terminal processes running on your agents. Start a session to run commands, scripts, or interactive shells.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.sessions.list.startSession', 'Start Session')}\n </button>\n )\n }\n />\n );\n }\n\n return (\n <div className=\"border border-border-default rounded-lg overflow-visible\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n {onToggleSelect && (\n <th className=\"px-3 py-2.5 w-10\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={isAllSelected ?? false}\n onChange={() => {\n if (isAllSelected && onDeselectAll) {\n onDeselectAll();\n } else if (onSelectAll) {\n onSelectAll();\n }\n }}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n title={isAllSelected ? 'Deselect all' : 'Select all'}\n />\n </th>\n )}\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colName', 'Name')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colType', 'Type')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colStatus', 'Status')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colAgent', 'Agent')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colVibe', 'Vibe')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colUpdated', 'Updated')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {sessions.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n onOpen={onOpen}\n onStart={onStart}\n onStop={onStop}\n onRestart={onRestart}\n onTerminate={onTerminate}\n onOpenTerminal={onOpenTerminal}\n onShare={onShare}\n selected={selectedIds?.has(session.id)}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(session.id) : undefined}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionList.js","names":[],"sources":["../../../src/components/sessions/SessionList.tsx"],"sourcesContent":["import type { Session } from './types';\nimport { SessionStatusBadge } from './SessionStatusBadge';\nimport { AgentInfoPopover, TargetInfoPopover } from './EntityInfoPopover';\nimport { EmptyState } from '../shared/EmptyState';\nimport { Terminal, Plus, Play, Square, RotateCcw, MoreVertical, Clock, Share2 } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface SessionListProps {\n sessions: Session[];\n onOpen?: (session: Session) => void;\n onStart?: (session: Session) => void;\n onStop?: (session: Session) => void;\n onRestart?: (session: Session) => void;\n onTerminate?: (session: Session) => void;\n onOpenTerminal?: (session: Session) => void;\n onShare?: (session: Session) => void;\n onCreateNew?: () => void;\n isLoading?: boolean;\n /** Set of selected session IDs for bulk operations */\n selectedIds?: Set<string>;\n /** Toggle selection for a single session */\n onToggleSelect?: (sessionId: string) => void;\n /** Select all visible sessions */\n onSelectAll?: () => void;\n /** Deselect all sessions */\n onDeselectAll?: () => void;\n /**\n * Hide the table-header \"select all\" checkbox. Used when the list is\n * rendered inside a group (the group header owns selection instead),\n * so two competing select-alls don't fight over the same Set.\n */\n hideHeaderSelectAll?: boolean;\n}\n\nconst sessionTypeIcons: Record<string, string> = {\n TMUX: '🖥️',\n SSH: '🔐',\n TERMINAL: '💻',\n SCRIPT: '📜',\n CUSTOM: '⚙️',\n};\n\nfunction SessionRow({\n session,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n selected,\n onToggleSelect,\n}: {\n session: Session;\n onOpen?: (session: Session) => void;\n onStart?: (session: Session) => void;\n onStop?: (session: Session) => void;\n onRestart?: (session: Session) => void;\n onTerminate?: (session: Session) => void;\n onOpenTerminal?: (session: Session) => void;\n onShare?: (session: Session) => void;\n selected?: boolean;\n onToggleSelect?: () => void;\n}) {\n const tr = useTr();\n const [showMenu, setShowMenu] = useState(false);\n const isRunning = session.status === 'RUNNING';\n const isStopped = session.status === 'STOPPED' || session.status === 'TERMINATED';\n\n const formatTime = (timestamp: string | undefined | null) => {\n if (!timestamp) return 'Never';\n const diff = Date.now() - new Date(timestamp).getTime();\n if (diff < 60000) return 'Just now';\n if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;\n if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;\n return new Date(timestamp).toLocaleDateString();\n };\n\n return (\n <tr\n className={`border-b border-border-subtle hover:bg-bg-sunken/50 cursor-pointer transition-colors ${selected ? 'bg-action-primary-bg/5' : ''}`}\n onClick={() => onOpen?.(session)}\n >\n {/* Selection checkbox */}\n {onToggleSelect && (\n <td className=\"p-3 w-10\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selected ?? false}\n onChange={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n </td>\n )}\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <Terminal className=\"size-4 text-text-secondary shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{session.name}</p>\n {session.command && (\n <p className=\"text-xs font-mono text-text-muted truncate max-w-[200px]\">\n $ {session.command}\n </p>\n )}\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-sm\">{sessionTypeIcons[session.type] || '💻'}</span>\n <span className=\"text-sm text-text-secondary\">{session.type}</span>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <SessionStatusBadge status={session.status} />\n </td>\n <td className=\"px-4 py-3\">\n {session.agent ? (\n <AgentInfoPopover agent={session.agent} />\n ) : (\n <span className=\"text-sm text-text-secondary\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n {session.agent?.target ? (\n <TargetInfoPopover target={session.agent.target} />\n ) : (\n <span className=\"text-sm text-text-secondary\">-</span>\n )}\n </td>\n <td className=\"px-4 py-3\">\n <span className=\"text-sm text-text-secondary truncate block max-w-[120px]\">\n {session.vibe?.name || '-'}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1.5 text-xs text-text-muted\">\n <Clock className=\"size-3\" />\n {formatTime(session.updatedAt)}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-1 justify-end\">\n {isStopped && onStart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStart(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Start\"\n >\n <Play className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onStop && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onStop(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Stop\"\n >\n <Square className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onRestart && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onRestart(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Restart\"\n >\n <RotateCcw className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onOpenTerminal && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onOpenTerminal(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Open Terminal\"\n >\n <Terminal className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {isRunning && onShare && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onShare(session);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n title=\"Share Session\"\n >\n <Share2 className=\"size-3.5 text-text-secondary\" />\n </button>\n )}\n {onTerminate && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-3.5 text-text-secondary\" />\n </button>\n {showMenu && (\n <div className=\"absolute right-0 mt-1 w-36 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\">\n {onTerminate && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onTerminate(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-lg\"\n >\n {tr('vibecontrols.sessions.list.terminate', 'Terminate')}\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </td>\n </tr>\n );\n}\n\nexport function SessionList({\n sessions,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n onCreateNew,\n isLoading,\n selectedIds,\n onToggleSelect,\n onSelectAll,\n onDeselectAll,\n hideHeaderSelectAll = false,\n}: SessionListProps) {\n const tr = useTr();\n const isAllSelected = selectedIds && sessions.length > 0 && selectedIds.size === sessions.length;\n\n if (!isLoading && sessions.length === 0) {\n return (\n <EmptyState\n icon={<Terminal className=\"size-8 text-text-secondary\" />}\n title={tr('vibecontrols.sessions.list.noSessionsTitle', 'No sessions yet')}\n description={tr(\n 'vibecontrols.sessions.list.noSessionsDescription',\n 'Sessions are terminal processes running on your agents. Start a session to run commands, scripts, or interactive shells.'\n )}\n action={\n onCreateNew && (\n <button\n type=\"button\"\n onClick={onCreateNew}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"size-4\" />\n {tr('vibecontrols.sessions.list.startSession', 'Start Session')}\n </button>\n )\n }\n />\n );\n }\n\n return (\n <div className=\"border border-border-default rounded-lg overflow-visible\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"bg-bg-sunken border-b border-border-default\">\n {onToggleSelect && hideHeaderSelectAll && <th className=\"px-3 py-2.5 w-10\" />}\n {onToggleSelect && !hideHeaderSelectAll && (\n <th className=\"px-3 py-2.5 w-10\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={isAllSelected ?? false}\n onChange={() => {\n if (isAllSelected && onDeselectAll) {\n onDeselectAll();\n } else if (onSelectAll) {\n onSelectAll();\n }\n }}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n title={isAllSelected ? 'Deselect all' : 'Select all'}\n />\n </th>\n )}\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colName', 'Name')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colType', 'Type')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colStatus', 'Status')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colAgent', 'Agent')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colTarget', 'Target')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colVibe', 'Vibe')}\n </th>\n <th className=\"px-4 py-2.5 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colUpdated', 'Updated')}\n </th>\n <th className=\"px-4 py-2.5 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n {tr('vibecontrols.sessions.list.colActions', 'Actions')}\n </th>\n </tr>\n </thead>\n <tbody>\n {sessions.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n onOpen={onOpen}\n onStart={onStart}\n onStop={onStop}\n onRestart={onRestart}\n onTerminate={onTerminate}\n onOpenTerminal={onOpenTerminal}\n onShare={onShare}\n selected={selectedIds?.has(session.id)}\n onToggleSelect={onToggleSelect ? () => onToggleSelect(session.id) : undefined}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|