dialog.svelte 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <script lang="ts">
  2. import { Toolbox } from '@lucide/svelte';
  3. import { untrack } from 'svelte';
  4. import * as toolsApi from '$lib/api/tools';
  5. import { Empty } from '$lib/components/common/empty';
  6. import { SelectDialog } from '$lib/components/common/select-dialog';
  7. import { Spinner } from '$lib/components/controls/spinner';
  8. import { ToolCard } from '$lib/components/tools/card';
  9. import type { IDRef, Tool } from '$lib/types/entities';
  10. let {
  11. workspaceId,
  12. multiple = true,
  13. open = $bindable(false),
  14. value = $bindable([]),
  15. onselect,
  16. }: {
  17. workspaceId: string;
  18. multiple?: boolean;
  19. open?: boolean;
  20. value?: IDRef[];
  21. onselect?: (tools: IDRef[]) => IDRef[];
  22. } = $props();
  23. let query = $state('');
  24. let loading = $state(false);
  25. let tools = $state<Tool[]>([]);
  26. const loadTools = async () => {
  27. if (loading || tools.length > 0) {
  28. return;
  29. }
  30. loading = true;
  31. try {
  32. tools = await toolsApi.list(workspaceId);
  33. } catch (error) {
  34. console.error('Failed to load tools', error);
  35. }
  36. loading = false;
  37. };
  38. $effect(() => {
  39. if (open) {
  40. untrack(() => {
  41. loadTools();
  42. });
  43. }
  44. });
  45. const select = (tool: Tool) => {
  46. const newValue = {
  47. type: tool.mode,
  48. id: tool.id,
  49. name: tool.name,
  50. icon: tool.icon,
  51. };
  52. if (multiple) {
  53. if (value.find((ref) => ref.id === tool.id)) {
  54. value = value.filter((t) => t.id !== tool.id);
  55. } else {
  56. value = [...value, newValue];
  57. }
  58. } else {
  59. value = [newValue];
  60. }
  61. if (onselect) {
  62. value = onselect(value);
  63. }
  64. };
  65. </script>
  66. <SelectDialog
  67. selected={value.map((ref) => ref.id)}
  68. title={multiple ? 'Select Tools' : 'Select Tool'}
  69. bind:open
  70. bind:query
  71. onsave={multiple ? () => (open = false) : undefined}
  72. >
  73. {#snippet content()}
  74. {#if loading}
  75. <div class="flex h-full w-full items-center justify-center gap-2">
  76. <Spinner />
  77. <div class="text-sm text-white/50">Loading Tools...</div>
  78. </div>
  79. {:else if tools.length === 0}
  80. <Empty icon={Toolbox}>
  81. {#snippet caption()}
  82. <div>No tools found</div>
  83. {/snippet}
  84. </Empty>
  85. {:else}
  86. <div class="grid grid-cols-2 gap-2">
  87. {#each tools as tool (tool.id)}
  88. <ToolCard
  89. class={value.find((ref) => ref.id === tool.id)
  90. ? 'border-primary/50 bg-primary/25 hover:border-primary/75 hover:bg-primary/50'
  91. : ''}
  92. {tool}
  93. onselect={() => select(tool)}
  94. />
  95. {/each}
  96. </div>
  97. {/if}
  98. {/snippet}
  99. </SelectDialog>