@burdenoff/microfe-vibecontrols 2026.519.2 → 2026.519.4

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 (40) hide show
  1. package/dist/components/actions/ActionForm.js +138 -134
  2. package/dist/components/actions/ActionForm.js.map +1 -1
  3. package/dist/components/docs/DocsPageEditor.js +83 -69
  4. package/dist/components/docs/DocsPageEditor.js.map +1 -1
  5. package/dist/components/notes/NotesTabPanel.js +4 -5
  6. package/dist/components/notes/NotesTabPanel.js.map +1 -1
  7. package/dist/components/shared/EntityTagPicker.js +110 -81
  8. package/dist/components/shared/EntityTagPicker.js.map +1 -1
  9. package/dist/components/shared/TagFilter.js +4 -4
  10. package/dist/components/shared/index.js +1 -1
  11. package/dist/components/tunnels/TunnelCreateWizard.js +208 -187
  12. package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
  13. package/dist/components/vibedeck/VibeDeckForm.js +41 -37
  14. package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
  15. package/dist/pages/ActionsPage.js +225 -217
  16. package/dist/pages/ActionsPage.js.map +1 -1
  17. package/dist/pages/AgentDetailsPage.js +13 -13
  18. package/dist/pages/AgentDetailsPage.js.map +1 -1
  19. package/dist/pages/CatalogDetailPage.js +248 -234
  20. package/dist/pages/CatalogDetailPage.js.map +1 -1
  21. package/dist/pages/CatalogDocsPage.js +119 -105
  22. package/dist/pages/CatalogDocsPage.js.map +1 -1
  23. package/dist/pages/ComponentDetailPage.js +257 -243
  24. package/dist/pages/ComponentDetailPage.js.map +1 -1
  25. package/dist/pages/ComponentDocsPage.js +119 -105
  26. package/dist/pages/ComponentDocsPage.js.map +1 -1
  27. package/dist/pages/SessionDetailsPage.js +4 -4
  28. package/dist/pages/TargetDetailsPage.js +136 -119
  29. package/dist/pages/TargetDetailsPage.js.map +1 -1
  30. package/dist/pages/TemplateDetailPage.js +47 -33
  31. package/dist/pages/TemplateDetailPage.js.map +1 -1
  32. package/dist/pages/VibeDeckPage.js +161 -153
  33. package/dist/pages/VibeDeckPage.js.map +1 -1
  34. package/dist/pages/VibeDetailsPage.js +407 -407
  35. package/dist/pages/VibeDetailsPage.js.map +1 -1
  36. package/dist/pages/VibeDocsPage.js +119 -105
  37. package/dist/pages/VibeDocsPage.js.map +1 -1
  38. package/dist/pages/tunnels/TunnelDetailsPage.js +95 -77
  39. package/dist/pages/tunnels/TunnelDetailsPage.js.map +1 -1
  40. package/package.json +1 -1
@@ -5,83 +5,115 @@ import { Loader2 as d, Plus as f, Search as p, Tag as m, X as h } from "lucide-r
5
5
  import { jsx as g, jsxs as _ } from "react/jsx-runtime";
6
6
  //#region src/components/shared/EntityTagPicker.tsx
7
7
  var v = 4e3, y = 250;
8
- function b({ entityType: b, entityId: x, readOnly: S = !1 }) {
9
- let C = e(), [w, T] = u(!1), [E, D] = u(""), [O, k] = u(null), { data: A, loading: j, refetch: M } = n(b, x), { data: N, loading: P } = t(50, { skip: !w }), [F, { data: I, loading: L }] = r(), [R, { loading: z }] = i(), [B, { loading: V }] = a(), H = A?.vibecontrolsEntityTags ?? [], U = c(() => new Set(H.map((e) => e.tagId)), [H]), W = c(() => ((E ? I?.searchVibecontrolsTags : N?.availableVibecontrolsTags) ?? []).filter((e) => !U.has(e.id)), [
10
- E,
11
- I,
12
- N,
13
- U
8
+ function b({ entityType: b, entityId: x, onDraftChange: S, readOnly: C = !1 }) {
9
+ let w = e(), T = !x, [E, D] = u(!1), [O, k] = u(""), [A, j] = u(null), [M, N] = u([]), { data: P, loading: F, refetch: I } = n(b, x ?? ""), { data: L, loading: R } = t(50, { skip: !E }), [z, { data: B, loading: V }] = r(), [H, { loading: U }] = i(), [W, { loading: G }] = a(), K = c(() => P?.vibecontrolsEntityTags ?? [], [P]), q = c(() => T ? M.map((e) => ({
10
+ key: e.id,
11
+ tagId: e.id,
12
+ label: e.label
13
+ })) : K.map((e) => ({
14
+ key: e.id,
15
+ tagId: e.tagId,
16
+ label: e.tag?.label ?? w("vibecontrols.shared.entityTagPicker.untitledTag", "Untitled tag")
17
+ })), [
18
+ T,
19
+ M,
20
+ K,
21
+ w
22
+ ]), J = c(() => new Set(q.map((e) => e.tagId)), [q]), Y = c(() => ((O ? B?.searchVibecontrolsTags : L?.availableVibecontrolsTags) ?? []).filter((e) => !J.has(e.id)), [
23
+ O,
24
+ B,
25
+ L,
26
+ J
14
27
  ]);
15
28
  s(() => {
16
- if (!O) return;
17
- let e = setTimeout(() => k(null), v);
29
+ if (!A) return;
30
+ let e = setTimeout(() => j(null), v);
18
31
  return () => clearTimeout(e);
19
- }, [O]);
20
- let G = l(null);
32
+ }, [A]);
33
+ let X = l(null);
21
34
  s(() => () => {
22
- G.current && clearTimeout(G.current);
35
+ X.current && clearTimeout(X.current);
23
36
  }, []);
24
- let K = o((e) => {
25
- D(e), G.current && clearTimeout(G.current), !(e.length < 2) && (G.current = setTimeout(() => {
26
- F({ variables: {
37
+ let Z = o((e) => {
38
+ k(e), X.current && clearTimeout(X.current), !(e.length < 2) && (X.current = setTimeout(() => {
39
+ z({ variables: {
27
40
  query: e,
28
41
  limit: 10
29
42
  } });
30
43
  }, y));
31
- }, [F]), q = o(async (e) => {
44
+ }, [z]), Q = o(async (e) => {
45
+ if (T) {
46
+ N((t) => {
47
+ if (t.some((t) => t.id === e.id)) return t;
48
+ let n = [...t, e];
49
+ return S?.(n.map((e) => e.id)), n;
50
+ }), k(""), j(null);
51
+ return;
52
+ }
32
53
  try {
33
- await R({ variables: { input: {
54
+ await H({ variables: { input: {
34
55
  entityType: b,
35
- entityId: x,
36
- tagId: e
37
- } } }), await M(), D(""), k(null);
56
+ entityId: x ?? "",
57
+ tagId: e.id
58
+ } } }), await I(), k(""), j(null);
38
59
  } catch (e) {
39
- k(e instanceof Error ? e.message : C("vibecontrols.shared.entityTagPicker.failedToAddTag", "Failed to add tag"));
60
+ j(e instanceof Error ? e.message : w("vibecontrols.shared.entityTagPicker.failedToAddTag", "Failed to add tag"));
40
61
  }
41
62
  }, [
42
- R,
63
+ T,
64
+ S,
65
+ H,
43
66
  b,
44
67
  x,
45
- M,
46
- C
47
- ]), J = o(async (e) => {
68
+ I,
69
+ w
70
+ ]), $ = o(async (e) => {
71
+ if (T) {
72
+ N((t) => {
73
+ let n = t.filter((t) => t.id !== e);
74
+ return S?.(n.map((e) => e.id)), n;
75
+ }), j(null);
76
+ return;
77
+ }
48
78
  try {
49
- await B({ variables: { input: {
79
+ await W({ variables: { input: {
50
80
  entityType: b,
51
- entityId: x,
81
+ entityId: x ?? "",
52
82
  tagId: e
53
- } } }), await M(), k(null);
83
+ } } }), await I(), j(null);
54
84
  } catch (e) {
55
- k(e instanceof Error ? e.message : C("vibecontrols.shared.entityTagPicker.failedToRemoveTag", "Failed to remove tag"));
85
+ j(e instanceof Error ? e.message : w("vibecontrols.shared.entityTagPicker.failedToRemoveTag", "Failed to remove tag"));
56
86
  }
57
87
  }, [
58
- B,
88
+ T,
89
+ S,
90
+ W,
59
91
  b,
60
92
  x,
61
- M,
62
- C
63
- ]), Y = o((e) => e.tag?.label ?? C("vibecontrols.shared.entityTagPicker.untitledTag", "Untitled tag"), [C]);
64
- return j ? /* @__PURE__ */ _("div", {
93
+ I,
94
+ w
95
+ ]);
96
+ return F && !T ? /* @__PURE__ */ _("div", {
65
97
  className: "flex items-center gap-2 text-text-secondary text-sm",
66
98
  role: "status",
67
99
  "aria-live": "polite",
68
100
  children: [/* @__PURE__ */ g(d, {
69
101
  className: "h-4 w-4 animate-spin",
70
102
  "aria-hidden": "true"
71
- }), C("vibecontrols.shared.entityTagPicker.loadingTags", "Loading tags...")]
103
+ }), w("vibecontrols.shared.entityTagPicker.loadingTags", "Loading tags...")]
72
104
  }) : /* @__PURE__ */ _("div", {
73
105
  className: "space-y-3",
74
106
  children: [
75
- O && /* @__PURE__ */ _("div", {
107
+ A && /* @__PURE__ */ _("div", {
76
108
  role: "alert",
77
109
  className: "flex items-start gap-2 rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700",
78
110
  children: [/* @__PURE__ */ g("span", {
79
111
  className: "flex-1",
80
- children: O
112
+ children: A
81
113
  }), /* @__PURE__ */ g("button", {
82
114
  type: "button",
83
- onClick: () => k(null),
84
- "aria-label": C("vibecontrols.shared.entityTagPicker.dismissError", "Dismiss error"),
115
+ onClick: () => j(null),
116
+ "aria-label": w("vibecontrols.shared.entityTagPicker.dismissError", "Dismiss error"),
85
117
  className: "text-red-700 hover:text-red-900",
86
118
  children: /* @__PURE__ */ g(h, {
87
119
  className: "h-3.5 w-3.5",
@@ -91,49 +123,46 @@ function b({ entityType: b, entityId: x, readOnly: S = !1 }) {
91
123
  }),
92
124
  /* @__PURE__ */ _("div", {
93
125
  className: "flex flex-wrap gap-2",
94
- "aria-label": C("vibecontrols.shared.entityTagPicker.assignedTags", "Assigned tags"),
126
+ "aria-label": w("vibecontrols.shared.entityTagPicker.assignedTags", "Assigned tags"),
95
127
  children: [
96
- H.length === 0 && !w && /* @__PURE__ */ g("p", {
128
+ q.length === 0 && !E && /* @__PURE__ */ g("p", {
97
129
  className: "text-text-secondary text-sm",
98
- children: C("vibecontrols.shared.entityTagPicker.noTagsAssigned", "No tags assigned")
130
+ children: w("vibecontrols.shared.entityTagPicker.noTagsAssigned", "No tags assigned")
99
131
  }),
100
- H.map((e) => {
101
- let t = Y(e);
102
- return /* @__PURE__ */ _("span", {
103
- className: "inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full",
104
- children: [
105
- /* @__PURE__ */ g(m, {
132
+ q.map((e) => /* @__PURE__ */ _("span", {
133
+ className: "inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full",
134
+ children: [
135
+ /* @__PURE__ */ g(m, {
136
+ className: "h-3 w-3",
137
+ "aria-hidden": "true"
138
+ }),
139
+ e.label,
140
+ !C && /* @__PURE__ */ g("button", {
141
+ type: "button",
142
+ onClick: () => $(e.tagId),
143
+ disabled: G,
144
+ "aria-label": `Remove tag ${e.label}`,
145
+ className: "ml-1 hover:text-red-500 transition-colors disabled:opacity-50",
146
+ children: /* @__PURE__ */ g(h, {
106
147
  className: "h-3 w-3",
107
148
  "aria-hidden": "true"
108
- }),
109
- t,
110
- !S && /* @__PURE__ */ g("button", {
111
- type: "button",
112
- onClick: () => J(e.tagId),
113
- disabled: V,
114
- "aria-label": `Remove tag ${t}`,
115
- className: "ml-1 hover:text-red-500 transition-colors disabled:opacity-50",
116
- children: /* @__PURE__ */ g(h, {
117
- className: "h-3 w-3",
118
- "aria-hidden": "true"
119
- })
120
149
  })
121
- ]
122
- }, e.id);
123
- }),
124
- !S && !w && /* @__PURE__ */ _("button", {
150
+ })
151
+ ]
152
+ }, e.key)),
153
+ !C && !E && /* @__PURE__ */ _("button", {
125
154
  type: "button",
126
- onClick: () => T(!0),
127
- "aria-label": C("vibecontrols.shared.entityTagPicker.addTag", "Add tag"),
155
+ onClick: () => D(!0),
156
+ "aria-label": w("vibecontrols.shared.entityTagPicker.addTag", "Add tag"),
128
157
  className: "inline-flex items-center gap-1 px-3 py-1 border border-dashed border-border-default text-text-secondary text-sm rounded-full hover:border-action-primary-bg hover:text-action-primary-bg transition-colors",
129
158
  children: [/* @__PURE__ */ g(f, {
130
159
  className: "h-3 w-3",
131
160
  "aria-hidden": "true"
132
- }), C("vibecontrols.shared.entityTagPicker.addTag", "Add Tag")]
161
+ }), w("vibecontrols.shared.entityTagPicker.addTag", "Add Tag")]
133
162
  })
134
163
  ]
135
164
  }),
136
- w && /* @__PURE__ */ _("div", {
165
+ E && /* @__PURE__ */ _("div", {
137
166
  className: "border border-border-default rounded-lg p-3 space-y-2 bg-bg-surface",
138
167
  children: [
139
168
  /* @__PURE__ */ _("div", {
@@ -143,30 +172,30 @@ function b({ entityType: b, entityId: x, readOnly: S = !1 }) {
143
172
  "aria-hidden": "true"
144
173
  }), /* @__PURE__ */ g("input", {
145
174
  type: "text",
146
- value: E,
147
- onChange: (e) => K(e.target.value),
148
- placeholder: C("vibecontrols.shared.entityTagPicker.searchPlaceholder", "Search tags..."),
175
+ value: O,
176
+ onChange: (e) => Z(e.target.value),
177
+ placeholder: w("vibecontrols.shared.entityTagPicker.searchPlaceholder", "Search tags..."),
149
178
  autoFocus: !0,
150
- "aria-label": C("vibecontrols.shared.entityTagPicker.searchAvailableTags", "Search available tags"),
179
+ "aria-label": w("vibecontrols.shared.entityTagPicker.searchAvailableTags", "Search available tags"),
151
180
  className: "w-full pl-9 pr-3 py-2 text-sm bg-bg-default border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary placeholder:text-text-secondary"
152
181
  })]
153
182
  }),
154
183
  /* @__PURE__ */ _("div", {
155
184
  className: "flex flex-wrap gap-2 max-h-32 overflow-y-auto",
156
- "aria-label": C("vibecontrols.shared.entityTagPicker.tagSuggestions", "Tag suggestions"),
185
+ "aria-label": w("vibecontrols.shared.entityTagPicker.tagSuggestions", "Tag suggestions"),
157
186
  children: [
158
- (L || P && !E) && /* @__PURE__ */ g("span", {
187
+ (V || R && !O) && /* @__PURE__ */ g("span", {
159
188
  className: "text-text-secondary text-xs",
160
- children: C("vibecontrols.shared.entityTagPicker.loading", "Loading...")
189
+ children: w("vibecontrols.shared.entityTagPicker.loading", "Loading...")
161
190
  }),
162
- !L && !P && W.length === 0 && /* @__PURE__ */ g("span", {
191
+ !V && !R && Y.length === 0 && /* @__PURE__ */ g("span", {
163
192
  className: "text-text-secondary text-xs",
164
- children: E ? C("vibecontrols.shared.entityTagPicker.noMatchingTags", "No matching tags") : C("vibecontrols.shared.entityTagPicker.noAvailableTags", "No available tags")
193
+ children: O ? w("vibecontrols.shared.entityTagPicker.noMatchingTags", "No matching tags") : w("vibecontrols.shared.entityTagPicker.noAvailableTags", "No available tags")
165
194
  }),
166
- W.map((e) => /* @__PURE__ */ _("button", {
195
+ Y.map((e) => /* @__PURE__ */ _("button", {
167
196
  type: "button",
168
- onClick: () => q(e.id),
169
- disabled: z,
197
+ onClick: () => Q(e),
198
+ disabled: U,
170
199
  "aria-label": `Add tag ${e.label}`,
171
200
  className: "inline-flex items-center gap-1 px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full hover:bg-action-primary-bg/10 hover:text-action-primary-bg transition-colors cursor-pointer disabled:opacity-50",
172
201
  children: [/* @__PURE__ */ g(f, {
@@ -179,10 +208,10 @@ function b({ entityType: b, entityId: x, readOnly: S = !1 }) {
179
208
  /* @__PURE__ */ g("button", {
180
209
  type: "button",
181
210
  onClick: () => {
182
- T(!1), D("");
211
+ D(!1), k("");
183
212
  },
184
213
  className: "text-xs text-text-secondary hover:text-text-primary",
185
- children: C("vibecontrols.shared.entityTagPicker.done", "Done")
214
+ children: w("vibecontrols.shared.entityTagPicker.done", "Done")
186
215
  })
187
216
  ]
188
217
  })
@@ -1 +1 @@
1
- {"version":3,"file":"EntityTagPicker.js","names":[],"sources":["../../../src/components/shared/EntityTagPicker.tsx"],"sourcesContent":["/**\n * EntityTagPicker — Reusable tag picker for any VibeControls entity\n *\n * Usage:\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" entityId={vibe.id} />\n */\nimport { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { Tag, Plus, X, Search, Loader2 } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetEntityTags,\n useAvailableTags,\n useSearchTags,\n useTagEntity,\n useUntagEntity,\n} from '../../hooks/useEntityTags';\nimport type { AvailableTag, EntityTagAssignment } from '../../hooks/useEntityTags';\n\ninterface EntityTagPickerProps {\n entityType: string;\n entityId: string;\n readOnly?: boolean;\n}\n\nconst ERROR_DISMISS_MS = 4000;\nconst SEARCH_DEBOUNCE_MS = 250;\n\nexport function EntityTagPicker({ entityType, entityId, readOnly = false }: EntityTagPickerProps) {\n const tr = useTr();\n const [isAdding, setIsAdding] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const {\n data: tagsData,\n loading: tagsLoading,\n refetch: refetchTags,\n } = useGetEntityTags(entityType, entityId);\n const { data: availableData, loading: availableLoading } = useAvailableTags(50, {\n skip: !isAdding,\n });\n const [searchTags, { data: searchData, loading: searchLoading }] = useSearchTags();\n const [tagEntity, { loading: tagging }] = useTagEntity();\n const [untagEntity, { loading: untagging }] = useUntagEntity();\n\n // Apollo Client v4 returns DeepPartialObject from useQuery data, but our\n // hook types declare full shapes — we cast back to the declared types since\n // the resolver is guaranteed to return non-null fields per the schema.\n const assignedTags = (tagsData?.vibecontrolsEntityTags ?? []) as EntityTagAssignment[];\n const assignedTagIds = useMemo(() => new Set(assignedTags.map((t) => t.tagId)), [assignedTags]);\n\n const suggestions: AvailableTag[] = useMemo(() => {\n const source = searchQuery\n ? (searchData?.searchVibecontrolsTags as AvailableTag[] | undefined)\n : (availableData?.availableVibecontrolsTags as AvailableTag[] | undefined);\n return (source ?? []).filter((t) => !assignedTagIds.has(t.id));\n }, [searchQuery, searchData, availableData, assignedTagIds]);\n\n // Auto-dismiss error toast after a few seconds\n useEffect(() => {\n if (!errorMessage) return;\n const timer = setTimeout(() => setErrorMessage(null), ERROR_DISMISS_MS);\n return () => clearTimeout(timer);\n }, [errorMessage]);\n\n // Debounce the search query so we don't fire a request on every keystroke.\n const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(() => {\n return () => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n };\n }, []);\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query);\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n if (query.length < 2) return;\n searchTimerRef.current = setTimeout(() => {\n searchTags({ variables: { query, limit: 10 } });\n }, SEARCH_DEBOUNCE_MS);\n },\n [searchTags]\n );\n\n const handleAddTag = useCallback(\n async (tagId: string) => {\n try {\n await tagEntity({ variables: { input: { entityType, entityId, tagId } } });\n await refetchTags();\n setSearchQuery('');\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToAddTag', 'Failed to add tag')\n );\n }\n },\n [tagEntity, entityType, entityId, refetchTags, tr]\n );\n\n const handleRemoveTag = useCallback(\n async (tagId: string) => {\n try {\n await untagEntity({ variables: { input: { entityType, entityId, tagId } } });\n await refetchTags();\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToRemoveTag', 'Failed to remove tag')\n );\n }\n },\n [untagEntity, entityType, entityId, refetchTags, tr]\n );\n\n // The assignment fragment always carries `tag.label` (see\n // EntityTagPickerAssignmentFields in useEntityTags.ts), so we read directly\n // from it. Falling back to `availableData` here is dead code in display\n // mode because availableData is only fetched when `isAdding=true`.\n const getTagLabel = useCallback(\n (assignment: EntityTagAssignment): string =>\n assignment.tag?.label ??\n tr('vibecontrols.shared.entityTagPicker.untitledTag', 'Untitled tag'),\n [tr]\n );\n\n if (tagsLoading) {\n return (\n <div\n className=\"flex items-center gap-2 text-text-secondary text-sm\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.loadingTags', 'Loading tags...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n <span className=\"flex-1\">{errorMessage}</span>\n <button\n type=\"button\"\n onClick={() => setErrorMessage(null)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.dismissError', 'Dismiss error')}\n className=\"text-red-700 hover:text-red-900\"\n >\n <X className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n <div\n className=\"flex flex-wrap gap-2\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.assignedTags', 'Assigned tags')}\n >\n {assignedTags.length === 0 && !isAdding && (\n <p className=\"text-text-secondary text-sm\">\n {tr('vibecontrols.shared.entityTagPicker.noTagsAssigned', 'No tags assigned')}\n </p>\n )}\n {assignedTags.map((assignment) => {\n const label = getTagLabel(assignment);\n return (\n <span\n key={assignment.id}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" />\n {label}\n {!readOnly && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(assignment.tagId)}\n disabled={untagging}\n aria-label={`Remove tag ${label}`}\n className=\"ml-1 hover:text-red-500 transition-colors disabled:opacity-50\"\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n )}\n </span>\n );\n })}\n {!readOnly && !isAdding && (\n <button\n type=\"button\"\n onClick={() => setIsAdding(true)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.addTag', 'Add tag')}\n className=\"inline-flex items-center gap-1 px-3 py-1 border border-dashed border-border-default text-text-secondary text-sm rounded-full hover:border-action-primary-bg hover:text-action-primary-bg transition-colors\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.addTag', 'Add Tag')}\n </button>\n )}\n </div>\n\n {isAdding && (\n <div className=\"border border-border-default rounded-lg p-3 space-y-2 bg-bg-surface\">\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n placeholder={tr(\n 'vibecontrols.shared.entityTagPicker.searchPlaceholder',\n 'Search tags...'\n )}\n autoFocus\n aria-label={tr(\n 'vibecontrols.shared.entityTagPicker.searchAvailableTags',\n 'Search available tags'\n )}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-bg-default border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div\n className=\"flex flex-wrap gap-2 max-h-32 overflow-y-auto\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.tagSuggestions', 'Tag suggestions')}\n >\n {(searchLoading || (availableLoading && !searchQuery)) && (\n <span className=\"text-text-secondary text-xs\">\n {tr('vibecontrols.shared.entityTagPicker.loading', 'Loading...')}\n </span>\n )}\n {!searchLoading && !availableLoading && suggestions.length === 0 && (\n <span className=\"text-text-secondary text-xs\">\n {searchQuery\n ? tr('vibecontrols.shared.entityTagPicker.noMatchingTags', 'No matching tags')\n : tr('vibecontrols.shared.entityTagPicker.noAvailableTags', 'No available tags')}\n </span>\n )}\n {suggestions.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag.id)}\n disabled={tagging}\n aria-label={`Add tag ${tag.label}`}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full hover:bg-action-primary-bg/10 hover:text-action-primary-bg transition-colors cursor-pointer disabled:opacity-50\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tag.label}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setIsAdding(false);\n setSearchQuery('');\n }}\n className=\"text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.shared.entityTagPicker.done', 'Done')}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EntityTagPicker.js","names":[],"sources":["../../../src/components/shared/EntityTagPicker.tsx"],"sourcesContent":["/**\n * EntityTagPicker — Reusable tag picker for any VibeControls entity\n *\n * Two modes:\n * - Persisted (entityId provided): self-fetches assignments and writes\n * immediately via tag/untag mutations. Used on detail/edit surfaces.\n * - Draft (entityId omitted/empty): no queries or mutations — selections\n * are held locally and surfaced via `onDraftChange(tagIds)`. The parent\n * create form persists them after the entity is created (e.g. via\n * replaceVibecontrolsEntityTags with the new id). Used in create\n * forms/wizards where no entity id exists yet.\n *\n * Usage:\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" entityId={vibe.id} />\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setTagIds} />\n */\nimport { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { Tag, Plus, X, Search, Loader2 } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetEntityTags,\n useAvailableTags,\n useSearchTags,\n useTagEntity,\n useUntagEntity,\n} from '../../hooks/useEntityTags';\nimport type { AvailableTag, EntityTagAssignment } from '../../hooks/useEntityTags';\n\ninterface EntityTagPickerProps {\n entityType: string;\n /**\n * Existing entity id. Omit (or pass empty) to run in draft mode for\n * create forms — selections are emitted via `onDraftChange` instead of\n * being persisted immediately.\n */\n entityId?: string;\n /**\n * Draft-mode callback. Receives the current selected tag ids whenever\n * the draft selection changes. Only invoked when `entityId` is absent.\n */\n onDraftChange?: (tagIds: string[]) => void;\n readOnly?: boolean;\n}\n\nconst ERROR_DISMISS_MS = 4000;\nconst SEARCH_DEBOUNCE_MS = 250;\n\nexport function EntityTagPicker({\n entityType,\n entityId,\n onDraftChange,\n readOnly = false,\n}: EntityTagPickerProps) {\n const tr = useTr();\n const draftMode = !entityId;\n const [isAdding, setIsAdding] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n // Draft-mode local selection (no persistence until the parent flushes it).\n const [draftTags, setDraftTags] = useState<AvailableTag[]>([]);\n\n const {\n data: tagsData,\n loading: tagsLoading,\n refetch: refetchTags,\n } = useGetEntityTags(entityType, entityId ?? '');\n const { data: availableData, loading: availableLoading } = useAvailableTags(50, {\n skip: !isAdding,\n });\n const [searchTags, { data: searchData, loading: searchLoading }] = useSearchTags();\n const [tagEntity, { loading: tagging }] = useTagEntity();\n const [untagEntity, { loading: untagging }] = useUntagEntity();\n\n // Apollo Client v4 returns DeepPartialObject from useQuery data, but our\n // hook types declare full shapes — we cast back to the declared types since\n // the resolver is guaranteed to return non-null fields per the schema.\n const assignedTags = useMemo(\n () => (tagsData?.vibecontrolsEntityTags ?? []) as EntityTagAssignment[],\n [tagsData]\n );\n\n // Unified chip list — draft selections in draft mode, persisted\n // assignments otherwise. `tagId` drives removal; `key` is render-stable.\n const chipItems = useMemo(\n () =>\n draftMode\n ? draftTags.map((t) => ({ key: t.id, tagId: t.id, label: t.label }))\n : assignedTags.map((a) => ({\n key: a.id,\n tagId: a.tagId,\n label:\n a.tag?.label ?? tr('vibecontrols.shared.entityTagPicker.untitledTag', 'Untitled tag'),\n })),\n [draftMode, draftTags, assignedTags, tr]\n );\n\n const assignedTagIds = useMemo(() => new Set(chipItems.map((c) => c.tagId)), [chipItems]);\n\n const suggestions: AvailableTag[] = useMemo(() => {\n const source = searchQuery\n ? (searchData?.searchVibecontrolsTags as AvailableTag[] | undefined)\n : (availableData?.availableVibecontrolsTags as AvailableTag[] | undefined);\n return (source ?? []).filter((t) => !assignedTagIds.has(t.id));\n }, [searchQuery, searchData, availableData, assignedTagIds]);\n\n // Auto-dismiss error toast after a few seconds\n useEffect(() => {\n if (!errorMessage) return;\n const timer = setTimeout(() => setErrorMessage(null), ERROR_DISMISS_MS);\n return () => clearTimeout(timer);\n }, [errorMessage]);\n\n // Debounce the search query so we don't fire a request on every keystroke.\n const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(() => {\n return () => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n };\n }, []);\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query);\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n if (query.length < 2) return;\n searchTimerRef.current = setTimeout(() => {\n searchTags({ variables: { query, limit: 10 } });\n }, SEARCH_DEBOUNCE_MS);\n },\n [searchTags]\n );\n\n const handleAddTag = useCallback(\n async (tag: AvailableTag) => {\n if (draftMode) {\n setDraftTags((prev) => {\n if (prev.some((t) => t.id === tag.id)) return prev;\n const next = [...prev, tag];\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setSearchQuery('');\n setErrorMessage(null);\n return;\n }\n try {\n await tagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId: tag.id } },\n });\n await refetchTags();\n setSearchQuery('');\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToAddTag', 'Failed to add tag')\n );\n }\n },\n [draftMode, onDraftChange, tagEntity, entityType, entityId, refetchTags, tr]\n );\n\n const handleRemoveTag = useCallback(\n async (tagId: string) => {\n if (draftMode) {\n setDraftTags((prev) => {\n const next = prev.filter((t) => t.id !== tagId);\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setErrorMessage(null);\n return;\n }\n try {\n await untagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId } },\n });\n await refetchTags();\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToRemoveTag', 'Failed to remove tag')\n );\n }\n },\n [draftMode, onDraftChange, untagEntity, entityType, entityId, refetchTags, tr]\n );\n\n if (tagsLoading && !draftMode) {\n return (\n <div\n className=\"flex items-center gap-2 text-text-secondary text-sm\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.loadingTags', 'Loading tags...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n <span className=\"flex-1\">{errorMessage}</span>\n <button\n type=\"button\"\n onClick={() => setErrorMessage(null)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.dismissError', 'Dismiss error')}\n className=\"text-red-700 hover:text-red-900\"\n >\n <X className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n <div\n className=\"flex flex-wrap gap-2\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.assignedTags', 'Assigned tags')}\n >\n {chipItems.length === 0 && !isAdding && (\n <p className=\"text-text-secondary text-sm\">\n {tr('vibecontrols.shared.entityTagPicker.noTagsAssigned', 'No tags assigned')}\n </p>\n )}\n {chipItems.map((chip) => (\n <span\n key={chip.key}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" />\n {chip.label}\n {!readOnly && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(chip.tagId)}\n disabled={untagging}\n aria-label={`Remove tag ${chip.label}`}\n className=\"ml-1 hover:text-red-500 transition-colors disabled:opacity-50\"\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n )}\n </span>\n ))}\n {!readOnly && !isAdding && (\n <button\n type=\"button\"\n onClick={() => setIsAdding(true)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.addTag', 'Add tag')}\n className=\"inline-flex items-center gap-1 px-3 py-1 border border-dashed border-border-default text-text-secondary text-sm rounded-full hover:border-action-primary-bg hover:text-action-primary-bg transition-colors\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.addTag', 'Add Tag')}\n </button>\n )}\n </div>\n\n {isAdding && (\n <div className=\"border border-border-default rounded-lg p-3 space-y-2 bg-bg-surface\">\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n placeholder={tr(\n 'vibecontrols.shared.entityTagPicker.searchPlaceholder',\n 'Search tags...'\n )}\n autoFocus\n aria-label={tr(\n 'vibecontrols.shared.entityTagPicker.searchAvailableTags',\n 'Search available tags'\n )}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-bg-default border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div\n className=\"flex flex-wrap gap-2 max-h-32 overflow-y-auto\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.tagSuggestions', 'Tag suggestions')}\n >\n {(searchLoading || (availableLoading && !searchQuery)) && (\n <span className=\"text-text-secondary text-xs\">\n {tr('vibecontrols.shared.entityTagPicker.loading', 'Loading...')}\n </span>\n )}\n {!searchLoading && !availableLoading && suggestions.length === 0 && (\n <span className=\"text-text-secondary text-xs\">\n {searchQuery\n ? tr('vibecontrols.shared.entityTagPicker.noMatchingTags', 'No matching tags')\n : tr('vibecontrols.shared.entityTagPicker.noAvailableTags', 'No available tags')}\n </span>\n )}\n {suggestions.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag)}\n disabled={tagging}\n aria-label={`Add tag ${tag.label}`}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full hover:bg-action-primary-bg/10 hover:text-action-primary-bg transition-colors cursor-pointer disabled:opacity-50\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tag.label}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setIsAdding(false);\n setSearchQuery('');\n }}\n className=\"text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.shared.entityTagPicker.done', 'Done')}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { useTr as e } from "../../shared/hooks/useTr.js";
2
- import { FilterDropdown as t } from "./FilterDropdown.js";
3
- import { useAvailableTags as n } from "../../hooks/useEntityTags.js";
2
+ import { useAvailableTags as t } from "../../hooks/useEntityTags.js";
3
+ import { FilterDropdown as n } from "./FilterDropdown.js";
4
4
  import { useMemo as r } from "react";
5
5
  import { jsx as i } from "react/jsx-runtime";
6
6
  //#region src/components/shared/TagFilter.tsx
7
7
  function a({ value: a, onChange: o, label: s }) {
8
- let c = e(), l = s ?? c("vibecontrols.shared.tagFilter.label", "Tag"), { data: u } = n(50), d = r(() => {
8
+ let c = e(), l = s ?? c("vibecontrols.shared.tagFilter.label", "Tag"), { data: u } = t(50), d = r(() => {
9
9
  let e = u?.availableVibecontrolsTags ?? [];
10
10
  return [{
11
11
  value: "all",
@@ -15,7 +15,7 @@ function a({ value: a, onChange: o, label: s }) {
15
15
  label: e.label
16
16
  }))];
17
17
  }, [u, c]);
18
- return d.length <= 1 ? null : /* @__PURE__ */ i(t, {
18
+ return d.length <= 1 ? null : /* @__PURE__ */ i(n, {
19
19
  label: l,
20
20
  options: d,
21
21
  value: a,
@@ -5,6 +5,7 @@ import "./LoadingState.js";
5
5
  import "./ErrorState.js";
6
6
  import "./ConfirmDialog.js";
7
7
  import "./QuotaExhaustedDialog.js";
8
+ import "./EntityTagPicker.js";
8
9
  import "./EmptyState.js";
9
10
  import "./SearchInput.js";
10
11
  import "./FilterDropdown.js";
@@ -17,7 +18,6 @@ import "./GetHelpButton.js";
17
18
  import "./SearchableSelect.js";
18
19
  import "./SessionNotificationBanner.js";
19
20
  import "./SelectionCheckbox.js";
20
- import "./EntityTagPicker.js";
21
21
  import "./TagFilter.js";
22
22
  import "./PluginGate.js";
23
23
  import "./RequireAgentReachable.js";