windmill-components 1.532.0 → 1.537.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 (135) hide show
  1. package/package/components/ArgInput.svelte +25 -18
  2. package/package/components/Auth0Setting.svelte +8 -3
  3. package/package/components/Dev.svelte +5 -4
  4. package/package/components/DiffDrawer.svelte +2 -2
  5. package/package/components/DiffEditor.svelte +34 -37
  6. package/package/components/DiffEditor.svelte.d.ts +23 -39
  7. package/package/components/EditableSchemaForm.svelte +42 -51
  8. package/package/components/EditableSchemaForm.svelte.d.ts +2 -3
  9. package/package/components/Editor.svelte +30 -9
  10. package/package/components/Editor.svelte.d.ts +5 -0
  11. package/package/components/FlowBuilder.svelte +7 -4
  12. package/package/components/FlowPreviewContent.svelte +3 -3
  13. package/package/components/FlowStatusViewer.svelte +28 -0
  14. package/package/components/FlowStatusViewerInner.svelte +72 -20
  15. package/package/components/FlowStatusViewerInner.svelte.d.ts +7 -0
  16. package/package/components/ModulePreview.svelte +2 -1
  17. package/package/components/ModulePreview.svelte.d.ts +1 -0
  18. package/package/components/ModulePreviewForm.svelte +72 -65
  19. package/package/components/ModulePreviewResultViewer.svelte +13 -18
  20. package/package/components/ModuleTest.svelte +6 -5
  21. package/package/components/ModuleTest.svelte.d.ts +1 -0
  22. package/package/components/OktaSetting.svelte +8 -3
  23. package/package/components/Portal.svelte +11 -7
  24. package/package/components/Portal.svelte.d.ts +19 -39
  25. package/package/components/RunForm.svelte +2 -2
  26. package/package/components/RunForm.svelte.d.ts +1 -1
  27. package/package/components/RunFormAdvancedPopup.svelte +13 -1
  28. package/package/components/SchemaForm.svelte +1 -2
  29. package/package/components/ScriptBuilder.svelte +1 -1
  30. package/package/components/ScriptEditor.svelte +21 -7
  31. package/package/components/SimpleEditor.svelte +0 -1
  32. package/package/components/apps/components/layout/AppModal.svelte +2 -2
  33. package/package/components/apps/editor/component/ComponentNavigation.svelte +3 -2
  34. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +1 -1
  35. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte +0 -1
  36. package/package/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +3 -1
  37. package/package/components/apps/editor/settingsPanel/GridCondition.svelte +3 -1
  38. package/package/components/apps/editor/settingsPanel/GridNavbar.svelte +3 -1
  39. package/package/components/apps/editor/settingsPanel/GridTab.svelte +3 -1
  40. package/package/components/apps/editor/settingsPanel/OneOfInputSpecsEditor.svelte +55 -53
  41. package/package/components/apps/editor/settingsPanel/TableActions.svelte +3 -1
  42. package/package/components/common/button/model.d.ts +1 -1
  43. package/package/components/common/drawer/Disposable.svelte +51 -30
  44. package/package/components/common/drawer/Disposable.svelte.d.ts +12 -44
  45. package/package/components/common/drawer/Drawer.svelte +15 -11
  46. package/package/components/copilot/MetadataGen.svelte +14 -3
  47. package/package/components/copilot/chat/AIChatInput.svelte +0 -1
  48. package/package/components/copilot/chat/AIChatManager.svelte.js +3 -3
  49. package/package/components/copilot/chat/AvailableContextList.svelte +192 -66
  50. package/package/components/copilot/chat/AvailableContextList.svelte.d.ts +2 -2
  51. package/package/components/copilot/chat/ContextElementBadge.svelte +3 -3
  52. package/package/components/copilot/chat/ContextManager.svelte.js +36 -13
  53. package/package/components/copilot/chat/ContextTextarea.svelte +21 -48
  54. package/package/components/copilot/chat/ToolContentDisplay.svelte +10 -1
  55. package/package/components/copilot/chat/ToolExecutionDisplay.svelte +3 -3
  56. package/package/components/copilot/chat/context.d.ts +7 -2
  57. package/package/components/copilot/chat/flow/FlowAIChat.svelte +110 -8
  58. package/package/components/copilot/chat/flow/core.d.ts +11 -0
  59. package/package/components/copilot/chat/flow/core.js +121 -3
  60. package/package/components/copilot/chat/flow/uiIntents.d.ts +8 -0
  61. package/package/components/copilot/chat/flow/uiIntents.js +5 -0
  62. package/package/components/copilot/chat/flow/useUiIntent.d.ts +5 -0
  63. package/package/components/copilot/chat/flow/useUiIntent.js +12 -0
  64. package/package/components/copilot/chat/monaco-adapter.d.ts +22 -4
  65. package/package/components/copilot/chat/monaco-adapter.js +55 -16
  66. package/package/components/copilot/chat/script/core.js +3 -2
  67. package/package/components/copilot/chat/shared.d.ts +3 -2
  68. package/package/components/copilot/chat/shared.js +24 -12
  69. package/package/components/copilot/lib.js +12 -7
  70. package/package/components/copilot/shared.d.ts +1 -1
  71. package/package/components/copilot/shared.js +16 -10
  72. package/package/components/flows/FlowEditor.svelte +4 -2
  73. package/package/components/flows/FlowEditor.svelte.d.ts +1 -0
  74. package/package/components/flows/FlowModuleIcon.svelte +8 -8
  75. package/package/components/flows/common/FlowCardHeader.svelte +4 -1
  76. package/package/components/flows/content/FlowBranchesAllWrapper.svelte +6 -0
  77. package/package/components/flows/content/FlowBranchesOneWrapper.svelte +6 -0
  78. package/package/components/flows/content/FlowEditorPanel.svelte +2 -1
  79. package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -0
  80. package/package/components/flows/content/FlowInput.svelte +31 -34
  81. package/package/components/flows/content/FlowInput.svelte.d.ts +1 -0
  82. package/package/components/flows/content/FlowLoop.svelte +7 -0
  83. package/package/components/flows/content/FlowModuleComponent.svelte +37 -44
  84. package/package/components/flows/content/FlowModuleScript.svelte +1 -1
  85. package/package/components/flows/content/FlowModuleSuspend.svelte +16 -18
  86. package/package/components/flows/content/FlowWhileLoop.svelte +6 -0
  87. package/package/components/flows/content/ScriptEditorDrawer.svelte +9 -11
  88. package/package/components/flows/dfs.d.ts +1 -1
  89. package/package/components/flows/dfs.js +6 -6
  90. package/package/components/flows/flowInfers.js +7 -7
  91. package/package/components/flows/flowStateUtils.svelte.js +1 -2
  92. package/package/components/flows/map/FlowModuleSchemaItem.svelte +12 -26
  93. package/package/components/flows/map/MapItem.svelte +8 -4
  94. package/package/components/flows/map/VirtualItem.svelte +1 -1
  95. package/package/components/flows/pickers/TopLevelNode.svelte +1 -1
  96. package/package/components/flows/propPicker/InputPickerInner.svelte +5 -5
  97. package/package/components/flows/propPicker/OutputPickerInner.svelte +143 -118
  98. package/package/components/flows/propPicker/OutputPickerInner.svelte.d.ts +7 -16
  99. package/package/components/flows/{testSteps.svelte.d.ts → stepsInputArgs.svelte.d.ts} +2 -1
  100. package/package/components/flows/{testSteps.svelte.js → stepsInputArgs.svelte.js} +15 -3
  101. package/package/components/flows/types.d.ts +16 -3
  102. package/package/components/flows/utils.js +3 -0
  103. package/package/components/graph/FlowGraphV2.svelte +1 -1
  104. package/package/components/graph/renderers/nodes/AIToolNode.svelte +4 -4
  105. package/package/components/graph/renderers/nodes/NewAIToolNode.svelte +71 -54
  106. package/package/components/propertyPicker/ObjectViewer.svelte +11 -3
  107. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +1 -1
  108. package/package/components/schema/AddPropertyV2.svelte +2 -7
  109. package/package/components/schema/AddPropertyV2.svelte.d.ts +3 -20
  110. package/package/components/schema/EditableSchemaDrawer.svelte +109 -115
  111. package/package/components/schema/EditableSchemaDrawer.svelte.d.ts +2 -1
  112. package/package/components/schema/EditableSchemaSdkWrapper.svelte +16 -3
  113. package/package/components/schema/EditableSchemaSdkWrapper.svelte.d.ts +4 -1
  114. package/package/components/schema/EditableSchemaWrapper.svelte +3 -10
  115. package/package/components/schema/FlowPropertyEditor.svelte +9 -41
  116. package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
  117. package/package/components/schema/SchemaFormDND.svelte +11 -10
  118. package/package/components/schema/SchemaFormDND.svelte.d.ts +3 -2
  119. package/package/components/schema/editable_schema_wrapper.d.ts +0 -3
  120. package/package/components/settings/PremiumInfo.svelte +7 -2
  121. package/package/components/triggers/CaptureWrapper.svelte +2 -13
  122. package/package/components/triggers/CaptureWrapper.svelte.d.ts +1 -1
  123. package/package/components/triggers/TriggersWrapper.svelte +1 -0
  124. package/package/components/triggers/http/RouteEditorInner.svelte +1 -1
  125. package/package/components/triggers/nats/NatsTriggerEditorInner.svelte +23 -20
  126. package/package/components/triggers/nats/NatsTriggersConfigSection.svelte +15 -27
  127. package/package/components/triggers/nats/NatsTriggersConfigSection.svelte.d.ts +7 -5
  128. package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +16 -16
  129. package/package/hubPaths.json +3 -1
  130. package/package/script_helpers.d.ts +2 -2
  131. package/package/script_helpers.js +2 -0
  132. package/package/stores.d.ts +1 -0
  133. package/package/stores.js +8 -1
  134. package/package.json +2 -2
  135. package/package/components/ModulePreviewResultViewer.svelte.d.ts +0 -28
@@ -1,7 +1,7 @@
1
1
  export declare const BUTTON_COLORS: readonly ["blue", "red", "dark", "light", "green", "gray", "none"];
2
2
  export declare namespace ButtonType {
3
3
  type Size = 'xs3' | 'xs2' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
4
- type Color = string;
4
+ type Color = 'blue' | 'red' | 'dark' | 'light' | 'green' | 'gray' | 'none' | 'marine' | 'nord';
5
5
  type Variant = 'contained' | 'border' | 'divider';
6
6
  type Target = '_self' | '_blank';
7
7
  type Element = HTMLButtonElement | HTMLAnchorElement;
@@ -1,17 +1,11 @@
1
- <script lang="ts" context="module">export let openedDrawers = [];
1
+ <script lang="ts" module>export let openedDrawers = $state({ val: [] });
2
2
  </script>
3
3
 
4
- <script lang="ts">import { createDispatcherIfMounted } from '../../../createDispatcherIfMounted';
5
- import { zIndexes } from '../../../zIndexes';
6
- import { createEventDispatcher } from 'svelte';
7
- export let open = false;
8
- export let id = (Math.random() + 1).toString(36).substring(10);
9
- export let preventEscape = false;
10
- if (open) {
11
- openedDrawers.push(id);
12
- }
13
- export let initialOffset = 0;
14
- let offset = initialOffset;
4
+ <script lang="ts">import { zIndexes } from '../../../zIndexes';
5
+ import { untrack } from 'svelte';
6
+ let { open = $bindable(false), id = (Math.random() + 1).toString(36).substring(10), preventEscape = false, initialOffset = 0, children, onOpen, onClose } = $props();
7
+ let offset = $state(initialOffset);
8
+ let zIndex = $derived(zIndexes.disposables + offset);
15
9
  export function toggleDrawer() {
16
10
  if (!open) {
17
11
  openDrawer();
@@ -21,26 +15,28 @@ export function toggleDrawer() {
21
15
  }
22
16
  }
23
17
  export function openDrawer() {
24
- open = true;
25
- offset = openedDrawers.length - 1;
26
- if (openedDrawers.includes(id)) {
27
- return;
18
+ if (!open) {
19
+ open = true;
20
+ if (openedDrawers.val.includes(id)) {
21
+ return;
22
+ }
23
+ offset = openedDrawers.val.length - 1;
24
+ openedDrawers.val.push(id);
28
25
  }
29
- openedDrawers.push(id);
30
26
  }
31
27
  export function closeDrawer() {
32
- open = false;
33
- offset = initialOffset;
34
- // remove the last opened drawer
35
- openedDrawers = openedDrawers.filter((drawer) => drawer !== id);
28
+ if (open) {
29
+ open = false;
30
+ offset = initialOffset;
31
+ // remove the last opened drawer
32
+ openedDrawers.val = openedDrawers.val.filter((drawer) => drawer !== id);
33
+ }
36
34
  }
37
35
  export function isOpen() {
38
36
  return open;
39
37
  }
40
- const dispatch = createEventDispatcher();
41
- const dispatchIfMounted = createDispatcherIfMounted(dispatch);
42
38
  function handleClickAway(e) {
43
- const last = openedDrawers[openedDrawers.length - 1];
39
+ const last = openedDrawers.val[openedDrawers.val.length - 1];
44
40
  if (last === id) {
45
41
  e.stopPropagation();
46
42
  closeDrawer();
@@ -50,9 +46,10 @@ function onKeyDown(event) {
50
46
  if (open) {
51
47
  switch (event.key) {
52
48
  case 'Escape':
53
- if ((id == openedDrawers[openedDrawers.length - 1] || openedDrawers.length == 0) &&
49
+ if ((id == openedDrawers.val[openedDrawers.val.length - 1] ||
50
+ openedDrawers.val.length == 0) &&
54
51
  !preventEscape) {
55
- openedDrawers.pop();
52
+ openedDrawers.val.pop();
56
53
  event.preventDefault();
57
54
  event.stopPropagation();
58
55
  event.stopImmediatePropagation();
@@ -62,10 +59,34 @@ function onKeyDown(event) {
62
59
  }
63
60
  }
64
61
  }
65
- $: zIndex = zIndexes.disposables + offset;
66
- $: dispatchIfMounted(open ? 'open' : 'close');
62
+ if (open) {
63
+ openedDrawers.val.push(id);
64
+ }
65
+ let wasEverOpen = false;
66
+ let lastOpen = open;
67
+ $effect.pre(() => {
68
+ if (open === untrack(() => lastOpen)) {
69
+ return;
70
+ }
71
+ lastOpen = open;
72
+ if (open) {
73
+ // console.log('open', id, wasEverOpen)
74
+ wasEverOpen = true;
75
+ onOpen?.();
76
+ }
77
+ else if (untrack(() => wasEverOpen)) {
78
+ // console.log('close', id)
79
+ onClose?.();
80
+ }
81
+ });
67
82
  </script>
68
83
 
69
- <svelte:window on:keydown={onKeyDown} />
84
+ <svelte:window onkeydown={onKeyDown} />
70
85
 
71
- <slot {handleClickAway} {zIndex} {closeDrawer} {open} />
86
+ {@render children?.({
87
+ handleClickAway,
88
+ zIndex,
89
+ closeDrawer,
90
+ open,
91
+ isTop: openedDrawers.val[openedDrawers.val.length - 1] == id
92
+ })}
@@ -1,52 +1,20 @@
1
- export declare let openedDrawers: string[];
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
16
- default: any;
17
- } ? Props extends Record<string, never> ? any : {
18
- children?: any;
19
- } : {});
20
- declare const Disposable: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
1
+ export declare let openedDrawers: {
2
+ val: string[];
3
+ };
4
+ interface Props {
21
5
  open?: boolean;
22
- id?: string;
6
+ id?: any;
23
7
  preventEscape?: boolean;
24
8
  initialOffset?: number;
25
- toggleDrawer?: () => void;
26
- openDrawer?: () => void;
27
- closeDrawer?: () => void;
28
- isOpen?: () => boolean;
29
- }, {
30
- default: {
31
- handleClickAway: (e: any) => void;
32
- zIndex: number;
33
- closeDrawer: () => void;
34
- open: boolean;
35
- };
36
- }>, {
37
- [evt: string]: CustomEvent<any>;
38
- }, {
39
- default: {
40
- handleClickAway: (e: any) => void;
41
- zIndex: number;
42
- closeDrawer: () => void;
43
- open: boolean;
44
- };
45
- }, {
9
+ children?: import('svelte').Snippet<[any]>;
10
+ onOpen?: () => void;
11
+ onClose?: () => void;
12
+ }
13
+ declare const Disposable: import("svelte").Component<Props, {
46
14
  toggleDrawer: () => void;
47
15
  openDrawer: () => void;
48
16
  closeDrawer: () => void;
49
17
  isOpen: () => boolean;
50
- }, string>;
51
- type Disposable = InstanceType<typeof Disposable>;
18
+ }, "open">;
19
+ type Disposable = ReturnType<typeof Disposable>;
52
20
  export default Disposable;
@@ -1,9 +1,9 @@
1
- <script lang="ts">import { onMount, createEventDispatcher } from 'svelte';
1
+ <script lang="ts">import { onMount, createEventDispatcher, untrack } from 'svelte';
2
2
  import { BROWSER } from 'esm-env';
3
3
  import Disposable from './Disposable.svelte';
4
4
  import ConditionalPortal from './ConditionalPortal.svelte';
5
5
  import { chatState } from '../../copilot/chat/sharedChatState.svelte';
6
- let { open = $bindable(undefined), duration = 0.3, placement = 'right', size = '600px', alwaysOpen = false, shouldUsePortal = true, offset = 0, preventEscape = false, disableChatOffset = false, class: clazz = '', positionClass = undefined, children } = $props();
6
+ let { open = $bindable(undefined), duration = 0.3, placement = 'right', size = '600px', alwaysOpen = false, shouldUsePortal = true, offset = 0, preventEscape = false, disableChatOffset = false, class: clazz = '', positionClass = undefined, children: children_render } = $props();
7
7
  if (open === undefined) {
8
8
  open = false;
9
9
  }
@@ -16,10 +16,12 @@ export function openDrawer() {
16
16
  disposable?.openDrawer();
17
17
  }
18
18
  export function closeDrawer() {
19
+ if (open) {
20
+ setTimeout(() => {
21
+ dispatch('afterClose');
22
+ }, durationMs);
23
+ }
19
24
  disposable?.closeDrawer();
20
- setTimeout(() => {
21
- dispatch('afterClose');
22
- }, durationMs);
23
25
  }
24
26
  export function isOpen() {
25
27
  return open;
@@ -39,7 +41,10 @@ $effect(() => {
39
41
  scrollLock(open ?? false);
40
42
  });
41
43
  $effect(() => {
42
- open ? openDrawer() : closeDrawer();
44
+ open;
45
+ untrack(() => {
46
+ open ? openDrawer() : closeDrawer();
47
+ });
43
48
  });
44
49
  let timeout = $state(true);
45
50
  $effect(() => {
@@ -48,7 +53,6 @@ $effect(() => {
48
53
  onMount(() => {
49
54
  mounted = true;
50
55
  });
51
- const children_render = $derived(children);
52
56
  const aiChatOpen = $derived(chatState.size > 0);
53
57
  </script>
54
58
 
@@ -57,11 +61,11 @@ const aiChatOpen = $derived(chatState.size > 0);
57
61
  initialOffset={offset}
58
62
  bind:open
59
63
  bind:this={disposable}
60
- on:open
61
- on:close
64
+ onOpen={() => dispatch('open')}
65
+ onClose={() => dispatch('close')}
62
66
  {preventEscape}
63
67
  >
64
- {#snippet children({ handleClickAway, zIndex })}
68
+ {#snippet children({ handleClickAway, zIndex, isTop })}
65
69
  <aside
66
70
  class="drawer windmill-app windmill-drawer {clazz ?? ''} {positionClass ?? ''} {aiChatOpen
67
71
  ? 'respect-global-chat'
@@ -76,7 +80,7 @@ const aiChatOpen = $derived(chatState.size > 0);
76
80
  <div class="overlay {positionClass ?? ''}" onclick={handleClickAway}></div>
77
81
  <div class="panel {placement} {positionClass}" class:size>
78
82
  {#if open || !timeout || alwaysOpen}
79
- {@render children_render?.({ open })}
83
+ {@render children_render?.({ open, isTop })}
80
84
  {/if}
81
85
  </div>
82
86
  </aside>
@@ -16,6 +16,7 @@ const promptConfigs = {
16
16
  You are a helpful AI assistant. You generate very brief summaries from scripts.
17
17
  The summaries need to be as short as possible (maximum 8 words) and only give a global idea. Do not specify the programming language. Do not use any punctation. Avoid using prepositions and articles.
18
18
  Examples: List the commits of a GitHub repository, Divide a number by 16, etc..
19
+ **Return only the summary, no other text.**
19
20
  `,
20
21
  user: `
21
22
  Generate a very short summary for the script below:
@@ -32,6 +33,7 @@ These descriptions are used to explain to other users what the script does and h
32
33
  Be as short as possible to give a global idea, maximum 3-4 sentences.
33
34
  All scripts export an asynchronous function called main, do not include it in the description.
34
35
  Do not describe how to call it either.
36
+ **Return only the description, no other text.**
35
37
  `,
36
38
  user: `
37
39
  Generate a description for the script below:
@@ -45,6 +47,7 @@ Generate a description for the script below:
45
47
  system: `
46
48
  You are a helpful AI assistant. You generate very brief summaries from scripts.
47
49
  The summaries need to be as short as possible (maximum 8 words) and only give a global idea. Do not use any punctation. Avoid using prepositions and articles.
50
+ **Return only the summary, no other text.**
48
51
  `,
49
52
  user: `
50
53
  Summarize the flow below in one very short sentence without punctation:
@@ -57,6 +60,7 @@ You are a helpful AI assistant. You generate descriptions from flow.
57
60
  These descriptions are used to explain to other users what the flow does and how to use it.
58
61
  Be as short as possible to give a global idea, maximum 3-4 sentences.
59
62
  Do not include line breaks.
63
+ **Return only the description, no other text.**
60
64
  `,
61
65
  user: `
62
66
  Generate a description for the flow below:
@@ -65,13 +69,15 @@ Generate a description for the flow below:
65
69
  },
66
70
  agentToolFunctionName: {
67
71
  system: `
68
- You are a helpful AI assistant. You generate function names from scripts.
69
- These function names will be used by an AI agent to call this tool.
72
+ You are a helpful AI assistant. You generate tool names from scripts.
73
+ These tool names will be used by an AI agent to call this tool.
74
+ It has to be based on the script code content not on the main function name.
70
75
  It has to respect the following regex: /[a-zA-Z0-9_]+/
71
76
  Examples: generate_image, classify_image, summarize_text, etc.
77
+ **Return only the tool name, no other text.**
72
78
  `,
73
79
  user: `
74
- Generate a function name for the script below:
80
+ Generate a tool name for the script below:
75
81
  {code}`,
76
82
  placeholderName: 'code'
77
83
  }
@@ -290,6 +296,11 @@ onDestroy(() => {
290
296
  on:focus={() => (focused = true)}
291
297
  on:blur={() => (focused = false)}
292
298
  />
299
+ {#if promptConfigName === 'agentToolFunctionName' && !validateToolName(content ?? '')}
300
+ <div class="text-3xs text-red-400 absolute -bottom-4">
301
+ Invalid tool name, should only contain letters, numbers and underscores
302
+ </div>
303
+ {/if}
293
304
  {/if}
294
305
  <!-- <slot {updateFocus} {active} {generatedContent} classNames={active ? '!indent-[8.8rem]' : ''} /> -->
295
306
  </div>
@@ -100,7 +100,6 @@ $effect(() => {
100
100
  addContextToSelection(element)
101
101
  close()
102
102
  }}
103
- categorize
104
103
  />
105
104
  </svelte:fragment>
106
105
  </Popover>
@@ -12,10 +12,9 @@ import { getCompletion, getModelContextWindow } from '../lib';
12
12
  import { dfs } from '../../flows/previousResults';
13
13
  import { getStringError } from './utils';
14
14
  import { untrack } from 'svelte';
15
- import { copilotSessionModel } from '../../../stores';
15
+ import { getCurrentModel } from '../../../stores';
16
16
  import { askTools, prepareAskSystemMessage, prepareAskUserMessage } from './ask/core';
17
17
  import { chatState, DEFAULT_SIZE, triggerablesByAi } from './sharedChatState.svelte';
18
- import { get } from 'svelte/store';
19
18
  import { prepareApiSystemMessage, prepareApiUserMessage } from './api/core';
20
19
  // If the estimated token usage is greater than the model context window - the threshold, we delete the oldest message
21
20
  const MAX_TOKENS_THRESHOLD_PERCENTAGE = 0.05;
@@ -83,7 +82,8 @@ class AIChatManager {
83
82
  }
84
83
  return acc;
85
84
  }, 0);
86
- const modelContextWindow = getModelContextWindow(get(copilotSessionModel)?.model ?? '');
85
+ const model = getCurrentModel();
86
+ const modelContextWindow = getModelContextWindow(model.model);
87
87
  return (estimatedTokens >
88
88
  modelContextWindow -
89
89
  Math.max(modelContextWindow * MAX_TOKENS_THRESHOLD_PERCENTAGE, MAX_TOKENS_HARD_LIMIT));
@@ -1,16 +1,18 @@
1
1
  <script lang="ts">import FlowModuleIcon from '../../flows/FlowModuleIcon.svelte';
2
2
  import BarsStaggered from '../../icons/BarsStaggered.svelte';
3
3
  import { ContextIconMap } from './context';
4
- import { ArrowLeft, Diff, Database, Code, ChevronRight } from 'lucide-svelte';
5
- const { availableContext, selectedContext, onSelect, showAllAvailable = false, stringSearch = '', selectedIndex = 0, categorize = false } = $props();
4
+ import { ArrowLeft, Diff, Database, ChevronRight } from 'lucide-svelte';
5
+ const { availableContext, selectedContext, onSelect, setShowing, showAllAvailable = false, stringSearch = '', onViewChange } = $props();
6
6
  // Current view state: 'categories' or specific category type
7
7
  let currentView = $state('categories');
8
+ // Selected index for keyboard navigation
9
+ let itemSelectedIndex = $state(0);
10
+ let categorySelectedIndex = $state(0);
8
11
  // Category definitions
9
12
  const categories = [
10
13
  { id: 'diffs', label: 'Diffs', icon: Diff },
11
14
  { id: 'modules', label: 'Modules', icon: BarsStaggered },
12
- { id: 'databases', label: 'Databases', icon: Database },
13
- { id: 'code', label: 'Code', icon: Code }
15
+ { id: 'databases', label: 'Databases', icon: Database }
14
16
  ];
15
17
  const filteredAvailableContext = $derived(availableContext.filter((context) => {
16
18
  const filtered = (showAllAvailable ||
@@ -23,8 +25,7 @@ const contextByCategory = $derived.by(() => {
23
25
  const grouped = {
24
26
  diffs: [],
25
27
  modules: [],
26
- databases: [],
27
- code: []
28
+ databases: []
28
29
  };
29
30
  filteredAvailableContext.forEach((context) => {
30
31
  if (context.type === 'diff')
@@ -33,82 +34,149 @@ const contextByCategory = $derived.by(() => {
33
34
  grouped.modules.push(context);
34
35
  else if (context.type === 'db')
35
36
  grouped.databases.push(context);
36
- else if (context.type === 'code')
37
- grouped.code.push(context);
38
37
  });
39
38
  return grouped;
40
39
  });
41
40
  const currentCategoryItems = $derived(currentView !== 'categories' ? contextByCategory[currentView] : []);
41
+ // Filter to only show categories with items
42
+ const availableCategories = $derived(categories.filter((cat) => contextByCategory[cat.id].length > 0));
43
+ // Report view changes
44
+ $effect(() => {
45
+ if (onViewChange) {
46
+ if (currentView === 'categories') {
47
+ onViewChange(availableCategories.length);
48
+ }
49
+ else {
50
+ onViewChange(currentCategoryItems.length + 1);
51
+ }
52
+ }
53
+ });
42
54
  function handleCategoryClick(categoryId) {
43
55
  currentView = categoryId;
44
56
  }
45
57
  function handleBackClick() {
46
58
  currentView = 'categories';
59
+ itemSelectedIndex = 0;
60
+ }
61
+ function handleKeyDown(e) {
62
+ if (stringSearch.length > 0) {
63
+ // Navigation in search view (flat list)
64
+ if (e.key === 'ArrowDown') {
65
+ e.preventDefault();
66
+ e.stopPropagation();
67
+ if (filteredAvailableContext.length > 0) {
68
+ itemSelectedIndex = (itemSelectedIndex + 1) % filteredAvailableContext.length;
69
+ }
70
+ }
71
+ else if (e.key === 'ArrowUp') {
72
+ e.preventDefault();
73
+ e.stopPropagation();
74
+ if (filteredAvailableContext.length > 0) {
75
+ itemSelectedIndex =
76
+ (itemSelectedIndex - 1 + filteredAvailableContext.length) %
77
+ filteredAvailableContext.length;
78
+ }
79
+ }
80
+ else if (e.key === 'Enter' || e.key === 'Tab') {
81
+ if (e.key === 'Tab')
82
+ e.preventDefault();
83
+ e.stopPropagation();
84
+ const selectedItem = filteredAvailableContext[itemSelectedIndex];
85
+ if (selectedItem) {
86
+ onSelect(selectedItem);
87
+ }
88
+ }
89
+ }
90
+ else if (currentView === 'categories') {
91
+ // Navigation in categories view
92
+ if (e.key === 'ArrowDown') {
93
+ e.preventDefault();
94
+ e.stopPropagation();
95
+ categorySelectedIndex = (categorySelectedIndex + 1) % availableCategories.length;
96
+ }
97
+ else if (e.key === 'ArrowUp') {
98
+ e.preventDefault();
99
+ e.stopPropagation();
100
+ categorySelectedIndex =
101
+ (categorySelectedIndex - 1 + availableCategories.length) % availableCategories.length;
102
+ }
103
+ else if (e.key === 'Enter' || e.key === 'ArrowRight' || e.key === 'Tab') {
104
+ e.preventDefault();
105
+ e.stopPropagation();
106
+ const selectedCategory = availableCategories[categorySelectedIndex];
107
+ if (selectedCategory) {
108
+ handleCategoryClick(selectedCategory.id);
109
+ }
110
+ }
111
+ else if (e.key === 'Escape' || e.key === 'ArrowLeft') {
112
+ e.preventDefault();
113
+ e.stopPropagation();
114
+ setShowing?.(false);
115
+ }
116
+ }
117
+ else {
118
+ // Navigation in category items view
119
+ if (e.key === 'ArrowDown') {
120
+ e.preventDefault();
121
+ e.stopPropagation();
122
+ if (currentCategoryItems.length > 0) {
123
+ itemSelectedIndex = (itemSelectedIndex + 1) % currentCategoryItems.length;
124
+ }
125
+ }
126
+ else if (e.key === 'ArrowUp') {
127
+ e.preventDefault();
128
+ e.stopPropagation();
129
+ if (currentCategoryItems.length > 0) {
130
+ itemSelectedIndex =
131
+ (itemSelectedIndex - 1 + currentCategoryItems.length) % currentCategoryItems.length;
132
+ }
133
+ }
134
+ else if (e.key === 'Enter' || e.key === 'Tab') {
135
+ if (e.key === 'Tab')
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ const selectedItem = currentCategoryItems[itemSelectedIndex];
139
+ if (selectedItem) {
140
+ onSelect(selectedItem);
141
+ currentView = 'categories'; // Go back to categories after selection
142
+ }
143
+ }
144
+ else if (e.key === 'ArrowLeft' || e.key === 'Escape') {
145
+ e.preventDefault();
146
+ e.stopPropagation();
147
+ handleBackClick();
148
+ }
149
+ }
47
150
  }
151
+ // Listen for keyboard events
152
+ $effect(() => {
153
+ document.addEventListener('keydown', handleKeyDown);
154
+ return () => {
155
+ document.removeEventListener('keydown', handleKeyDown);
156
+ };
157
+ });
158
+ $effect(() => {
159
+ if (stringSearch.length > 0) {
160
+ itemSelectedIndex = 0;
161
+ }
162
+ });
48
163
  </script>
49
164
 
50
- <div class="flex flex-col gap-1 text-tertiary text-xs p-1 pr-0 min-w-24 max-h-48 overflow-y-scroll">
51
- {#if categorize}
52
- {#if currentView === 'categories'}
53
- {#each categories as category}
54
- {@const itemCount = contextByCategory[category.id].length}
55
- {@const Icon = category.icon}
56
- {#if itemCount > 0}
57
- <button
58
- class="hover:bg-surface-hover rounded-md p-1 pr-0 text-left flex flex-row gap-1 items-center font-normal transition-colors"
59
- onclick={() => handleCategoryClick(category.id)}
60
- >
61
- <Icon size={16} />
62
- <span class="flex-1">{category.label}</span>
63
- <ChevronRight size={16} />
64
- </button>
65
- {/if}
66
- {/each}
67
- {#if categories.every((cat) => contextByCategory[cat.id].length === 0)}
68
- <div class="text-center text-tertiary text-xs py-2">No available context</div>
69
- {/if}
70
- {:else}
71
- <!-- Category items view -->
72
- <button
73
- class="hover:bg-surface-hover rounded-md text-left flex flex-row gap-1 items-center font-normal transition-colors mb-1"
74
- onclick={handleBackClick}
75
- >
76
- <ArrowLeft size={12} />
77
- <span class="text-xs">Go back</span>
78
- </button>
79
-
80
- {#if currentCategoryItems.length === 0}
81
- <div class="text-center text-tertiary text-xs py-2">No items in this category</div>
82
- {:else}
83
- {#each currentCategoryItems as element}
84
- {@const Icon = ContextIconMap[element.type]}
85
- <button
86
- class="hover:bg-surface-hover rounded-md p-1 text-left flex flex-row gap-1 items-center font-normal transition-colors"
87
- onclick={() => {
88
- onSelect(element)
89
- currentView = 'categories' // Go back to categories after selection
90
- }}
91
- >
92
- {#if element.type === 'flow_module'}
93
- <FlowModuleIcon module={element as FlowModule} size={16} />
94
- {:else if Icon}
95
- <Icon size={16} />
96
- {/if}
97
- <span class="truncate">
98
- {element.type === 'diff' || element.type === 'flow_module'
99
- ? element.title.replace(/_/g, ' ')
100
- : element.title}
101
- </span>
102
- </button>
103
- {/each}
104
- {/if}
105
- {/if}
106
- {:else}
165
+ <div
166
+ class="flex flex-col gap-1 text-tertiary text-xs p-1 pr-0 min-w-24 max-h-48 overflow-y-scroll"
167
+ onmousedown={(e) =>
168
+ // avoids triggering onblur on the textinput and closing the tooltip
169
+ e.preventDefault()}
170
+ role="listbox"
171
+ tabindex={0}
172
+ >
173
+ {#if stringSearch.length > 0}
174
+ <!-- Search view - show flat list -->
107
175
  {#each filteredAvailableContext as element, i}
108
176
  {@const Icon = ContextIconMap[element.type]}
109
177
  <button
110
178
  class="hover:bg-surface-hover rounded-md p-1 text-left flex flex-row gap-1 items-center font-normal transition-colors {i ===
111
- selectedIndex
179
+ itemSelectedIndex
112
180
  ? 'bg-surface-hover'
113
181
  : ''}"
114
182
  onclick={() => {
@@ -127,5 +195,63 @@ function handleBackClick() {
127
195
  </span>
128
196
  </button>
129
197
  {/each}
198
+ {#if filteredAvailableContext.length === 0}
199
+ <div class="text-center text-tertiary text-xs py-2">No matching context</div>
200
+ {/if}
201
+ {:else if currentView === 'categories'}
202
+ <!-- Categories view -->
203
+ {#each availableCategories as category, i}
204
+ {@const Icon = category.icon}
205
+ <button
206
+ class="hover:bg-surface-hover rounded-md p-1 pr-0 text-left flex flex-row gap-1 items-center font-normal transition-colors {i ===
207
+ categorySelectedIndex
208
+ ? 'bg-surface-hover'
209
+ : ''}"
210
+ onclick={() => handleCategoryClick(category.id)}
211
+ >
212
+ <Icon size={16} />
213
+ <span class="flex-1">{category.label}</span>
214
+ <ChevronRight size={16} />
215
+ </button>
216
+ {/each}
217
+ {#if availableCategories.length === 0}
218
+ <div class="text-center text-tertiary text-xs py-2">No available context</div>
219
+ {/if}
220
+ {:else}
221
+ <!-- Category items view -->
222
+ <button
223
+ class="hover:bg-surface-hover rounded-md text-left flex flex-row gap-1 items-center font-normal transition-colors mb-1"
224
+ onclick={handleBackClick}
225
+ >
226
+ <ArrowLeft size={12} />
227
+ <span class="text-xs">Go back</span>
228
+ </button>
229
+
230
+ {#if currentCategoryItems.length === 0}
231
+ <div class="text-center text-tertiary text-xs py-2">No items in this category</div>
232
+ {:else}
233
+ {#each currentCategoryItems as element, i}
234
+ {@const Icon = ContextIconMap[element.type]}
235
+ <button
236
+ class="hover:bg-surface-hover rounded-md p-1 text-left flex flex-row gap-1 items-center font-normal transition-colors {i ===
237
+ itemSelectedIndex
238
+ ? 'bg-surface-hover'
239
+ : ''}"
240
+ onclick={() => {
241
+ onSelect(element)
242
+ currentView = 'categories' // Go back to categories after selection
243
+ }}
244
+ >
245
+ {#if element.type === 'flow_module'}
246
+ <FlowModuleIcon module={element as FlowModule} size={16} />
247
+ {:else if Icon}
248
+ <Icon size={16} />
249
+ {/if}
250
+ <span class="truncate">
251
+ {element.type === 'diff' ? element.title.replace(/_/g, ' ') : element.title}
252
+ </span>
253
+ </button>
254
+ {/each}
255
+ {/if}
130
256
  {/if}
131
257
  </div>
@@ -3,10 +3,10 @@ interface Props {
3
3
  availableContext: ContextElement[];
4
4
  selectedContext: ContextElement[];
5
5
  onSelect: (element: ContextElement) => void;
6
+ setShowing?: (showing: boolean) => void;
6
7
  showAllAvailable?: boolean;
7
8
  stringSearch?: string;
8
- selectedIndex?: number;
9
- categorize?: boolean;
9
+ onViewChange?: (newNumber: number) => void;
10
10
  }
11
11
  declare const AvailableContextList: import("svelte").Component<Props, {}, "">;
12
12
  type AvailableContextList = ReturnType<typeof AvailableContextList>;