@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.
Files changed (41) hide show
  1. package/dist/components/agents/AgentForm.js +1 -1
  2. package/dist/components/agents/AgentForm.js.map +1 -1
  3. package/dist/components/sessions/EntityInfoPopover.js +183 -0
  4. package/dist/components/sessions/EntityInfoPopover.js.map +1 -0
  5. package/dist/components/sessions/SessionCard.js +80 -72
  6. package/dist/components/sessions/SessionCard.js.map +1 -1
  7. package/dist/components/sessions/SessionGroup.js +49 -0
  8. package/dist/components/sessions/SessionGroup.js.map +1 -0
  9. package/dist/components/sessions/SessionInfoPanel.js +128 -0
  10. package/dist/components/sessions/SessionInfoPanel.js.map +1 -0
  11. package/dist/components/sessions/SessionList.js +109 -96
  12. package/dist/components/sessions/SessionList.js.map +1 -1
  13. package/dist/components/sessions/SessionsCommandPalette.js +36 -0
  14. package/dist/components/sessions/SessionsCommandPalette.js.map +1 -0
  15. package/dist/components/sessions/ShortcutCheatsheet.js +73 -0
  16. package/dist/components/sessions/ShortcutCheatsheet.js.map +1 -0
  17. package/dist/components/sessions/TerminalPanel.js +640 -537
  18. package/dist/components/sessions/TerminalPanel.js.map +1 -1
  19. package/dist/components/sessions/groupSessions.js +46 -0
  20. package/dist/components/sessions/groupSessions.js.map +1 -0
  21. package/dist/components/sessions/index.js +7 -0
  22. package/dist/components/sessions/sessionShortcuts.js +149 -0
  23. package/dist/components/sessions/sessionShortcuts.js.map +1 -0
  24. package/dist/components/shared/AgentSetupBanner.js +1 -1
  25. package/dist/components/shared/AgentSetupBanner.js.map +1 -1
  26. package/dist/components/shared/FilterDropdown.js +14 -13
  27. package/dist/components/shared/FilterDropdown.js.map +1 -1
  28. package/dist/components/shared/InstallTabs.js +2 -2
  29. package/dist/components/shared/InstallTabs.js.map +1 -1
  30. package/dist/components/shared/LinkToVibePanel.js.map +1 -1
  31. package/dist/components/vibe/VibeLinkedEntitiesPanel.js.map +1 -1
  32. package/dist/generated/wspace-operations.js +21 -0
  33. package/dist/generated/wspace-operations.js.map +1 -1
  34. package/dist/pages/SessionsPage.js +1189 -920
  35. package/dist/pages/SessionsPage.js.map +1 -1
  36. package/dist/pages/ai/AgentManagerPage.js.map +1 -1
  37. package/dist/store/vibeControlsStore.js +16 -1
  38. package/dist/store/vibeControlsStore.js.map +1 -1
  39. package/dist/translations/en.js +97 -2
  40. package/dist/translations/en.js.map +1 -1
  41. 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 { useState as n } from "react";
4
- import { Clock as r, MoreVertical as i, Play as a, RotateCcw as o, Share2 as s, Square as c, Terminal as l, Trash2 as u } from "lucide-react";
5
- import { AnimatePresence as d, motion as f } from "framer-motion";
6
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
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 h = {
9
+ var _ = {
9
10
  TMUX: "🖥️",
10
11
  SSH: "🔐",
11
12
  TERMINAL: "💻",
12
13
  SCRIPT: "📜",
13
14
  CUSTOM: "⚙️"
14
- }, g = ({ session: g, onOpen: _, onStart: v, onStop: y, onRestart: b, onTerminate: x, onOpenTerminal: S, onShare: C, selected: w = !1, onToggleSelect: T, className: E = "" }) => {
15
- let D = e(), [O, k] = n(!1), A = (e) => {
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
- }, j = g.status === "RUNNING", M = g.status === "STOPPED" || g.status === "TERMINATED";
20
- return /* @__PURE__ */ m(f.div, {
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 ${w ? "border-action-primary-bg ring-1 ring-action-primary-bg/30" : "border-border-default"} ${E}`,
35
- onClick: () => _?.(g),
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__ */ m("div", {
38
+ /* @__PURE__ */ g("div", {
38
39
  className: "flex items-start justify-between gap-2",
39
- children: [/* @__PURE__ */ m("div", {
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
- T && /* @__PURE__ */ p("div", {
43
+ D && /* @__PURE__ */ h("div", {
43
44
  className: "flex-shrink-0 pt-0.5",
44
- children: /* @__PURE__ */ p("input", {
45
+ children: /* @__PURE__ */ h("input", {
45
46
  "aria-label": "control",
46
47
  type: "checkbox",
47
- checked: w,
48
+ checked: E,
48
49
  onChange: (e) => {
49
- e.stopPropagation(), T();
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__ */ p("div", {
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__ */ p(l, { className: "size-4 sm:w-5 sm:h-5 text-text-secondary" })
58
+ children: /* @__PURE__ */ h(d, { className: "size-4 sm:w-5 sm:h-5 text-text-secondary" })
58
59
  }),
59
- /* @__PURE__ */ m("div", {
60
+ /* @__PURE__ */ g("div", {
60
61
  className: "min-w-0 flex-1",
61
- children: [/* @__PURE__ */ m("div", {
62
- className: "flex items-center gap-1.5 sm:gap-2 flex-wrap",
63
- children: [/* @__PURE__ */ p("h3", {
64
- className: "font-semibold text-text-primary truncate max-w-[120px] sm:max-w-none text-sm sm:text-base",
65
- children: g.name
66
- }), /* @__PURE__ */ p("span", {
67
- className: "text-base sm:text-lg flex-shrink-0",
68
- children: h[g.type] || "💻"
69
- })]
70
- }), /* @__PURE__ */ p("p", {
71
- className: "text-xs sm:text-sm text-text-secondary",
72
- children: g.type
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__ */ m("div", {
84
+ }), /* @__PURE__ */ g("div", {
77
85
  className: "flex items-center gap-1 sm:gap-2 flex-shrink-0",
78
- children: [/* @__PURE__ */ p(t, { status: g.status }), (v || y || b || x) && /* @__PURE__ */ m("div", {
86
+ children: [/* @__PURE__ */ h(t, { status: v.status }), (b || x || S || C) && /* @__PURE__ */ g("div", {
79
87
  className: "relative",
80
- children: [/* @__PURE__ */ p("button", {
88
+ children: [/* @__PURE__ */ h("button", {
81
89
  type: "button",
82
90
  onClick: (e) => {
83
- e.stopPropagation(), k(!O);
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__ */ p(i, { className: "size-4 text-text-secondary" })
87
- }), /* @__PURE__ */ p(d, { children: O && /* @__PURE__ */ m(f.div, {
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
- M && v && /* @__PURE__ */ m(f.button, {
114
+ P && b && /* @__PURE__ */ g(m.button, {
107
115
  whileTap: { scale: .97 },
108
116
  onClick: (e) => {
109
- e.stopPropagation(), v(g), k(!1);
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__ */ p(a, { className: "size-4" }), D("vibecontrols.sessions.card.start", "Start")]
120
+ children: [/* @__PURE__ */ h(s, { className: "size-4" }), k("vibecontrols.sessions.card.start", "Start")]
113
121
  }),
114
- j && y && /* @__PURE__ */ m(f.button, {
122
+ N && x && /* @__PURE__ */ g(m.button, {
115
123
  whileTap: { scale: .97 },
116
124
  onClick: (e) => {
117
- e.stopPropagation(), y(g), k(!1);
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__ */ p(c, { className: "size-4" }), D("vibecontrols.sessions.card.stop", "Stop")]
128
+ children: [/* @__PURE__ */ h(u, { className: "size-4" }), k("vibecontrols.sessions.card.stop", "Stop")]
121
129
  }),
122
- j && b && /* @__PURE__ */ m(f.button, {
130
+ N && S && /* @__PURE__ */ g(m.button, {
123
131
  whileTap: { scale: .97 },
124
132
  onClick: (e) => {
125
- e.stopPropagation(), b(g), k(!1);
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__ */ p(o, { className: "size-4" }), D("vibecontrols.sessions.card.restart", "Restart")]
136
+ children: [/* @__PURE__ */ h(c, { className: "size-4" }), k("vibecontrols.sessions.card.restart", "Restart")]
129
137
  }),
130
- j && S && /* @__PURE__ */ m(f.button, {
138
+ N && w && /* @__PURE__ */ g(m.button, {
131
139
  whileTap: { scale: .97 },
132
140
  onClick: (e) => {
133
- e.stopPropagation(), S(g), k(!1);
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__ */ p(l, { className: "size-4" }), D("vibecontrols.sessions.card.openTerminal", "Open Terminal")]
144
+ children: [/* @__PURE__ */ h(d, { className: "size-4" }), k("vibecontrols.sessions.card.openTerminal", "Open Terminal")]
137
145
  }),
138
- j && C && /* @__PURE__ */ m(f.button, {
146
+ N && T && /* @__PURE__ */ g(m.button, {
139
147
  whileTap: { scale: .97 },
140
148
  onClick: (e) => {
141
- e.stopPropagation(), C(g), k(!1);
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__ */ p(s, { className: "size-4" }), D("vibecontrols.sessions.card.shareSession", "Share Session")]
152
+ children: [/* @__PURE__ */ h(l, { className: "size-4" }), k("vibecontrols.sessions.card.shareSession", "Share Session")]
145
153
  }),
146
- x && /* @__PURE__ */ m(f.button, {
154
+ C && /* @__PURE__ */ g(m.button, {
147
155
  whileTap: { scale: .97 },
148
156
  onClick: (e) => {
149
- e.stopPropagation(), x(g), k(!1);
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__ */ p(u, { className: "size-4" }), D("vibecontrols.sessions.card.terminate", "Terminate")]
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 = g.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 || g.providerSessionName || null;
161
- return t ? /* @__PURE__ */ m("div", {
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__ */ m("span", {
173
+ children: [/* @__PURE__ */ g("span", {
166
174
  className: "opacity-70",
167
- children: [g.type.toLowerCase(), ": "]
168
- }), /* @__PURE__ */ p("span", {
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
- g.command && /* @__PURE__ */ m("div", {
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: ["$ ", g.command]
184
+ children: ["$ ", v.command]
177
185
  }),
178
- g.lastOutput && /* @__PURE__ */ p("div", {
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: g.lastOutput
188
+ children: v.lastOutput
181
189
  }),
182
- /* @__PURE__ */ m("div", {
190
+ /* @__PURE__ */ g("div", {
183
191
  className: "mt-3 flex items-center justify-between text-sm text-text-secondary",
184
- children: [/* @__PURE__ */ m("div", {
192
+ children: [/* @__PURE__ */ g("div", {
185
193
  className: "flex items-center gap-2",
186
- children: [/* @__PURE__ */ p(r, { className: "size-4" }), /* @__PURE__ */ p("span", { children: A(g.updatedAt) })]
187
- }), g.pid && /* @__PURE__ */ m("span", {
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: ", g.pid]
197
+ children: ["PID: ", v.pid]
190
198
  })]
191
199
  })
192
200
  ]
193
201
  });
194
202
  };
195
203
  //#endregion
196
- export { g as SessionCard };
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":";;;;;;;AAkCA,IAAM,IAA2C;CAC/C,MAAM;CACN,KAAK;CACL,UAAU;CACV,QAAQ;CACR,QAAQ;CACT,EAKY,KAAqC,EAChD,YACA,WACA,YACA,WACA,cACA,gBACA,mBACA,YACA,cAAW,IACX,mBACA,eAAY,SACR;CACJ,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAS,GAAM,EAEzC,KAAc,MAAyC;AAC3D,MAAI,CAAC,EAAW,QAAO;EACvB,IAAM,IAAO,KAAK,KAAK,GAAG,IAAI,KAAK,EAAU,CAAC,SAAS;AAIvD,SAHI,IAAO,MAAc,aACrB,IAAO,OAAgB,GAAG,KAAK,MAAM,IAAO,IAAM,CAAC,SACnD,IAAO,QAAiB,GAAG,KAAK,MAAM,IAAO,KAAQ,CAAC,SACnD,IAAI,KAAK,EAAU,CAAC,oBAAoB;IAG3C,IAAY,EAAQ,WAAW,WAC/B,IAAY,EAAQ,WAAW,aAAa,EAAQ,WAAW;AAErE,QACE,kBAAC,EAAO,KAAR;EACE,YAAY;GAAE,GAAG;GAAI,YAAY;IAAE,UAAU;IAAM,MAAM;KAAC;KAAK;KAAG;KAAK;KAAE;IAAE;GAAE;EAC7E,UAAU,EAAE,OAAO,KAAM;EACzB,WAAW,gHAAgH,IAAW,8DAA8D,wBAAwB,GAAG;EAC/N,eAAe,IAAS,EAAQ;YAJlC;GAME,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,SAAD;QACE,cAAW;QACX,MAAK;QACL,SAAS;QACT,WAAW,MAAM;AAEf,SADA,EAAE,iBAAiB,EACnB,GAAgB;;QAElB,UAAU,MAAM,EAAE,iBAAiB;QACnC,WAAU;QACV,CAAA;OACE,CAAA;MAER,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA;OAC7D,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAQ;SACN,CAAA,EACL,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAiB,EAAQ,SAAS;SAC9B,CAAA,CACH;WACN,kBAAC,KAAD;QAAG,WAAU;kBAA0C,EAAQ;QAAS,CAAA,CACpE;;MACF;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAoB,QAAQ,EAAQ,QAAU,CAAA,GAC5C,KAAW,KAAU,KAAa,MAClC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAM;AAEd,QADA,EAAE,iBAAiB,EACnB,EAAY,CAAC,EAAS;;OAExB,WAAU;iBAEV,kBAAC,GAAD,EAAc,WAAU,8BAA+B,CAAA;OAChD,CAAA,EACT,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,EAAO,KAAR;OACE,SAAS;QAAE,SAAS;QAAG,OAAO;QAAM,GAAG;QAAI;OAC3C,SAAS;QAAE,SAAS;QAAG,OAAO;QAAG,GAAG;QAAG;OACvC,MAAM;QAAE,SAAS;QAAG,OAAO;QAAM,GAAG;QAAI;OACxC,YAAY,EAAE,UAAU,KAAM;OAC9B,WAAU;iBALZ;QAOG,KAAa,KACZ,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAQ,EAAQ,EAChB,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,oCAAoC,QAAQ,CAClC;;QAEjB,KAAa,KACZ,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAO,EAAQ,EACf,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAC5B,EAAG,mCAAmC,OAAO,CAChC;;QAEjB,KAAa,KACZ,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAU,EAAQ,EAClB,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,sCAAsC,UAAU,CACtC;;QAEjB,KAAa,KACZ,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAe,EAAQ,EACvB,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,EAAG,2CAA2C,gBAAgB,CACjD;;QAEjB,KAAa,KACZ,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAQ,EAAQ,EAChB,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAC5B,EAAG,2CAA2C,gBAAgB,CACjD;;QAEjB,KACC,kBAAC,EAAO,QAAR;SACE,UAAU,EAAE,OAAO,KAAM;SACzB,UAAU,MAAM;AAGd,UAFA,EAAE,iBAAiB,EACnB,EAAY,EAAQ,EACpB,EAAY,GAAM;;SAEpB,WAAU;mBAPZ,CASE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAC5B,EAAG,wCAAwC,YAAY,CAC1C;;QAEP;UAEC,CAAA,CACd;QAEJ;OACF;;UAME;IACN,IAAM,IAAQ,EAAQ,YAA+C,EAAE,EACjE,IACH,OAAO,EAAK,mBAAoB,YAAY,EAAK,mBACjD,OAAO,EAAK,oBAAqB,YAAY,EAAK,oBAClD,OAAO,EAAK,qBAAsB,YAAY,EAAK,qBACnD,OAAO,EAAK,uBAAwB,YAAY,EAAK,uBACtD,EAAQ,uBACR;AACF,WAAO,IACL,kBAAC,OAAD;KACE,WAAU;KACV,OAAO,qBAAqB;KAC5B,eAAY;eAHd,CAKE,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CAA8B,EAAQ,KAAK,aAAa,EAAC,KAAS;SAClE,kBAAC,QAAD;MAAM,WAAU;gBAAa;MAAoB,CAAA,CAC7C;SACJ;OACF;GAEH,EAAQ,WACP,kBAAC,OAAD;IAAK,WAAU;cAAf,CAA8F,MACzF,EAAQ,QACP;;GAGP,EAAQ,cACP,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ;IACL,CAAA;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC5B,kBAAC,QAAD,EAAA,UAAO,EAAW,EAAQ,UAAU,EAAQ,CAAA,CACxC;QACL,EAAQ,OAAO,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAAoC,SAAM,EAAQ,IAAW;OACzE;;GACK"}
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":";;;;;;;AAmBA,SAAS,EAAM,EAAE,UAAO,YAA2C;AACjE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aAAsB;GAAa,CAAA,EACnD,kBAAC,QAAD;GAAM,WAAU;GAAqD,OAAO;aACzE;GACI,CAAA,CACH;;;AAUV,SAAgB,EAAiB,EAAE,SAAM,iBAAc,cAAkC;CACvF,IAAM,IAAK,GAAO,EACZ,IAAW,GAAiB,EAC5B,IAAQ,GAAS,OACjB,IAAS,GAAO,QAChB,IAAO,GAAS;AAEtB,QACE,kBAAC,GAAD;EAAa;EAAoB;YAC/B,kBAAC,GAAD;GACE,MAAK;GACL,WAAU;GACV,eAAY;aAHd,CAKE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAY,WAAU;cAAtB,CACE,kBAAC,GAAD,EAAU,WAAU,8BAA+B,CAAA,EAClD,GAAS,QAAQ,EAAG,4BAA4B,kBAAkB,CACxD;OACb,kBAAC,GAAD,EAAA,UACG,EAAG,2BAA2B,+CAA+C,EAC7D,CAAA,CACP,EAAA,CAAA,EAEd,kBAAC,OAAD;IAAK,WAAU;cACX,IAKA,kBAAA,GAAA,EAAA,UAAA;KACE,kBAAC,WAAD,EAAA,UAAA;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAChC,EAAG,yBAAyB,UAAU,CACpC;;MACL,kBAAC,GAAD;OAAO,OAAO,EAAG,sBAAsB,OAAO;OAAE,OAAO,EAAQ;OAAQ,CAAA;MACvE,kBAAC,GAAD;OAAO,OAAO,EAAG,wBAAwB,SAAS;OAAE,OAAO,EAAQ;OAAU,CAAA;MAC5E,EAAQ,oBACP,kBAAC,GAAD;OACE,OAAO,EAAG,qBAAqB,cAAc;OAC7C,OAAO,EAAQ;OACf,CAAA;MAEI,EAAA,CAAA;KAET,KACC,kBAAC,WAAD,EAAA,UAAA;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACE,kBAAC,GAAD,EAAQ,WAAU,YAAa,CAAA,EAC9B,EAAG,uBAAuB,QAAQ,CAChC;;MACL,kBAAC,GAAD;OAAO,OAAO,EAAG,sBAAsB,OAAO;OAAE,OAAO,EAAM;OAAQ,CAAA;MACrE,kBAAC,GAAD;OACE,OAAO,EAAG,wBAAwB,SAAS;OAC3C,OAAO,EAAe,EAAM;OAC5B,CAAA;MACD,EAAM,YACL,kBAAC,GAAD;OAAO,OAAO,EAAG,sBAAsB,OAAO;OAAE,OAAO,EAAM;OAAY,CAAA;MAE1E,EAAM,YACL,kBAAC,GAAD;OACE,OAAO,EAAG,0BAA0B,WAAW;OAC/C,OAAO,EAAM;OACb,CAAA;MAEJ,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,WAAW,EAAM,KAAK;OAC9C,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,EAClC,EAAG,2BAA2B,aAAa,CACrC;;MACD,EAAA,CAAA;KAGX,KACC,kBAAC,WAAD,EAAA,UAAA;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACE,kBAAC,GAAD,EAAmB,WAAU,YAAa,CAAA,EACzC,EAAG,wBAAwB,SAAS,CAClC;;MACL,kBAAC,GAAD;OAAO,OAAO,EAAG,sBAAsB,OAAO;OAAE,OAAO,EAAO;OAAQ,CAAA;MACtE,kBAAC,GAAD;OAAO,OAAO,EAAG,sBAAsB,OAAO;OAAE,OAAO,EAAO;OAAQ,CAAA;MACtE,kBAAC,GAAD;OAAO,OAAO,EAAG,wBAAwB,SAAS;OAAE,OAAO,EAAO;OAAU,CAAA;MAC3E,EAAO,QACN,kBAAC,GAAD;OACE,OAAO,EAAG,yBAAyB,UAAU;OAC7C,OAAO,GAAG,EAAO,OAAO,EAAO,OAAO,IAAI,EAAO,SAAS;OAC1D,CAAA;MAEJ,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,YAAY,EAAO,KAAK;OAChD,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAc,WAAU,UAAW,CAAA,EAClC,EAAG,4BAA4B,cAAc,CACvC;;MACD,EAAA,CAAA;KAGX,KACC,kBAAC,WAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD,EAAO,WAAU,YAAa,CAAA,EAC7B,EAAG,sBAAsB,OAAO,CAC9B;SACL,kBAAC,GAAD;MAAO,OAAO,EAAG,sBAAsB,OAAO;MAAE,OAAO,EAAK;MAAQ,CAAA,CAC5D,EAAA,CAAA;KAEX,EAAA,CAAA,GAtFH,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,0BAA0B,8BAA8B;KAC1D,CAAA;IAsFF,CAAA,CACO;;EACT,CAAA"}