|
45 | 45 | import { zod4 } from 'sveltekit-superforms/adapters'; |
46 | 46 | import * as schemas from './schemas'; |
47 | 47 |
|
48 | | - import type { HTMLInputTypeAttribute } from 'svelte/elements'; |
49 | 48 | import type { paths, components, operations } from '$generated/api'; |
50 | 49 | import { onMount, tick } from 'svelte'; |
51 | 50 |
|
|
69 | 68 | import { fade } from 'svelte/transition'; |
70 | 69 | import CopyButton from '$lib/components/copy-button.svelte'; |
71 | 70 | import Pip from '$lib/components/pip.svelte'; |
| 71 | + import OptionField from './OptionField.svelte'; |
72 | 72 |
|
73 | 73 | type CreateSessionRequest = NonNullable< |
74 | 74 | operations['createSession']['requestBody'] |
|
79 | 79 | [P in keyof Required<T>]: Pick<T, P> extends Required<Pick<T, P>> ? T[P] : T[P] | undefined; |
80 | 80 | }; |
81 | 81 |
|
82 | | - const inputTypes: { |
83 | | - [K in PublicRegistryAgent['options'][string]['type']]: HTMLInputTypeAttribute; |
84 | | - } = { |
85 | | - string: 'text', |
86 | | - number: 'number', |
87 | | - secret: 'password', |
88 | | - blob: 'file', |
89 | | - 'list[blob]': 'file', |
90 | | - bool: 'number', |
91 | | - i8: 'number', |
92 | | - 'list[i8]': 'number', |
93 | | - f64: 'number', |
94 | | - 'list[f64]': 'number', |
95 | | - f32: 'number', |
96 | | - 'list[f32]': 'number', |
97 | | - i32: 'number', |
98 | | - 'list[i32]': 'number', |
99 | | - i64: 'text', |
100 | | - 'list[i64]': 'text', |
101 | | - i16: 'number', |
102 | | - 'list[i16]': 'number', |
103 | | - 'list[string]': 'string', |
104 | | - u8: 'number', |
105 | | - 'list[u8]': 'number', |
106 | | - u32: 'number', |
107 | | - 'list[u32]': 'number', |
108 | | - u64: 'text', |
109 | | - 'list[u64]': 'text', |
110 | | - u16: 'number', |
111 | | - 'list[u16]': 'number' |
112 | | - }; |
113 | | -
|
114 | 82 | let ctx = appContext.get(); |
115 | 83 |
|
116 | 84 | let error: string | null = $state(null); |
|
174 | 142 | server: ctx.server |
175 | 143 | }); |
176 | 144 | } else { |
177 | | - throw new Error('no data received'); |
178 | 145 | sendingForm = false; |
| 146 | + throw new Error('no data received'); |
179 | 147 | } |
180 | 148 | } catch (e) { |
181 | 149 | console.log(e); |
|
223 | 191 | } |
224 | 192 | } satisfies Provider; |
225 | 193 |
|
226 | | - let newServerAddress: string = $state(''); |
227 | | - let newServerPort: string = $state(''); |
228 | | -
|
229 | 194 | const importFromJson = (json: string) => { |
230 | 195 | try { |
231 | 196 | const data: CreateSessionRequest = JSON.parse(json); |
|
616 | 581 | } |
617 | 582 | </script> |
618 | 583 |
|
619 | | -{#snippet optionRow(name: any, opt: any)} |
620 | | - <li class="hover:bg-muted/50 border-b px-4 py-2"> |
621 | | - <Form.ElementField |
622 | | - class="flex gap-2" |
623 | | - {form} |
624 | | - name="agents[{selectedAgent!}].options.{name}.value" |
625 | | - > |
626 | | - <Form.Control> |
627 | | - {#snippet children({ props })} |
628 | | - <TooltipLabel |
629 | | - class="max-w-1/4 min-w-1/4 {opt.required ? 'hover:pr-[0.5em]' : ''}" |
630 | | - title={name} |
631 | | - tooltip={opt?.display?.description ?? 'No description provided.'} |
632 | | - extra={{ |
633 | | - required: opt.required, |
634 | | - type: opt.type |
635 | | - }} |
636 | | - > |
637 | | - {opt?.display?.label ?? name} |
638 | | - </TooltipLabel> |
639 | | - |
640 | | - {#if opt.type === 'blob'} |
641 | | - <Input |
642 | | - {...props} |
643 | | - class="m-0" |
644 | | - type={inputTypes[opt.type as keyof typeof inputTypes]} |
645 | | - bind:value={ |
646 | | - () => $formData.agents[selectedAgent!]!.options[name]?.value, |
647 | | - (value) => { |
648 | | - $formData.agents[selectedAgent!]!.options[name] = { |
649 | | - type: opt.type, |
650 | | - value |
651 | | - } as any; // FIXME: !! |
652 | | - } |
653 | | - } |
654 | | - aria-invalid={$allErrors.length > 0 && |
655 | | - opt.required && |
656 | | - $formData.agents[selectedAgent!]!.options[name]?.value === undefined} |
657 | | - placeholder={'default' in opt ? opt.default?.toString() : undefined} |
658 | | - /> |
659 | | - {:else if opt.type.includes('list')} |
660 | | - {@const list = Array.isArray($formData.agents[selectedAgent!]!.options[name]?.value) |
661 | | - ? ($formData.agents[selectedAgent!]!.options[name]!.value as any[]) |
662 | | - : []} |
663 | | - <ol class="flex w-full flex-col gap-1 rounded-md"> |
664 | | - <li> |
665 | | - <Button |
666 | | - onclick={() => { |
667 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
668 | | - if (optObj && Array.isArray(optObj.value)) { |
669 | | - (optObj.value as string[]).push(''); |
670 | | - } else { |
671 | | - $formData.agents[selectedAgent!]!.options[name] = { |
672 | | - type: opt.type, |
673 | | - value: [''] |
674 | | - } as any; |
675 | | - } |
676 | | - $formData.agents = $formData.agents; |
677 | | - }} |
678 | | - class="m-0 w-full">Add value</Button |
679 | | - > |
680 | | - </li> |
681 | | - {#each list, i} |
682 | | - <li> |
683 | | - <ButtonGroup.Root class="m-0 w-full"> |
684 | | - <Input |
685 | | - type={opt.secret |
686 | | - ? 'password' |
687 | | - : inputTypes[opt.type as keyof typeof inputTypes]} |
688 | | - bind:value={ |
689 | | - () => { |
690 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
691 | | - const arr = Array.isArray(optObj?.value) |
692 | | - ? (optObj.value as any[]) |
693 | | - : undefined; |
694 | | - return arr ? arr[i] : ''; |
695 | | - }, |
696 | | - (value) => { |
697 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
698 | | - if (!optObj || !Array.isArray(optObj.value)) { |
699 | | - // initialize as array and set the i'th element |
700 | | - $formData.agents[selectedAgent!]!.options[name] = { |
701 | | - type: opt.type, |
702 | | - value: [] |
703 | | - } as any; |
704 | | - } |
705 | | - ($formData.agents[selectedAgent!]!.options[name]!.value as any[])[i] = |
706 | | - value; |
707 | | - // trigger reactivity |
708 | | - $formData.agents = $formData.agents; |
709 | | - } |
710 | | - } |
711 | | - /> |
712 | | - <Button |
713 | | - variant="outline" |
714 | | - class="m-0" |
715 | | - size="icon" |
716 | | - onclick={() => { |
717 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
718 | | - if (optObj && Array.isArray(optObj.value)) { |
719 | | - (optObj.value as string[]).splice(i, 1); |
720 | | - // trigger reactivity |
721 | | - $formData.agents = $formData.agents; |
722 | | - } |
723 | | - }} |
724 | | - > |
725 | | - <IconXRegular /> |
726 | | - </Button> |
727 | | - </ButtonGroup.Root> |
728 | | - </li> |
729 | | - {/each} |
730 | | - </ol> |
731 | | - {:else if opt.type === 'bool'} |
732 | | - <ButtonGroup.Root class="m-0 justify-start"> |
733 | | - <Button |
734 | | - class={cn( |
735 | | - ($formData.agents[selectedAgent!]!.options[name]?.value ?? opt.default) === |
736 | | - true && 'bg-accent text-accent-foreground' |
737 | | - )} |
738 | | - onclick={() => { |
739 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
740 | | - if (optObj) { |
741 | | - optObj.value = true; |
742 | | - } else { |
743 | | - $formData.agents[selectedAgent!]!.options[name] = { |
744 | | - type: opt.type, |
745 | | - value: true |
746 | | - } as any; |
747 | | - } |
748 | | - $formData.agents = $formData.agents; |
749 | | - }}>True</Button |
750 | | - > |
751 | | - <Button |
752 | | - class=" {$formData.agents[selectedAgent!]!.options[name]?.value === false || |
753 | | - ($formData.agents[selectedAgent!]!.options[name]?.value === undefined && |
754 | | - opt.default === false) |
755 | | - ? 'bg-accent text-accent-foreground' |
756 | | - : ''}" |
757 | | - onclick={() => { |
758 | | - const optObj = $formData.agents[selectedAgent!]!.options[name]; |
759 | | - if (optObj) { |
760 | | - optObj.value = false; |
761 | | - } else { |
762 | | - $formData.agents[selectedAgent!]!.options[name] = { |
763 | | - type: opt.type, |
764 | | - value: false |
765 | | - } as any; |
766 | | - } |
767 | | - $formData.agents = $formData.agents; |
768 | | - }}>False</Button |
769 | | - > |
770 | | - </ButtonGroup.Root> |
771 | | - {:else if opt?.display?.multiline === true} |
772 | | - <Textarea |
773 | | - {...props} |
774 | | - class="relative m-0 h-42 resize-none" |
775 | | - bind:value={ |
776 | | - () => { |
777 | | - const v = $formData.agents[selectedAgent!]!.options[name]?.value; |
778 | | - return typeof v === 'string' || typeof v === 'number' ? String(v) : ''; |
779 | | - }, |
780 | | - (value) => { |
781 | | - $formData.agents[selectedAgent!]!.options[name] = { |
782 | | - type: opt.type, |
783 | | - value |
784 | | - } as any; |
785 | | - } |
786 | | - } |
787 | | - defaultValue={opt.default} |
788 | | - aria-invalid={(() => { |
789 | | - const error = $errors?.agents?.[selectedAgent!]?.options?.[name]; |
790 | | - if (error && JSON.stringify(error).includes('{}')) return undefined; |
791 | | - else if (error) return true; |
792 | | - else return undefined; |
793 | | - })()} |
794 | | - placeholder={'default' in opt ? opt.default?.toString() : undefined} |
795 | | - /> |
796 | | - {:else} |
797 | | - <Input |
798 | | - {...props} |
799 | | - type={opt.secret ? 'password' : inputTypes[opt.type as keyof typeof inputTypes]} |
800 | | - bind:value={ |
801 | | - () => $formData.agents[selectedAgent!]!.options[name]?.value, |
802 | | - (value) => { |
803 | | - $formData.agents[selectedAgent!]!.options[name] = { |
804 | | - type: opt.type, |
805 | | - value |
806 | | - } as any; // FIXME: !! |
807 | | - } |
808 | | - } |
809 | | - class="m-0 w-full " |
810 | | - defaultValue={opt.default} |
811 | | - aria-invalid={(() => { |
812 | | - const error = $errors?.agents?.[selectedAgent!]?.options?.[name]; |
813 | | - if (error && JSON.stringify(error).includes('{}')) return undefined; |
814 | | - else if (error) return true; |
815 | | - else return undefined; |
816 | | - })()} |
817 | | - placeholder={'default' in opt ? opt.default?.toString() : undefined} |
818 | | - /> |
819 | | - {/if} |
820 | | - {/snippet} |
821 | | - </Form.Control> |
822 | | - </Form.ElementField> |
823 | | - |
824 | | - {#if JSON.stringify($errors?.agents?.[selectedAgent!]?.options?.[name]) !== '{}' && JSON.stringify($errors?.agents?.[selectedAgent!]?.options?.[name])} |
825 | | - <span class="text-xs"> |
826 | | - {$errors?.agents?.[selectedAgent!]?.options?.[name]?.value ?? |
827 | | - $errors?.agents?.[selectedAgent!]?.options?.[name]} |
828 | | - </span> |
829 | | - {/if} |
830 | | - </li> |
831 | | -{/snippet} |
832 | | - |
833 | 584 | <header class="bg-background sticky top-0 flex h-16 shrink-0 items-center gap-2 border-b px-4"> |
834 | 585 | <Sidebar.Trigger class="-ml-1" /> |
835 | 586 | <Separator orientation="vertical" class="mr-2 h-4" /> |
|
1393 | 1144 | <Accordion.Content class="!p-0"> |
1394 | 1145 | <ol> |
1395 | 1146 | {#each entries as [name, opt] (name)} |
1396 | | - {@render optionRow(name, opt)} |
| 1147 | + <OptionField |
| 1148 | + superform={form} |
| 1149 | + agent={selectedAgent!} |
| 1150 | + {name} |
| 1151 | + meta={opt} |
| 1152 | + /> |
1397 | 1153 | {/each} |
1398 | 1154 | </ol> |
1399 | 1155 | </Accordion.Content> |
|
1402 | 1158 | {:else} |
1403 | 1159 | <ol> |
1404 | 1160 | {#each entries as [name, opt] (name)} |
1405 | | - {@render optionRow(name, opt)} |
| 1161 | + <OptionField |
| 1162 | + superform={form} |
| 1163 | + agent={selectedAgent!} |
| 1164 | + {name} |
| 1165 | + meta={opt} |
| 1166 | + /> |
1406 | 1167 | {/each} |
1407 | 1168 | </ol> |
1408 | 1169 | {/if} |
|
0 commit comments