@burdenoff/microfe-vibecontrols 2026.826.1 → 2026.829.1

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 (61) hide show
  1. package/dist/components/actions/ActionCard.js +99 -84
  2. package/dist/components/actions/ActionCard.js.map +1 -1
  3. package/dist/components/actions/ActionWebhookEndpoint.js +57 -0
  4. package/dist/components/actions/ActionWebhookEndpoint.js.map +1 -0
  5. package/dist/components/actions/index.js +1 -0
  6. package/dist/components/agent-manager/BridleConfigPanel.js +10 -10
  7. package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
  8. package/dist/components/agent-manager/FilePickerModal.js +71 -65
  9. package/dist/components/agent-manager/FilePickerModal.js.map +1 -1
  10. package/dist/components/ai/AIQueueTab.js +5 -5
  11. package/dist/components/components/ComponentHealthCell.js +62 -0
  12. package/dist/components/components/ComponentHealthCell.js.map +1 -0
  13. package/dist/components/components/ComponentTierBadge.js +42 -0
  14. package/dist/components/components/ComponentTierBadge.js.map +1 -0
  15. package/dist/components/components/ComponentsTable.js +192 -0
  16. package/dist/components/components/ComponentsTable.js.map +1 -0
  17. package/dist/components/components/index.js +3 -0
  18. package/dist/components/graph/MeshAgentNode.js +39 -38
  19. package/dist/components/graph/MeshAgentNode.js.map +1 -1
  20. package/dist/components/sessions/SessionPublicLinkPanel.js +10 -10
  21. package/dist/components/shared/DiffView.js +7 -0
  22. package/dist/components/shared/index.js +1 -0
  23. package/dist/components/tunnels/TunnelConnectionCard.js +38 -0
  24. package/dist/components/tunnels/TunnelConnectionCard.js.map +1 -0
  25. package/dist/components/tunnels/TunnelCopyButton.js +34 -0
  26. package/dist/components/tunnels/TunnelCopyButton.js.map +1 -0
  27. package/dist/components/tunnels/TunnelMetricTiles.js +132 -0
  28. package/dist/components/tunnels/TunnelMetricTiles.js.map +1 -0
  29. package/dist/components/tunnels/TunnelSparkline.js +28 -0
  30. package/dist/components/tunnels/TunnelSparkline.js.map +1 -0
  31. package/dist/components/tunnels/tunnelEndpoints.js +34 -0
  32. package/dist/components/tunnels/tunnelEndpoints.js.map +1 -0
  33. package/dist/components/tunnels/tunnelMetrics.js +43 -0
  34. package/dist/components/tunnels/tunnelMetrics.js.map +1 -0
  35. package/dist/components/vibe/VibeEnvVarsPanel.js +8 -8
  36. package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +10 -10
  37. package/dist/components/vibes/VibeList.js +88 -93
  38. package/dist/components/vibes/VibeList.js.map +1 -1
  39. package/dist/generated/wspace-operations.js +5 -0
  40. package/dist/generated/wspace-operations.js.map +1 -1
  41. package/dist/pages/ActionsPage.js.map +1 -1
  42. package/dist/pages/AgentDetailsPage.js +11 -11
  43. package/dist/pages/AgentGraphPage.js +1 -1
  44. package/dist/pages/AgentsPage.js +15 -15
  45. package/dist/pages/ComponentsPage.js +205 -167
  46. package/dist/pages/ComponentsPage.js.map +1 -1
  47. package/dist/pages/EnvTopologyPage.js +7 -7
  48. package/dist/pages/HealthPage.js +1 -1
  49. package/dist/pages/TargetsPage.js +478 -478
  50. package/dist/pages/TargetsPage.js.map +1 -1
  51. package/dist/pages/tunnels/tabs/TunnelEndpointsTab.js +26 -38
  52. package/dist/pages/tunnels/tabs/TunnelEndpointsTab.js.map +1 -1
  53. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js +136 -122
  54. package/dist/pages/tunnels/tabs/TunnelOverviewTab.js.map +1 -1
  55. package/dist/pages/tunnels/tabs/TunnelUsageTab.js +37 -39
  56. package/dist/pages/tunnels/tabs/TunnelUsageTab.js.map +1 -1
  57. package/dist/translations/en.js +119 -2
  58. package/dist/translations/en.js.map +1 -1
  59. package/dist/utils/shareUrl.js +18 -0
  60. package/dist/utils/shareUrl.js.map +1 -0
  61. package/package.json +1 -1
@@ -3,168 +3,183 @@ import { VibeEntityType as t } from "../../generated/wspace-types.js";
3
3
  import { EntityVibeLinkControl as n } from "../shared/EntityVibeLinkControl.js";
4
4
  import { SelectionCheckbox as r } from "../shared/SelectionCheckbox.js";
5
5
  import { ActionTypeBadge as i, SourceTypeBadge as a, TriggerTypeBadge as o } from "./ActionTypeBadge.js";
6
- import { EntityShareButton as s } from "../sharing/EntityShareButton.js";
6
+ import { ActionWebhookEndpoint as s } from "./ActionWebhookEndpoint.js";
7
+ import { EntityShareButton as c } from "../sharing/EntityShareButton.js";
7
8
  import "../sharing/index.js";
8
- import { AlarmClock as c, CalendarClock as l, Clock as u, Copy as d, Hash as f, Pencil as p, Play as m, Trash2 as h } from "lucide-react";
9
- import { motion as g, useReducedMotion as _ } from "framer-motion";
10
- import { jsx as v, jsxs as y } from "react/jsx-runtime";
9
+ import { AlarmClock as l, AlertTriangle as u, CalendarClock as d, Clock as f, Copy as p, Hash as m, Pencil as h, Play as g, Trash2 as _ } from "lucide-react";
10
+ import { motion as v, useReducedMotion as y } from "framer-motion";
11
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
11
12
  //#region src/components/actions/ActionCard.tsx
12
- function b({ action: b, onExecute: x, onEdit: S, onDuplicate: C, onDelete: w, onSchedule: T, onAddToCalendar: E, canUpdate: D, canDelete: O, canExecute: k, isExecuting: A = !1, selected: j = !1, onToggleSelect: M }) {
13
- let N = _(), P = e();
14
- return /* @__PURE__ */ y(g.div, {
15
- className: `bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${j ? "border-action-primary-bg ring-1 ring-action-primary-bg/25" : "border-border-default"}`,
16
- whileHover: N ? void 0 : {
13
+ function S({ action: S, onExecute: C, onEdit: w, onDuplicate: T, onDelete: E, onSchedule: D, onAddToCalendar: O, canUpdate: k, canDelete: A, canExecute: j, isExecuting: M = !1, selected: N = !1, onToggleSelect: P }) {
14
+ let F = y(), I = e();
15
+ return /* @__PURE__ */ x(v.div, {
16
+ className: `bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${N ? "border-action-primary-bg ring-1 ring-action-primary-bg/25" : "border-border-default"}`,
17
+ whileHover: F ? void 0 : {
17
18
  y: -2,
18
19
  boxShadow: "0 4px 12px var(--color-border-default)"
19
20
  },
20
21
  transition: { duration: .15 },
21
22
  layout: !0,
22
23
  role: "group",
23
- "aria-label": `Action: ${b.displayName || b.name}`,
24
+ "aria-label": `Action: ${S.displayName || S.name}`,
24
25
  children: [
25
- /* @__PURE__ */ y("div", {
26
+ /* @__PURE__ */ x("div", {
26
27
  className: "flex items-start justify-between gap-3 mb-3",
27
- children: [/* @__PURE__ */ y("div", {
28
+ children: [/* @__PURE__ */ x("div", {
28
29
  className: "flex items-start gap-3 min-w-0 flex-1",
29
- children: [M && /* @__PURE__ */ v("div", {
30
+ children: [P && /* @__PURE__ */ b("div", {
30
31
  onClick: (e) => e.stopPropagation(),
31
- children: /* @__PURE__ */ v(r, {
32
- checked: j,
33
- onChange: M,
34
- ariaLabel: `Select action ${b.displayName || b.name}`
32
+ children: /* @__PURE__ */ b(r, {
33
+ checked: N,
34
+ onChange: P,
35
+ ariaLabel: `Select action ${S.displayName || S.name}`
35
36
  })
36
- }), /* @__PURE__ */ y("div", {
37
+ }), /* @__PURE__ */ x("div", {
37
38
  className: "min-w-0 flex-1",
38
- children: [/* @__PURE__ */ v("h3", {
39
+ children: [/* @__PURE__ */ b("h3", {
39
40
  className: "font-semibold text-text-primary truncate",
40
- children: b.displayName || b.name
41
- }), b.description && /* @__PURE__ */ v("p", {
41
+ children: S.displayName || S.name
42
+ }), S.description && /* @__PURE__ */ b("p", {
42
43
  className: "text-sm text-text-secondary mt-0.5 line-clamp-2",
43
- children: b.description
44
+ children: S.description
44
45
  })]
45
46
  })]
46
- }), /* @__PURE__ */ y("div", {
47
+ }), /* @__PURE__ */ x("div", {
47
48
  className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0",
48
49
  children: [
49
- k && /* @__PURE__ */ v("button", {
50
+ j && /* @__PURE__ */ b("button", {
50
51
  type: "button",
51
52
  onClick: (e) => {
52
- e.stopPropagation(), x(b.id);
53
+ e.stopPropagation(), C(S.id);
53
54
  },
54
- disabled: A,
55
+ disabled: M,
55
56
  className: "p-1.5 rounded-md hover:bg-status-success-bg-subtle text-status-success-text transition-colors disabled:opacity-50",
56
- title: P("vibecontrols.actions.card.execute", "Execute"),
57
- children: /* @__PURE__ */ v(m, { size: 14 })
57
+ title: I("vibecontrols.actions.card.execute", "Execute"),
58
+ children: /* @__PURE__ */ b(g, { size: 14 })
58
59
  }),
59
- D && /* @__PURE__ */ v("button", {
60
+ k && /* @__PURE__ */ b("button", {
60
61
  type: "button",
61
62
  onClick: (e) => {
62
- e.stopPropagation(), S(b.id);
63
+ e.stopPropagation(), w(S.id);
63
64
  },
64
65
  className: "p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors",
65
- title: P("vibecontrols.actions.card.edit", "Edit"),
66
- children: /* @__PURE__ */ v(p, { size: 14 })
66
+ title: I("vibecontrols.actions.card.edit", "Edit"),
67
+ children: /* @__PURE__ */ b(h, { size: 14 })
67
68
  }),
68
- T && /* @__PURE__ */ v("button", {
69
+ D && /* @__PURE__ */ b("button", {
69
70
  type: "button",
70
71
  onClick: (e) => {
71
- e.stopPropagation(), T(b.id);
72
+ e.stopPropagation(), D(S.id);
72
73
  },
73
74
  className: "p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors",
74
- title: P("vibecontrols.actions.card.schedule", "Schedule this action"),
75
- children: /* @__PURE__ */ v(c, { size: 14 })
75
+ title: I("vibecontrols.actions.card.schedule", "Schedule this action"),
76
+ children: /* @__PURE__ */ b(l, { size: 14 })
76
77
  }),
77
- E && /* @__PURE__ */ v("button", {
78
+ O && /* @__PURE__ */ b("button", {
78
79
  type: "button",
79
80
  onClick: (e) => {
80
- e.stopPropagation(), E(b.id);
81
+ e.stopPropagation(), O(S.id);
81
82
  },
82
83
  className: "p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors",
83
- title: P("vibecontrols.actions.card.addToCalendar", "Add to calendar"),
84
- children: /* @__PURE__ */ v(l, { size: 14 })
84
+ title: I("vibecontrols.actions.card.addToCalendar", "Add to calendar"),
85
+ children: /* @__PURE__ */ b(d, { size: 14 })
85
86
  }),
86
- /* @__PURE__ */ v("button", {
87
+ /* @__PURE__ */ b("button", {
87
88
  type: "button",
88
89
  onClick: (e) => {
89
- e.stopPropagation(), C(b.id);
90
+ e.stopPropagation(), T(S.id);
90
91
  },
91
92
  className: "p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors",
92
- title: P("vibecontrols.actions.card.duplicate", "Duplicate"),
93
- children: /* @__PURE__ */ v(d, { size: 14 })
93
+ title: I("vibecontrols.actions.card.duplicate", "Duplicate"),
94
+ children: /* @__PURE__ */ b(p, { size: 14 })
94
95
  }),
95
- /* @__PURE__ */ v(s, {
96
+ /* @__PURE__ */ b(c, {
96
97
  entity: "action",
97
98
  resource: {
98
- id: b.id,
99
- name: b.displayName || b.name,
100
- vibeId: b.vibeId ?? null
99
+ id: S.id,
100
+ name: S.displayName || S.name,
101
+ vibeId: S.vibeId ?? null
101
102
  },
102
- label: P("vibecontrols.actions.card.share", "Share")
103
+ label: I("vibecontrols.actions.card.share", "Share")
103
104
  }),
104
- O && /* @__PURE__ */ v("button", {
105
+ A && /* @__PURE__ */ b("button", {
105
106
  type: "button",
106
107
  onClick: (e) => {
107
- e.stopPropagation(), w(b.id);
108
+ e.stopPropagation(), E(S.id);
108
109
  },
109
110
  className: "p-1.5 rounded-md hover:bg-status-error-bg-subtle text-status-error-text transition-colors",
110
- title: P("vibecontrols.actions.card.delete", "Delete"),
111
- children: /* @__PURE__ */ v(h, { size: 14 })
111
+ title: I("vibecontrols.actions.card.delete", "Delete"),
112
+ children: /* @__PURE__ */ b(_, { size: 14 })
112
113
  })
113
114
  ]
114
115
  })]
115
116
  }),
116
- /* @__PURE__ */ y("div", {
117
+ /* @__PURE__ */ x("div", {
117
118
  className: "flex flex-wrap items-center gap-1.5 mb-3",
118
119
  children: [
119
- /* @__PURE__ */ v(i, { type: b.actionType }),
120
- /* @__PURE__ */ v(o, { type: b.triggerType }),
121
- b.sourceType && /* @__PURE__ */ v(a, { type: b.sourceType })
120
+ /* @__PURE__ */ b(i, { type: S.actionType }),
121
+ /* @__PURE__ */ b(o, { type: S.triggerType }),
122
+ S.sourceType && /* @__PURE__ */ b(a, { type: S.sourceType })
122
123
  ]
123
124
  }),
124
- /* @__PURE__ */ v("div", {
125
+ S.actionType === "WEBHOOK" && S.webhookUrl && /* @__PURE__ */ b("div", {
126
+ className: "mb-3",
127
+ children: /* @__PURE__ */ b(s, {
128
+ url: S.webhookUrl,
129
+ method: S.webhookMethod
130
+ })
131
+ }),
132
+ /* @__PURE__ */ b("div", {
125
133
  className: "mb-3",
126
134
  onClick: (e) => e.stopPropagation(),
127
- children: /* @__PURE__ */ v(n, {
135
+ children: /* @__PURE__ */ b(n, {
128
136
  entityType: t.Action,
129
- entityId: b.id,
130
- currentVibeId: b.vibeId ?? null,
131
- currentVisibility: b.visibility ?? null,
137
+ entityId: S.id,
138
+ currentVibeId: S.vibeId ?? null,
139
+ currentVisibility: S.visibility ?? null,
132
140
  refetchQueries: ["ListActions"]
133
141
  })
134
142
  }),
135
- (b.category || b.tags && b.tags.length > 0) && /* @__PURE__ */ y("div", {
143
+ (S.category || S.tags && S.tags.length > 0) && /* @__PURE__ */ x("div", {
136
144
  className: "flex flex-wrap items-center gap-1.5 mb-3",
137
- children: [b.category && /* @__PURE__ */ v("span", {
145
+ children: [S.category && /* @__PURE__ */ b("span", {
138
146
  className: "text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary",
139
- children: b.category
140
- }), b.tags?.map((e) => /* @__PURE__ */ y("span", {
147
+ children: S.category
148
+ }), S.tags?.map((e) => /* @__PURE__ */ x("span", {
141
149
  className: "text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-placeholder",
142
150
  children: ["#", e]
143
151
  }, e))]
144
152
  }),
145
- /* @__PURE__ */ y("div", {
153
+ /* @__PURE__ */ x("div", {
146
154
  className: "flex items-center gap-4 text-xs text-text-placeholder",
147
- children: [/* @__PURE__ */ y("span", {
148
- className: "inline-flex items-center gap-1",
149
- children: [/* @__PURE__ */ v(f, { size: 12 }), P("vibecontrols.actions.card.executionRuns", "{{count}} runs", { count: b.executionCount ?? 0 })]
150
- }), /* @__PURE__ */ y("span", {
151
- className: "inline-flex items-center gap-1",
152
- children: [/* @__PURE__ */ v(u, { size: 12 }), ((e) => {
153
- if (!e) return P("vibecontrols.actions.card.never", "Never");
154
- let t = new Date(e), n = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), r = Math.floor(n / 6e4);
155
- if (r < 1) return P("vibecontrols.actions.card.justNow", "Just now");
156
- if (r < 60) return P("vibecontrols.actions.card.minutesAgo", "{{count}}m ago", { count: r });
157
- let i = Math.floor(r / 60);
158
- if (i < 24) return P("vibecontrols.actions.card.hoursAgo", "{{count}}h ago", { count: i });
159
- let a = Math.floor(i / 24);
160
- return a < 7 ? `${a}d ago` : t.toLocaleDateString();
161
- })(b.lastExecutedAt)]
162
- })]
155
+ children: [
156
+ S.confirmationRequired && /* @__PURE__ */ x("span", {
157
+ className: "inline-flex items-center gap-1 text-status-warning-text",
158
+ children: [/* @__PURE__ */ b(u, { size: 12 }), I("vibecontrols.actions.card.confirmationRequired", "Confirmation required")]
159
+ }),
160
+ /* @__PURE__ */ x("span", {
161
+ className: "inline-flex items-center gap-1",
162
+ children: [/* @__PURE__ */ b(m, { size: 12 }), I("vibecontrols.actions.card.executionRuns", "{{count}} runs", { count: S.executionCount ?? 0 })]
163
+ }),
164
+ /* @__PURE__ */ x("span", {
165
+ className: "inline-flex items-center gap-1",
166
+ children: [/* @__PURE__ */ b(f, { size: 12 }), ((e) => {
167
+ if (!e) return I("vibecontrols.actions.card.never", "Never");
168
+ let t = new Date(e), n = (/* @__PURE__ */ new Date()).getTime() - t.getTime(), r = Math.floor(n / 6e4);
169
+ if (r < 1) return I("vibecontrols.actions.card.justNow", "Just now");
170
+ if (r < 60) return I("vibecontrols.actions.card.minutesAgo", "{{count}}m ago", { count: r });
171
+ let i = Math.floor(r / 60);
172
+ if (i < 24) return I("vibecontrols.actions.card.hoursAgo", "{{count}}h ago", { count: i });
173
+ let a = Math.floor(i / 24);
174
+ return a < 7 ? `${a}d ago` : t.toLocaleDateString();
175
+ })(S.lastExecutedAt)]
176
+ })
177
+ ]
163
178
  })
164
179
  ]
165
180
  });
166
181
  }
167
182
  //#endregion
168
- export { b as ActionCard };
183
+ export { S as ActionCard };
169
184
 
170
185
  //# sourceMappingURL=ActionCard.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ActionCard.js","names":[],"sources":["../../../src/components/actions/ActionCard.tsx"],"sourcesContent":["import { Play, Pencil, Copy, Trash2, Clock, Hash, CalendarClock, AlarmClock } from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { ActionTypeBadge, TriggerTypeBadge, SourceTypeBadge } from './ActionTypeBadge';\nimport { SelectionCheckbox } from '../shared/SelectionCheckbox';\nimport { EntityVibeLinkControl } from '../shared/EntityVibeLinkControl';\nimport { EntityShareButton } from '../sharing';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { VibeEntityType, VibeVisibility } from '@/generated/wspace-types';\n\ninterface ActionCardAction {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired?: boolean;\n vibeId?: string | null;\n visibility?: VibeVisibility | null;\n}\n\ninterface ActionCardProps {\n action: ActionCardAction;\n onExecute: (id: string) => void;\n onEdit: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n onSchedule?: (id: string) => void;\n onAddToCalendar?: (id: string) => void;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n isExecuting?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\nexport function ActionCard({\n action,\n onExecute,\n onEdit,\n onDuplicate,\n onDelete,\n onSchedule,\n onAddToCalendar,\n canUpdate,\n canDelete,\n canExecute,\n isExecuting = false,\n selected = false,\n onToggleSelect,\n}: ActionCardProps) {\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n const formatDate = (dateStr: string | null | undefined) => {\n if (!dateStr) return tr('vibecontrols.actions.card.never', 'Never');\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tr('vibecontrols.actions.card.justNow', 'Just now');\n if (diffMin < 60)\n return tr('vibecontrols.actions.card.minutesAgo', '{{count}}m ago', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tr('vibecontrols.actions.card.hoursAgo', '{{count}}h ago', { count: diffHr });\n const diffDays = Math.floor(diffHr / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n };\n\n return (\n <motion.div\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25'\n : 'border-border-default'\n }`}\n whileHover={\n shouldReduceMotion\n ? undefined\n : { y: -2, boxShadow: '0 4px 12px var(--color-border-default)' }\n }\n transition={{ duration: 0.15 }}\n layout\n role=\"group\"\n aria-label={`Action: ${action.displayName || action.name}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select action ${action.displayName || action.name}`}\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate\">\n {action.displayName || action.name}\n </h3>\n {action.description && (\n <p className=\"text-sm text-text-secondary mt-0.5 line-clamp-2\">\n {action.description}\n </p>\n )}\n </div>\n </div>\n {/* Action buttons */}\n <div className=\"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\">\n {canExecute && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onExecute(action.id);\n }}\n disabled={isExecuting}\n className=\"p-1.5 rounded-md hover:bg-status-success-bg-subtle text-status-success-text transition-colors disabled:opacity-50\"\n title={tr('vibecontrols.actions.card.execute', 'Execute')}\n >\n <Play size={14} />\n </button>\n )}\n {canUpdate && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onEdit(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.edit', 'Edit')}\n >\n <Pencil size={14} />\n </button>\n )}\n {onSchedule && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onSchedule(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.schedule', 'Schedule this action')}\n >\n <AlarmClock size={14} />\n </button>\n )}\n {onAddToCalendar && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onAddToCalendar(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.addToCalendar', 'Add to calendar')}\n >\n <CalendarClock size={14} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDuplicate(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.duplicate', 'Duplicate')}\n >\n <Copy size={14} />\n </button>\n <EntityShareButton\n entity=\"action\"\n resource={{\n id: action.id,\n name: action.displayName || action.name,\n vibeId: action.vibeId ?? null,\n }}\n label={tr('vibecontrols.actions.card.share', 'Share')}\n />\n {canDelete && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDelete(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-status-error-bg-subtle text-status-error-text transition-colors\"\n title={tr('vibecontrols.actions.card.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n )}\n </div>\n </div>\n\n {/* Badges */}\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n <ActionTypeBadge type={action.actionType} />\n <TriggerTypeBadge type={action.triggerType} />\n {action.sourceType && <SourceTypeBadge type={action.sourceType} />}\n </div>\n\n {/* Linked vibe — map / unmap this action to a vibe */}\n <div className=\"mb-3\" onClick={(event) => event.stopPropagation()}>\n <EntityVibeLinkControl\n entityType={VibeEntityType.Action}\n entityId={action.id}\n currentVibeId={action.vibeId ?? null}\n currentVisibility={action.visibility ?? null}\n refetchQueries={['ListActions']}\n />\n </div>\n\n {/* Category and Tags */}\n {(action.category || (action.tags && action.tags.length > 0)) && (\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n {action.category && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary\">\n {action.category}\n </span>\n )}\n {action.tags?.map((tag) => (\n <span\n key={tag}\n className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-placeholder\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Stats */}\n <div className=\"flex items-center gap-4 text-xs text-text-placeholder\">\n <span className=\"inline-flex items-center gap-1\">\n <Hash size={12} />\n {tr('vibecontrols.actions.card.executionRuns', '{{count}} runs', {\n count: action.executionCount ?? 0,\n })}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={12} />\n {formatDate(action.lastExecutedAt)}\n </span>\n </div>\n </motion.div>\n );\n}\n"],"mappings":";;;;;;;;;;;AA0CA,SAAgB,EAAW,EACzB,WACA,cACA,WACA,gBACA,aACA,eACA,oBACA,cACA,cACA,eACA,iBAAc,IACd,cAAW,IACX,qBACkB;CAClB,IAAM,IAAqB,GAAkB,EACvC,IAAK,GAAO;AAmBlB,QACE,kBAAC,EAAO,KAAR;EACE,WAAW,0FACT,IACI,8DACA;EAEN,YACE,IACI,KAAA,IACA;GAAE,GAAG;GAAI,WAAW;GAA0C;EAEpE,YAAY,EAAE,UAAU,KAAM;EAC9B,QAAA;EACA,MAAK;EACL,cAAY,WAAW,EAAO,eAAe,EAAO;YAdtD;GAiBE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,KACC,kBAAC,OAAD;MAAK,UAAU,MAAU,EAAM,iBAAiB;gBAC9C,kBAAC,GAAD;OACE,SAAS;OACT,UAAU;OACV,WAAW,iBAAiB,EAAO,eAAe,EAAO;OACzD,CAAA;MACE,CAAA,EAER,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAO,eAAe,EAAO;OAC3B,CAAA,EACJ,EAAO,eACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAO;OACN,CAAA,CAEF;QACF;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAU,EAAO,GAAG;;OAEtB,UAAU;OACV,WAAU;OACV,OAAO,EAAG,qCAAqC,UAAU;iBAEzD,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;OACX,CAAA;MAEV,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAO,EAAO,GAAG;;OAEnB,WAAU;OACV,OAAO,EAAG,kCAAkC,OAAO;iBAEnD,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;OACb,CAAA;MAEV,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAW,EAAO,GAAG;;OAEvB,WAAU;OACV,OAAO,EAAG,sCAAsC,uBAAuB;iBAEvE,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;OACjB,CAAA;MAEV,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAgB,EAAO,GAAG;;OAE5B,WAAU;OACV,OAAO,EAAG,2CAA2C,kBAAkB;iBAEvE,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA;OACpB,CAAA;MAEX,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAY,EAAO,GAAG;;OAExB,WAAU;OACV,OAAO,EAAG,uCAAuC,YAAY;iBAE7D,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;OACX,CAAA;MACT,kBAAC,GAAD;OACE,QAAO;OACP,UAAU;QACR,IAAI,EAAO;QACX,MAAM,EAAO,eAAe,EAAO;QACnC,QAAQ,EAAO,UAAU;QAC1B;OACD,OAAO,EAAG,mCAAmC,QAAQ;OACrD,CAAA;MACD,KACC,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,MAAU;AAElB,QADA,EAAM,iBAAiB,EACvB,EAAS,EAAO,GAAG;;OAErB,WAAU;OACV,OAAO,EAAG,oCAAoC,SAAS;iBAEvD,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;OACb,CAAA;MAEP;OACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAiB,MAAM,EAAO,YAAc,CAAA;KAC5C,kBAAC,GAAD,EAAkB,MAAM,EAAO,aAAe,CAAA;KAC7C,EAAO,cAAc,kBAAC,GAAD,EAAiB,MAAM,EAAO,YAAc,CAAA;KAC9D;;GAGN,kBAAC,OAAD;IAAK,WAAU;IAAO,UAAU,MAAU,EAAM,iBAAiB;cAC/D,kBAAC,GAAD;KACE,YAAY,EAAe;KAC3B,UAAU,EAAO;KACjB,eAAe,EAAO,UAAU;KAChC,mBAAmB,EAAO,cAAc;KACxC,gBAAgB,CAAC,cAAc;KAC/B,CAAA;IACE,CAAA;IAGJ,EAAO,YAAa,EAAO,QAAQ,EAAO,KAAK,SAAS,MACxD,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAO,YACN,kBAAC,QAAD;KAAM,WAAU;eACb,EAAO;KACH,CAAA,EAER,EAAO,MAAM,KAAK,MACjB,kBAAC,QAAD;KAEE,WAAU;eAFZ,CAGC,KACG,EACG;OAJA,EAIA,CACP,CACE;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,2CAA2C,kBAAkB,EAC/D,OAAO,EAAO,kBAAkB,GACjC,CAAC,CACG;QACP,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,IAlMP,MAAuC;AACzD,UAAI,CAAC,EAAS,QAAO,EAAG,mCAAmC,QAAQ;MACnE,IAAM,IAAO,IAAI,KAAK,EAAQ,EAExB,qBADM,IAAI,MAAM,EACH,SAAS,GAAG,EAAK,SAAS,EACvC,IAAU,KAAK,MAAM,IAAS,IAAM;AAC1C,UAAI,IAAU,EAAG,QAAO,EAAG,qCAAqC,WAAW;AAC3E,UAAI,IAAU,GACZ,QAAO,EAAG,wCAAwC,kBAAkB,EAAE,OAAO,GAAS,CAAC;MACzF,IAAM,IAAS,KAAK,MAAM,IAAU,GAAG;AACvC,UAAI,IAAS,GACX,QAAO,EAAG,sCAAsC,kBAAkB,EAAE,OAAO,GAAQ,CAAC;MACtF,IAAM,IAAW,KAAK,MAAM,IAAS,GAAG;AAExC,aADI,IAAW,IAAU,GAAG,EAAS,SAC9B,EAAK,oBAAoB;QAqLd,EAAO,eAAe,CAC7B;OACH;;GACK"}
1
+ {"version":3,"file":"ActionCard.js","names":[],"sources":["../../../src/components/actions/ActionCard.tsx"],"sourcesContent":["import {\n Play,\n Pencil,\n Copy,\n Trash2,\n Clock,\n Hash,\n CalendarClock,\n AlarmClock,\n AlertTriangle,\n} from 'lucide-react';\nimport { motion, useReducedMotion } from 'framer-motion';\nimport { ActionTypeBadge, TriggerTypeBadge, SourceTypeBadge } from './ActionTypeBadge';\nimport { ActionWebhookEndpoint } from './ActionWebhookEndpoint';\nimport { SelectionCheckbox } from '../shared/SelectionCheckbox';\nimport { EntityVibeLinkControl } from '../shared/EntityVibeLinkControl';\nimport { EntityShareButton } from '../sharing';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { VibeEntityType, VibeVisibility } from '@/generated/wspace-types';\n\ninterface ActionCardAction {\n id: string;\n name: string;\n displayName?: string | null;\n description?: string | null;\n actionType: string;\n sourceType?: string | null;\n triggerType: string;\n category?: string | null;\n tags?: string[] | null;\n executionCount?: number | null;\n lastExecutedAt?: string | null;\n confirmationRequired?: boolean;\n webhookUrl?: string | null;\n webhookMethod?: string | null;\n vibeId?: string | null;\n visibility?: VibeVisibility | null;\n}\n\ninterface ActionCardProps {\n action: ActionCardAction;\n onExecute: (id: string) => void;\n onEdit: (id: string) => void;\n onDuplicate: (id: string) => void;\n onDelete: (id: string) => void;\n onSchedule?: (id: string) => void;\n onAddToCalendar?: (id: string) => void;\n canUpdate: boolean;\n canDelete: boolean;\n canExecute: boolean;\n isExecuting?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\nexport function ActionCard({\n action,\n onExecute,\n onEdit,\n onDuplicate,\n onDelete,\n onSchedule,\n onAddToCalendar,\n canUpdate,\n canDelete,\n canExecute,\n isExecuting = false,\n selected = false,\n onToggleSelect,\n}: ActionCardProps) {\n const shouldReduceMotion = useReducedMotion();\n const tr = useTr();\n\n const formatDate = (dateStr: string | null | undefined) => {\n if (!dateStr) return tr('vibecontrols.actions.card.never', 'Never');\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMin = Math.floor(diffMs / 60000);\n if (diffMin < 1) return tr('vibecontrols.actions.card.justNow', 'Just now');\n if (diffMin < 60)\n return tr('vibecontrols.actions.card.minutesAgo', '{{count}}m ago', { count: diffMin });\n const diffHr = Math.floor(diffMin / 60);\n if (diffHr < 24)\n return tr('vibecontrols.actions.card.hoursAgo', '{{count}}h ago', { count: diffHr });\n const diffDays = Math.floor(diffHr / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n };\n\n return (\n <motion.div\n className={`bg-bg-surface border rounded-lg p-4 hover:border-border-strong transition-colors group ${\n selected\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg/25'\n : 'border-border-default'\n }`}\n whileHover={\n shouldReduceMotion\n ? undefined\n : { y: -2, boxShadow: '0 4px 12px var(--color-border-default)' }\n }\n transition={{ duration: 0.15 }}\n layout\n role=\"group\"\n aria-label={`Action: ${action.displayName || action.name}`}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-3 mb-3\">\n <div className=\"flex items-start gap-3 min-w-0 flex-1\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select action ${action.displayName || action.name}`}\n />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"font-semibold text-text-primary truncate\">\n {action.displayName || action.name}\n </h3>\n {action.description && (\n <p className=\"text-sm text-text-secondary mt-0.5 line-clamp-2\">\n {action.description}\n </p>\n )}\n </div>\n </div>\n {/* Action buttons */}\n <div className=\"flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0\">\n {canExecute && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onExecute(action.id);\n }}\n disabled={isExecuting}\n className=\"p-1.5 rounded-md hover:bg-status-success-bg-subtle text-status-success-text transition-colors disabled:opacity-50\"\n title={tr('vibecontrols.actions.card.execute', 'Execute')}\n >\n <Play size={14} />\n </button>\n )}\n {canUpdate && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onEdit(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.edit', 'Edit')}\n >\n <Pencil size={14} />\n </button>\n )}\n {onSchedule && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onSchedule(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.schedule', 'Schedule this action')}\n >\n <AlarmClock size={14} />\n </button>\n )}\n {onAddToCalendar && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onAddToCalendar(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.addToCalendar', 'Add to calendar')}\n >\n <CalendarClock size={14} />\n </button>\n )}\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDuplicate(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-bg-elevated text-text-secondary transition-colors\"\n title={tr('vibecontrols.actions.card.duplicate', 'Duplicate')}\n >\n <Copy size={14} />\n </button>\n <EntityShareButton\n entity=\"action\"\n resource={{\n id: action.id,\n name: action.displayName || action.name,\n vibeId: action.vibeId ?? null,\n }}\n label={tr('vibecontrols.actions.card.share', 'Share')}\n />\n {canDelete && (\n <button\n type=\"button\"\n onClick={(event) => {\n event.stopPropagation();\n onDelete(action.id);\n }}\n className=\"p-1.5 rounded-md hover:bg-status-error-bg-subtle text-status-error-text transition-colors\"\n title={tr('vibecontrols.actions.card.delete', 'Delete')}\n >\n <Trash2 size={14} />\n </button>\n )}\n </div>\n </div>\n\n {/* Badges */}\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n <ActionTypeBadge type={action.actionType} />\n <TriggerTypeBadge type={action.triggerType} />\n {action.sourceType && <SourceTypeBadge type={action.sourceType} />}\n </div>\n\n {/* Webhook target — only webhook actions have one to show. */}\n {action.actionType === 'WEBHOOK' && action.webhookUrl && (\n <div className=\"mb-3\">\n <ActionWebhookEndpoint url={action.webhookUrl} method={action.webhookMethod} />\n </div>\n )}\n\n {/* Linked vibe — map / unmap this action to a vibe */}\n <div className=\"mb-3\" onClick={(event) => event.stopPropagation()}>\n <EntityVibeLinkControl\n entityType={VibeEntityType.Action}\n entityId={action.id}\n currentVibeId={action.vibeId ?? null}\n currentVisibility={action.visibility ?? null}\n refetchQueries={['ListActions']}\n />\n </div>\n\n {/* Category and Tags */}\n {(action.category || (action.tags && action.tags.length > 0)) && (\n <div className=\"flex flex-wrap items-center gap-1.5 mb-3\">\n {action.category && (\n <span className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-secondary\">\n {action.category}\n </span>\n )}\n {action.tags?.map((tag) => (\n <span\n key={tag}\n className=\"text-xs px-1.5 py-0.5 rounded bg-bg-sunken text-text-placeholder\"\n >\n #{tag}\n </span>\n ))}\n </div>\n )}\n\n {/* Stats */}\n <div className=\"flex items-center gap-4 text-xs text-text-placeholder\">\n {/* This action prompts before it runs — worth knowing before you hit\n Execute, and the only place the flag is visible outside the form. */}\n {action.confirmationRequired && (\n <span className=\"inline-flex items-center gap-1 text-status-warning-text\">\n <AlertTriangle size={12} />\n {tr('vibecontrols.actions.card.confirmationRequired', 'Confirmation required')}\n </span>\n )}\n <span className=\"inline-flex items-center gap-1\">\n <Hash size={12} />\n {tr('vibecontrols.actions.card.executionRuns', '{{count}} runs', {\n count: action.executionCount ?? 0,\n })}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={12} />\n {formatDate(action.lastExecutedAt)}\n </span>\n </div>\n </motion.div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { notify as t } from "../../lib/toast.js";
3
+ import { copyTextToClipboard as n } from "../../utils/shareUrl.js";
4
+ import { useState as r } from "react";
5
+ import { Check as i, Copy as a } from "lucide-react";
6
+ import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ //#region src/components/actions/ActionWebhookEndpoint.tsx
8
+ var c = 34, l = "POST";
9
+ function u(e) {
10
+ let t;
11
+ try {
12
+ let n = new URL(e);
13
+ t = n.pathname === "/" ? n.host : `${n.host}${n.pathname}`;
14
+ } catch {
15
+ t = e.replace(/^[a-zA-Z][a-zA-Z\d+.-]*:\/\//, "");
16
+ }
17
+ return t.length <= c ? t : `${t.slice(0, c - 1)}…`;
18
+ }
19
+ function d({ url: c, method: d }) {
20
+ let f = e(), [p, m] = r(!1), h = (d ?? l).toUpperCase(), g = u(c), _ = async () => {
21
+ try {
22
+ await n(c), m(!0), t.success(f("vibecontrols.actions.card.endpointCopied", "Webhook URL copied")), window.setTimeout(() => m(!1), 1500);
23
+ } catch {
24
+ t.error(f("vibecontrols.actions.card.endpointCopyFailed", "Could not copy — copy it manually"));
25
+ }
26
+ };
27
+ return /* @__PURE__ */ s("div", {
28
+ className: "flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-2 py-1.5",
29
+ onClick: (e) => e.stopPropagation(),
30
+ children: [
31
+ /* @__PURE__ */ o("span", {
32
+ className: "font-mono text-xs font-semibold text-text-secondary flex-shrink-0",
33
+ children: h
34
+ }),
35
+ /* @__PURE__ */ o("span", {
36
+ className: "flex-1 truncate font-mono text-xs text-text-placeholder",
37
+ title: g,
38
+ children: g
39
+ }),
40
+ /* @__PURE__ */ o("button", {
41
+ type: "button",
42
+ onClick: () => void _(),
43
+ className: "p-0.5 rounded text-text-secondary hover:text-text-primary transition-colors flex-shrink-0",
44
+ "aria-label": f("vibecontrols.actions.card.copyEndpoint", "Copy webhook URL"),
45
+ title: f("vibecontrols.actions.card.copyEndpoint", "Copy webhook URL"),
46
+ children: p ? /* @__PURE__ */ o(i, {
47
+ size: 12,
48
+ className: "text-status-success-text"
49
+ }) : /* @__PURE__ */ o(a, { size: 12 })
50
+ })
51
+ ]
52
+ });
53
+ }
54
+ //#endregion
55
+ export { d as ActionWebhookEndpoint };
56
+
57
+ //# sourceMappingURL=ActionWebhookEndpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionWebhookEndpoint.js","names":[],"sources":["../../../src/components/actions/ActionWebhookEndpoint.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Copy, Check } from 'lucide-react';\nimport { notify } from '@/lib/toast';\nimport { copyTextToClipboard } from '@/utils/shareUrl';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/**\n * Longest endpoint string we render before eliding. Sized so a preview still\n * fits on one line inside a three-up action card at the narrowest grid step.\n */\nconst MAX_ENDPOINT_LENGTH = 34;\n\n/**\n * Webhook actions default to POST in ActionForm, so a row that predates an\n * explicit method (or a null from the API) reads as the same default here\n * instead of rendering a blank verb.\n */\nconst DEFAULT_METHOD = 'POST';\n\n/**\n * Build the at-a-glance form of a webhook target: host + path, no scheme.\n *\n * The query string is deliberately dropped from the *preview* — webhook URLs\n * routinely carry a token there (Slack, Discord, PagerDuty all do) and a card\n * in a shared library is exactly where that should not be shoulder-surfable.\n * The copy button still copies the untouched URL, so nothing is lost.\n */\nfunction formatEndpoint(rawUrl: string): string {\n let display: string;\n try {\n const parsed = new URL(rawUrl);\n display = parsed.pathname === '/' ? parsed.host : `${parsed.host}${parsed.pathname}`;\n } catch {\n // Not a parseable absolute URL (a template or a relative target) — strip\n // any scheme we can recognise and show the rest verbatim.\n display = rawUrl.replace(/^[a-zA-Z][a-zA-Z\\d+.-]*:\\/\\//, '');\n }\n if (display.length <= MAX_ENDPOINT_LENGTH) return display;\n return `${display.slice(0, MAX_ENDPOINT_LENGTH - 1)}…`;\n}\n\ninterface ActionWebhookEndpointProps {\n /** The full webhook target as stored on the action. */\n url: string;\n /** HTTP verb the action fires; falls back to POST when unset. */\n method?: string | null;\n}\n\n/**\n * ActionWebhookEndpoint — the `POST hooks.slack.com/…` line on a webhook action\n * card, with one-click copy of the full URL.\n *\n * Without it a webhook action is indistinguishable from every other webhook\n * action on the library screen: the card shows the name and a \"Webhook\" badge\n * but nothing about where the request actually goes. Surfacing method + host\n * makes \"which of these three alert hooks points at the on-call channel?\"\n * answerable without opening the edit form.\n */\nexport function ActionWebhookEndpoint({ url, method }: ActionWebhookEndpointProps) {\n const tr = useTr();\n const [copied, setCopied] = useState(false);\n\n const verb = (method ?? DEFAULT_METHOD).toUpperCase();\n const endpoint = formatEndpoint(url);\n\n const handleCopy = async (): Promise<void> => {\n try {\n await copyTextToClipboard(url);\n setCopied(true);\n notify.success(tr('vibecontrols.actions.card.endpointCopied', 'Webhook URL copied'));\n window.setTimeout(() => setCopied(false), 1500);\n } catch {\n notify.error(\n tr('vibecontrols.actions.card.endpointCopyFailed', 'Could not copy — copy it manually')\n );\n }\n };\n\n return (\n <div\n className=\"flex items-center gap-2 rounded-md border border-border-default bg-bg-sunken px-2 py-1.5\"\n onClick={(event) => event.stopPropagation()}\n >\n <span className=\"font-mono text-xs font-semibold text-text-secondary flex-shrink-0\">\n {verb}\n </span>\n <span className=\"flex-1 truncate font-mono text-xs text-text-placeholder\" title={endpoint}>\n {endpoint}\n </span>\n <button\n type=\"button\"\n onClick={() => void handleCopy()}\n className=\"p-0.5 rounded text-text-secondary hover:text-text-primary transition-colors flex-shrink-0\"\n aria-label={tr('vibecontrols.actions.card.copyEndpoint', 'Copy webhook URL')}\n title={tr('vibecontrols.actions.card.copyEndpoint', 'Copy webhook URL')}\n >\n {copied ? <Check size={12} className=\"text-status-success-text\" /> : <Copy size={12} />}\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAUA,IAAM,IAAsB,IAOtB,IAAiB;AAUvB,SAAS,EAAe,GAAwB;CAC9C,IAAI;AACJ,KAAI;EACF,IAAM,IAAS,IAAI,IAAI,EAAO;AAC9B,MAAU,EAAO,aAAa,MAAM,EAAO,OAAO,GAAG,EAAO,OAAO,EAAO;SACpE;AAGN,MAAU,EAAO,QAAQ,gCAAgC,GAAG;;AAG9D,QADI,EAAQ,UAAU,IAA4B,IAC3C,GAAG,EAAQ,MAAM,GAAG,IAAsB,EAAE,CAAC;;AAoBtD,SAAgB,EAAsB,EAAE,QAAK,aAAsC;CACjF,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAM,EAErC,KAAQ,KAAU,GAAgB,aAAa,EAC/C,IAAW,EAAe,EAAI,EAE9B,IAAa,YAA2B;AAC5C,MAAI;AAIF,GAHA,MAAM,EAAoB,EAAI,EAC9B,EAAU,GAAK,EACf,EAAO,QAAQ,EAAG,4CAA4C,qBAAqB,CAAC,EACpF,OAAO,iBAAiB,EAAU,GAAM,EAAE,KAAK;UACzC;AACN,KAAO,MACL,EAAG,gDAAgD,oCAAoC,CACxF;;;AAIL,QACE,kBAAC,OAAD;EACE,WAAU;EACV,UAAU,MAAU,EAAM,iBAAiB;YAF7C;GAIE,kBAAC,QAAD;IAAM,WAAU;cACb;IACI,CAAA;GACP,kBAAC,QAAD;IAAM,WAAU;IAA0D,OAAO;cAC9E;IACI,CAAA;GACP,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,GAAY;IAChC,WAAU;IACV,cAAY,EAAG,0CAA0C,mBAAmB;IAC5E,OAAO,EAAG,0CAA0C,mBAAmB;cAEtE,IAAS,kBAAC,GAAD;KAAO,MAAM;KAAI,WAAU;KAA6B,CAAA,GAAG,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;IAChF,CAAA;GACL"}
@@ -1,4 +1,5 @@
1
1
  import "./ActionTypeBadge.js";
2
+ import "./ActionWebhookEndpoint.js";
2
3
  import "./ActionCard.js";
3
4
  import "./ActionForm.js";
4
5
  import "./ExecutionHistory.js";
@@ -179,11 +179,11 @@ function g({ agent: g }) {
179
179
  /* @__PURE__ */ p("div", {
180
180
  className: "flex gap-2",
181
181
  children: /* @__PURE__ */ p("input", {
182
- "aria-label": "e.g., work, personal, minimal",
182
+ "aria-label": _("vibecontrols.agentManager.bridle.profileNameAriaLabel", "Profile name"),
183
183
  type: "text",
184
184
  value: S,
185
185
  onChange: (e) => C(e.target.value),
186
- placeholder: "e.g., work, personal, minimal",
186
+ placeholder: _("vibecontrols.agentManager.bridle.profileNamePlaceholder", "e.g., work, personal, minimal"),
187
187
  className: "flex-1 px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong"
188
188
  })
189
189
  }),
@@ -237,11 +237,11 @@ function g({ agent: g }) {
237
237
  /* @__PURE__ */ m("div", {
238
238
  className: "flex gap-2",
239
239
  children: [/* @__PURE__ */ p("input", {
240
- "aria-label": "owner/repo or full GitHub URL",
240
+ "aria-label": _("vibecontrols.agentManager.bridle.installSourceAriaLabel", "GitHub repository or full URL"),
241
241
  type: "text",
242
242
  value: w,
243
243
  onChange: (e) => T(e.target.value),
244
- placeholder: "owner/repo or full GitHub URL",
244
+ placeholder: _("vibecontrols.agentManager.bridle.installSourcePlaceholder", "owner/repo or full GitHub URL"),
245
245
  className: "flex-1 px-3 py-2 text-sm rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong"
246
246
  }), /* @__PURE__ */ m("button", {
247
247
  type: "button",
@@ -275,27 +275,27 @@ function g({ agent: g }) {
275
275
  /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
276
276
  htmlFor: "bridleconfigpanel-4",
277
277
  className: "block text-xs font-medium text-text-secondary mb-1",
278
- children: "Key"
278
+ children: _("vibecontrols.agentManager.bridle.configKeyLabel", "Key")
279
279
  }), /* @__PURE__ */ p("input", {
280
280
  id: "bridleconfigpanel-4",
281
- "aria-label": "e.g., editor, default_harness, profile_marker",
281
+ "aria-label": _("vibecontrols.agentManager.bridle.configKeyAriaLabel", "Configuration key"),
282
282
  type: "text",
283
283
  value: E,
284
284
  onChange: (e) => D(e.target.value),
285
- placeholder: "e.g., editor, default_harness, profile_marker",
285
+ placeholder: _("vibecontrols.agentManager.bridle.configKeyPlaceholder", "e.g., editor, default_harness, profile_marker"),
286
286
  className: "w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong"
287
287
  })] }),
288
288
  /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
289
289
  htmlFor: "bridleconfigpanel-3",
290
290
  className: "block text-xs font-medium text-text-secondary mb-1",
291
- children: "Value"
291
+ children: _("vibecontrols.agentManager.bridle.configValueLabel", "Value")
292
292
  }), /* @__PURE__ */ p("input", {
293
293
  id: "bridleconfigpanel-3",
294
- "aria-label": "value",
294
+ "aria-label": _("vibecontrols.agentManager.bridle.configValueAriaLabel", "Configuration value"),
295
295
  type: "text",
296
296
  value: O,
297
297
  onChange: (e) => k(e.target.value),
298
- placeholder: "value",
298
+ placeholder: _("vibecontrols.agentManager.bridle.configValuePlaceholder", "value"),
299
299
  className: "w-full px-3 py-1.5 text-xs rounded border border-border-default bg-bg-surface text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-border-strong"
300
300
  })] }),
301
301
  /* @__PURE__ */ m("div", {