windmill-components 1.550.0 → 1.555.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/package/aiStore.d.ts +13 -0
  2. package/package/aiStore.js +70 -0
  3. package/package/common.d.ts +2 -1
  4. package/package/components/AIProviderPicker.svelte +25 -8
  5. package/package/components/ArgEnum.svelte +3 -2
  6. package/package/components/ArgEnum.svelte.d.ts +1 -0
  7. package/package/components/ArgInput.svelte +235 -174
  8. package/package/components/ArgInput.svelte.d.ts +4 -1
  9. package/package/components/ArrayTypeNarrowing.svelte +38 -32
  10. package/package/components/AutoscalingEvents.svelte +21 -5
  11. package/package/components/AutoscalingEvents.svelte.d.ts +4 -18
  12. package/package/components/DateTimeInput.svelte +8 -6
  13. package/package/components/DeployButton.svelte +1 -1
  14. package/package/components/Dev.svelte +6 -4
  15. package/package/components/EditableSchemaForm.svelte +7 -6
  16. package/package/components/Editor.svelte +2 -1
  17. package/package/components/EditorSettings.svelte +5 -5
  18. package/package/components/EditorSettings.svelte.d.ts +4 -18
  19. package/package/components/FakeMonacoPlaceHolder.svelte +4 -2
  20. package/package/components/FakeMonacoPlaceHolder.svelte.d.ts +1 -0
  21. package/package/components/FieldHeader.svelte +5 -7
  22. package/package/components/FirstStepInputs.svelte +1 -1
  23. package/package/components/FlowLoopIterationPreview.svelte.d.ts +1 -1
  24. package/package/components/FlowPlugConnect.svelte +8 -2
  25. package/package/components/FlowPlugConnect.svelte.d.ts +1 -0
  26. package/package/components/FlowPreviewContent.svelte +113 -92
  27. package/package/components/FlowPreviewContent.svelte.d.ts +3 -3
  28. package/package/components/FlowStatusViewer.svelte +3 -2
  29. package/package/components/FlowStatusViewerInner.svelte +1 -1
  30. package/package/components/FolderEditor.svelte +6 -7
  31. package/package/components/GroupEditor.svelte +148 -141
  32. package/package/components/GroupEditor.svelte.d.ts +5 -4
  33. package/package/components/InputTransformForm.svelte +88 -82
  34. package/package/components/InputTransformSchemaForm.svelte +5 -4
  35. package/package/components/InstanceSetting.svelte +17 -9
  36. package/package/components/JsonEditor.svelte +18 -9
  37. package/package/components/JsonEditor.svelte.d.ts +1 -1
  38. package/package/components/JsonInputs.svelte +1 -1
  39. package/package/components/ModulePreviewForm.svelte +23 -19
  40. package/package/components/NumberTypeNarrowing.svelte +32 -16
  41. package/package/components/ObjectStoreConfigSettings.svelte +27 -19
  42. package/package/components/Path.svelte +2 -8
  43. package/package/components/Path.svelte.d.ts +1 -1
  44. package/package/components/ResourceEditor.svelte +3 -10
  45. package/package/components/ResourcePicker.svelte +85 -72
  46. package/package/components/ResourcePicker.svelte.d.ts +2 -0
  47. package/package/components/RunChart.svelte +1 -1
  48. package/package/components/RunForm.svelte +11 -7
  49. package/package/components/S3ArrayHelperButton.svelte +12 -6
  50. package/package/components/S3ArrayHelperButton.svelte.d.ts +1 -0
  51. package/package/components/S3FilePicker.svelte +1 -1
  52. package/package/components/SchemaForm.svelte +18 -10
  53. package/package/components/SchemaForm.svelte.d.ts +7 -1
  54. package/package/components/SchemaFormWithArgPicker.svelte +1 -1
  55. package/package/components/ScriptBuilder.svelte +2 -2
  56. package/package/components/ScriptEditor.svelte +4 -3
  57. package/package/components/ScriptEditor.svelte.d.ts +1 -1
  58. package/package/components/ShareModal.svelte +4 -4
  59. package/package/components/SimpleEditor.svelte +6 -2
  60. package/package/components/SimpleEditor.svelte.d.ts +3 -0
  61. package/package/components/StringTypeNarrowing.svelte +5 -1
  62. package/package/components/SuperadminSettingsInner.svelte +3 -3
  63. package/package/components/TemplateEditor.svelte +18 -9
  64. package/package/components/Toast.svelte +2 -7
  65. package/package/components/Toast.svelte.d.ts +4 -18
  66. package/package/components/Toggle.svelte +17 -7
  67. package/package/components/ToggleHubWorkspaceQuick.svelte +3 -3
  68. package/package/components/WorkerGroup.svelte +2 -14
  69. package/package/components/apps/components/buttons/AppButton.svelte +57 -39
  70. package/package/components/apps/components/display/dbtable/InsertRow.svelte +32 -2
  71. package/package/components/apps/components/display/dbtable/queries/insert.js +2 -1
  72. package/package/components/apps/components/display/dbtable/utils.d.ts +8 -8
  73. package/package/components/apps/components/display/table/utils.js +13 -3
  74. package/package/components/apps/components/helpers/RunnableComponent.svelte +3 -3
  75. package/package/components/apps/components/inputs/currency/CurrencyInput.svelte +2 -1
  76. package/package/components/apps/editor/AppEditorHeader.svelte +33 -271
  77. package/package/components/apps/editor/AppEditorHeaderDeploy.svelte +233 -0
  78. package/package/components/apps/editor/AppEditorHeaderDeploy.svelte.d.ts +18 -0
  79. package/package/components/apps/editor/AppEditorHeaderDeployInitialDraft.svelte +47 -0
  80. package/package/components/apps/editor/AppEditorHeaderDeployInitialDraft.svelte.d.ts +8 -0
  81. package/package/components/apps/editor/GridEditor.svelte +7 -2
  82. package/package/components/apps/editor/appDeploy.svelte.d.ts +1 -0
  83. package/package/components/apps/editor/appDeploy.svelte.js +6 -0
  84. package/package/components/apps/editor/appUtils.d.ts +1 -0
  85. package/package/components/apps/editor/appUtils.js +30 -1
  86. package/package/components/apps/editor/component/ComponentNavigation.svelte +3 -1
  87. package/package/components/apps/editor/component/components.d.ts +3 -3
  88. package/package/components/apps/editor/component/components.js +1 -1
  89. package/package/components/apps/editor/contextPanel/ComponentOutputViewer.svelte +1 -1
  90. package/package/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte +6 -4
  91. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte.d.ts +1 -1
  92. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditorDrawer.svelte.d.ts +1 -1
  93. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte.d.ts +1 -1
  94. package/package/components/apps/editor/settingsPanel/InputsSpecEditor.svelte +58 -8
  95. package/package/components/auditLogs/AuditLogsFilters.svelte +1 -1
  96. package/package/components/common/ResizeTransitionWrapper.svelte +39 -0
  97. package/package/components/common/ResizeTransitionWrapper.svelte.d.ts +12 -0
  98. package/package/components/common/badge/CountBadge.svelte +29 -0
  99. package/package/components/common/badge/CountBadge.svelte.d.ts +8 -0
  100. package/package/components/common/button/Button.svelte +1 -0
  101. package/package/components/common/button/ConnectionButton.svelte +6 -1
  102. package/package/components/common/button/ConnectionButton.svelte.d.ts +2 -0
  103. package/package/components/common/button/RefreshButton.svelte +8 -4
  104. package/package/components/common/button/RefreshButton.svelte.d.ts +3 -0
  105. package/package/components/common/calendarPicker/CalendarPicker.svelte +1 -1
  106. package/package/components/common/fileInput/FileInput.svelte +7 -6
  107. package/package/components/common/fileUpload/S3ArgInput.svelte +11 -9
  108. package/package/components/common/fileUpload/S3ArgInput.svelte.d.ts +1 -0
  109. package/package/components/common/popup/PopupV2.svelte +6 -0
  110. package/package/components/common/toggleButton-v2/ToggleButton.svelte +17 -26
  111. package/package/components/common/toggleButton-v2/ToggleButton.svelte.d.ts +16 -30
  112. package/package/components/common/toggleButton-v2/ToggleButtonGroup.svelte +1 -1
  113. package/package/components/common/toggleButton-v2/ToggleButtonMore.svelte +3 -3
  114. package/package/components/common/toggleButton-v2/ToggleButtonMore.svelte.d.ts +1 -0
  115. package/package/components/copilot/CodeCompletionStatus.svelte +2 -1
  116. package/package/components/copilot/CronGen.svelte +1 -1
  117. package/package/components/copilot/FlowInlineScriptAIButton.svelte +2 -2
  118. package/package/components/copilot/IteratorGen.svelte +30 -25
  119. package/package/components/copilot/IteratorGen.svelte.d.ts +8 -7
  120. package/package/components/copilot/MetadataGen.svelte +4 -3
  121. package/package/components/copilot/PredicateGen.svelte +15 -12
  122. package/package/components/copilot/PredicateGen.svelte.d.ts +5 -4
  123. package/package/components/copilot/RegexGen.svelte +1 -1
  124. package/package/components/copilot/ScriptFix.svelte +1 -1
  125. package/package/components/copilot/ScriptGen.svelte +2 -1
  126. package/package/components/copilot/StepGenQuick.svelte +15 -16
  127. package/package/components/copilot/StepGenQuick.svelte.d.ts +14 -13
  128. package/package/components/copilot/StepInputGen.svelte +50 -36
  129. package/package/components/copilot/StepInputGen.svelte.d.ts +13 -10
  130. package/package/components/copilot/StepInputsGen.svelte +18 -19
  131. package/package/components/copilot/StepInputsGen.svelte.d.ts +4 -18
  132. package/package/components/copilot/autocomplete/Autocompletor.js +1 -1
  133. package/package/components/copilot/autocomplete/request.js +1 -1
  134. package/package/components/copilot/chat/AIChat.svelte +2 -1
  135. package/package/components/copilot/chat/AIChatManager.svelte.js +2 -1
  136. package/package/components/copilot/chat/AiChatLayout.svelte +2 -1
  137. package/package/components/copilot/chat/ProviderModelSelector.svelte +10 -9
  138. package/package/components/copilot/chat/ProviderModelSelector.svelte.d.ts +2 -17
  139. package/package/components/copilot/chat/flow/FlowAIButton.svelte +1 -1
  140. package/package/components/copilot/chat/script/core.js +2 -1
  141. package/package/components/copilot/chat/shared.js +2 -1
  142. package/package/components/copilot/lib.js +2 -1
  143. package/package/components/details/DetailPageLayout.svelte +3 -2
  144. package/package/components/details/DetailPageLayout.svelte.d.ts +1 -0
  145. package/package/components/flows/CreateActionsFlow.svelte +1 -1
  146. package/package/components/flows/FlowChatInterface.svelte +404 -0
  147. package/package/components/flows/FlowChatInterface.svelte.d.ts +19 -0
  148. package/package/components/flows/FlowChatMessage.svelte +41 -0
  149. package/package/components/flows/FlowChatMessage.svelte.d.ts +9 -0
  150. package/package/components/flows/FlowConversationsSidebar.svelte +213 -0
  151. package/package/components/flows/FlowConversationsSidebar.svelte.d.ts +15 -0
  152. package/package/components/flows/FlowEditor.svelte.d.ts +1 -1
  153. package/package/components/flows/FlowModuleIcon.svelte +10 -10
  154. package/package/components/flows/common/FlowCard.svelte +10 -2
  155. package/package/components/flows/common/FlowCard.svelte.d.ts +1 -0
  156. package/package/components/flows/common/FlowCardHeader.svelte +2 -1
  157. package/package/components/flows/common/FlowCardHeader.svelte.d.ts +1 -0
  158. package/package/components/flows/content/DynamicInputHelpBox.svelte +4 -4
  159. package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -1
  160. package/package/components/flows/content/FlowInput.svelte +381 -259
  161. package/package/components/flows/content/FlowInput.svelte.d.ts +1 -1
  162. package/package/components/flows/content/FlowInputsQuick.svelte +55 -34
  163. package/package/components/flows/content/FlowInputsQuick.svelte.d.ts +2 -2
  164. package/package/components/flows/content/FlowModuleComponent.svelte +5 -10
  165. package/package/components/flows/flowInfers.d.ts +60 -0
  166. package/package/components/flows/flowInfers.js +72 -66
  167. package/package/components/flows/{flowStore.d.ts → flowStore.svelte.d.ts} +1 -0
  168. package/package/components/flows/header/FlowPreviewButtons.svelte +1 -1
  169. package/package/components/flows/map/FlowErrorHandlerItem.svelte +4 -2
  170. package/package/components/flows/map/FlowErrorHandlerItem.svelte.d.ts +1 -0
  171. package/package/components/flows/map/FlowModuleSchemaItem.svelte +1 -1
  172. package/package/components/flows/map/FlowModuleSchemaMap.svelte +5 -2
  173. package/package/components/flows/map/FlowStickyNode.svelte +2 -2
  174. package/package/components/flows/map/FlowStickyNode.svelte.d.ts +1 -0
  175. package/package/components/flows/map/InsertModuleButton.svelte +5 -2
  176. package/package/components/flows/map/InsertModuleButton.svelte.d.ts +4 -3
  177. package/package/components/flows/map/InsertModuleInner.svelte +3 -1
  178. package/package/components/flows/map/InsertModuleInner.svelte.d.ts +2 -2
  179. package/package/components/flows/map/VirtualItem.svelte +1 -2
  180. package/package/components/flows/pickers/PickHubScriptQuick.svelte +8 -3
  181. package/package/components/flows/pickers/PickHubScriptQuick.svelte.d.ts +1 -1
  182. package/package/components/flows/pickers/WorkspaceScriptPickerQuick.svelte +15 -12
  183. package/package/components/flows/propPicker/PropPickerWrapper.svelte +1 -15
  184. package/package/components/graph/FlowGraphV2.svelte +2 -1
  185. package/package/components/graph/FlowGraphV2.svelte.d.ts +1 -0
  186. package/package/components/graph/graphBuilder.svelte.d.ts +2 -0
  187. package/package/components/graph/graphBuilder.svelte.js +1 -0
  188. package/package/components/graph/renderers/edges/BaseEdge.svelte +1 -0
  189. package/package/components/graph/renderers/nodes/InputNode.svelte +13 -2
  190. package/package/components/graph/renderers/triggers/TriggersBadge.svelte +2 -27
  191. package/package/components/instanceSettings.js +17 -0
  192. package/package/components/progressBar/ProgressBar.svelte +1 -1
  193. package/package/components/raw_apps/FileEditorIcon.svelte +1 -1
  194. package/package/components/raw_apps/FileEditorIcon.svelte.d.ts +4 -18
  195. package/package/components/raw_apps/RawAppBackgroundRunner.svelte +2 -8
  196. package/package/components/raw_apps/RawAppBackgroundRunner.svelte.d.ts +4 -18
  197. package/package/components/raw_apps/RawAppEditor.svelte +6 -7
  198. package/package/components/raw_apps/RawAppEditorHeader.svelte +48 -301
  199. package/package/components/raw_apps/RawAppEditorHeader.svelte.d.ts +18 -19
  200. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +10 -16
  201. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte.d.ts +13 -13
  202. package/package/components/raw_apps/RawAppInlineScriptPanelList.svelte +8 -11
  203. package/package/components/raw_apps/RawAppInlineScriptPanelList.svelte.d.ts +1 -2
  204. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte +0 -1
  205. package/package/components/raw_apps/RawAppInlineScriptsPanel.svelte +7 -13
  206. package/package/components/raw_apps/RawAppInlineScriptsPanel.svelte.d.ts +8 -8
  207. package/package/components/raw_apps/RawAppPreview.svelte +3 -7
  208. package/package/components/raw_apps/RawAppPreview.svelte.d.ts +5 -19
  209. package/package/components/raw_apps/utils.d.ts +1 -1
  210. package/package/components/raw_apps/utils.js +3 -3
  211. package/package/components/runs/RunOption.svelte +2 -2
  212. package/package/components/runs/RunsFilter.svelte +15 -12
  213. package/package/components/runs/RunsFilter.svelte.d.ts +1 -1
  214. package/package/components/schema/EditableSchemaDrawer.svelte +19 -18
  215. package/package/components/schema/FlowPropertyEditor.svelte +9 -2
  216. package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
  217. package/package/components/schema/PropertyEditor.svelte +22 -26
  218. package/package/components/schema/SchemaFormDND.svelte +3 -2
  219. package/package/components/schema/SchemaFormDND.svelte.d.ts +1 -0
  220. package/package/components/select/DraggableTags.svelte +2 -2
  221. package/package/components/select/MultiSelect.svelte +14 -8
  222. package/package/components/select/Select.svelte +12 -5
  223. package/package/components/select/Select.svelte.d.ts +11 -0
  224. package/package/components/select/SelectDropdown.svelte +98 -46
  225. package/package/components/select/SelectDropdown.svelte.d.ts +10 -0
  226. package/package/components/select/utils.svelte.js +2 -0
  227. package/package/components/settings/CreateToken.svelte +76 -49
  228. package/package/components/settings/WorkspaceUserSettings.svelte +20 -17
  229. package/package/components/sidebar/CriticalAlertTable.svelte +2 -1
  230. package/package/components/sidebar/Linkify.svelte +14 -0
  231. package/package/components/sidebar/Linkify.svelte.d.ts +5 -0
  232. package/package/components/sidebar/WorkspaceMenu.svelte +8 -3
  233. package/package/components/table/tableUtils.js +1 -1
  234. package/package/components/text_input/TextInput.svelte +30 -0
  235. package/package/components/text_input/TextInput.svelte.d.ts +17 -0
  236. package/package/components/triggers/TriggersEditor.svelte +11 -1
  237. package/package/components/triggers/triggers.svelte.d.ts +1 -1
  238. package/package/components/triggers/triggers.svelte.js +8 -4
  239. package/package/components/tutorials/FlowBuilderTutorialErrorHandler.svelte +2 -2
  240. package/package/components/tutorials/FlowBuilderTutorialForLoop.svelte +3 -0
  241. package/package/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte +49 -17
  242. package/package/components/tutorials/Tutorial.svelte +9 -0
  243. package/package/components/tutorials/Tutorial.svelte.d.ts +1 -0
  244. package/package/components/tutorials/app/AppTutorial.svelte +41 -57
  245. package/package/components/tutorials/app/BackgroundRunnablesTutorial.svelte +3 -5
  246. package/package/components/tutorials/app/ConnectionTutorial.svelte +2 -2
  247. package/package/components/tutorials/utils.js +2 -154
  248. package/package/components/vscode.js +16 -8
  249. package/package/components/workspaceSettings/AISettings.svelte +4 -3
  250. package/package/components/workspaceSettings/CreateWorkspace.svelte +2 -2
  251. package/package/components/workspaceSettings/DucklakeSettings.svelte +64 -7
  252. package/package/components/workspaceSettings/StorageSettings.svelte +24 -26
  253. package/package/editorUtils.d.ts +1 -1
  254. package/package/gen/core/OpenAPI.js +1 -1
  255. package/package/gen/schemas.gen.d.ts +82 -1
  256. package/package/gen/schemas.gen.js +82 -1
  257. package/package/gen/services.gen.d.ts +175 -1
  258. package/package/gen/services.gen.js +345 -2
  259. package/package/gen/types.gen.d.ts +1324 -549
  260. package/package/script_helpers.js +5 -5
  261. package/package/services/JobManager.js +4 -2
  262. package/package/stores.d.ts +4 -13
  263. package/package/stores.js +5 -68
  264. package/package/toast.js +2 -1
  265. package/package/utils.d.ts +1 -0
  266. package/package/utils.js +3 -0
  267. package/package.json +5 -5
  268. /package/package/components/flows/{flowStore.js → flowStore.svelte.js} +0 -0
@@ -18,12 +18,15 @@ declare function $$render<Item extends {
18
18
  inputClass?: string;
19
19
  disablePortal?: boolean;
20
20
  loading?: boolean;
21
+ error?: boolean;
21
22
  autofocus?: boolean;
22
23
  RightIcon?: any;
23
24
  createText?: string;
24
25
  noItemsMsg?: string;
25
26
  open?: boolean;
26
27
  id?: string;
28
+ itemLabelWrapperClasses?: string;
29
+ itemButtonWrapperClasses?: string;
27
30
  groupBy?: (item: Item) => string;
28
31
  sortBy?: (a: Item, b: Item) => number;
29
32
  onFocus?: () => void;
@@ -32,6 +35,14 @@ declare function $$render<Item extends {
32
35
  onCreateItem?: (value: string) => void;
33
36
  startSnippet?: Snippet<[{
34
37
  item: ProcessedItem<Item["value"]>;
38
+ close: () => void;
39
+ }]>;
40
+ endSnippet?: Snippet<[{
41
+ item: ProcessedItem<Item["value"]>;
42
+ close: () => void;
43
+ }]>;
44
+ bottomSnippet?: Snippet<[{
45
+ close: () => void;
35
46
  }]>;
36
47
  };
37
48
  exports: {};
@@ -2,7 +2,7 @@
2
2
  import ConditionalPortal from '../common/drawer/ConditionalPortal.svelte';
3
3
  import { untrack } from 'svelte';
4
4
  import { twMerge } from 'tailwind-merge';
5
- let { processedItems: _processedItems, value, filterText, listAutoWidth = true, disabled, disablePortal = false, open, noItemsMsg = 'No items found', class: className = '', ulClass = '', header, getInputRect, onSelectValue, startSnippet } = $props();
5
+ let { processedItems: _processedItems, value, filterText, listAutoWidth = true, disabled, disablePortal = false, open, noItemsMsg = 'No items found', class: className = '', ulClass = '', itemLabelWrapperClasses = '', itemButtonWrapperClasses = '', header, getInputRect, onSelectValue, startSnippet, endSnippet, bottomSnippet } = $props();
6
6
  let processedItems = $derived(!filterText
7
7
  ? _processedItems
8
8
  : _processedItems?.filter((item) => item.__is_create || item?.label?.toLowerCase().includes(filterText?.toLowerCase())));
@@ -11,15 +11,15 @@ let dropdownPos = $state(computeDropdownPos());
11
11
  let keyArrowPos = $state();
12
12
  function computeDropdownPos() {
13
13
  if (!getInputRect || !listEl)
14
- return { width: 0, x: 0, y: 0 };
14
+ return { width: 0, height: 0, x: 0, y: 0, isBelow: true };
15
15
  let inputR = getInputRect();
16
16
  const listR = listEl.getBoundingClientRect();
17
- const openBelow = inputR.y + inputR.height + listR.height <= window.innerHeight;
17
+ const isBelow = inputR.y + inputR.height + listR.height <= window.innerHeight;
18
18
  let [x, y] = disablePortal ? [0, 0] : [inputR.x, inputR.y];
19
- if (openBelow)
20
- return { width: inputR.width, x: x, y: y + inputR.height };
19
+ if (isBelow)
20
+ return { width: inputR.width, height: listR.height, x: x, y: y + inputR.height, isBelow };
21
21
  else {
22
- return { width: inputR.width, x: x, y: y - listR.height };
22
+ return { width: inputR.width, height: listR.height, x: x, y: y - listR.height, isBelow };
23
23
  }
24
24
  }
25
25
  $effect(() => {
@@ -38,11 +38,47 @@ $effect(() => {
38
38
  [open, processedItems];
39
39
  untrack(() => (keyArrowPos = open && filterText ? 0 : undefined));
40
40
  });
41
+ // We do not want to render the dropdown when it is closed for performance reasons
42
+ // but we want to keep it in the DOM for a short time to allow for transitions to finish
43
+ //
44
+ // We do not use Svelte transitions because they can not animate in the opposite direction
45
+ // when the dropdown is opens above the input
46
+ // Also CSS transitions are smoother because they do not rely on JS / animation frames
47
+ let uiState = $state({ domExists: open, visible: open, timeout: null });
48
+ let initial = true;
49
+ $effect(() => {
50
+ let isOpen = open && !disabled;
51
+ untrack(() => {
52
+ if (initial) {
53
+ initial = false;
54
+ return;
55
+ }
56
+ if (uiState.timeout)
57
+ clearTimeout(uiState.timeout);
58
+ uiState = {
59
+ domExists: true,
60
+ visible: !isOpen,
61
+ timeout: setTimeout(() => {
62
+ if (isOpen) {
63
+ uiState.visible = true;
64
+ uiState.timeout = null;
65
+ }
66
+ else if (!isOpen) {
67
+ uiState.visible = false;
68
+ uiState.timeout = setTimeout(() => {
69
+ uiState.domExists = false;
70
+ uiState.timeout = null;
71
+ }, 500); // leave time for transition to finish
72
+ }
73
+ }, 0) // We need the height to be 0 then change immediately for the transition to play
74
+ };
75
+ });
76
+ });
41
77
  </script>
42
78
 
43
79
  <svelte:window
44
80
  on:keydown={(e) => {
45
- if (!open || !processedItems?.length) return
81
+ if (!uiState.visible || !processedItems?.length) return
46
82
  if (e.key === 'ArrowUp' && keyArrowPos !== undefined && processedItems.length > 0) {
47
83
  keyArrowPos = keyArrowPos <= 0 ? undefined : keyArrowPos - 1
48
84
  } else if (e.key === 'ArrowDown') {
@@ -62,55 +98,71 @@ $effect(() => {
62
98
  />
63
99
 
64
100
  <ConditionalPortal condition={!disablePortal} name="select-dropdown-portal">
65
- {#if open && !disabled}
101
+ {#if uiState.domExists}
66
102
  <div
67
103
  class={twMerge(
68
104
  disablePortal ? 'absolute' : 'fixed',
69
- 'flex flex-col z-[5001] max-h-64 overflow-y-auto bg-surface-secondary text-tertiary text-sm select-none border rounded-lg shadow-lg',
105
+ 'z-[5001] text-tertiary text-sm select-none',
106
+ dropdownPos.isBelow ? '' : 'flex flex-col justify-end',
107
+ uiState.visible ? '' : 'pointer-events-none',
70
108
  className
71
109
  )}
72
110
  style="{`top: ${dropdownPos.y}px; left: ${dropdownPos.x}px;`} {listAutoWidth
73
- ? `min-width: ${dropdownPos.width}px;`
111
+ ? `min-width: ${dropdownPos.width}px; height: ${dropdownPos.height}px;`
74
112
  : ''}"
75
- bind:this={listEl}
76
113
  >
77
- {@render header?.()}
78
- {#if processedItems?.length === 0}
79
- <div class="py-8 px-4 text-center text-primary">{noItemsMsg}</div>
80
- {/if}
81
- <ul class={twMerge('flex-1 overflow-y-auto flex flex-col', ulClass)}>
82
- {#each processedItems ?? [] as item, itemIndex}
83
- {#if (item.__select_group && itemIndex === 0) || processedItems?.[itemIndex - 1]?.__select_group !== item.__select_group}
84
- <li
85
- class={twMerge(
86
- 'mx-4 pb-1 mb-2 text-xs font-semibold text-primary border-b',
87
- itemIndex === 0 ? 'mt-3' : 'mt-6'
88
- )}
89
- >
90
- {item.__select_group}
91
- </li>
114
+ <div
115
+ class={twMerge(
116
+ 'overflow-clip rounded-md bg-surface-secondary shadow-lg transition-height',
117
+ dropdownPos.isBelow ? '' : 'flex flex-col justify-end'
118
+ )}
119
+ style="height: {uiState.visible ? dropdownPos.height : 0}px;"
120
+ >
121
+ <div bind:this={listEl} class="flex flex-col max-h-64 border rounded-md overflow-clip">
122
+ {@render header?.()}
123
+ {#if processedItems?.length === 0}
124
+ <div class="py-8 px-4 text-center text-primary">{noItemsMsg}</div>
92
125
  {/if}
93
- <li>
94
- <button
95
- class={twMerge(
96
- 'py-2 px-4 w-full font-normal text-left text-primary',
97
- itemIndex === keyArrowPos ? 'bg-surface-hover' : '',
98
- item.value === value ? 'bg-surface-selected' : 'hover:bg-surface-hover'
99
- )}
100
- onclick={(e) => {
101
- e.stopImmediatePropagation()
102
- onSelectValue(item)
103
- }}
104
- >
105
- {@render startSnippet?.({ item })}
106
- {item.label || '\xa0'}
107
- {#if item.subtitle}
108
- <div class="text-xs text-tertiary">{item.subtitle}</div>
126
+ <ul class={twMerge('flex-1 overflow-y-auto flex flex-col', ulClass)}>
127
+ {#each processedItems ?? [] as item, itemIndex}
128
+ {#if (item.__select_group && itemIndex === 0) || processedItems?.[itemIndex - 1]?.__select_group !== item.__select_group}
129
+ <li
130
+ class={twMerge(
131
+ 'mx-4 pb-1 mb-2 text-xs font-semibold text-primary border-b',
132
+ itemIndex === 0 ? 'mt-3' : 'mt-6'
133
+ )}
134
+ >
135
+ {item.__select_group}
136
+ </li>
109
137
  {/if}
110
- </button>
111
- </li>
112
- {/each}
113
- </ul>
138
+ <li>
139
+ <button
140
+ class={twMerge(
141
+ 'py-2 px-4 w-full font-normal text-left text-primary',
142
+ itemIndex === keyArrowPos ? 'bg-surface-hover' : '',
143
+ item.value === value ? 'bg-surface-selected' : 'hover:bg-surface-hover',
144
+ itemButtonWrapperClasses
145
+ )}
146
+ onclick={(e) => {
147
+ e.stopImmediatePropagation()
148
+ onSelectValue(item)
149
+ }}
150
+ >
151
+ {@render startSnippet?.({ item, close: () => (open = false) })}
152
+ <span class={itemLabelWrapperClasses}>
153
+ {item.label || '\xa0'}
154
+ </span>
155
+ {@render endSnippet?.({ item, close: () => (open = false) })}
156
+ {#if item.subtitle}
157
+ <div class="text-xs text-tertiary">{item.subtitle}</div>
158
+ {/if}
159
+ </button>
160
+ </li>
161
+ {/each}
162
+ </ul>
163
+ {@render bottomSnippet?.({ close: () => (open = false) })}
164
+ </div>
165
+ </div>
114
166
  </div>
115
167
  {/if}
116
168
  </ConditionalPortal>
@@ -12,11 +12,21 @@ declare function $$render<T>(): {
12
12
  noItemsMsg?: string;
13
13
  class?: string;
14
14
  ulClass?: string;
15
+ itemLabelWrapperClasses?: string;
16
+ itemButtonWrapperClasses?: string;
15
17
  header?: Snippet;
16
18
  getInputRect?: () => DOMRect;
17
19
  onSelectValue: (item: ProcessedItem<T>) => void;
18
20
  startSnippet?: Snippet<[{
19
21
  item: ProcessedItem<T>;
22
+ close: () => void;
23
+ }]>;
24
+ endSnippet?: Snippet<[{
25
+ item: ProcessedItem<T>;
26
+ close: () => void;
27
+ }]>;
28
+ bottomSnippet?: Snippet<[{
29
+ close: () => void;
20
30
  }]>;
21
31
  };
22
32
  exports: {};
@@ -39,6 +39,8 @@ export function getLabel(item) {
39
39
  export function safeSelectItems(list) {
40
40
  if (!list)
41
41
  return [];
42
+ if (!Array.isArray(list))
43
+ return [];
42
44
  return list
43
45
  .filter((item) => item !== undefined && item !== null)
44
46
  .map((item) => {
@@ -11,7 +11,11 @@ import TokenDisplay from './TokenDisplay.svelte';
11
11
  import ScopeSelector from './ScopeSelector.svelte';
12
12
  import Alert from '../common/alert/Alert.svelte';
13
13
  import FolderPicker from '../FolderPicker.svelte';
14
+ import TextInput from '../text_input/TextInput.svelte';
15
+ import Select from '../select/Select.svelte';
14
16
  let { showMcpMode = false, defaultNewTokenWorkspace, scopes, onTokenCreated, newTokenLabel = $bindable(undefined) } = $props();
17
+ // MCP clients do not allow names longer than 60 characters, here we use 55 because final tool name server side will add ~5 characters
18
+ const MAX_PATH_LENGTH = 55;
15
19
  let newToken = $state(undefined);
16
20
  let newMcpToken = $state(undefined);
17
21
  let newTokenExpiration = $state(undefined);
@@ -83,8 +87,15 @@ async function createToken(mcpMode = false) {
83
87
  }
84
88
  const workspaces = $derived(ensureCurrentWorkspaceIncluded($userWorkspaces, $workspaceStore));
85
89
  const mcpBaseUrl = $derived(`${window.location.origin}/api/mcp/w/${newTokenWorkspace}/sse?token=`);
86
- const warning = $derived(newMcpScope === 'favorites' ? `You do not have any favorite scripts or flows. You can favorite some scripts and flows to include them, or change the scope to "All scripts/flows" to include all your scripts and flows.` : 'Create your first scripts or flows to make them available via MCP.');
90
+ const warning = $derived(newMcpScope === 'favorites'
91
+ ? `You do not have any favorite scripts or flows. You can favorite some scripts and flows to include them, or change the scope to "All scripts/flows" to include all your scripts and flows.`
92
+ : 'Create your first scripts or flows to make them available via MCP.');
87
93
  const noScriptsOrFlowsAvailableWarning = $derived(includedRunnables.length === 0 ? warning : '');
94
+ const longPathRunnables = $derived(includedRunnables.filter((path) => path.length > MAX_PATH_LENGTH));
95
+ const validRunnables = $derived(includedRunnables.filter((path) => path.length <= MAX_PATH_LENGTH));
96
+ const longPathWarning = $derived(longPathRunnables.length > 0
97
+ ? `${longPathRunnables.length} script(s)/flow(s) have paths longer than 60 characters and will be excluded from MCP tools. Consider shortening the paths: ${longPathRunnables.slice(0, 3).join(', ')}${longPathRunnables.length > 3 ? ` and ${longPathRunnables.length - 3} more` : ''}`
98
+ : '');
88
99
  $effect(() => {
89
100
  if (mcpCreationMode) {
90
101
  getAllApps();
@@ -144,7 +155,10 @@ async function getScriptsAndFlows(favoriteOnly = false, workspace, folder) {
144
155
  }
145
156
  try {
146
157
  loadingRunnables = true;
147
- const [scripts, flows] = await Promise.all([getScripts(favoriteOnly, workspace, folder), getFlows(favoriteOnly, workspace, folder)]);
158
+ const [scripts, flows] = await Promise.all([
159
+ getScripts(favoriteOnly, workspace, folder),
160
+ getFlows(favoriteOnly, workspace, folder)
161
+ ]);
148
162
  const combined = [...scripts, ...flows];
149
163
  runnablesCache.set(cacheKey, combined);
150
164
  includedRunnables = combined;
@@ -301,7 +315,11 @@ $effect(() => {
301
315
 
302
316
  <div>
303
317
  <span class="block mb-1">Label <span class="text-xs text-tertiary">(optional)</span></span>
304
- <input type="text" bind:value={newTokenLabel} class="w-full" />
318
+ <TextInput
319
+ inputProps={{ type: 'text' }}
320
+ bind:value={newTokenLabel}
321
+ class="w-full !bg-surface"
322
+ />
305
323
  </div>
306
324
 
307
325
  {#if !mcpCreationMode}
@@ -309,58 +327,66 @@ $effect(() => {
309
327
  <span class="block mb-1"
310
328
  >Expires In <span class="text-xs text-tertiary">(optional)</span></span
311
329
  >
312
- <select bind:value={newTokenExpiration} class="w-full">
313
- <option value={undefined}>No expiration</option>
314
- <option value={15 * 60}>15m</option>
315
- <option value={30 * 60}>30m</option>
316
- <option value={1 * 60 * 60}>1h</option>
317
- <option value={1 * 24 * 60 * 60}>1d</option>
318
- <option value={7 * 24 * 60 * 60}>7d</option>
319
- <option value={30 * 24 * 60 * 60}>30d</option>
320
- <option value={90 * 24 * 60 * 60}>90d</option>
321
- </select>
330
+ <Select
331
+ bind:value={newTokenExpiration}
332
+ placeholder="No expiration"
333
+ inputClass="!bg-surface"
334
+ items={[
335
+ { label: 'No expiration', value: undefined },
336
+ { label: '15 minutes', value: 15 * 60 },
337
+ { label: '30 minutes', value: 30 * 60 },
338
+ { label: '1 hour', value: 1 * 60 * 60 },
339
+ { label: '1 day', value: 1 * 24 * 60 * 60 },
340
+ { label: '7 days', value: 7 * 24 * 60 * 60 },
341
+ { label: '30 days', value: 30 * 24 * 60 * 60 },
342
+ { label: '90 days', value: 90 * 24 * 60 * 60 }
343
+ ]}
344
+ />
322
345
  </div>
323
346
  {/if}
324
347
  {#if mcpCreationMode && (newMcpScope !== 'folder' || selectedFolder.length > 0)}
325
- {#if loadingRunnables}
326
- <div class="flex flex-col gap-2 col-span-2 pr-4">
327
- <span class="block text-xs text-tertiary">Scripts & Flows that will be available via MCP</span>
328
- <div class="flex flex-wrap gap-1">
329
- <Badge rounded small color="dark-gray" baseClass="animate-skeleton">Loading...</Badge>
330
- </div>
331
- </div>
332
- {:else}
333
- <div class="flex flex-col gap-2 col-span-2 pr-4">
334
- {#if noScriptsOrFlowsAvailableWarning}
335
- <Alert type="info" title="No scripts or flows available" size="xs">
336
- {noScriptsOrFlowsAvailableWarning}
337
- </Alert>
338
- {:else}
339
- <span class="block text-xs text-tertiary">Scripts & Flows that will be available via MCP</span>
348
+ {#if loadingRunnables}
349
+ <div class="flex flex-col gap-2 col-span-2 pr-4">
350
+ <span class="block text-xs text-tertiary"
351
+ >Scripts & Flows that will be available via MCP</span
352
+ >
340
353
  <div class="flex flex-wrap gap-1">
341
-
342
- {#if includedRunnables.length <= 5}
343
- {#each includedRunnables as scriptOrFlow}
344
- <Badge rounded small color="blue">{scriptOrFlow}</Badge>
345
- {/each}
354
+ <Badge rounded small color="dark-gray" baseClass="animate-skeleton">Loading...</Badge>
355
+ </div>
356
+ </div>
357
+ {:else}
358
+ <div class="flex flex-col gap-2 col-span-2 pr-4">
359
+ {#if noScriptsOrFlowsAvailableWarning}
360
+ <Alert type="info" title="No scripts or flows available" size="xs">
361
+ {noScriptsOrFlowsAvailableWarning}
362
+ </Alert>
346
363
  {:else}
347
- {#each includedRunnables.slice(0, 3) as scriptOrFlow}
348
- <Badge rounded small color="blue">{scriptOrFlow}</Badge>
349
- {/each}
350
- <Badge
351
- rounded
352
- small
353
- color="dark-gray"
354
- >
355
- +{includedRunnables.length - 3} more
356
- </Badge>
364
+ {#if longPathWarning}
365
+ <Alert type="warning" title="Some paths are too long" size="xs">
366
+ {longPathWarning}
367
+ </Alert>
357
368
  {/if}
358
- </div>
359
- {/if}
360
- </div>
361
- {/if}
369
+ <span class="block text-xs text-tertiary"
370
+ >Scripts & Flows that will be available via MCP</span
371
+ >
372
+ <div class="flex flex-wrap gap-1">
373
+ {#if validRunnables.length <= 5}
374
+ {#each validRunnables as scriptOrFlow}
375
+ <Badge rounded small color="blue">{scriptOrFlow}</Badge>
376
+ {/each}
377
+ {:else}
378
+ {#each validRunnables.slice(0, 3) as scriptOrFlow}
379
+ <Badge rounded small color="blue">{scriptOrFlow}</Badge>
380
+ {/each}
381
+ <Badge rounded small color="dark-gray">
382
+ +{validRunnables.length - 3} more
383
+ </Badge>
384
+ {/if}
385
+ </div>
386
+ {/if}
387
+ </div>
388
+ {/if}
362
389
  {/if}
363
-
364
390
  </div>
365
391
 
366
392
  <div class="mt-4 flex justify-end gap-2 flex-row">
@@ -373,7 +399,8 @@ $effect(() => {
373
399
  </Button>
374
400
  <Button
375
401
  on:click={() => createToken(mcpCreationMode)}
376
- disabled={mcpCreationMode && (newTokenWorkspace == undefined || (newMcpScope === 'folder' && !selectedFolder))}
402
+ disabled={mcpCreationMode &&
403
+ (newTokenWorkspace == undefined || (newMcpScope === 'folder' && !selectedFolder))}
377
404
  >
378
405
  New token
379
406
  </Button>
@@ -39,8 +39,8 @@ let selectedNewInstanceGroup = $state(undefined);
39
39
  let selectedNewRole = $state('developer');
40
40
  // Available groups for dropdowns - filter out already configured groups
41
41
  let availableGroupItems = $derived(instanceGroups
42
- .filter(group => !autoAddInstanceGroups.includes(group.name))
43
- .map(group => ({
42
+ .filter((group) => !autoAddInstanceGroups.includes(group.name))
43
+ .map((group) => ({
44
44
  value: group.name,
45
45
  label: group.name + (group.summary ? ` - ${group.summary}` : '')
46
46
  })));
@@ -63,7 +63,7 @@ function canConvertToGroup(user) {
63
63
  // Check if user's email is in any configured instance group
64
64
  const userEmail = user.email;
65
65
  for (const groupName of autoAddInstanceGroups) {
66
- const group = instanceGroups.find(g => g.name === groupName);
66
+ const group = instanceGroups.find((g) => g.name === groupName);
67
67
  if (group && group.emails && group.emails.includes(userEmail)) {
68
68
  return true;
69
69
  }
@@ -147,7 +147,7 @@ async function addInstanceGroup() {
147
147
  }
148
148
  catch (e) {
149
149
  // Rollback on error
150
- autoAddInstanceGroups = autoAddInstanceGroups.filter(g => g !== groupToAdd);
150
+ autoAddInstanceGroups = autoAddInstanceGroups.filter((g) => g !== groupToAdd);
151
151
  delete autoAddInstanceGroupsRoles[groupToAdd];
152
152
  sendUserToast('Failed to add instance group', true);
153
153
  }
@@ -156,7 +156,7 @@ async function removeInstanceGroup(groupName) {
156
156
  const previousGroups = [...autoAddInstanceGroups];
157
157
  const previousRole = autoAddInstanceGroupsRoles[groupName];
158
158
  try {
159
- autoAddInstanceGroups = autoAddInstanceGroups.filter(g => g !== groupName);
159
+ autoAddInstanceGroups = autoAddInstanceGroups.filter((g) => g !== groupName);
160
160
  delete autoAddInstanceGroupsRoles[groupName];
161
161
  await saveInstanceGroupSettings();
162
162
  }
@@ -324,8 +324,8 @@ $effect(() => {
324
324
  }}
325
325
  >
326
326
  {#snippet children({ item })}
327
- <ToggleButton value="invite" size="xs" label="Auto-invite" {item} />
328
- <ToggleButton value="add" size="xs" label="Auto-add" {item} />
327
+ <ToggleButton value="invite" small label="Auto-invite" {item} />
328
+ <ToggleButton value="add" small label="Auto-add" {item} />
329
329
  {/snippet}
330
330
  </ToggleButtonGroup>
331
331
  {/if}
@@ -425,9 +425,7 @@ $effect(() => {
425
425
  {#snippet content()}
426
426
  <div class="flex flex-col p-4 min-w-[500px]">
427
427
  <div class="flex flex-col gap-4">
428
- <span class="text-sm leading-6 font-semibold">
429
- Auto-add instance groups
430
- </span>
428
+ <span class="text-sm leading-6 font-semibold"> Auto-add instance groups </span>
431
429
 
432
430
  <!-- Add new instance group form -->
433
431
  {#if availableGroupItems.length > 0}
@@ -503,7 +501,7 @@ $effect(() => {
503
501
  </thead>
504
502
  <tbody>
505
503
  {#each autoAddInstanceGroups as groupName (groupName)}
506
- {@const group = instanceGroups.find(g => g.name === groupName)}
504
+ {@const group = instanceGroups.find((g) => g.name === groupName)}
507
505
  <tr class="border-t border-gray-200 dark:border-gray-700">
508
506
  <td class="py-2">
509
507
  <div class="font-medium">{groupName}</div>
@@ -603,7 +601,8 @@ $effect(() => {
603
601
  <Cell head>
604
602
  Added via
605
603
  <Tooltip>
606
- Shows how the user was added to the workspace: manually, via domain auto-invite, or through an instance group.
604
+ Shows how the user was added to the workspace: manually, via domain auto-invite, or
605
+ through an instance group.
607
606
  </Tooltip>
608
607
  </Cell>
609
608
  {/if}
@@ -630,9 +629,7 @@ $effect(() => {
630
629
  {#if hasNonManualUsers && index > 0 && sortedUsers()[index - 1]?.added_via?.source !== 'instance_group' && added_via?.source === 'instance_group'}
631
630
  <tr class="bg-surface-secondary">
632
631
  <td colspan={hasNonManualUsers ? 8 : 7} class="px-4 py-2">
633
- <div class="text-xs text-tertiary font-bold">
634
- Instance group users
635
- </div>
632
+ <div class="text-xs text-tertiary font-bold"> Instance group users </div>
636
633
  </td>
637
634
  </tr>
638
635
  {/if}
@@ -763,7 +760,10 @@ $effect(() => {
763
760
  >
764
761
  Remove
765
762
  </Button>
766
- <Tooltip>Cannot remove users synced from instance groups. Either disable the user or remove them from the SCIM group.</Tooltip>
763
+ <Tooltip
764
+ >Cannot remove users synced from instance groups. Either disable the user or
765
+ remove them from the SCIM group.</Tooltip
766
+ >
767
767
  </div>
768
768
  {:else if canConvertToGroup(user)}
769
769
  <Button
@@ -984,7 +984,10 @@ $effect(() => {
984
984
  }}
985
985
  >
986
986
  <div class="flex flex-col w-full space-y-4">
987
- <span>Are you sure you want to remove this instance group from auto-add? This will not remove users already added from this group.</span>
987
+ <span
988
+ >Are you sure you want to remove this instance group from auto-add? This will not remove
989
+ users already added from this group.</span
990
+ >
988
991
  </div>
989
992
  </ConfirmationModal>
990
993
  </div>
@@ -7,6 +7,7 @@ import { AlertCircle, CheckCircle2 } from 'lucide-svelte';
7
7
  import { devopsRole } from '../../stores';
8
8
  import List from '../common/layout/List.svelte';
9
9
  import { Skeleton } from '../common';
10
+ import Linkify from './Linkify.svelte';
10
11
  export let alerts;
11
12
  export let hideAcknowledged = false;
12
13
  export let goToNextPage;
@@ -107,7 +108,7 @@ $: availableHeight = (contentHeight - headerHeight - pageSize - 1) / pageSize;
107
108
  </Cell>
108
109
 
109
110
  <Cell wrap>
110
- <div class="flex-shrink min-w-0 break-words">{message}</div>
111
+ <div class="flex-shrink min-w-0 break-words"><Linkify text={message} /></div>
111
112
  </Cell>
112
113
  <!-- Flexible width -->
113
114
  <Cell wrap>{formatDate(created_at)}</Cell>
@@ -0,0 +1,14 @@
1
+ <script lang="ts">"use strict";
2
+ let { text } = $props();
3
+ // Function to convert text with URLs to HTML
4
+ function linkifyText(text) {
5
+ // Regex to match URLs starting with http/https and ending with )
6
+ const urlRegex = /\(((https?:\/\/[^\s)]+))\)/g;
7
+ // Replace matched patterns with anchor tags
8
+ return text.replace(urlRegex, '(<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>)');
9
+ }
10
+ let processedText = $derived(linkifyText(text));
11
+ </script>
12
+
13
+ <!-- Use @html to render the HTML -->
14
+ {@html processedText}
@@ -0,0 +1,5 @@
1
+ declare const Linkify: import("svelte").Component<{
2
+ text: any;
3
+ }, {}, "">;
4
+ type Linkify = ReturnType<typeof Linkify>;
5
+ export default Linkify;
@@ -120,12 +120,17 @@ const groupedWorkspaces = $derived(() => {
120
120
  <MenuItem
121
121
  class={twMerge(
122
122
  'text-xs min-w-0 w-full overflow-hidden flex flex-col py-1.5',
123
+ workspace.disabled && 'opacity-50 cursor-not-allowed',
123
124
  $workspaceStore === workspace.id
124
125
  ? 'cursor-default bg-surface-selected'
125
- : 'cursor-pointer hover:bg-surface-hover data-[highlighted]:bg-surface-hover'
126
+ : workspace.disabled
127
+ ? ''
128
+ : 'cursor-pointer hover:bg-surface-hover data-[highlighted]:bg-surface-hover'
126
129
  )}
127
130
  onClick={async () => {
128
- await toggleSwitchWorkspace(workspace.id)
131
+ if (!workspace.disabled) {
132
+ await toggleSwitchWorkspace(workspace.id)
133
+ }
129
134
  }}
130
135
  {item}
131
136
  >
@@ -146,7 +151,7 @@ const groupedWorkspaces = $derived(() => {
146
151
  isForked ? 'text-secondary' : 'text-primary'
147
152
  )}
148
153
  >
149
- {workspace.name}
154
+ {workspace.name}{workspace.disabled ? ' (user disabled)' : ''}
150
155
  </div>
151
156
  <div
152
157
  class={twMerge(
@@ -38,6 +38,6 @@ export function convertJsonToCsv(arr) {
38
38
  return csv;
39
39
  }
40
40
  catch (err) {
41
- throw new Error('An error occured when generating CSV:' + err);
41
+ throw new Error('An error occurred when generating CSV:' + err);
42
42
  }
43
43
  }
@@ -0,0 +1,30 @@
1
+ <script module lang="ts">export function inputBorderClass({ error, forceFocus } = {}) {
2
+ return twMerge('transition-colors border', forceFocus
3
+ ? '!border-nord-900 dark:!border-nord-900'
4
+ : '!border-nord-400 dark:!border-nord-300 hover:!border-nord-900/50 hover:dark:!border-nord-900/50 focus:!border-nord-900 dark:focus:!border-nord-900', error
5
+ ? '!border-red-300 focus:!border-red-400 hover:!border-red-500 dark:!border-red-400/40 dark:hover:!border-red-600/40'
6
+ : '');
7
+ }
8
+ export const inputBaseClass = 'rounded-md focus:ring-0 no-default-style text-sm text-tertiary dark:text-secondary !bg-surface-secondary disabled:!bg-surface-disabled disabled:!border-none disabled:!text-hint disabled:cursor-not-allowed shadow-none py-2 px-3 placeholder-hint';
9
+ </script>
10
+
11
+ <script lang="ts">import { twMerge } from 'tailwind-merge';
12
+ export function focus() {
13
+ inputEl?.focus();
14
+ }
15
+ let inputEl = $state();
16
+ let { inputProps, value = $bindable(), class: className = '', error } = $props();
17
+ </script>
18
+
19
+ <input
20
+ {...inputProps}
21
+ class={twMerge(
22
+ inputBaseClass,
23
+ 'w-full',
24
+ '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
25
+ inputBorderClass({ error: !!error }),
26
+ className
27
+ )}
28
+ bind:this={inputEl}
29
+ bind:value
30
+ />