mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-11 03:38:02 +00:00
refac
This commit is contained in:
parent
8d0ff76f2b
commit
44b71f87ab
4 changed files with 222 additions and 211 deletions
|
|
@ -9,6 +9,7 @@
|
|||
export let locale = '';
|
||||
export let localeLabel = '';
|
||||
export let onChange = () => {};
|
||||
export let disabled = false;
|
||||
|
||||
const clone = (value) => JSON.parse(JSON.stringify(value ?? []));
|
||||
|
||||
|
|
@ -47,32 +48,30 @@
|
|||
</script>
|
||||
|
||||
{#if !locale}
|
||||
<PromptSuggestions bind:promptSuggestions onChange={() => onChange()}>
|
||||
<PromptSuggestions bind:promptSuggestions {disabled} onChange={() => onChange()}>
|
||||
<span slot="label"><slot name="label" /></span>
|
||||
<svelte:fragment slot="actions"><slot name="language" /></svelte:fragment>
|
||||
</PromptSuggestions>
|
||||
{:else}
|
||||
<PromptSuggestions
|
||||
{disabled}
|
||||
promptSuggestions={localePrompts}
|
||||
inherited={!hasCustomLocalePrompts}
|
||||
onChange={updateLocalePrompts}
|
||||
>
|
||||
<span
|
||||
slot="label"
|
||||
title={localeLabel}
|
||||
class="flex flex-wrap items-center gap-x-3 gap-y-1 text-[0.6875rem] text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<svelte:fragment slot="label">
|
||||
{#if hasCustomLocalePrompts}
|
||||
<button
|
||||
title={localeLabel}
|
||||
type="button"
|
||||
class="text-gray-500 transition hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
aria-label={$i18n.t('Use default')}
|
||||
{disabled}
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
on:click={useDefault}
|
||||
>
|
||||
{$i18n.t('Use default')}
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="actions">
|
||||
<slot name="language" />
|
||||
</svelte:fragment>
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@
|
|||
let showSystemPrompt = true;
|
||||
let showAdvanced = false;
|
||||
let showPreview = false;
|
||||
let customPromptDraft: any[] | null = null;
|
||||
let showAccessControlModal = false;
|
||||
|
||||
let loaded = false;
|
||||
|
|
@ -584,6 +585,7 @@
|
|||
};
|
||||
|
||||
const loadModel = async (value: any) => {
|
||||
customPromptDraft = null;
|
||||
model = value ? structuredClone(value) : null;
|
||||
backgroundFile = null;
|
||||
avatarFile = null;
|
||||
|
|
@ -970,7 +972,7 @@
|
|||
<!-- LICENSE covers this Open WebUI fallback logo.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<div class="group relative size-11 shrink-0">
|
||||
<div class="group relative size-12 shrink-0">
|
||||
<button
|
||||
class="group relative flex size-full items-center overflow-hidden rounded-xl {info
|
||||
.meta.profile_image_url !== `${WEBUI_BASE_URL}/static/favicon.png`
|
||||
|
|
@ -1394,46 +1396,57 @@
|
|||
|
||||
<fieldset disabled={readOnly} class="min-w-0">
|
||||
<section class="my-3">
|
||||
<div class="flex w-full items-center justify-between">
|
||||
<ModelSettingsSection
|
||||
label={$i18n.t('Prompts')}
|
||||
summary={editingLocale
|
||||
? Array.isArray(info.meta.i18n?.[editingLocale]?.suggestion_prompts)
|
||||
? $i18n.t('Custom')
|
||||
: $i18n.t('Default')
|
||||
: info.meta.suggestion_prompts == null
|
||||
? $i18n.t('Default')
|
||||
: `${$i18n.t('Custom')} · ${info.meta.suggestion_prompts.length}`}
|
||||
>
|
||||
{#if editingLocale}
|
||||
<div class="self-center text-xs font-normal text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Prompts')}
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class="flex w-full items-center justify-between gap-3 rounded-sm text-start text-xs font-normal text-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
|
||||
type="button"
|
||||
aria-pressed={(info?.meta?.suggestion_prompts ?? null) !== null}
|
||||
on:click={() => {
|
||||
if ((info?.meta?.suggestion_prompts ?? null) === null) {
|
||||
info.meta.suggestion_prompts = [{ content: '', title: ['', ''] }];
|
||||
} else {
|
||||
info.meta.suggestion_prompts = null;
|
||||
}
|
||||
}}
|
||||
<LocalizedPromptSuggestions
|
||||
promptSuggestions={info.meta.suggestion_prompts ?? []}
|
||||
bind:localizedPromptSuggestions={info.meta.i18n}
|
||||
locale={editingLocale}
|
||||
localeLabel={editingLocaleLabel}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
{:else if info.meta.suggestion_prompts != null}
|
||||
<PromptSuggestions
|
||||
bind:promptSuggestions={info.meta.suggestion_prompts}
|
||||
disabled={readOnly}
|
||||
>
|
||||
<span class="font-normal text-gray-600 dark:text-gray-400"
|
||||
>{$i18n.t('Prompts')}</span
|
||||
<button
|
||||
slot="label"
|
||||
disabled={readOnly}
|
||||
type="button"
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
on:click={() => {
|
||||
customPromptDraft = structuredClone(info.meta.suggestion_prompts);
|
||||
info.meta.suggestion_prompts = null;
|
||||
}}>{$i18n.t('Use default')}</button
|
||||
>
|
||||
{#if (info?.meta?.suggestion_prompts ?? null) === null}
|
||||
<span>{$i18n.t('Default')}</span>
|
||||
{:else}
|
||||
<span>{$i18n.t('Custom')}</span>
|
||||
{/if}
|
||||
</button>
|
||||
</PromptSuggestions>
|
||||
{:else}
|
||||
<div
|
||||
class="flex items-center justify-between gap-3 px-1 py-1 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<span>{$i18n.t('Using default prompt suggestions')}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="shrink-0 hover:text-gray-900 dark:hover:text-gray-100"
|
||||
on:click={() => {
|
||||
info.meta.suggestion_prompts = customPromptDraft ?? [
|
||||
{ content: '', title: ['', ''] }
|
||||
];
|
||||
}}>{$i18n.t('Customize')}</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if editingLocale}
|
||||
<LocalizedPromptSuggestions
|
||||
promptSuggestions={info.meta.suggestion_prompts ?? []}
|
||||
bind:localizedPromptSuggestions={info.meta.i18n}
|
||||
locale={editingLocale}
|
||||
localeLabel={editingLocaleLabel}
|
||||
/>
|
||||
{:else if info?.meta?.suggestion_prompts}
|
||||
<PromptSuggestions bind:promptSuggestions={info.meta.suggestion_prompts} />
|
||||
{/if}
|
||||
</ModelSettingsSection>
|
||||
</section>
|
||||
|
||||
<hr class="my-3 border-gray-100/60 dark:border-gray-850/60" />
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
import { getContext, tick } from 'svelte';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import Plus from '$lib/components/icons/Plus.svelte';
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
const i18n = getContext('i18n');
|
||||
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||
const i18n = getContext<any>('i18n');
|
||||
|
||||
export let promptSuggestions = [];
|
||||
export let onChange = (suggestions) => {};
|
||||
export let inherited = false;
|
||||
export let disabled = false;
|
||||
let promptInputs: HTMLTextAreaElement[] = [];
|
||||
|
||||
let _promptSuggestions = [];
|
||||
let importInput: HTMLInputElement;
|
||||
const toolClass =
|
||||
'flex size-7 shrink-0 items-center justify-center rounded-lg text-gray-500 transition hover:bg-black/5 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-200 disabled:opacity-40';
|
||||
const updateSuggestions = (suggestions) => {
|
||||
if (disabled) return;
|
||||
promptSuggestions = suggestions;
|
||||
onChange(suggestions);
|
||||
};
|
||||
|
|
@ -49,181 +51,172 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="mb-1 flex min-h-7 w-full flex-wrap items-center justify-between gap-x-2 gap-y-1">
|
||||
<div class="min-w-fit flex-1 self-center text-xs font-normal text-gray-600 dark:text-gray-400">
|
||||
<slot name="label">{$i18n.t('Default Prompt Suggestions')}</slot>
|
||||
</div>
|
||||
<div class="space-y-0">
|
||||
<input
|
||||
bind:this={importInput}
|
||||
type="file"
|
||||
accept=".json"
|
||||
hidden
|
||||
on:change={(e) => {
|
||||
const files = e.target.files;
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
<div class="ms-auto flex max-w-full shrink-0 items-center justify-end gap-1">
|
||||
<slot name="actions" />
|
||||
<input
|
||||
bind:this={importInput}
|
||||
type="file"
|
||||
accept=".json"
|
||||
hidden
|
||||
on:change={(e) => {
|
||||
const files = e.target.files;
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
let reader = new FileReader();
|
||||
reader.onload = async (event) => {
|
||||
try {
|
||||
let suggestions = JSON.parse(event.target.result);
|
||||
if (
|
||||
!Array.isArray(suggestions) ||
|
||||
suggestions.some(
|
||||
(s) =>
|
||||
!s ||
|
||||
typeof s.content !== 'string' ||
|
||||
(s.title != null &&
|
||||
typeof s.title !== 'string' &&
|
||||
(!Array.isArray(s.title) || s.title.some((part) => typeof part !== 'string')))
|
||||
)
|
||||
)
|
||||
throw new Error('Invalid prompt suggestions');
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.onload = async (event) => {
|
||||
try {
|
||||
let suggestions = JSON.parse(event.target.result);
|
||||
if (
|
||||
!Array.isArray(suggestions) ||
|
||||
suggestions.some(
|
||||
(s) =>
|
||||
!s ||
|
||||
typeof s.content !== 'string' ||
|
||||
(s.title != null &&
|
||||
typeof s.title !== 'string' &&
|
||||
(!Array.isArray(s.title) || s.title.some((part) => typeof part !== 'string')))
|
||||
)
|
||||
)
|
||||
throw new Error('Invalid prompt suggestions');
|
||||
|
||||
suggestions = suggestions.map((s) => {
|
||||
if (typeof s.title === 'string') {
|
||||
s.title = [s.title, ''];
|
||||
} else if (!Array.isArray(s.title)) {
|
||||
s.title = ['', ''];
|
||||
}
|
||||
|
||||
return s;
|
||||
});
|
||||
|
||||
updateSuggestions([...promptSuggestions, ...suggestions]);
|
||||
} catch (error) {
|
||||
toast.error($i18n.t('Invalid JSON file'));
|
||||
return;
|
||||
suggestions = suggestions.map((s) => {
|
||||
if (typeof s.title === 'string') {
|
||||
s.title = [s.title, ''];
|
||||
} else if (!Array.isArray(s.title)) {
|
||||
s.title = ['', ''];
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsText(files[0]);
|
||||
return s;
|
||||
});
|
||||
|
||||
e.target.value = ''; // Reset the input value
|
||||
}}
|
||||
/>
|
||||
updateSuggestions([...promptSuggestions, ...suggestions]);
|
||||
} catch (error) {
|
||||
toast.error($i18n.t('Invalid JSON file'));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
<button
|
||||
class="shrink-0 px-1 py-0.5 text-xs text-gray-500 transition hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Import')}
|
||||
on:click={() => importInput.click()}
|
||||
>
|
||||
{$i18n.t('Import')}
|
||||
</button>
|
||||
reader.readAsText(files[0]);
|
||||
|
||||
{#if promptSuggestions.length}
|
||||
<button
|
||||
class="shrink-0 px-1 py-0.5 text-xs text-gray-500 transition hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Export')}
|
||||
disabled={!promptSuggestions.length}
|
||||
on:click={async () => {
|
||||
let blob = new Blob([JSON.stringify(promptSuggestions)], {
|
||||
type: 'application/json'
|
||||
});
|
||||
saveAs(blob, `prompt-suggestions-export-${Date.now()}.json`);
|
||||
e.target.value = ''; // Reset the input value
|
||||
}}
|
||||
/>
|
||||
|
||||
{#each _promptSuggestions as prompt, promptIdx}
|
||||
<div
|
||||
class="rounded-xl px-2 py-1.5 focus-within:bg-gray-50 dark:focus-within:bg-gray-850 {inherited
|
||||
? 'opacity-60 focus-within:opacity-100'
|
||||
: ''}"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
<textarea
|
||||
bind:this={promptInputs[promptIdx]}
|
||||
class="min-h-5 min-w-0 flex-1 resize-none overflow-hidden bg-transparent text-[0.8125rem] font-normal leading-5 text-gray-900 outline-hidden placeholder:text-gray-500 dark:text-gray-100 dark:placeholder:text-gray-400"
|
||||
placeholder={$i18n.t('Write a prompt…')}
|
||||
aria-label={$i18n.t('Prompt')}
|
||||
rows="1"
|
||||
{disabled}
|
||||
use:autosize={prompt.content}
|
||||
value={prompt.content}
|
||||
on:input={(e) => {
|
||||
prompt.content = e.currentTarget.value;
|
||||
updateSuggestions(_promptSuggestions);
|
||||
}}
|
||||
></textarea>
|
||||
<Tooltip content={$i18n.t('Remove prompt suggestion')}>
|
||||
<button
|
||||
type="button"
|
||||
{disabled}
|
||||
aria-label={$i18n.t('Remove prompt suggestion')}
|
||||
class="flex size-5 shrink-0 items-center justify-center text-gray-400 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-300"
|
||||
on:click={() =>
|
||||
updateSuggestions(promptSuggestions.filter((_, index) => index !== promptIdx))}
|
||||
>
|
||||
<XMark className="size-3" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<details class="group/prompt mt-0.5">
|
||||
<summary
|
||||
class="flex w-fit max-w-full cursor-pointer list-none items-center gap-1 text-xs text-gray-500 dark:text-gray-400 [&::-webkit-details-marker]:hidden"
|
||||
>
|
||||
{$i18n.t('Export')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Add prompt suggestion')}>
|
||||
<button
|
||||
class={toolClass}
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add prompt suggestion')}
|
||||
on:click={() => {
|
||||
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') {
|
||||
updateSuggestions([...promptSuggestions, { content: '', title: ['', ''] }]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3.5" strokeWidth="2.25" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if _promptSuggestions.length > 0}
|
||||
<div class="flex flex-col gap-1.5">
|
||||
{#each _promptSuggestions as prompt, promptIdx}
|
||||
<div
|
||||
class="flex gap-1 rounded-lg border border-gray-100/40 bg-transparent px-2 py-1 transition focus-within:border-gray-300 dark:border-gray-850/50 dark:focus-within:border-gray-600 {inherited
|
||||
? 'opacity-60 focus-within:opacity-100'
|
||||
: ''}"
|
||||
>
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<div class="grid min-w-0 gap-1 md:grid-cols-2 md:gap-1.5">
|
||||
<Tooltip content={$i18n.t('e.g. Tell me a fun fact')} placement="top-start">
|
||||
<input
|
||||
class="w-full bg-transparent text-[0.8125rem] leading-5 outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700 font-normal text-gray-900 dark:text-gray-100"
|
||||
placeholder={$i18n.t('Title')}
|
||||
aria-label={$i18n.t('Title')}
|
||||
value={prompt.title[0]}
|
||||
on:input={(e) => {
|
||||
prompt.title[0] = e.currentTarget.value;
|
||||
updateSuggestions(_promptSuggestions);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content={$i18n.t('e.g. about the Roman Empire')} placement="top-start">
|
||||
<input
|
||||
class="w-full bg-transparent text-[0.8125rem] leading-5 outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700 font-normal text-gray-900 dark:text-gray-100"
|
||||
placeholder={$i18n.t('Subtitle')}
|
||||
aria-label={$i18n.t('Subtitle')}
|
||||
value={prompt.title[1]}
|
||||
on:input={(e) => {
|
||||
prompt.title[1] = e.currentTarget.value;
|
||||
updateSuggestions(_promptSuggestions);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
className="flex min-w-0"
|
||||
content={$i18n.t('e.g. Tell me a fun fact about the Roman Empire')}
|
||||
placement="top-start"
|
||||
>
|
||||
<textarea
|
||||
class="min-h-5 w-full resize-none overflow-hidden bg-transparent text-[0.8125rem] leading-5 outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700 font-normal text-gray-900 dark:text-gray-100"
|
||||
placeholder={$i18n.t('Content')}
|
||||
aria-label={$i18n.t('Content')}
|
||||
rows="1"
|
||||
use:autosize={prompt.content}
|
||||
value={prompt.content}
|
||||
<ChevronRight
|
||||
className="size-3 shrink-0 transition-transform group-open/prompt:rotate-90"
|
||||
/>
|
||||
<span class="shrink-0">{$i18n.t('Display text')}</span>
|
||||
{#if prompt.title.some((part) => part.trim())}
|
||||
<span class="truncate text-gray-600 dark:text-gray-300">
|
||||
{prompt.title.filter((part) => part.trim()).join(' · ')}
|
||||
</span>
|
||||
{/if}
|
||||
</summary>
|
||||
<div class="grid gap-2 py-1.5 sm:grid-cols-2">
|
||||
{#each ['Title', 'Subtitle'] as label, index}
|
||||
<label class="flex min-w-0 flex-col gap-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
<span>{$i18n.t(label)}</span>
|
||||
<input
|
||||
{disabled}
|
||||
class="min-w-0 w-full bg-transparent text-[0.8125rem] font-normal leading-5 text-gray-900 outline-hidden placeholder:text-gray-400 dark:text-gray-100 dark:placeholder:text-gray-500"
|
||||
aria-label={$i18n.t(label)}
|
||||
placeholder={index === 0
|
||||
? $i18n.t('e.g. Tell me a fun fact')
|
||||
: $i18n.t('e.g. about the Roman Empire')}
|
||||
value={prompt.title[index]}
|
||||
on:input={(e) => {
|
||||
prompt.content = e.currentTarget.value;
|
||||
prompt.title[index] = e.currentTarget.value;
|
||||
updateSuggestions(_promptSuggestions);
|
||||
}}
|
||||
></textarea>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="flex size-6 shrink-0 items-center justify-center text-gray-400 opacity-70 transition hover:text-gray-700 hover:opacity-100 dark:text-gray-600 dark:hover:text-gray-300"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Remove prompt suggestion')}
|
||||
on:click={() => {
|
||||
updateSuggestions(promptSuggestions.filter((_, index) => index !== promptIdx));
|
||||
}}
|
||||
>
|
||||
<XMark className="size-3.5" />
|
||||
</button>
|
||||
/>
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</details>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="mb-1.5 w-full text-center text-xs text-gray-500 dark:text-gray-600">
|
||||
<p class="px-2 py-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('No suggestion prompts')}
|
||||
</p>
|
||||
{/each}
|
||||
<div
|
||||
class="flex flex-wrap items-center justify-between gap-x-3 gap-y-1 pt-1 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
{disabled}
|
||||
aria-label={$i18n.t('Add prompt suggestion')}
|
||||
class="flex items-center gap-1.5 px-2 py-1 text-xs text-gray-500 hover:text-gray-700 disabled:opacity-40 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
on:click={async () => {
|
||||
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content.trim() !== '') {
|
||||
updateSuggestions([...promptSuggestions, { content: '', title: ['', ''] }]);
|
||||
}
|
||||
await tick();
|
||||
promptInputs[promptSuggestions.length - 1]?.focus();
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3" strokeWidth="1.5" />
|
||||
{$i18n.t('Add prompt')}
|
||||
</button>
|
||||
<div class="ml-auto flex max-w-full flex-wrap items-center justify-end gap-3 px-2 py-1">
|
||||
<slot name="label" />
|
||||
<slot name="actions" />
|
||||
<button
|
||||
type="button"
|
||||
{disabled}
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
on:click={() => importInput.click()}>{$i18n.t('Import')}</button
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled || !promptSuggestions.length}
|
||||
class="hover:text-gray-900 disabled:opacity-40 dark:hover:text-gray-100"
|
||||
on:click={() => {
|
||||
saveAs(
|
||||
new Blob([JSON.stringify(promptSuggestions)], { type: 'application/json' }),
|
||||
`prompt-suggestions-export-${Date.now()}.json`
|
||||
);
|
||||
}}>{$i18n.t('Export')}</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3820,5 +3820,11 @@
|
|||
"Default terminal": "Default terminal",
|
||||
"No terminal servers available": "No terminal servers available",
|
||||
"Add tools in the Tools workspace or connect a tool server.": "Add tools in the Tools workspace or connect a tool server.",
|
||||
"Used in native function calling mode. Workspace settings and user permissions still apply.": "Used in native function calling mode. Workspace settings and user permissions still apply."
|
||||
"Used in native function calling mode. Workspace settings and user permissions still apply.": "Used in native function calling mode. Workspace settings and user permissions still apply.",
|
||||
"Prompt actions": "Prompt actions",
|
||||
"Write a prompt…": "Write a prompt…",
|
||||
"Display text": "Display text",
|
||||
"Add prompt": "Add prompt",
|
||||
"Using default prompt suggestions": "Using default prompt suggestions",
|
||||
"Customize": "Customize"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue