@burdenoff/microfe-vibecontrols 2026.601.6 → 2026.601.8
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/agents/AgentForm.js +1 -1
- package/dist/components/agents/AgentForm.js.map +1 -1
- 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/AgentSetupBanner.js +1 -1
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/shared/FilterDropdown.js +14 -13
- package/dist/components/shared/FilterDropdown.js.map +1 -1
- package/dist/components/shared/InstallTabs.js +2 -2
- package/dist/components/shared/InstallTabs.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
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { SessionStatusBadge as t } from "./SessionStatusBadge.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { AgentInfoPopover as n, TargetInfoPopover as r } from "./EntityInfoPopover.js";
|
|
4
|
+
import { useState as i } from "react";
|
|
5
|
+
import { Clock as a, MoreVertical as o, Play as s, RotateCcw as c, Share2 as l, Square as u, Terminal as d, Trash2 as f } from "lucide-react";
|
|
6
|
+
import { AnimatePresence as p, motion as m } from "framer-motion";
|
|
7
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/sessions/SessionCard.tsx
|
|
8
|
-
var
|
|
9
|
+
var _ = {
|
|
9
10
|
TMUX: "🖥️",
|
|
10
11
|
SSH: "🔐",
|
|
11
12
|
TERMINAL: "💻",
|
|
12
13
|
SCRIPT: "📜",
|
|
13
14
|
CUSTOM: "⚙️"
|
|
14
|
-
},
|
|
15
|
-
let
|
|
15
|
+
}, v = ({ session: v, onOpen: y, onStart: b, onStop: x, onRestart: S, onTerminate: C, onOpenTerminal: w, onShare: T, selected: E = !1, onToggleSelect: D, className: O = "" }) => {
|
|
16
|
+
let k = e(), [A, j] = i(!1), M = (e) => {
|
|
16
17
|
if (!e) return "Never";
|
|
17
18
|
let t = Date.now() - new Date(e).getTime();
|
|
18
19
|
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();
|
|
19
|
-
},
|
|
20
|
-
return /* @__PURE__ */ m
|
|
20
|
+
}, N = v.status === "RUNNING", P = v.status === "STOPPED" || v.status === "TERMINATED";
|
|
21
|
+
return /* @__PURE__ */ g(m.div, {
|
|
21
22
|
whileHover: {
|
|
22
23
|
y: -2,
|
|
23
24
|
transition: {
|
|
@@ -31,60 +32,67 @@ var h = {
|
|
|
31
32
|
}
|
|
32
33
|
},
|
|
33
34
|
whileTap: { scale: .98 },
|
|
34
|
-
className: `bg-bg-surface border rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${
|
|
35
|
-
onClick: () =>
|
|
35
|
+
className: `bg-bg-surface border rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${E ? "border-action-primary-bg ring-1 ring-action-primary-bg/30" : "border-border-default"} ${O}`,
|
|
36
|
+
onClick: () => y?.(v),
|
|
36
37
|
children: [
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
+
/* @__PURE__ */ g("div", {
|
|
38
39
|
className: "flex items-start justify-between gap-2",
|
|
39
|
-
children: [/* @__PURE__ */
|
|
40
|
+
children: [/* @__PURE__ */ g("div", {
|
|
40
41
|
className: "flex items-start gap-2 sm:gap-3 min-w-0 flex-1",
|
|
41
42
|
children: [
|
|
42
|
-
|
|
43
|
+
D && /* @__PURE__ */ h("div", {
|
|
43
44
|
className: "flex-shrink-0 pt-0.5",
|
|
44
|
-
children: /* @__PURE__ */
|
|
45
|
+
children: /* @__PURE__ */ h("input", {
|
|
45
46
|
"aria-label": "control",
|
|
46
47
|
type: "checkbox",
|
|
47
|
-
checked:
|
|
48
|
+
checked: E,
|
|
48
49
|
onChange: (e) => {
|
|
49
|
-
e.stopPropagation(),
|
|
50
|
+
e.stopPropagation(), D();
|
|
50
51
|
},
|
|
51
52
|
onClick: (e) => e.stopPropagation(),
|
|
52
53
|
className: "size-4 rounded border-border-default cursor-pointer"
|
|
53
54
|
})
|
|
54
55
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
+
/* @__PURE__ */ h("div", {
|
|
56
57
|
className: "flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center",
|
|
57
|
-
children: /* @__PURE__ */
|
|
58
|
+
children: /* @__PURE__ */ h(d, { className: "size-4 sm:w-5 sm:h-5 text-text-secondary" })
|
|
58
59
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ g("div", {
|
|
60
61
|
className: "min-w-0 flex-1",
|
|
61
|
-
children: [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ g("div", {
|
|
64
|
+
className: "flex items-center gap-1.5 sm:gap-2 flex-wrap",
|
|
65
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
66
|
+
className: "font-semibold text-text-primary truncate max-w-[120px] sm:max-w-none text-sm sm:text-base",
|
|
67
|
+
children: v.name
|
|
68
|
+
}), /* @__PURE__ */ h("span", {
|
|
69
|
+
className: "text-base sm:text-lg flex-shrink-0",
|
|
70
|
+
children: _[v.type] || "💻"
|
|
71
|
+
})]
|
|
72
|
+
}),
|
|
73
|
+
/* @__PURE__ */ h("p", {
|
|
74
|
+
className: "text-xs sm:text-sm text-text-secondary",
|
|
75
|
+
children: v.type
|
|
76
|
+
}),
|
|
77
|
+
v.agent && /* @__PURE__ */ g("div", {
|
|
78
|
+
className: "mt-1.5 flex items-center gap-1 flex-wrap",
|
|
79
|
+
children: [/* @__PURE__ */ h(n, { agent: v.agent }), v.agent.target && /* @__PURE__ */ h(r, { target: v.agent.target })]
|
|
80
|
+
})
|
|
81
|
+
]
|
|
74
82
|
})
|
|
75
83
|
]
|
|
76
|
-
}), /* @__PURE__ */
|
|
84
|
+
}), /* @__PURE__ */ g("div", {
|
|
77
85
|
className: "flex items-center gap-1 sm:gap-2 flex-shrink-0",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ h(t, { status: v.status }), (b || x || S || C) && /* @__PURE__ */ g("div", {
|
|
79
87
|
className: "relative",
|
|
80
|
-
children: [/* @__PURE__ */
|
|
88
|
+
children: [/* @__PURE__ */ h("button", {
|
|
81
89
|
type: "button",
|
|
82
90
|
onClick: (e) => {
|
|
83
|
-
e.stopPropagation(),
|
|
91
|
+
e.stopPropagation(), j(!A);
|
|
84
92
|
},
|
|
85
93
|
className: "p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
86
|
-
children: /* @__PURE__ */
|
|
87
|
-
}), /* @__PURE__ */ p
|
|
94
|
+
children: /* @__PURE__ */ h(o, { className: "size-4 text-text-secondary" })
|
|
95
|
+
}), /* @__PURE__ */ h(p, { children: A && /* @__PURE__ */ g(m.div, {
|
|
88
96
|
initial: {
|
|
89
97
|
opacity: 0,
|
|
90
98
|
scale: .95,
|
|
@@ -103,53 +111,53 @@ var h = {
|
|
|
103
111
|
transition: { duration: .15 },
|
|
104
112
|
className: "absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10",
|
|
105
113
|
children: [
|
|
106
|
-
|
|
114
|
+
P && b && /* @__PURE__ */ g(m.button, {
|
|
107
115
|
whileTap: { scale: .97 },
|
|
108
116
|
onClick: (e) => {
|
|
109
|
-
e.stopPropagation(), v
|
|
117
|
+
e.stopPropagation(), b(v), j(!1);
|
|
110
118
|
},
|
|
111
119
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ h(s, { className: "size-4" }), k("vibecontrols.sessions.card.start", "Start")]
|
|
113
121
|
}),
|
|
114
|
-
|
|
122
|
+
N && x && /* @__PURE__ */ g(m.button, {
|
|
115
123
|
whileTap: { scale: .97 },
|
|
116
124
|
onClick: (e) => {
|
|
117
|
-
e.stopPropagation(),
|
|
125
|
+
e.stopPropagation(), x(v), j(!1);
|
|
118
126
|
},
|
|
119
127
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
128
|
+
children: [/* @__PURE__ */ h(u, { className: "size-4" }), k("vibecontrols.sessions.card.stop", "Stop")]
|
|
121
129
|
}),
|
|
122
|
-
|
|
130
|
+
N && S && /* @__PURE__ */ g(m.button, {
|
|
123
131
|
whileTap: { scale: .97 },
|
|
124
132
|
onClick: (e) => {
|
|
125
|
-
e.stopPropagation(),
|
|
133
|
+
e.stopPropagation(), S(v), j(!1);
|
|
126
134
|
},
|
|
127
135
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
136
|
+
children: [/* @__PURE__ */ h(c, { className: "size-4" }), k("vibecontrols.sessions.card.restart", "Restart")]
|
|
129
137
|
}),
|
|
130
|
-
|
|
138
|
+
N && w && /* @__PURE__ */ g(m.button, {
|
|
131
139
|
whileTap: { scale: .97 },
|
|
132
140
|
onClick: (e) => {
|
|
133
|
-
e.stopPropagation(),
|
|
141
|
+
e.stopPropagation(), w(v), j(!1);
|
|
134
142
|
},
|
|
135
143
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ h(d, { className: "size-4" }), k("vibecontrols.sessions.card.openTerminal", "Open Terminal")]
|
|
137
145
|
}),
|
|
138
|
-
|
|
146
|
+
N && T && /* @__PURE__ */ g(m.button, {
|
|
139
147
|
whileTap: { scale: .97 },
|
|
140
148
|
onClick: (e) => {
|
|
141
|
-
e.stopPropagation(),
|
|
149
|
+
e.stopPropagation(), T(v), j(!1);
|
|
142
150
|
},
|
|
143
151
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken",
|
|
144
|
-
children: [/* @__PURE__ */
|
|
152
|
+
children: [/* @__PURE__ */ h(l, { className: "size-4" }), k("vibecontrols.sessions.card.shareSession", "Share Session")]
|
|
145
153
|
}),
|
|
146
|
-
|
|
154
|
+
C && /* @__PURE__ */ g(m.button, {
|
|
147
155
|
whileTap: { scale: .97 },
|
|
148
156
|
onClick: (e) => {
|
|
149
|
-
e.stopPropagation(),
|
|
157
|
+
e.stopPropagation(), C(v), j(!1);
|
|
150
158
|
},
|
|
151
159
|
className: "flex items-center gap-2 w-full px-3 py-2 text-sm text-status-error-text hover:bg-bg-sunken rounded-b-lg",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ h(f, { className: "size-4" }), k("vibecontrols.sessions.card.terminate", "Terminate")]
|
|
153
161
|
})
|
|
154
162
|
]
|
|
155
163
|
}) })]
|
|
@@ -157,42 +165,42 @@ var h = {
|
|
|
157
165
|
})]
|
|
158
166
|
}),
|
|
159
167
|
(() => {
|
|
160
|
-
let e =
|
|
161
|
-
return t ? /* @__PURE__ */
|
|
168
|
+
let e = v.metadata || {}, t = typeof e.tmuxSessionName == "string" && e.tmuxSessionName || typeof e.weztermWorkspace == "string" && e.weztermWorkspace || typeof e.zellijSessionName == "string" && e.zellijSessionName || typeof e.externalSessionName == "string" && e.externalSessionName || v.providerSessionName || null;
|
|
169
|
+
return t ? /* @__PURE__ */ g("div", {
|
|
162
170
|
className: "mt-2 text-[10px] font-mono text-text-tertiary",
|
|
163
171
|
title: `Provider session: ${t}`,
|
|
164
172
|
"data-testid": "session-card-external-name",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ g("span", {
|
|
166
174
|
className: "opacity-70",
|
|
167
|
-
children: [
|
|
168
|
-
}), /* @__PURE__ */
|
|
175
|
+
children: [v.type.toLowerCase(), ": "]
|
|
176
|
+
}), /* @__PURE__ */ h("span", {
|
|
169
177
|
className: "break-all",
|
|
170
178
|
children: t
|
|
171
179
|
})]
|
|
172
180
|
}) : null;
|
|
173
181
|
})(),
|
|
174
|
-
|
|
182
|
+
v.command && /* @__PURE__ */ g("div", {
|
|
175
183
|
className: "mt-3 text-xs font-mono bg-bg-sunken p-2 rounded truncate text-text-secondary",
|
|
176
|
-
children: ["$ ",
|
|
184
|
+
children: ["$ ", v.command]
|
|
177
185
|
}),
|
|
178
|
-
|
|
186
|
+
v.lastOutput && /* @__PURE__ */ h("div", {
|
|
179
187
|
className: "mt-2 text-xs font-mono bg-bg-sunken text-status-success-text p-2 rounded truncate",
|
|
180
|
-
children:
|
|
188
|
+
children: v.lastOutput
|
|
181
189
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
190
|
+
/* @__PURE__ */ g("div", {
|
|
183
191
|
className: "mt-3 flex items-center justify-between text-sm text-text-secondary",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
192
|
+
children: [/* @__PURE__ */ g("div", {
|
|
185
193
|
className: "flex items-center gap-2",
|
|
186
|
-
children: [/* @__PURE__ */
|
|
187
|
-
}),
|
|
194
|
+
children: [/* @__PURE__ */ h(a, { className: "size-4" }), /* @__PURE__ */ h("span", { children: M(v.updatedAt) })]
|
|
195
|
+
}), v.pid && /* @__PURE__ */ g("span", {
|
|
188
196
|
className: "text-xs font-mono",
|
|
189
|
-
children: ["PID: ",
|
|
197
|
+
children: ["PID: ", v.pid]
|
|
190
198
|
})]
|
|
191
199
|
})
|
|
192
200
|
]
|
|
193
201
|
});
|
|
194
202
|
};
|
|
195
203
|
//#endregion
|
|
196
|
-
export {
|
|
204
|
+
export { v as SessionCard };
|
|
197
205
|
|
|
198
206
|
//# sourceMappingURL=SessionCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionCard.js","names":[],"sources":["../../../src/components/sessions/SessionCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Terminal,\n Clock,\n MoreVertical,\n Play,\n Square,\n RotateCcw,\n Trash2,\n Share2,\n} from 'lucide-react';\nimport { SessionStatusBadge } from './SessionStatusBadge';\nimport type { Session } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface SessionCardProps {\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 /** Callback to open the share dialog */\n onShare?: (session: Session) => void;\n /** Whether this card is selected for bulk operations */\n selected?: boolean;\n /** Toggle selection for bulk operations */\n onToggleSelect?: () => void;\n className?: string;\n}\n\nconst sessionTypeIcons: Record<string, string> = {\n TMUX: '🖥️',\n SSH: '🔐',\n TERMINAL: '💻',\n SCRIPT: '📜',\n CUSTOM: '⚙️',\n};\n\n/**\n * SessionCard - Display terminal session information\n */\nexport const SessionCard: FC<SessionCardProps> = ({\n session,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n selected = false,\n onToggleSelect,\n className = '',\n}) => {\n const tr = useTr();\n const [showMenu, setShowMenu] = useState(false);\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 const isRunning = session.status === 'RUNNING';\n const isStopped = session.status === 'STOPPED' || session.status === 'TERMINATED';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${selected ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30' : 'border-border-default'} ${className}`}\n onClick={() => onOpen?.(session)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div className=\"flex-shrink-0 pt-0.5\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"size-4 rounded border-border-default cursor-pointer\"\n />\n </div>\n )}\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Terminal className=\"size-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary truncate max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {session.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">\n {sessionTypeIcons[session.type] || '💻'}\n </span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary\">{session.type}</p>\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <SessionStatusBadge status={session.status} />\n {(onStart || onStop || onRestart || onTerminate) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {isStopped && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"size-4\" />\n {tr('vibecontrols.sessions.card.start', 'Start')}\n </motion.button>\n )}\n {isRunning && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Square className=\"size-4\" />\n {tr('vibecontrols.sessions.card.stop', 'Stop')}\n </motion.button>\n )}\n {isRunning && onRestart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onRestart(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <RotateCcw className=\"size-4\" />\n {tr('vibecontrols.sessions.card.restart', 'Restart')}\n </motion.button>\n )}\n {isRunning && onOpenTerminal && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onOpenTerminal(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Terminal className=\"size-4\" />\n {tr('vibecontrols.sessions.card.openTerminal', 'Open Terminal')}\n </motion.button>\n )}\n {isRunning && onShare && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n {tr('vibecontrols.sessions.card.shareSession', 'Share Session')}\n </motion.button>\n )}\n {onTerminate && (\n <motion.button\n whileTap={{ scale: 0.97 }}\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-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n {tr('vibecontrols.sessions.card.terminate', 'Terminate')}\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n {/* Provider-native session name (e.g. tmux session name like\n `vibe-051c9c62`). Surfaced so users can recognise their session\n if they ever need to attach from a CLI (`tmux attach -t …`)\n and so reconnect-after-restart flows are obvious. */}\n {(() => {\n const meta = (session.metadata as Record<string, unknown> | null) || {};\n const externalName =\n (typeof meta.tmuxSessionName === 'string' && meta.tmuxSessionName) ||\n (typeof meta.weztermWorkspace === 'string' && meta.weztermWorkspace) ||\n (typeof meta.zellijSessionName === 'string' && meta.zellijSessionName) ||\n (typeof meta.externalSessionName === 'string' && meta.externalSessionName) ||\n session.providerSessionName ||\n null;\n return externalName ? (\n <div\n className=\"mt-2 text-[10px] font-mono text-text-tertiary\"\n title={`Provider session: ${externalName}`}\n data-testid=\"session-card-external-name\"\n >\n <span className=\"opacity-70\">{session.type.toLowerCase()}: </span>\n <span className=\"break-all\">{externalName}</span>\n </div>\n ) : null;\n })()}\n\n {session.command && (\n <div className=\"mt-3 text-xs font-mono bg-bg-sunken p-2 rounded truncate text-text-secondary\">\n $ {session.command}\n </div>\n )}\n\n {session.lastOutput && (\n <div className=\"mt-2 text-xs font-mono bg-bg-sunken text-status-success-text p-2 rounded truncate\">\n {session.lastOutput}\n </div>\n )}\n\n <div className=\"mt-3 flex items-center justify-between text-sm text-text-secondary\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-4\" />\n <span>{formatTime(session.updatedAt)}</span>\n </div>\n {session.pid && <span className=\"text-xs font-mono\">PID: {session.pid}</span>}\n </div>\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionCard.js","names":[],"sources":["../../../src/components/sessions/SessionCard.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n Terminal,\n Clock,\n MoreVertical,\n Play,\n Square,\n RotateCcw,\n Trash2,\n Share2,\n} from 'lucide-react';\nimport { SessionStatusBadge } from './SessionStatusBadge';\nimport { AgentInfoPopover, TargetInfoPopover } from './EntityInfoPopover';\nimport type { Session } from './types';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface SessionCardProps {\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 /** Callback to open the share dialog */\n onShare?: (session: Session) => void;\n /** Whether this card is selected for bulk operations */\n selected?: boolean;\n /** Toggle selection for bulk operations */\n onToggleSelect?: () => void;\n className?: string;\n}\n\nconst sessionTypeIcons: Record<string, string> = {\n TMUX: '🖥️',\n SSH: '🔐',\n TERMINAL: '💻',\n SCRIPT: '📜',\n CUSTOM: '⚙️',\n};\n\n/**\n * SessionCard - Display terminal session information\n */\nexport const SessionCard: FC<SessionCardProps> = ({\n session,\n onOpen,\n onStart,\n onStop,\n onRestart,\n onTerminate,\n onOpenTerminal,\n onShare,\n selected = false,\n onToggleSelect,\n className = '',\n}) => {\n const tr = useTr();\n const [showMenu, setShowMenu] = useState(false);\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 const isRunning = session.status === 'RUNNING';\n const isStopped = session.status === 'STOPPED' || session.status === 'TERMINATED';\n\n return (\n <motion.div\n whileHover={{ y: -2, transition: { duration: 0.15, ease: [0.4, 0, 0.2, 1] } }}\n whileTap={{ scale: 0.98 }}\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong hover:shadow-md transition-all cursor-pointer ${selected ? 'border-action-primary-bg ring-1 ring-action-primary-bg/30' : 'border-border-default'} ${className}`}\n onClick={() => onOpen?.(session)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"flex items-start gap-2 sm:gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div className=\"flex-shrink-0 pt-0.5\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => {\n e.stopPropagation();\n onToggleSelect();\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"size-4 rounded border-border-default cursor-pointer\"\n />\n </div>\n )}\n <div className=\"flex-shrink-0 size-8 sm:w-10 sm:h-10 rounded-lg bg-bg-sunken flex items-center justify-center\">\n <Terminal className=\"size-4 sm:w-5 sm:h-5 text-text-secondary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5 sm:gap-2 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary truncate max-w-[120px] sm:max-w-none text-sm sm:text-base\">\n {session.name}\n </h3>\n <span className=\"text-base sm:text-lg flex-shrink-0\">\n {sessionTypeIcons[session.type] || '💻'}\n </span>\n </div>\n <p className=\"text-xs sm:text-sm text-text-secondary\">{session.type}</p>\n {session.agent && (\n <div className=\"mt-1.5 flex items-center gap-1 flex-wrap\">\n <AgentInfoPopover agent={session.agent} />\n {session.agent.target && <TargetInfoPopover target={session.agent.target} />}\n </div>\n )}\n </div>\n </div>\n {/* Status and Actions - always visible */}\n <div className=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <SessionStatusBadge status={session.status} />\n {(onStart || onStop || onRestart || onTerminate) && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowMenu(!showMenu);\n }}\n className=\"p-1 sm:p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <MoreVertical className=\"size-4 text-text-secondary\" />\n </button>\n <AnimatePresence>\n {showMenu && (\n <motion.div\n initial={{ opacity: 0, scale: 0.95, y: -4 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, y: -4 }}\n transition={{ duration: 0.15 }}\n className=\"absolute right-0 mt-1 w-40 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10\"\n >\n {isStopped && onStart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStart(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken rounded-t-lg\"\n >\n <Play className=\"size-4\" />\n {tr('vibecontrols.sessions.card.start', 'Start')}\n </motion.button>\n )}\n {isRunning && onStop && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onStop(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Square className=\"size-4\" />\n {tr('vibecontrols.sessions.card.stop', 'Stop')}\n </motion.button>\n )}\n {isRunning && onRestart && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onRestart(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <RotateCcw className=\"size-4\" />\n {tr('vibecontrols.sessions.card.restart', 'Restart')}\n </motion.button>\n )}\n {isRunning && onOpenTerminal && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onOpenTerminal(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Terminal className=\"size-4\" />\n {tr('vibecontrols.sessions.card.openTerminal', 'Open Terminal')}\n </motion.button>\n )}\n {isRunning && onShare && (\n <motion.button\n whileTap={{ scale: 0.97 }}\n onClick={(e) => {\n e.stopPropagation();\n onShare(session);\n setShowMenu(false);\n }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken\"\n >\n <Share2 className=\"size-4\" />\n {tr('vibecontrols.sessions.card.shareSession', 'Share Session')}\n </motion.button>\n )}\n {onTerminate && (\n <motion.button\n whileTap={{ scale: 0.97 }}\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-b-lg\"\n >\n <Trash2 className=\"size-4\" />\n {tr('vibecontrols.sessions.card.terminate', 'Terminate')}\n </motion.button>\n )}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )}\n </div>\n </div>\n\n {/* Provider-native session name (e.g. tmux session name like\n `vibe-051c9c62`). Surfaced so users can recognise their session\n if they ever need to attach from a CLI (`tmux attach -t …`)\n and so reconnect-after-restart flows are obvious. */}\n {(() => {\n const meta = (session.metadata as Record<string, unknown> | null) || {};\n const externalName =\n (typeof meta.tmuxSessionName === 'string' && meta.tmuxSessionName) ||\n (typeof meta.weztermWorkspace === 'string' && meta.weztermWorkspace) ||\n (typeof meta.zellijSessionName === 'string' && meta.zellijSessionName) ||\n (typeof meta.externalSessionName === 'string' && meta.externalSessionName) ||\n session.providerSessionName ||\n null;\n return externalName ? (\n <div\n className=\"mt-2 text-[10px] font-mono text-text-tertiary\"\n title={`Provider session: ${externalName}`}\n data-testid=\"session-card-external-name\"\n >\n <span className=\"opacity-70\">{session.type.toLowerCase()}: </span>\n <span className=\"break-all\">{externalName}</span>\n </div>\n ) : null;\n })()}\n\n {session.command && (\n <div className=\"mt-3 text-xs font-mono bg-bg-sunken p-2 rounded truncate text-text-secondary\">\n $ {session.command}\n </div>\n )}\n\n {session.lastOutput && (\n <div className=\"mt-2 text-xs font-mono bg-bg-sunken text-status-success-text p-2 rounded truncate\">\n {session.lastOutput}\n </div>\n )}\n\n <div className=\"mt-3 flex items-center justify-between text-sm text-text-secondary\">\n <div className=\"flex items-center gap-2\">\n <Clock className=\"size-4\" />\n <span>{formatTime(session.updatedAt)}</span>\n </div>\n {session.pid && <span className=\"text-xs font-mono\">PID: {session.pid}</span>}\n </div>\n </motion.div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { ChevronDown as t, ChevronRight as n } from "lucide-react";
|
|
3
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/sessions/SessionGroup.tsx
|
|
5
|
+
function a({ label: a, count: o, collapsed: s, onToggleCollapse: c, allSelected: l, someSelected: u, onToggleSelectGroup: d, showSelect: f = !0, children: p }) {
|
|
6
|
+
let m = e();
|
|
7
|
+
return /* @__PURE__ */ i("div", {
|
|
8
|
+
"data-testid": "session-group",
|
|
9
|
+
"data-group-label": a,
|
|
10
|
+
children: [/* @__PURE__ */ i("div", {
|
|
11
|
+
className: "flex items-center gap-2 px-1 py-1.5 sticky top-0 z-[1] bg-bg-surface/95 backdrop-blur-sm",
|
|
12
|
+
children: [f && /* @__PURE__ */ r("input", {
|
|
13
|
+
"aria-label": m("sessions.group.selectGroup", "Select group"),
|
|
14
|
+
type: "checkbox",
|
|
15
|
+
checked: l,
|
|
16
|
+
ref: (e) => {
|
|
17
|
+
e && (e.indeterminate = !l && u);
|
|
18
|
+
},
|
|
19
|
+
onChange: (e) => {
|
|
20
|
+
e.stopPropagation(), d();
|
|
21
|
+
},
|
|
22
|
+
onClick: (e) => e.stopPropagation(),
|
|
23
|
+
className: "size-3.5 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg"
|
|
24
|
+
}), /* @__PURE__ */ i("button", {
|
|
25
|
+
type: "button",
|
|
26
|
+
onClick: c,
|
|
27
|
+
className: "flex items-center gap-1.5 text-sm font-semibold text-text-primary hover:text-text-link transition-colors min-w-0",
|
|
28
|
+
children: [
|
|
29
|
+
r(s ? n : t, { className: "size-3.5 text-text-tertiary shrink-0" }),
|
|
30
|
+
/* @__PURE__ */ r("span", {
|
|
31
|
+
className: "truncate",
|
|
32
|
+
children: a
|
|
33
|
+
}),
|
|
34
|
+
/* @__PURE__ */ r("span", {
|
|
35
|
+
className: "ml-1 inline-flex items-center justify-center min-w-[1.25rem] px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-bg-sunken text-text-secondary",
|
|
36
|
+
children: o
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
})]
|
|
40
|
+
}), !s && /* @__PURE__ */ r("div", {
|
|
41
|
+
className: "pb-2",
|
|
42
|
+
children: p
|
|
43
|
+
})]
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { a as SessionGroup };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=SessionGroup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionGroup.js","names":[],"sources":["../../../src/components/sessions/SessionGroup.tsx"],"sourcesContent":["/**\n * SessionGroup — a collapsible section wrapping one group of sessions in\n * the grid/list views. The header carries the group label, a count, and a\n * tri-state select-all checkbox that toggles only this group's ids in the\n * page-level selection Set (so the global bulk-action bar keeps working).\n */\nimport type { ReactNode } from 'react';\nimport { ChevronDown, ChevronRight } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\n\nexport interface SessionGroupProps {\n label: string;\n count: number;\n collapsed: boolean;\n onToggleCollapse: () => void;\n /** All sessions in this group are selected. */\n allSelected: boolean;\n /** Some (but not all) sessions in this group are selected. */\n someSelected: boolean;\n /** Toggle selection of every session in this group. */\n onToggleSelectGroup: () => void;\n /** Whether to show the per-group select checkbox (bulk mode). */\n showSelect?: boolean;\n children: ReactNode;\n}\n\nexport function SessionGroup({\n label,\n count,\n collapsed,\n onToggleCollapse,\n allSelected,\n someSelected,\n onToggleSelectGroup,\n showSelect = true,\n children,\n}: SessionGroupProps) {\n const tr = useTr();\n return (\n <div data-testid=\"session-group\" data-group-label={label}>\n <div className=\"flex items-center gap-2 px-1 py-1.5 sticky top-0 z-[1] bg-bg-surface/95 backdrop-blur-sm\">\n {showSelect && (\n <input\n aria-label={tr('sessions.group.selectGroup', 'Select group')}\n type=\"checkbox\"\n checked={allSelected}\n ref={(el) => {\n if (el) el.indeterminate = !allSelected && someSelected;\n }}\n onChange={(e) => {\n e.stopPropagation();\n onToggleSelectGroup();\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"size-3.5 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n )}\n <button\n type=\"button\"\n onClick={onToggleCollapse}\n className=\"flex items-center gap-1.5 text-sm font-semibold text-text-primary hover:text-text-link transition-colors min-w-0\"\n >\n {collapsed ? (\n <ChevronRight className=\"size-3.5 text-text-tertiary shrink-0\" />\n ) : (\n <ChevronDown className=\"size-3.5 text-text-tertiary shrink-0\" />\n )}\n <span className=\"truncate\">{label}</span>\n <span className=\"ml-1 inline-flex items-center justify-center min-w-[1.25rem] px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-bg-sunken text-text-secondary\">\n {count}\n </span>\n </button>\n </div>\n {!collapsed && <div className=\"pb-2\">{children}</div>}\n </div>\n );\n}\n"],"mappings":";;;;AA0BA,SAAgB,EAAa,EAC3B,UACA,UACA,cACA,qBACA,gBACA,iBACA,wBACA,gBAAa,IACb,eACoB;CACpB,IAAM,IAAK,GAAO;AAClB,QACE,kBAAC,OAAD;EAAK,eAAY;EAAgB,oBAAkB;YAAnD,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,KACC,kBAAC,SAAD;IACE,cAAY,EAAG,8BAA8B,eAAe;IAC5D,MAAK;IACL,SAAS;IACT,MAAM,MAAO;AACX,KAAI,MAAI,EAAG,gBAAgB,CAAC,KAAe;;IAE7C,WAAW,MAAM;AAEf,KADA,EAAE,iBAAiB,EACnB,GAAqB;;IAEvB,UAAU,MAAM,EAAE,iBAAiB;IACnC,WAAU;IACV,CAAA,EAEJ,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAHZ;KAMI,EADD,IACE,IAEA,GAFD,EAAc,WAAU,wCAAyC,CAED;KAElE,kBAAC,QAAD;MAAM,WAAU;gBAAY;MAAa,CAAA;KACzC,kBAAC,QAAD;MAAM,WAAU;gBACb;MACI,CAAA;KACA;MACL;MACL,CAAC,KAAa,kBAAC,OAAD;GAAK,WAAU;GAAQ;GAAe,CAAA,CACjD"}
|
|
@@ -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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"}
|