@flanksource/clicky-ui 0.3.6 → 0.3.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 (215) hide show
  1. package/README.md +42 -0
  2. package/dist/components/DateField.d.ts +1 -1
  3. package/dist/components/DatePicker.d.ts +1 -1
  4. package/dist/components/DateTimePicker.d.ts +1 -1
  5. package/dist/components/FormLookupProvider.cjs +12 -0
  6. package/dist/components/FormLookupProvider.cjs.map +1 -0
  7. package/dist/components/FormLookupProvider.d.ts +7 -0
  8. package/dist/components/FormLookupProvider.d.ts.map +1 -0
  9. package/dist/components/FormLookupProvider.js +12 -0
  10. package/dist/components/FormLookupProvider.js.map +1 -0
  11. package/dist/components/JsonSchemaForm.cjs +2 -2
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.js +1 -1
  14. package/dist/components/JsonSchemaForm.js.map +1 -1
  15. package/dist/components/ListMenu.cjs +145 -0
  16. package/dist/components/ListMenu.cjs.map +1 -0
  17. package/dist/components/ListMenu.d.ts +54 -0
  18. package/dist/components/ListMenu.d.ts.map +1 -0
  19. package/dist/components/ListMenu.js +145 -0
  20. package/dist/components/ListMenu.js.map +1 -0
  21. package/dist/components/MdxEditorField.cjs +128 -0
  22. package/dist/components/MdxEditorField.cjs.map +1 -0
  23. package/dist/components/MdxEditorField.d.ts +4 -0
  24. package/dist/components/MdxEditorField.d.ts.map +1 -0
  25. package/dist/components/MdxEditorField.js +106 -0
  26. package/dist/components/MdxEditorField.js.map +1 -0
  27. package/dist/components/MdxEditorToolbar.cjs +76 -0
  28. package/dist/components/MdxEditorToolbar.cjs.map +1 -0
  29. package/dist/components/MdxEditorToolbar.d.ts +8 -0
  30. package/dist/components/MdxEditorToolbar.d.ts.map +1 -0
  31. package/dist/components/MdxEditorToolbar.js +76 -0
  32. package/dist/components/MdxEditorToolbar.js.map +1 -0
  33. package/dist/components/button-variants.d.ts +1 -1
  34. package/dist/components/button.d.ts +1 -1
  35. package/dist/components/form-lookup-context.cjs +1 -8
  36. package/dist/components/form-lookup-context.cjs.map +1 -1
  37. package/dist/components/form-lookup-context.d.ts +1 -5
  38. package/dist/components/form-lookup-context.d.ts.map +1 -1
  39. package/dist/components/form-lookup-context.js +1 -8
  40. package/dist/components/form-lookup-context.js.map +1 -1
  41. package/dist/components/jotai-bindings-core.cjs +197 -0
  42. package/dist/components/jotai-bindings-core.cjs.map +1 -0
  43. package/dist/components/jotai-bindings-core.d.ts +45 -0
  44. package/dist/components/jotai-bindings-core.d.ts.map +1 -0
  45. package/dist/components/jotai-bindings-core.js +197 -0
  46. package/dist/components/jotai-bindings-core.js.map +1 -0
  47. package/dist/components/jotai-bindings.cjs +17 -0
  48. package/dist/components/jotai-bindings.cjs.map +1 -0
  49. package/dist/components/jotai-bindings.d.ts +4 -0
  50. package/dist/components/jotai-bindings.d.ts.map +1 -0
  51. package/dist/components/jotai-bindings.js +17 -0
  52. package/dist/components/jotai-bindings.js.map +1 -0
  53. package/dist/components/json-schema-form-fields.cjs +37 -0
  54. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  55. package/dist/components/json-schema-form-fields.d.ts +6 -0
  56. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  57. package/dist/components/json-schema-form-fields.js +38 -1
  58. package/dist/components/json-schema-form-fields.js.map +1 -1
  59. package/dist/components/json-schema-form-render.cjs +3 -1
  60. package/dist/components/json-schema-form-render.cjs.map +1 -1
  61. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  62. package/dist/components/json-schema-form-render.js +4 -2
  63. package/dist/components/json-schema-form-render.js.map +1 -1
  64. package/dist/components/json-schema-form-resolve.cjs +9 -0
  65. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  66. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  67. package/dist/components/json-schema-form-resolve.js +9 -0
  68. package/dist/components/json-schema-form-resolve.js.map +1 -1
  69. package/dist/components/json-schema-form-types.d.ts +4 -1
  70. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  71. package/dist/components/mdx-editor-field-plugins.cjs +124 -0
  72. package/dist/components/mdx-editor-field-plugins.cjs.map +1 -0
  73. package/dist/components/mdx-editor-field-plugins.d.ts +6 -0
  74. package/dist/components/mdx-editor-field-plugins.d.ts.map +1 -0
  75. package/dist/components/mdx-editor-field-plugins.js +124 -0
  76. package/dist/components/mdx-editor-field-plugins.js.map +1 -0
  77. package/dist/components/mdx-editor-options.d.ts +77 -0
  78. package/dist/components/mdx-editor-options.d.ts.map +1 -0
  79. package/dist/components/use-list-menu-selection.cjs +77 -0
  80. package/dist/components/use-list-menu-selection.cjs.map +1 -0
  81. package/dist/components/use-list-menu-selection.d.ts +40 -0
  82. package/dist/components/use-list-menu-selection.d.ts.map +1 -0
  83. package/dist/components/use-list-menu-selection.js +77 -0
  84. package/dist/components/use-list-menu-selection.js.map +1 -0
  85. package/dist/components.cjs +10 -1
  86. package/dist/components.cjs.map +1 -1
  87. package/dist/components.d.ts +4 -1
  88. package/dist/components.d.ts.map +1 -1
  89. package/dist/components.js +10 -1
  90. package/dist/components.js.map +1 -1
  91. package/dist/data/MarkdownEditor.model.d.ts +1 -1
  92. package/dist/data/PromptBanner.cjs +199 -0
  93. package/dist/data/PromptBanner.cjs.map +1 -0
  94. package/dist/data/PromptBanner.d.ts +17 -0
  95. package/dist/data/PromptBanner.d.ts.map +1 -0
  96. package/dist/data/PromptBanner.js +199 -0
  97. package/dist/data/PromptBanner.js.map +1 -0
  98. package/dist/data/PromptBanner.model.cjs +132 -0
  99. package/dist/data/PromptBanner.model.cjs.map +1 -0
  100. package/dist/data/PromptBanner.model.d.ts +23 -0
  101. package/dist/data/PromptBanner.model.d.ts.map +1 -0
  102. package/dist/data/PromptBanner.model.js +132 -0
  103. package/dist/data/PromptBanner.model.js.map +1 -0
  104. package/dist/data/PromptDialog.cjs +43 -0
  105. package/dist/data/PromptDialog.cjs.map +1 -0
  106. package/dist/data/PromptDialog.d.ts +14 -0
  107. package/dist/data/PromptDialog.d.ts.map +1 -0
  108. package/dist/data/PromptDialog.js +43 -0
  109. package/dist/data/PromptDialog.js.map +1 -0
  110. package/dist/data/TaskManager.cjs +8 -1
  111. package/dist/data/TaskManager.cjs.map +1 -1
  112. package/dist/data/TaskManager.d.ts.map +1 -1
  113. package/dist/data/TaskManager.js +8 -1
  114. package/dist/data/TaskManager.js.map +1 -1
  115. package/dist/data/TaskProgress.cjs +15 -2
  116. package/dist/data/TaskProgress.cjs.map +1 -1
  117. package/dist/data/TaskProgress.d.ts +0 -10
  118. package/dist/data/TaskProgress.d.ts.map +1 -1
  119. package/dist/data/TaskProgress.js +15 -2
  120. package/dist/data/TaskProgress.js.map +1 -1
  121. package/dist/data/ai/ChatWindow.cjs +53 -9
  122. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  123. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  124. package/dist/data/ai/ChatWindow.js +53 -9
  125. package/dist/data/ai/ChatWindow.js.map +1 -1
  126. package/dist/data/ai/ToolPreferences.cjs +232 -39
  127. package/dist/data/ai/ToolPreferences.cjs.map +1 -1
  128. package/dist/data/ai/ToolPreferences.d.ts +4 -7
  129. package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
  130. package/dist/data/ai/ToolPreferences.js +234 -41
  131. package/dist/data/ai/ToolPreferences.js.map +1 -1
  132. package/dist/data/chat/clickyOperationsToTools.cjs +89 -3
  133. package/dist/data/chat/clickyOperationsToTools.cjs.map +1 -1
  134. package/dist/data/chat/clickyOperationsToTools.d.ts +1 -1
  135. package/dist/data/chat/clickyOperationsToTools.d.ts.map +1 -1
  136. package/dist/data/chat/clickyOperationsToTools.js +89 -3
  137. package/dist/data/chat/clickyOperationsToTools.js.map +1 -1
  138. package/dist/data/chat/types.cjs.map +1 -1
  139. package/dist/data/chat/types.d.ts +7 -1
  140. package/dist/data/chat/types.d.ts.map +1 -1
  141. package/dist/data/chat/types.js.map +1 -1
  142. package/dist/data/task-status.cjs +19 -12
  143. package/dist/data/task-status.cjs.map +1 -1
  144. package/dist/data/task-status.d.ts +2 -1
  145. package/dist/data/task-status.d.ts.map +1 -1
  146. package/dist/data/task-status.js +19 -12
  147. package/dist/data/task-status.js.map +1 -1
  148. package/dist/data/test-runner/TestRunner.fixtures.d.ts +11 -4
  149. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
  150. package/dist/data/version-info.cjs +3 -3
  151. package/dist/data/version-info.js +3 -3
  152. package/dist/data.cjs +7 -0
  153. package/dist/data.cjs.map +1 -1
  154. package/dist/data.d.ts +3 -0
  155. package/dist/data.d.ts.map +1 -1
  156. package/dist/data.js +7 -0
  157. package/dist/data.js.map +1 -1
  158. package/dist/hooks/use-prompts.cjs +83 -0
  159. package/dist/hooks/use-prompts.cjs.map +1 -0
  160. package/dist/hooks/use-prompts.d.ts +43 -0
  161. package/dist/hooks/use-prompts.d.ts.map +1 -0
  162. package/dist/hooks/use-prompts.js +83 -0
  163. package/dist/hooks/use-prompts.js.map +1 -0
  164. package/dist/hooks.cjs +3 -0
  165. package/dist/hooks.cjs.map +1 -1
  166. package/dist/hooks.d.ts +1 -0
  167. package/dist/hooks.d.ts.map +1 -1
  168. package/dist/hooks.js +3 -0
  169. package/dist/hooks.js.map +1 -1
  170. package/dist/index.cjs +17 -1
  171. package/dist/index.cjs.map +1 -1
  172. package/dist/index.js +17 -1
  173. package/dist/index.js.map +1 -1
  174. package/dist/jotai.cjs +9 -0
  175. package/dist/jotai.cjs.map +1 -0
  176. package/dist/jotai.d.ts +4 -0
  177. package/dist/jotai.d.ts.map +1 -0
  178. package/dist/jotai.js +9 -0
  179. package/dist/jotai.js.map +1 -0
  180. package/dist/lib/utils.cjs +18 -1
  181. package/dist/lib/utils.cjs.map +1 -1
  182. package/dist/lib/utils.d.ts.map +1 -1
  183. package/dist/lib/utils.js +18 -1
  184. package/dist/lib/utils.js.map +1 -1
  185. package/dist/mdx-editor.cjs +5 -0
  186. package/dist/mdx-editor.cjs.map +1 -0
  187. package/dist/mdx-editor.css +2 -0
  188. package/dist/mdx-editor.d.ts +2 -0
  189. package/dist/mdx-editor.d.ts.map +1 -0
  190. package/dist/mdx-editor.js +5 -0
  191. package/dist/mdx-editor.js.map +1 -0
  192. package/dist/rpc/apiClient.cjs +1 -1
  193. package/dist/rpc/apiClient.cjs.map +1 -1
  194. package/dist/rpc/apiClient.js +1 -1
  195. package/dist/rpc/apiClient.js.map +1 -1
  196. package/dist/rpc/surfaceIconMap.cjs +12 -1
  197. package/dist/rpc/surfaceIconMap.cjs.map +1 -1
  198. package/dist/rpc/surfaceIconMap.d.ts.map +1 -1
  199. package/dist/rpc/surfaceIconMap.js +12 -1
  200. package/dist/rpc/surfaceIconMap.js.map +1 -1
  201. package/dist/rpc/types.cjs.map +1 -1
  202. package/dist/rpc/types.d.ts +1 -0
  203. package/dist/rpc/types.d.ts.map +1 -1
  204. package/dist/rpc/types.js.map +1 -1
  205. package/dist/styles.css +1 -1
  206. package/oxlint-plugins/README.md +148 -0
  207. package/oxlint-plugins/clicky-ui-no-adhoc-overlay.js +88 -0
  208. package/oxlint-plugins/clicky-ui-prefer-components.js +46 -0
  209. package/oxlint-plugins/clicky-ui-prefer-icons.js +121 -0
  210. package/oxlint-plugins/clicky-ui-prefer-tailwind.js +98 -0
  211. package/oxlint-plugins/clicky-ui-prefer-theme.js +134 -0
  212. package/oxlint-plugins/clicky-ui-shared.js +132 -0
  213. package/oxlint-plugins/clicky-ui.js +34 -0
  214. package/oxlint-plugins/no-iconify-names.js +37 -0
  215. package/package.json +24 -4
@@ -1,78 +1,271 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useMemo } from "react";
1
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
2
+ import { useState, useMemo } from "react";
3
3
  import { cn } from "../../lib/utils.js";
4
4
  import { DropdownMenu } from "../../overlay/DropdownMenu.js";
5
+ import { HoverCard } from "../../overlay/HoverCard.js";
6
+ import { Modal } from "../../overlay/Modal.js";
5
7
  import { Button } from "../../components/button.js";
6
8
  import { Icon } from "../Icon.js";
7
9
  import { UiSliders } from "../../icons/components/UiSliders.js";
10
+ import { UiInfo } from "../../icons/components/UiInfo.js";
8
11
  const MODE_CYCLE = ["enabled", "ask", "disabled"];
9
- const MODE_LABEL = { enabled: "Auto", ask: "Ask", disabled: "Off" };
12
+ const MODE_LABEL = {
13
+ enabled: "Auto",
14
+ ask: "Ask",
15
+ disabled: "Off"
16
+ };
10
17
  function ToolRow({
11
- meta,
18
+ entry,
12
19
  mode,
13
20
  onToggle
14
21
  }) {
22
+ var _a;
23
+ const title = entry.tools.length > 1 ? `${entry.tools.length} tools` : (_a = entry.tools[0]) == null ? void 0 : _a.description;
15
24
  return /* @__PURE__ */ jsxs(
16
25
  "button",
17
26
  {
18
27
  type: "button",
19
28
  className: "flex w-full items-center justify-between rounded px-2 py-1 text-left hover:bg-accent",
20
- onClick: () => onToggle(meta.name),
29
+ title,
30
+ onClick: () => onToggle(entry.key, mode),
21
31
  children: [
22
- /* @__PURE__ */ jsx("span", { className: cn("text-xs", mode === "disabled" && "text-muted-foreground line-through"), children: meta.label }),
23
32
  /* @__PURE__ */ jsx(
24
33
  "span",
25
34
  {
26
35
  className: cn(
27
- "rounded px-1.5 py-0.5 text-[10px] font-medium",
28
- mode === "enabled" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300",
29
- mode === "ask" && "bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300",
30
- mode === "disabled" && "bg-muted text-muted-foreground"
36
+ "text-xs",
37
+ mode === "disabled" && "text-muted-foreground line-through"
31
38
  ),
32
- children: MODE_LABEL[mode]
39
+ children: entry.label
33
40
  }
34
- )
41
+ ),
42
+ /* @__PURE__ */ jsx(ModeBadge, { mode })
43
+ ]
44
+ }
45
+ );
46
+ }
47
+ function AdvancedToolRow({
48
+ tool,
49
+ mode,
50
+ onToggle
51
+ }) {
52
+ var _a;
53
+ const description = ((_a = tool.description) == null ? void 0 : _a.trim()) || "No description";
54
+ return /* @__PURE__ */ jsxs(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ className: "grid h-9 w-full grid-cols-[minmax(0,1fr)_4rem] items-center gap-3 border-b border-border px-2 text-left hover:bg-accent",
59
+ title: description,
60
+ onClick: () => onToggle(tool.name, mode),
61
+ children: [
62
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
63
+ /* @__PURE__ */ jsx(
64
+ "span",
65
+ {
66
+ className: cn(
67
+ "min-w-0 truncate font-mono text-xs",
68
+ mode === "disabled" && "text-muted-foreground line-through"
69
+ ),
70
+ children: tool.name
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsx(
74
+ HoverCard,
75
+ {
76
+ placement: "top",
77
+ delay: 120,
78
+ trigger: /* @__PURE__ */ jsx(
79
+ "span",
80
+ {
81
+ "aria-label": `Description for ${tool.name}`,
82
+ className: "inline-flex shrink-0 text-muted-foreground",
83
+ title: description,
84
+ children: /* @__PURE__ */ jsx(Icon, { icon: UiInfo, className: "size-3.5" })
85
+ }
86
+ ),
87
+ cardClassName: "max-w-xs whitespace-normal",
88
+ children: description
89
+ }
90
+ )
91
+ ] }),
92
+ /* @__PURE__ */ jsx("span", { className: "justify-self-end", children: /* @__PURE__ */ jsx(ModeBadge, { mode }) })
35
93
  ]
36
94
  }
37
95
  );
38
96
  }
39
- function ToolPreferences({ tools, value, onChange, className = "" }) {
97
+ function ModeBadge({ mode }) {
98
+ return /* @__PURE__ */ jsx(
99
+ "span",
100
+ {
101
+ className: cn(
102
+ "rounded px-1.5 py-0.5 text-[10px] font-medium",
103
+ mode === "enabled" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300",
104
+ mode === "ask" && "bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300",
105
+ mode === "disabled" && "bg-muted text-muted-foreground"
106
+ ),
107
+ children: MODE_LABEL[mode]
108
+ }
109
+ );
110
+ }
111
+ function ToolPreferences({
112
+ tools,
113
+ value,
114
+ onChange,
115
+ className = ""
116
+ }) {
117
+ const [advancedOpen, setAdvancedOpen] = useState(false);
40
118
  const groups = useMemo(() => {
41
- var _a;
119
+ const maps = {};
120
+ for (const meta of tools) {
121
+ const group = toolGroup(meta);
122
+ const key = toolPreferenceKey(meta);
123
+ const map = maps[group] ?? (maps[group] = /* @__PURE__ */ new Map());
124
+ let entry = map.get(key);
125
+ if (!entry) {
126
+ entry = {
127
+ key,
128
+ label: key === meta.name ? meta.label : key,
129
+ group,
130
+ tools: [],
131
+ defaultMode: toolDefaultMode(meta)
132
+ };
133
+ map.set(key, entry);
134
+ }
135
+ entry.tools.push(meta);
136
+ entry.defaultMode = mostRestrictiveMode(
137
+ entry.defaultMode,
138
+ toolDefaultMode(meta)
139
+ );
140
+ }
42
141
  const g = {};
43
- for (const meta of tools) (g[_a = meta.group ?? "Tools"] ?? (g[_a] = [])).push(meta);
142
+ for (const [group, map] of Object.entries(maps)) {
143
+ g[group] = [...map.values()].sort((a, b) => a.label.localeCompare(b.label));
144
+ }
44
145
  return g;
45
146
  }, [tools]);
46
- const handleToggle = (name) => {
47
- const current = value[name] ?? "enabled";
147
+ const advancedGroups = useMemo(
148
+ () => groupedToolMetas(tools),
149
+ [tools]
150
+ );
151
+ const handleToggle = (name, current) => {
48
152
  const next = MODE_CYCLE[(MODE_CYCLE.indexOf(current) + 1) % MODE_CYCLE.length];
49
153
  onChange({ ...value, [name]: next });
50
154
  };
51
- return /* @__PURE__ */ jsx(
52
- DropdownMenu,
53
- {
54
- align: "right",
55
- className,
56
- menuClassName: "w-64 max-h-[400px] overflow-y-auto p-3",
57
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", title: "Tool preferences", "data-testid": "tool-preferences-btn", children: /* @__PURE__ */ jsx(Icon, { icon: UiSliders, className: "size-4" }) }),
58
- children: () => /* @__PURE__ */ jsxs("div", { children: [
59
- /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold", children: "Tool Preferences" }),
60
- /* @__PURE__ */ jsx("p", { className: "mb-2 text-[10px] text-muted-foreground", children: "Click to cycle: Auto → Ask → Off" }),
61
- Object.entries(groups).map(([group, metas]) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
62
- /* @__PURE__ */ jsx("div", { className: "mb-0.5 px-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
63
- metas.map((meta) => /* @__PURE__ */ jsx(
64
- ToolRow,
155
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
156
+ /* @__PURE__ */ jsx(
157
+ DropdownMenu,
158
+ {
159
+ align: "right",
160
+ className,
161
+ menuClassName: "w-64 max-h-[400px] overflow-y-auto p-3",
162
+ trigger: /* @__PURE__ */ jsx(
163
+ Button,
164
+ {
165
+ variant: "ghost",
166
+ size: "icon",
167
+ title: "Tool preferences",
168
+ "data-testid": "tool-preferences-btn",
169
+ children: /* @__PURE__ */ jsx(Icon, { icon: UiSliders, className: "size-4" })
170
+ }
171
+ ),
172
+ children: (closeMenu) => /* @__PURE__ */ jsxs("div", { children: [
173
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-semibold", children: "Tool Preferences" }),
174
+ Object.entries(groups).sort(([a], [b]) => a.localeCompare(b)).map(([group, metas]) => /* @__PURE__ */ jsxs("div", { className: "mb-2", children: [
175
+ /* @__PURE__ */ jsx("div", { className: "mb-0.5 px-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
176
+ metas.map((entry) => {
177
+ const mode = value[entry.key] ?? entry.defaultMode;
178
+ return /* @__PURE__ */ jsx(
179
+ ToolRow,
180
+ {
181
+ entry,
182
+ mode,
183
+ onToggle: handleToggle
184
+ },
185
+ entry.key
186
+ );
187
+ })
188
+ ] }, group)),
189
+ /* @__PURE__ */ jsx("div", { className: "mt-2 border-t border-border pt-2", children: /* @__PURE__ */ jsx(
190
+ "button",
65
191
  {
66
- meta,
67
- mode: value[meta.name] ?? "enabled",
68
- onToggle: handleToggle
69
- },
70
- meta.name
71
- ))
72
- ] }, group))
73
- ] })
74
- }
75
- );
192
+ type: "button",
193
+ className: "w-full rounded px-2 py-1.5 text-left text-xs hover:bg-accent",
194
+ onClick: () => {
195
+ closeMenu();
196
+ setAdvancedOpen(true);
197
+ },
198
+ children: "Advanced"
199
+ }
200
+ ) })
201
+ ] })
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsx(
205
+ Modal,
206
+ {
207
+ open: advancedOpen,
208
+ onClose: () => setAdvancedOpen(false),
209
+ title: "Advanced Tool Preferences",
210
+ size: "xl",
211
+ children: /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded border border-border", children: [
212
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_4rem] gap-3 border-b border-border bg-muted/40 px-2 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: [
213
+ /* @__PURE__ */ jsx("span", { children: "Name" }),
214
+ /* @__PURE__ */ jsx("span", { className: "text-right", children: "Mode" })
215
+ ] }),
216
+ /* @__PURE__ */ jsx("div", { className: "max-h-[60vh] overflow-y-auto", children: advancedGroups.length > 0 ? advancedGroups.map(([group, groupTools]) => /* @__PURE__ */ jsxs("div", { children: [
217
+ /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-[1] border-b border-border bg-muted/60 px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: group }),
218
+ groupTools.map((tool) => {
219
+ const mode = toolMode(tool, value);
220
+ return /* @__PURE__ */ jsx(
221
+ AdvancedToolRow,
222
+ {
223
+ tool,
224
+ mode,
225
+ onToggle: handleToggle
226
+ },
227
+ tool.name
228
+ );
229
+ })
230
+ ] }, group)) : /* @__PURE__ */ jsx("div", { className: "px-2 py-6 text-center text-xs text-muted-foreground", children: "No tools" }) })
231
+ ] })
232
+ }
233
+ )
234
+ ] });
235
+ }
236
+ function toolGroup(meta) {
237
+ return meta.group ?? "Tools";
238
+ }
239
+ function groupedToolMetas(tools) {
240
+ var _a;
241
+ const groups = {};
242
+ for (const tool of tools) {
243
+ (groups[_a = toolGroup(tool)] ?? (groups[_a] = [])).push(tool);
244
+ }
245
+ return Object.entries(groups).map(([group, groupTools]) => [
246
+ group,
247
+ [...groupTools].sort((a, b) => a.name.localeCompare(b.name))
248
+ ]).sort(([a], [b]) => a.localeCompare(b));
249
+ }
250
+ function toolPreferenceKey(meta) {
251
+ return meta.preferenceKey ?? meta.name;
252
+ }
253
+ function toolDefaultMode(meta) {
254
+ return meta.defaultMode ?? "enabled";
255
+ }
256
+ function toolMode(meta, value) {
257
+ const own = value[meta.name];
258
+ if (own) return own;
259
+ const key = toolPreferenceKey(meta);
260
+ if (key !== meta.name) {
261
+ const grouped = value[key];
262
+ if (grouped) return grouped;
263
+ }
264
+ return toolDefaultMode(meta);
265
+ }
266
+ function mostRestrictiveMode(a, b) {
267
+ const rank = { enabled: 0, ask: 1, disabled: 2 };
268
+ return rank[a] >= rank[b] ? a : b;
76
269
  }
77
270
  export {
78
271
  ToolPreferences
@@ -1 +1 @@
1
- {"version":3,"file":"ToolPreferences.js","sources":["../../../src/data/ai/ToolPreferences.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { DropdownMenu } from \"../../overlay/DropdownMenu\";\nimport { Button } from \"../../components/button\";\nimport { Icon } from \"../Icon\";\nimport { UiSliders } from \"../../icons\";\nimport type { ToolMeta } from \"../chat/types\";\n\nexport type { ToolMeta };\n\n/** How a tool may be invoked: run automatically, pause for approval, or be\n * hidden from the model entirely. */\nexport type ToolMode = \"enabled\" | \"ask\" | \"disabled\";\n\nconst MODE_CYCLE: ToolMode[] = [\"enabled\", \"ask\", \"disabled\"];\nconst MODE_LABEL: Record<ToolMode, string> = { enabled: \"Auto\", ask: \"Ask\", disabled: \"Off\" };\n\nexport type ToolPreferencesProps = {\n /** The tools to list. Replaces ai-financials' global registry — callers pass\n * whatever their backend exposes. */\n tools: ToolMeta[];\n /** Current per-tool mode. Missing entries default to \"enabled\". */\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n className?: string;\n};\n\nfunction ToolRow({\n meta,\n mode,\n onToggle,\n}: {\n meta: ToolMeta;\n mode: ToolMode;\n onToggle: (name: string) => void;\n}) {\n return (\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded px-2 py-1 text-left hover:bg-accent\"\n onClick={() => onToggle(meta.name)}\n >\n <span className={cn(\"text-xs\", mode === \"disabled\" && \"text-muted-foreground line-through\")}>\n {meta.label}\n </span>\n <span\n className={cn(\n \"rounded px-1.5 py-0.5 text-[10px] font-medium\",\n mode === \"enabled\" &&\n \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300\",\n mode === \"ask\" && \"bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300\",\n mode === \"disabled\" && \"bg-muted text-muted-foreground\",\n )}\n >\n {MODE_LABEL[mode]}\n </span>\n </button>\n );\n}\n\n/** A popover that cycles each tool through Auto → Ask → Off. The resulting\n * `Record<name, ToolMode>` is meant to be forwarded to the backend (e.g. via\n * `<Chat body={{ toolPreferences }}>`). */\nexport function ToolPreferences({ tools, value, onChange, className = \"\" }: ToolPreferencesProps) {\n const groups = useMemo(() => {\n const g: Record<string, ToolMeta[]> = {};\n for (const meta of tools) (g[meta.group ?? \"Tools\"] ??= []).push(meta);\n return g;\n }, [tools]);\n\n const handleToggle = (name: string) => {\n const current = value[name] ?? \"enabled\";\n const next = MODE_CYCLE[(MODE_CYCLE.indexOf(current) + 1) % MODE_CYCLE.length]!;\n onChange({ ...value, [name]: next });\n };\n\n return (\n <DropdownMenu\n align=\"right\"\n className={className}\n menuClassName=\"w-64 max-h-[400px] overflow-y-auto p-3\"\n trigger={\n <Button variant=\"ghost\" size=\"icon\" title=\"Tool preferences\" data-testid=\"tool-preferences-btn\">\n <Icon icon={UiSliders} className=\"size-4\" />\n </Button>\n }\n >\n {() => (\n <div>\n <div className=\"mb-2 text-xs font-semibold\">Tool Preferences</div>\n <p className=\"mb-2 text-[10px] text-muted-foreground\">Click to cycle: Auto → Ask → Off</p>\n {Object.entries(groups).map(([group, metas]) => (\n <div key={group} className=\"mb-2\">\n <div className=\"mb-0.5 px-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {group}\n </div>\n {metas.map((meta) => (\n <ToolRow\n key={meta.name}\n meta={meta}\n mode={value[meta.name] ?? \"enabled\"}\n onToggle={handleToggle}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n </DropdownMenu>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAcA,MAAM,aAAyB,CAAC,WAAW,OAAO,UAAU;AAC5D,MAAM,aAAuC,EAAE,SAAS,QAAQ,KAAK,OAAO,UAAU,MAAA;AAYtF,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,SAAS,MAAM,SAAS,KAAK,IAAI;AAAA,MAEjC,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAW,GAAG,WAAW,SAAS,cAAc,oCAAoC,GACvF,UAAA,KAAK,MAAA,CACR;AAAA,QACA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,SAAS,aACP;AAAA,cACF,SAAS,SAAS;AAAA,cAClB,SAAS,cAAc;AAAA,YAAA;AAAA,YAGxB,qBAAW,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAClB;AAAA,IAAA;AAAA,EAAA;AAGN;AAKO,SAAS,gBAAgB,EAAE,OAAO,OAAO,UAAU,YAAY,MAA4B;AAChG,QAAM,SAAS,QAAQ,MAAM;;AAC3B,UAAM,IAAgC,CAAA;AACtC,eAAW,QAAQ,MAAO,EAAC,OAAE,KAAK,SAAS,aAAhB,QAA6B,KAAI,KAAK,IAAI;AACrE,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,eAAe,CAAC,SAAiB;AACrC,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,UAAM,OAAO,YAAY,WAAW,QAAQ,OAAO,IAAI,KAAK,WAAW,MAAM;AAC7E,aAAS,EAAE,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM;AAAA,EACrC;AAEA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,OAAM;AAAA,MACN;AAAA,MACA,eAAc;AAAA,MACd,SACE,oBAAC,QAAA,EAAO,SAAQ,SAAQ,MAAK,QAAO,OAAM,oBAAmB,eAAY,wBACvE,UAAA,oBAAC,MAAA,EAAK,MAAM,WAAW,WAAU,UAAS,GAC5C;AAAA,MAGD,UAAA,2BACE,OAAA,EACC,UAAA;AAAA,QAAA,oBAAC,OAAA,EAAI,WAAU,8BAA6B,UAAA,oBAAgB;AAAA,QAC5D,oBAAC,KAAA,EAAE,WAAU,0CAAyC,UAAA,oCAAgC;AAAA,QACrF,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,KAAK,MACxC,qBAAC,OAAA,EAAgB,WAAU,QACzB,UAAA;AAAA,UAAA,oBAAC,OAAA,EAAI,WAAU,wFACZ,UAAA,OACH;AAAA,UACC,MAAM,IAAI,CAAC,SACV;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC;AAAA,cACA,MAAM,MAAM,KAAK,IAAI,KAAK;AAAA,cAC1B,UAAU;AAAA,YAAA;AAAA,YAHL,KAAK;AAAA,UAAA,CAKb;AAAA,QAAA,EAAA,GAXO,KAYV,CACD;AAAA,MAAA,EAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAIR;"}
1
+ {"version":3,"file":"ToolPreferences.js","sources":["../../../src/data/ai/ToolPreferences.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { DropdownMenu } from \"../../overlay/DropdownMenu\";\nimport { HoverCard } from \"../../overlay/HoverCard\";\nimport { Modal } from \"../../overlay/Modal\";\nimport { Button } from \"../../components/button\";\nimport { Icon } from \"../Icon\";\nimport { UiInfo, UiSliders } from \"../../icons\";\nimport type { ToolMeta, ToolMode } from \"../chat/types\";\n\nexport type { ToolMeta, ToolMode };\n\n/** How a tool may be invoked: run automatically, pause for approval, or be\n * hidden from the model entirely. */\nconst MODE_CYCLE: ToolMode[] = [\"enabled\", \"ask\", \"disabled\"];\nconst MODE_LABEL: Record<ToolMode, string> = {\n enabled: \"Auto\",\n ask: \"Ask\",\n disabled: \"Off\",\n};\n\ntype ToolPreferenceEntry = {\n key: string;\n label: string;\n group: string;\n tools: ToolMeta[];\n defaultMode: ToolMode;\n};\n\nexport type ToolPreferencesProps = {\n /** The tools to list. Replaces ai-financials' global registry — callers pass\n * whatever their backend exposes. */\n tools: ToolMeta[];\n /** Current per-tool mode. Missing entries default to \"enabled\". */\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n className?: string;\n};\n\nfunction ToolRow({\n entry,\n mode,\n onToggle,\n}: {\n entry: ToolPreferenceEntry;\n mode: ToolMode;\n onToggle: (name: string, current: ToolMode) => void;\n}) {\n const title =\n entry.tools.length > 1\n ? `${entry.tools.length} tools`\n : entry.tools[0]?.description;\n return (\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between rounded px-2 py-1 text-left hover:bg-accent\"\n title={title}\n onClick={() => onToggle(entry.key, mode)}\n >\n <span\n className={cn(\n \"text-xs\",\n mode === \"disabled\" && \"text-muted-foreground line-through\",\n )}\n >\n {entry.label}\n </span>\n <ModeBadge mode={mode} />\n </button>\n );\n}\n\nfunction AdvancedToolRow({\n tool,\n mode,\n onToggle,\n}: {\n tool: ToolMeta;\n mode: ToolMode;\n onToggle: (name: string, current: ToolMode) => void;\n}) {\n const description = tool.description?.trim() || \"No description\";\n return (\n <button\n type=\"button\"\n className=\"grid h-9 w-full grid-cols-[minmax(0,1fr)_4rem] items-center gap-3 border-b border-border px-2 text-left hover:bg-accent\"\n title={description}\n onClick={() => onToggle(tool.name, mode)}\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn(\n \"min-w-0 truncate font-mono text-xs\",\n mode === \"disabled\" && \"text-muted-foreground line-through\",\n )}\n >\n {tool.name}\n </span>\n <HoverCard\n placement=\"top\"\n delay={120}\n trigger={\n <span\n aria-label={`Description for ${tool.name}`}\n className=\"inline-flex shrink-0 text-muted-foreground\"\n title={description}\n >\n <Icon icon={UiInfo} className=\"size-3.5\" />\n </span>\n }\n cardClassName=\"max-w-xs whitespace-normal\"\n >\n {description}\n </HoverCard>\n </span>\n <span className=\"justify-self-end\">\n <ModeBadge mode={mode} />\n </span>\n </button>\n );\n}\n\nfunction ModeBadge({ mode }: { mode: ToolMode }) {\n return (\n <span\n className={cn(\n \"rounded px-1.5 py-0.5 text-[10px] font-medium\",\n mode === \"enabled\" &&\n \"bg-emerald-100 text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300\",\n mode === \"ask\" &&\n \"bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300\",\n mode === \"disabled\" && \"bg-muted text-muted-foreground\",\n )}\n >\n {MODE_LABEL[mode]}\n </span>\n );\n}\n\n/** A popover that cycles each tool/group through Auto → Ask → Off. The resulting\n * `Record<name, ToolMode>` is meant to be forwarded to the backend (e.g. via\n * `<Chat body={{ toolPreferences }}>`). */\nexport function ToolPreferences({\n tools,\n value,\n onChange,\n className = \"\",\n}: ToolPreferencesProps) {\n const [advancedOpen, setAdvancedOpen] = useState(false);\n const groups = useMemo(() => {\n const maps: Record<string, Map<string, ToolPreferenceEntry>> = {};\n for (const meta of tools) {\n const group = toolGroup(meta);\n const key = toolPreferenceKey(meta);\n const map = (maps[group] ??= new Map());\n let entry = map.get(key);\n if (!entry) {\n entry = {\n key,\n label: key === meta.name ? meta.label : key,\n group,\n tools: [],\n defaultMode: toolDefaultMode(meta),\n };\n map.set(key, entry);\n }\n entry.tools.push(meta);\n entry.defaultMode = mostRestrictiveMode(\n entry.defaultMode,\n toolDefaultMode(meta),\n );\n }\n const g: Record<string, ToolPreferenceEntry[]> = {};\n for (const [group, map] of Object.entries(maps)) {\n g[group] = [...map.values()].sort((a, b) => a.label.localeCompare(b.label));\n }\n return g;\n }, [tools]);\n\n const advancedGroups = useMemo(\n () => groupedToolMetas(tools),\n [tools],\n );\n\n const handleToggle = (name: string, current: ToolMode) => {\n const next =\n MODE_CYCLE[(MODE_CYCLE.indexOf(current) + 1) % MODE_CYCLE.length]!;\n onChange({ ...value, [name]: next });\n };\n\n return (\n <>\n <DropdownMenu\n align=\"right\"\n className={className}\n menuClassName=\"w-64 max-h-[400px] overflow-y-auto p-3\"\n trigger={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title=\"Tool preferences\"\n data-testid=\"tool-preferences-btn\"\n >\n <Icon icon={UiSliders} className=\"size-4\" />\n </Button>\n }\n >\n {(closeMenu) => (\n <div>\n <div className=\"mb-2 text-xs font-semibold\">Tool Preferences</div>\n {Object.entries(groups)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([group, metas]) => (\n <div key={group} className=\"mb-2\">\n <div className=\"mb-0.5 px-2 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {group}\n </div>\n {metas.map((entry) => {\n const mode = value[entry.key] ?? entry.defaultMode;\n return (\n <ToolRow\n key={entry.key}\n entry={entry}\n mode={mode}\n onToggle={handleToggle}\n />\n );\n })}\n </div>\n ))}\n <div className=\"mt-2 border-t border-border pt-2\">\n <button\n type=\"button\"\n className=\"w-full rounded px-2 py-1.5 text-left text-xs hover:bg-accent\"\n onClick={() => {\n closeMenu();\n setAdvancedOpen(true);\n }}\n >\n Advanced\n </button>\n </div>\n </div>\n )}\n </DropdownMenu>\n <Modal\n open={advancedOpen}\n onClose={() => setAdvancedOpen(false)}\n title=\"Advanced Tool Preferences\"\n size=\"xl\"\n >\n <div className=\"overflow-hidden rounded border border-border\">\n <div className=\"grid grid-cols-[minmax(0,1fr)_4rem] gap-3 border-b border-border bg-muted/40 px-2 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\">\n <span>Name</span>\n <span className=\"text-right\">Mode</span>\n </div>\n <div className=\"max-h-[60vh] overflow-y-auto\">\n {advancedGroups.length > 0 ? (\n advancedGroups.map(([group, groupTools]) => (\n <div key={group}>\n <div className=\"sticky top-0 z-[1] border-b border-border bg-muted/60 px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground\">\n {group}\n </div>\n {groupTools.map((tool) => {\n const mode = toolMode(tool, value);\n return (\n <AdvancedToolRow\n key={tool.name}\n tool={tool}\n mode={mode}\n onToggle={handleToggle}\n />\n );\n })}\n </div>\n ))\n ) : (\n <div className=\"px-2 py-6 text-center text-xs text-muted-foreground\">\n No tools\n </div>\n )}\n </div>\n </div>\n </Modal>\n </>\n );\n}\n\nfunction toolGroup(meta: ToolMeta): string {\n return meta.group ?? \"Tools\";\n}\n\nfunction groupedToolMetas(tools: ToolMeta[]): Array<[string, ToolMeta[]]> {\n const groups: Record<string, ToolMeta[]> = {};\n for (const tool of tools) {\n (groups[toolGroup(tool)] ??= []).push(tool);\n }\n return Object.entries(groups)\n .map(([group, groupTools]) => [\n group,\n [...groupTools].sort((a, b) => a.name.localeCompare(b.name)),\n ] as [string, ToolMeta[]])\n .sort(([a], [b]) => a.localeCompare(b));\n}\n\nfunction toolPreferenceKey(meta: ToolMeta): string {\n return meta.preferenceKey ?? meta.name;\n}\n\nfunction toolDefaultMode(meta: ToolMeta): ToolMode {\n return meta.defaultMode ?? \"enabled\";\n}\n\nfunction toolMode(meta: ToolMeta, value: Record<string, ToolMode>): ToolMode {\n const own = value[meta.name];\n if (own) return own;\n const key = toolPreferenceKey(meta);\n if (key !== meta.name) {\n const grouped = value[key];\n if (grouped) return grouped;\n }\n return toolDefaultMode(meta);\n}\n\nfunction mostRestrictiveMode(a: ToolMode, b: ToolMode): ToolMode {\n const rank: Record<ToolMode, number> = { enabled: 0, ask: 1, disabled: 2 };\n return rank[a] >= rank[b] ? a : b;\n}\n"],"names":[],"mappings":"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"}
@@ -1,29 +1,115 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const DISABLED_TOOL_GROUP = "Disabled";
4
+ const TOOL_GROUP_DEFAULTS = {
5
+ "Accounting Read": "enabled",
6
+ "Accounting Transaction Write": "ask",
7
+ "Accounting Metadata Write": "ask",
8
+ "Comments Read": "enabled",
9
+ "Comments Write": "ask",
10
+ "Xero Read": "disabled",
11
+ "Xero Write": "disabled",
12
+ "Takealot Read": "enabled",
13
+ "Takealot Write": "disabled",
14
+ "Admin Read": "enabled",
15
+ "Admin Write": "ask"
16
+ };
3
17
  function clickyOperationsToTools(operations) {
4
18
  const tools = [];
5
19
  for (const resolved of operations) {
6
- const tool = operationToTool(resolved.operation);
20
+ const tool = operationToTool(resolved.operation, resolved);
7
21
  if (tool) {
8
22
  tools.push(tool);
9
23
  }
10
24
  }
11
25
  return tools;
12
26
  }
13
- function operationToTool(operation) {
27
+ function operationToTool(operation, resolved) {
14
28
  if (!operation.operationId) {
15
29
  return null;
16
30
  }
31
+ if (isCobraHelpOrCompletionOperation(operation, resolved)) {
32
+ return null;
33
+ }
17
34
  const meta = operation["x-clicky"];
35
+ const group = operationToolGroup(operation, resolved);
36
+ if (group === DISABLED_TOOL_GROUP) {
37
+ return null;
38
+ }
18
39
  const description = operation.description ?? operation.summary;
19
40
  return {
20
41
  name: operation.operationId,
21
42
  label: toolLabel(operation),
22
- ...(meta == null ? void 0 : meta.surface) ? { group: meta.surface } : {},
43
+ ...group ? { group, preferenceKey: group, defaultMode: toolDefaultMode(group) } : (meta == null ? void 0 : meta.surface) ? { group: meta.surface } : {},
23
44
  ...description ? { description } : {},
24
45
  inputSchema: buildInputSchema(operation)
25
46
  };
26
47
  }
48
+ function operationToolGroup(operation, resolved) {
49
+ const meta = operation["x-clicky"];
50
+ if (meta == null ? void 0 : meta.group) return meta.group;
51
+ return inferToolGroup(operation, resolved);
52
+ }
53
+ function isCobraHelpOrCompletionOperation(operation, resolved) {
54
+ var _a;
55
+ return commandStartsWithCobraBuiltin(operation.operationId) || commandStartsWithCobraBuiltin((_a = operation["x-clicky"]) == null ? void 0 : _a.command) || pathContainsCobraBuiltin(resolved == null ? void 0 : resolved.path);
56
+ }
57
+ function commandStartsWithCobraBuiltin(raw) {
58
+ if (!raw) return false;
59
+ const normalized = raw.replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/[/_.-]+/g, " ").trim().toLowerCase();
60
+ const first = normalized.split(/\s+/)[0];
61
+ return first === "completion" || first === "help";
62
+ }
63
+ function pathContainsCobraBuiltin(path) {
64
+ if (!path) return false;
65
+ const parts = path.split("/").filter(Boolean).map((part) => part.toLowerCase());
66
+ const commandParts = parts[0] === "api" && /^v\d+$/.test(parts[1] ?? "") ? parts.slice(2) : parts;
67
+ const first = commandParts[0];
68
+ return first === "completion" || first === "help";
69
+ }
70
+ function inferToolGroup(operation, resolved) {
71
+ var _a;
72
+ const id = operation.operationId ?? "";
73
+ const tags = operation.tags ?? [];
74
+ const path = (resolved == null ? void 0 : resolved.path) ?? "";
75
+ const method = ((_a = resolved == null ? void 0 : resolved.method) == null ? void 0 : _a.toUpperCase()) ?? "";
76
+ const write = isWriteMethod(method);
77
+ if (path.startsWith("/api/v1/provider/") || id.startsWith("provider")) {
78
+ return write ? "Xero Write" : "Xero Read";
79
+ }
80
+ if (path.startsWith("/api/v1/comments") || path.startsWith("/api/v1/comment-messages") || /^(comments|commentMessages|comment)/.test(id)) {
81
+ return write ? "Comments Write" : "Comments Read";
82
+ }
83
+ if (path === "/api/v1/accounts/mapping" || id.startsWith("accountMapping")) {
84
+ return write ? "Accounting Metadata Write" : "Accounting Read";
85
+ }
86
+ if (path === "/api/v1/entity/metadata" || id.startsWith("entityMetadata")) {
87
+ return write ? "Accounting Metadata Write" : "Accounting Read";
88
+ }
89
+ if (path.startsWith("/api/v1/companies/") || id.startsWith("companyBranding")) {
90
+ return write ? "Accounting Metadata Write" : "Accounting Read";
91
+ }
92
+ if (path.startsWith("/api/v1/transactions") || path.startsWith("/api/v1/journals") || /^(transactions|journals)/.test(id)) {
93
+ return write ? "Accounting Transaction Write" : "Accounting Read";
94
+ }
95
+ if (path.startsWith("/api/v1/rules/") || path.startsWith("/api/v1/rates/") || path.startsWith("/api/v1/template/") || path.startsWith("/api/v1/formula/") || tags.some(
96
+ (tag) => ["rules", "rates", "template", "formula"].includes(tag)
97
+ ) || /^(rules|rates|template|formula)/.test(id)) {
98
+ return write && !isReadLikeOperation(id) ? "Admin Write" : "Admin Read";
99
+ }
100
+ return void 0;
101
+ }
102
+ function isWriteMethod(method) {
103
+ return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
104
+ }
105
+ function isReadLikeOperation(operationId) {
106
+ return /(list|load|render|pdf|eval|context|validate|preview|coverage|entries|get|history|export)/i.test(
107
+ operationId
108
+ );
109
+ }
110
+ function toolDefaultMode(group) {
111
+ return TOOL_GROUP_DEFAULTS[group] ?? "ask";
112
+ }
27
113
  function toolLabel(operation) {
28
114
  var _a, _b;
29
115
  const meta = operation["x-clicky"];
@@ -1 +1 @@
1
- {"version":3,"file":"clickyOperationsToTools.cjs","sources":["../../../src/data/chat/clickyOperationsToTools.ts"],"sourcesContent":["import type {\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPISchema,\n ResolvedOperation,\n} from \"../../rpc/types\";\nimport type { ChatToolInputSchema, JSONSchemaProperty, ToolMeta } from \"./types\";\n\n/** Converts a clicky RPC operation catalog into AI-tool metadata for display\n * and request scoping. Execution stays in the Go backend; this maps an\n * operation's `operationId` → tool name, a short `x-clicky` verb/action →\n * label, the `x-clicky` surface → group, `summary`/`description` → description,\n * and `parameters` + `requestBody` → a JSON-Schema input. Operations without an\n * `operationId` are skipped (a tool needs a stable name). */\nexport function clickyOperationsToTools(operations: ResolvedOperation[]): ToolMeta[] {\n const tools: ToolMeta[] = [];\n for (const resolved of operations) {\n const tool = operationToTool(resolved.operation);\n if (tool) {\n tools.push(tool);\n }\n }\n return tools;\n}\n\nexport function operationToTool(operation: OpenAPIOperation): ToolMeta | null {\n if (!operation.operationId) {\n return null;\n }\n const meta = operation[\"x-clicky\"];\n const description = operation.description ?? operation.summary;\n return {\n name: operation.operationId,\n label: toolLabel(operation),\n ...(meta?.surface ? { group: meta.surface } : {}),\n ...(description ? { description } : {}),\n inputSchema: buildInputSchema(operation),\n };\n}\n\n/** A concise popover label: the clicky action/verb (capitalized) when present,\n * else the operation summary, else a humanized operationId. Mirrors the intent\n * of rpc/clickyMetadata.ts `surfaceActionLabel`. */\nfunction toolLabel(operation: OpenAPIOperation): string {\n const meta = operation[\"x-clicky\"];\n const short = meta?.actionName?.trim() || meta?.verb?.trim();\n if (short && short !== \"action\") {\n return short.charAt(0).toUpperCase() + short.slice(1);\n }\n if (operation.summary) {\n return operation.summary;\n }\n return humanize(operation.operationId ?? \"\");\n}\n\nfunction humanize(operationId: string): string {\n const spaced = operationId.replace(/[_-]+/g, \" \").replace(/([a-z\\d])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\nfunction buildInputSchema(operation: OpenAPIOperation): ChatToolInputSchema {\n const properties: Record<string, JSONSchemaProperty> = {};\n const required: string[] = [];\n\n for (const param of operation.parameters ?? []) {\n properties[param.name] = parameterToProperty(param);\n if (param.required) {\n required.push(param.name);\n }\n }\n\n const body = requestBodySchema(operation);\n if (body?.properties) {\n for (const [name, schema] of Object.entries(body.properties)) {\n properties[name] = schemaToProperty(schema);\n }\n }\n\n return { type: \"object\", properties, required };\n}\n\nfunction parameterToProperty(param: OpenAPIParameter): JSONSchemaProperty {\n const prop = schemaToProperty(param.schema);\n if (param.description && !prop.description) {\n prop.description = param.description;\n }\n return prop;\n}\n\nfunction schemaToProperty(schema: OpenAPISchema | undefined): JSONSchemaProperty {\n if (!schema) {\n return { type: \"string\" };\n }\n const prop: JSONSchemaProperty = { type: schema.type ?? \"string\" };\n if (schema.description !== undefined) {\n prop.description = schema.description;\n }\n if (schema.enum !== undefined) {\n prop.enum = schema.enum;\n }\n if (schema.default !== undefined) {\n prop.default = schema.default;\n }\n return prop;\n}\n\nfunction requestBodySchema(operation: OpenAPIOperation): OpenAPISchema | undefined {\n const content = operation.requestBody?.content;\n if (!content) {\n return undefined;\n }\n return content[\"application/json\"]?.schema ?? Object.values(content)[0]?.schema;\n}\n"],"names":[],"mappings":";;AAcO,SAAS,wBAAwB,YAA6C;AACnF,QAAM,QAAoB,CAAA;AAC1B,aAAW,YAAY,YAAY;AACjC,UAAM,OAAO,gBAAgB,SAAS,SAAS;AAC/C,QAAI,MAAM;AACR,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,WAA8C;AAC5E,MAAI,CAAC,UAAU,aAAa;AAC1B,WAAO;AAAA,EACT;AACA,QAAM,OAAO,UAAU,UAAU;AACjC,QAAM,cAAc,UAAU,eAAe,UAAU;AACvD,SAAO;AAAA,IACL,MAAM,UAAU;AAAA,IAChB,OAAO,UAAU,SAAS;AAAA,IAC1B,IAAI,6BAAM,WAAU,EAAE,OAAO,KAAK,QAAA,IAAY,CAAA;AAAA,IAC9C,GAAI,cAAc,EAAE,YAAA,IAAgB,CAAA;AAAA,IACpC,aAAa,iBAAiB,SAAS;AAAA,EAAA;AAE3C;AAKA,SAAS,UAAU,WAAqC;;AACtD,QAAM,OAAO,UAAU,UAAU;AACjC,QAAM,UAAQ,kCAAM,eAAN,mBAAkB,aAAU,kCAAM,SAAN,mBAAY;AACtD,MAAI,SAAS,UAAU,UAAU;AAC/B,WAAO,MAAM,OAAO,CAAC,EAAE,gBAAgB,MAAM,MAAM,CAAC;AAAA,EACtD;AACA,MAAI,UAAU,SAAS;AACrB,WAAO,UAAU;AAAA,EACnB;AACA,SAAO,SAAS,UAAU,eAAe,EAAE;AAC7C;AAEA,SAAS,SAAS,aAA6B;AAC7C,QAAM,SAAS,YAAY,QAAQ,UAAU,GAAG,EAAE,QAAQ,qBAAqB,OAAO;AACtF,SAAO,OAAO,OAAO,CAAC,EAAE,gBAAgB,OAAO,MAAM,CAAC;AACxD;AAEA,SAAS,iBAAiB,WAAkD;AAC1E,QAAM,aAAiD,CAAA;AACvD,QAAM,WAAqB,CAAA;AAE3B,aAAW,SAAS,UAAU,cAAc,CAAA,GAAI;AAC9C,eAAW,MAAM,IAAI,IAAI,oBAAoB,KAAK;AAClD,QAAI,MAAM,UAAU;AAClB,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,OAAO,kBAAkB,SAAS;AACxC,MAAI,6BAAM,YAAY;AACpB,eAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,GAAG;AAC5D,iBAAW,IAAI,IAAI,iBAAiB,MAAM;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO,EAAE,MAAM,UAAU,YAAY,SAAA;AACvC;AAEA,SAAS,oBAAoB,OAA6C;AACxE,QAAM,OAAO,iBAAiB,MAAM,MAAM;AAC1C,MAAI,MAAM,eAAe,CAAC,KAAK,aAAa;AAC1C,SAAK,cAAc,MAAM;AAAA,EAC3B;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAuD;AAC/E,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,MAAM,SAAA;AAAA,EACjB;AACA,QAAM,OAA2B,EAAE,MAAM,OAAO,QAAQ,SAAA;AACxD,MAAI,OAAO,gBAAgB,QAAW;AACpC,SAAK,cAAc,OAAO;AAAA,EAC5B;AACA,MAAI,OAAO,SAAS,QAAW;AAC7B,SAAK,OAAO,OAAO;AAAA,EACrB;AACA,MAAI,OAAO,YAAY,QAAW;AAChC,SAAK,UAAU,OAAO;AAAA,EACxB;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,WAAwD;;AACjF,QAAM,WAAU,eAAU,gBAAV,mBAAuB;AACvC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AACA,WAAO,aAAQ,kBAAkB,MAA1B,mBAA6B,aAAU,YAAO,OAAO,OAAO,EAAE,CAAC,MAAxB,mBAA2B;AAC3E;;;"}
1
+ {"version":3,"file":"clickyOperationsToTools.cjs","sources":["../../../src/data/chat/clickyOperationsToTools.ts"],"sourcesContent":["import type {\n OpenAPIOperation,\n OpenAPIParameter,\n OpenAPISchema,\n ResolvedOperation,\n} from \"../../rpc/types\";\nimport type {\n ChatToolInputSchema,\n JSONSchemaProperty,\n ToolMeta,\n ToolMode,\n} from \"./types\";\n\nconst DISABLED_TOOL_GROUP = \"Disabled\";\n\nconst TOOL_GROUP_DEFAULTS: Record<string, ToolMode> = {\n \"Accounting Read\": \"enabled\",\n \"Accounting Transaction Write\": \"ask\",\n \"Accounting Metadata Write\": \"ask\",\n \"Comments Read\": \"enabled\",\n \"Comments Write\": \"ask\",\n \"Xero Read\": \"disabled\",\n \"Xero Write\": \"disabled\",\n \"Takealot Read\": \"enabled\",\n \"Takealot Write\": \"disabled\",\n \"Admin Read\": \"enabled\",\n \"Admin Write\": \"ask\",\n};\n\n/** Converts a clicky RPC operation catalog into AI-tool metadata for display\n * and request scoping. Execution stays in the Go backend; this maps an\n * operation's `operationId` → tool name, a short `x-clicky` verb/action →\n * label, the `x-clicky` surface → group, `summary`/`description` → description,\n * and `parameters` + `requestBody` → a JSON-Schema input. Operations without an\n * `operationId` are skipped (a tool needs a stable name). */\nexport function clickyOperationsToTools(\n operations: ResolvedOperation[],\n): ToolMeta[] {\n const tools: ToolMeta[] = [];\n for (const resolved of operations) {\n const tool = operationToTool(resolved.operation, resolved);\n if (tool) {\n tools.push(tool);\n }\n }\n return tools;\n}\n\nexport function operationToTool(\n operation: OpenAPIOperation,\n resolved?: Pick<ResolvedOperation, \"path\" | \"method\">,\n): ToolMeta | null {\n if (!operation.operationId) {\n return null;\n }\n if (isCobraHelpOrCompletionOperation(operation, resolved)) {\n return null;\n }\n const meta = operation[\"x-clicky\"];\n const group = operationToolGroup(operation, resolved);\n if (group === DISABLED_TOOL_GROUP) {\n return null;\n }\n const description = operation.description ?? operation.summary;\n return {\n name: operation.operationId,\n label: toolLabel(operation),\n ...(group\n ? { group, preferenceKey: group, defaultMode: toolDefaultMode(group) }\n : meta?.surface\n ? { group: meta.surface }\n : {}),\n ...(description ? { description } : {}),\n inputSchema: buildInputSchema(operation),\n };\n}\n\nfunction operationToolGroup(\n operation: OpenAPIOperation,\n resolved?: Pick<ResolvedOperation, \"path\" | \"method\">,\n): string | undefined {\n const meta = operation[\"x-clicky\"];\n if (meta?.group) return meta.group;\n return inferToolGroup(operation, resolved);\n}\n\nfunction isCobraHelpOrCompletionOperation(\n operation: OpenAPIOperation,\n resolved?: Pick<ResolvedOperation, \"path\" | \"method\">,\n): boolean {\n return (\n commandStartsWithCobraBuiltin(operation.operationId) ||\n commandStartsWithCobraBuiltin(operation[\"x-clicky\"]?.command) ||\n pathContainsCobraBuiltin(resolved?.path)\n );\n}\n\nfunction commandStartsWithCobraBuiltin(raw: string | undefined): boolean {\n if (!raw) return false;\n const normalized = raw\n .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n .replace(/[/_.-]+/g, \" \")\n .trim()\n .toLowerCase();\n const first = normalized.split(/\\s+/)[0];\n return first === \"completion\" || first === \"help\";\n}\n\nfunction pathContainsCobraBuiltin(path: string | undefined): boolean {\n if (!path) return false;\n const parts = path\n .split(\"/\")\n .filter(Boolean)\n .map((part) => part.toLowerCase());\n const commandParts =\n parts[0] === \"api\" && /^v\\d+$/.test(parts[1] ?? \"\") ? parts.slice(2) : parts;\n const first = commandParts[0];\n return first === \"completion\" || first === \"help\";\n}\n\nfunction inferToolGroup(\n operation: OpenAPIOperation,\n resolved?: Pick<ResolvedOperation, \"path\" | \"method\">,\n): string | undefined {\n const id = operation.operationId ?? \"\";\n const tags = operation.tags ?? [];\n const path = resolved?.path ?? \"\";\n const method = resolved?.method?.toUpperCase() ?? \"\";\n const write = isWriteMethod(method);\n\n if (path.startsWith(\"/api/v1/provider/\") || id.startsWith(\"provider\")) {\n return write ? \"Xero Write\" : \"Xero Read\";\n }\n if (\n path.startsWith(\"/api/v1/comments\") ||\n path.startsWith(\"/api/v1/comment-messages\") ||\n /^(comments|commentMessages|comment)/.test(id)\n ) {\n return write ? \"Comments Write\" : \"Comments Read\";\n }\n if (path === \"/api/v1/accounts/mapping\" || id.startsWith(\"accountMapping\")) {\n return write ? \"Accounting Metadata Write\" : \"Accounting Read\";\n }\n if (path === \"/api/v1/entity/metadata\" || id.startsWith(\"entityMetadata\")) {\n return write ? \"Accounting Metadata Write\" : \"Accounting Read\";\n }\n if (\n path.startsWith(\"/api/v1/companies/\") ||\n id.startsWith(\"companyBranding\")\n ) {\n return write ? \"Accounting Metadata Write\" : \"Accounting Read\";\n }\n if (\n path.startsWith(\"/api/v1/transactions\") ||\n path.startsWith(\"/api/v1/journals\") ||\n /^(transactions|journals)/.test(id)\n ) {\n return write ? \"Accounting Transaction Write\" : \"Accounting Read\";\n }\n if (\n path.startsWith(\"/api/v1/rules/\") ||\n path.startsWith(\"/api/v1/rates/\") ||\n path.startsWith(\"/api/v1/template/\") ||\n path.startsWith(\"/api/v1/formula/\") ||\n tags.some((tag) =>\n [\"rules\", \"rates\", \"template\", \"formula\"].includes(tag),\n ) ||\n /^(rules|rates|template|formula)/.test(id)\n ) {\n return write && !isReadLikeOperation(id) ? \"Admin Write\" : \"Admin Read\";\n }\n return undefined;\n}\n\nfunction isWriteMethod(method: string): boolean {\n return (\n method === \"POST\" ||\n method === \"PUT\" ||\n method === \"PATCH\" ||\n method === \"DELETE\"\n );\n}\n\nfunction isReadLikeOperation(operationId: string): boolean {\n return /(list|load|render|pdf|eval|context|validate|preview|coverage|entries|get|history|export)/i.test(\n operationId,\n );\n}\n\nfunction toolDefaultMode(group: string): ToolMode {\n return TOOL_GROUP_DEFAULTS[group] ?? \"ask\";\n}\n\n/** A concise popover label: the clicky action/verb (capitalized) when present,\n * else the operation summary, else a humanized operationId. Mirrors the intent\n * of rpc/clickyMetadata.ts `surfaceActionLabel`. */\nfunction toolLabel(operation: OpenAPIOperation): string {\n const meta = operation[\"x-clicky\"];\n const short = meta?.actionName?.trim() || meta?.verb?.trim();\n if (short && short !== \"action\") {\n return short.charAt(0).toUpperCase() + short.slice(1);\n }\n if (operation.summary) {\n return operation.summary;\n }\n return humanize(operation.operationId ?? \"\");\n}\n\nfunction humanize(operationId: string): string {\n const spaced = operationId\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\");\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\nfunction buildInputSchema(operation: OpenAPIOperation): ChatToolInputSchema {\n const properties: Record<string, JSONSchemaProperty> = {};\n const required: string[] = [];\n\n for (const param of operation.parameters ?? []) {\n properties[param.name] = parameterToProperty(param);\n if (param.required) {\n required.push(param.name);\n }\n }\n\n const body = requestBodySchema(operation);\n if (body?.properties) {\n for (const [name, schema] of Object.entries(body.properties)) {\n properties[name] = schemaToProperty(schema);\n }\n }\n\n return { type: \"object\", properties, required };\n}\n\nfunction parameterToProperty(param: OpenAPIParameter): JSONSchemaProperty {\n const prop = schemaToProperty(param.schema);\n if (param.description && !prop.description) {\n prop.description = param.description;\n }\n return prop;\n}\n\nfunction schemaToProperty(\n schema: OpenAPISchema | undefined,\n): JSONSchemaProperty {\n if (!schema) {\n return { type: \"string\" };\n }\n const prop: JSONSchemaProperty = { type: schema.type ?? \"string\" };\n if (schema.description !== undefined) {\n prop.description = schema.description;\n }\n if (schema.enum !== undefined) {\n prop.enum = schema.enum;\n }\n if (schema.default !== undefined) {\n prop.default = schema.default;\n }\n return prop;\n}\n\nfunction requestBodySchema(\n operation: OpenAPIOperation,\n): OpenAPISchema | undefined {\n const content = operation.requestBody?.content;\n if (!content) {\n return undefined;\n }\n return (\n content[\"application/json\"]?.schema ?? Object.values(content)[0]?.schema\n );\n}\n"],"names":[],"mappings":"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"}
@@ -7,5 +7,5 @@ import { ToolMeta } from './types';
7
7
  * and `parameters` + `requestBody` → a JSON-Schema input. Operations without an
8
8
  * `operationId` are skipped (a tool needs a stable name). */
9
9
  export declare function clickyOperationsToTools(operations: ResolvedOperation[]): ToolMeta[];
10
- export declare function operationToTool(operation: OpenAPIOperation): ToolMeta | null;
10
+ export declare function operationToTool(operation: OpenAPIOperation, resolved?: Pick<ResolvedOperation, "path" | "method">): ToolMeta | null;
11
11
  //# sourceMappingURL=clickyOperationsToTools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clickyOperationsToTools.d.ts","sourceRoot":"","sources":["../../../src/data/chat/clickyOperationsToTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAGhB,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAA2C,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEjF;;;;;8DAK8D;AAC9D,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,QAAQ,EAAE,CASnF;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,QAAQ,GAAG,IAAI,CAa5E"}
1
+ {"version":3,"file":"clickyOperationsToTools.d.ts","sourceRoot":"","sources":["../../../src/data/chat/clickyOperationsToTools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAGhB,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAGV,QAAQ,EAET,MAAM,SAAS,CAAC;AAkBjB;;;;;8DAK8D;AAC9D,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,iBAAiB,EAAE,GAC9B,QAAQ,EAAE,CASZ;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,QAAQ,CAAC,GACpD,QAAQ,GAAG,IAAI,CAwBjB"}