This commit is contained in:
Timothy Jaeryang Baek 2026-07-17 05:46:09 -04:00
parent d027a32ed1
commit 437c06c479
3 changed files with 63 additions and 48 deletions

View file

@ -73,6 +73,8 @@
let modelOrderList: string[] = [];
let defaultModelIds: string[] = [];
let defaultPinnedModelIds: string[] = [];
let defaultModelIdSet = new Set<string>();
let defaultPinnedModelIdSet = new Set<string>();
let workspaceModels: ModelListItem[] = [];
let baseModels: ModelListItem[] = [];
@ -123,8 +125,8 @@
return 'text-gray-500 dark:text-gray-400';
};
const isDefaultModel = (model) => defaultModelIds.includes(model.id);
const isDefaultPinnedModel = (model) => defaultPinnedModelIds.includes(model.id);
$: defaultModelIdSet = new Set(defaultModelIds);
$: defaultPinnedModelIdSet = new Set(defaultPinnedModelIds);
$: if (models) {
const modelOrder = new Map(modelOrderList.map((id, idx) => [id, idx]));
@ -138,6 +140,8 @@
if (viewOption === 'hidden') return m?.meta?.hidden === true;
if (viewOption === 'public') return isPublicModel(m);
if (viewOption === 'private') return !isPublicModel(m);
if (viewOption === 'selected') return defaultModelIdSet.has(m.id);
if (viewOption === 'pinned') return defaultPinnedModelIdSet.has(m.id);
return true; // All
})
.sort((a, b) => {
@ -348,28 +352,30 @@
};
const toggleDefaultModelHandler = async (model) => {
const nextDefaultModelIds = isDefaultModel(model)
const isSelected = defaultModelIdSet.has(model.id);
const nextDefaultModelIds = isSelected
? defaultModelIds.filter((id) => id !== model.id)
: [...new Set([...defaultModelIds, model.id])];
await saveModelDefaults(
nextDefaultModelIds,
defaultPinnedModelIds,
isDefaultModel(model)
isSelected
? $i18n.t('Model removed from selected models')
: $i18n.t('Model added to selected models')
);
};
const toggleDefaultPinnedModelHandler = async (model) => {
const nextDefaultPinnedModelIds = isDefaultPinnedModel(model)
const isPinned = defaultPinnedModelIdSet.has(model.id);
const nextDefaultPinnedModelIds = isPinned
? defaultPinnedModelIds.filter((id) => id !== model.id)
: [...new Set([...defaultPinnedModelIds, model.id])];
await saveModelDefaults(
defaultModelIds,
nextDefaultPinnedModelIds,
isDefaultPinnedModel(model)
isPinned
? $i18n.t('Model removed from pinned models')
: $i18n.t('Model added to pinned models')
);
@ -945,7 +951,7 @@
{modelAccessLabel(model)}
</span>
{#if isDefaultModel(model)}
{#if defaultModelIdSet.has(model.id)}
<span
class="shrink-0 text-[11px] font-normal leading-4 text-gray-500 dark:text-gray-400"
>
@ -953,7 +959,7 @@
</span>
{/if}
{#if isDefaultPinnedModel(model)}
{#if defaultPinnedModelIdSet.has(model.id)}
<span
class="shrink-0 text-[11px] font-normal leading-4 text-gray-500 dark:text-gray-400"
>
@ -1018,8 +1024,8 @@
privacyHandler={() => {
toggleModelPrivacyHandler(model);
}}
isDefaultSelected={isDefaultModel(model)}
isDefaultPinned={isDefaultPinnedModel(model)}
isDefaultSelected={defaultModelIdSet.has(model.id)}
isDefaultPinned={defaultPinnedModelIdSet.has(model.id)}
defaultSelectedHandler={() => {
toggleDefaultModelHandler(model);
}}

View file

@ -19,7 +19,9 @@
{ value: 'visible', label: $i18n.t('Visible') },
{ value: 'hidden', label: $i18n.t('Hidden') },
{ value: 'public', label: $i18n.t('Public') },
{ value: 'private', label: $i18n.t('Private') }
{ value: 'private', label: $i18n.t('Private') },
{ value: 'selected', label: $i18n.t('Selected') },
{ value: 'pinned', label: $i18n.t('Pinned') }
];
</script>

View file

@ -28,7 +28,6 @@
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import PromptHistoryMenu from './PromptHistoryMenu.svelte';
import Badge from '$lib/components/common/Badge.svelte';
import Tags from '$lib/components/common/Tags.svelte';
dayjs.extend(localizedFormat);
@ -396,7 +395,7 @@
<span>{$i18n.t('Back')}</span>
</button>
<div class="flex shrink-0 items-start justify-between gap-3 pb-2">
<div class="flex shrink-0 items-start justify-between gap-3 pb-1">
<div class="min-w-0 flex-1">
<input
class="w-full bg-transparent text-sm outline-hidden"
@ -438,7 +437,7 @@
</div>
</div>
<div class="mb-2 flex justify-between items-center gap-2">
<div class="mb-1 flex justify-between items-center gap-2">
<div class="flex-1 min-w-0">
<Tags
{tags}
@ -493,7 +492,9 @@
{#if selectedHistoryEntry && !disabled}
<div class="flex items-center gap-2">
{#if selectedHistoryEntry.id === prompt?.version_id}
<Badge type="success" content={$i18n.t('Live')} />
<span class="inline-flex items-center text-xs text-gray-400 dark:text-gray-500">
{$i18n.t('Live')}
</span>
{:else}
<button
class="text-xs text-gray-500 hover:text-gray-900 dark:hover:text-gray-300 hover:underline transition"
@ -675,41 +676,47 @@
{#if history.length > 0}
<div class="space-y-0 flex-1 overflow-y-auto" on:scroll={handleHistoryScroll}>
{#each history as entry, index}
<div class="flex">
<!-- Content -->
<button
class="mb-0.5 flex-1 rounded-lg px-2 py-1.5 text-left transition group
{selectedHistoryEntry?.id === entry.id
? 'bg-gray-50/60 dark:bg-white/[0.03]'
: 'hover:bg-gray-50/60 dark:hover:bg-white/[0.03]'}"
on:click={() => (selectedHistoryEntry = entry)}
>
<!-- Commit Message -->
<div class="flex items-center gap-2 mb-1">
<div class="text-xs text-gray-900 dark:text-white truncate">
{entry.commit_message || $i18n.t('Update')}
</div>
{#if entry.id === prompt?.version_id}
<Badge type="success" content={$i18n.t('Live')} />
{/if}
</div>
<button
class="group relative w-full px-1.5 py-1.5 pl-3 text-left transition {selectedHistoryEntry?.id ===
entry.id
? 'text-gray-900 dark:text-white'
: 'text-gray-500 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-200'}"
on:click={() => (selectedHistoryEntry = entry)}
>
<span
class="absolute left-0 top-1.5 h-[calc(100%-0.75rem)] w-px rounded-full transition {selectedHistoryEntry?.id ===
entry.id
? 'bg-gray-900 dark:bg-gray-200'
: 'bg-transparent'}"
></span>
<!-- User + Time -->
<div class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
{#if entry.user}
<img
src={`/api/v1/users/${entry.user.id}/profile/image`}
alt={entry.user.name}
class="size-3 rounded-full mr-0.5"
on:error={(e) => (e.target.src = '/user.png')}
/>
<span class="truncate">{entry.user.name}</span>
<span>•</span>
{/if}
<span class="shrink-0">{renderDate(entry.created_at)}</span>
<div class="flex items-center gap-2 mb-1">
<div class="truncate text-xs">
{entry.commit_message || $i18n.t('Update')}
</div>
</button>
</div>
{#if entry.id === prompt?.version_id}
<span
class="inline-flex shrink-0 items-center text-xs text-gray-400 dark:text-gray-500"
>
{$i18n.t('Live')}
</span>
{/if}
</div>
<div class="flex items-center gap-1 text-xs text-gray-400 dark:text-gray-500">
{#if entry.user}
<img
src={`/api/v1/users/${entry.user.id}/profile/image`}
alt={entry.user.name}
class="size-3 rounded-full mr-0.5"
on:error={(e) => (e.target.src = '/user.png')}
/>
<span class="truncate">{entry.user.name}</span>
<span>•</span>
{/if}
<span class="shrink-0">{renderDate(entry.created_at)}</span>
</div>
</button>
{/each}
{#if historyLoading}