refac: folders

This commit is contained in:
Timothy Jaeryang Baek 2025-09-15 18:33:57 -05:00
parent 3f27d9ada1
commit 3ec1efb6e0
6 changed files with 138 additions and 131 deletions

View file

@ -432,15 +432,17 @@
{/if} {/if}
{:else} {:else}
<div <div
class="sticky {stickyButtonsClassName} left-0 right-0 py-2 pr-3 flex items-center justify-between w-full z-10 text-xs text-black dark:text-white" class="absolute left-0 right-0 py-2.5 pr-3 text-text-300 pl-4.5 text-xs font-medium dark:text-white"
> >
<div class="text-text-300 pl-4.5 text-xs font-medium dark:text-white"> {lang}
{lang} </div>
</div>
<div
class="sticky {stickyButtonsClassName} left-0 right-0 py-2 pr-3 flex items-center justify-end w-full z-10 text-xs text-black dark:text-white"
>
<div class="flex items-center gap-0.5"> <div class="flex items-center gap-0.5">
<button <button
class="flex gap-1 items-center bg-none border-none transition rounded-md px-1.5 py-0.5" class="flex gap-1 items-center bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
on:click={collapseCodeBlock} on:click={collapseCodeBlock}
> >
<div class=" -translate-y-[0.5px]"> <div class=" -translate-y-[0.5px]">
@ -454,12 +456,14 @@
{#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))} {#if ($config?.features?.enable_code_execution ?? true) && (lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code)))}
{#if executing} {#if executing}
<div class="run-code-button bg-none border-none p-0.5 cursor-not-allowed"> <div
class="run-code-button bg-none border-none p-0.5 cursor-not-allowed bg-white dark:bg-black"
>
{$i18n.t('Running')} {$i18n.t('Running')}
</div> </div>
{:else if run} {:else if run}
<button <button
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5" class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
on:click={async () => { on:click={async () => {
code = _code; code = _code;
await tick(); await tick();
@ -475,7 +479,7 @@
{#if save} {#if save}
<button <button
class="save-code-button bg-none border-none transition rounded-md px-1.5 py-0.5" class="save-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
on:click={saveCode} on:click={saveCode}
> >
{saved ? $i18n.t('Saved') : $i18n.t('Save')} {saved ? $i18n.t('Saved') : $i18n.t('Save')}
@ -483,13 +487,13 @@
{/if} {/if}
<button <button
class="copy-code-button bg-none border-none transition rounded-md px-1.5 py-0.5" class="copy-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
on:click={copyCode}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button on:click={copyCode}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
> >
{#if preview && ['html', 'svg'].includes(lang)} {#if preview && ['html', 'svg'].includes(lang)}
<button <button
class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5" class="flex gap-1 items-center run-code-button bg-none border-none transition rounded-md px-1.5 py-0.5 bg-white dark:bg-black"
on:click={previewCode} on:click={previewCode}
> >
<div> <div>

View file

@ -109,7 +109,7 @@
{save} {save}
{preview} {preview}
edit={editCodeBlock} edit={editCodeBlock}
stickyButtonsClassName={topPadding ? 'top-6' : 'top-0'} stickyButtonsClassName={topPadding ? 'top-7' : 'top-0'}
onSave={(value) => { onSave={(value) => {
onSave({ onSave({
raw: token.raw, raw: token.raw,

View file

@ -72,7 +72,7 @@
<nav class="sticky top-0 z-30 w-full py-1 -mb-8 flex flex-col items-center drag-region"> <nav class="sticky top-0 z-30 w-full py-1 -mb-8 flex flex-col items-center drag-region">
<div class="flex items-center w-full pl-1.5 pr-1"> <div class="flex items-center w-full pl-1.5 pr-1">
<div <div
class=" bg-linear-to-b via-40% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]" class=" bg-linear-to-b via-40% to-97% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1]"
></div> ></div>
<div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 pt-0.5 bg-transparent"> <div class=" flex max-w-full w-full mx-auto px-1.5 md:px-2 pt-0.5 bg-transparent">

View file

@ -16,14 +16,15 @@
export let name = ''; export let name = '';
export let collapsible = true; export let collapsible = true;
export let className = '';
export let buttonClassName = 'text-gray-600 dark:text-gray-400';
export let chevron = true; export let chevron = true;
export let onAddLabel: string = ''; export let onAddLabel: string = '';
export let onAdd: null | Function = null; export let onAdd: null | Function = null;
export let dragAndDrop = true; export let dragAndDrop = true;
export let className = '';
let folderElement; let folderElement;
let draggedOver = false; let draggedOver = false;
@ -138,20 +139,20 @@
> >
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div <div
class="w-full group rounded-lg relative flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-900 text-gray-500 dark:text-gray-500 transition" class="w-full group rounded-lg relative flex items-center justify-between hover:bg-gray-100 dark:hover:bg-gray-900 transition {buttonClassName}"
> >
<button class="w-full py-1.5 pl-2 flex items-center gap-1.5 text-xs font-medium"> <button class="w-full py-1.5 pl-2 flex items-center gap-1.5 text-xs font-medium">
{#if chevron} {#if chevron}
<div class="text-gray-300 dark:text-gray-600 p-[1px]"> <div class=" p-[1px]">
{#if open} {#if open}
<ChevronDown className=" size-3.5" strokeWidth="2.5" /> <ChevronDown className=" size-3" strokeWidth="2" />
{:else} {:else}
<ChevronRight className=" size-3.5" strokeWidth="2.5" /> <ChevronRight className=" size-3" strokeWidth="2" />
{/if} {/if}
</div> </div>
{/if} {/if}
<div class="translate-y-[0.5px]"> <div class="translate-y-[0.5px] {chevron ? '' : 'pl-1'}">
{name} {name}
</div> </div>
</button> </button>

View file

@ -871,14 +871,42 @@
</Folder> </Folder>
{/if} {/if}
{#if folders}
<Folder
className="px-2 mt-0.5"
name={$i18n.t('Folders')}
chevron={false}
dragAndDrop={false}
onAdd={() => {
showCreateFolderModal = true;
}}
onAddLabel={$i18n.t('New Folder')}
>
<Folders
{folders}
{shiftKey}
onDelete={(folderId) => {
selectedFolder.set(null);
initChatList();
}}
on:update={() => {
initChatList();
}}
on:import={(e) => {
const { folderId, items } = e.detail;
importChatHandler(items, false, folderId);
}}
on:change={async () => {
initChatList();
}}
/>
</Folder>
{/if}
<Folder <Folder
className="px-2 mt-0.5" className="px-2 mt-0.5"
name={$i18n.t('Chats')} name={$i18n.t('Chats')}
chevron={false} chevron={false}
onAdd={() => {
showCreateFolderModal = true;
}}
onAddLabel={$i18n.t('New Folder')}
on:change={async (e) => { on:change={async (e) => {
selectedFolder.set(null); selectedFolder.set(null);
await goto('/'); await goto('/');
@ -940,115 +968,89 @@
} }
}} }}
> >
{#if folders || $pinnedChats.length > 0} {#if $pinnedChats.length > 0}
<div class="mb-1"> <div class="mb-1">
{#if folders} <div class="flex flex-col space-y-1 rounded-xl">
<Folders <Folder
{folders} buttonClassName=" text-gray-500"
{shiftKey} bind:open={showPinnedChat}
onDelete={(folderId) => { on:change={(e) => {
selectedFolder.set(null); localStorage.setItem('showPinnedChat', e.detail);
initChatList(); console.log(e.detail);
}}
on:update={() => {
initChatList();
}} }}
on:import={(e) => { on:import={(e) => {
const { folderId, items } = e.detail; importChatHandler(e.detail, true);
importChatHandler(items, false, folderId);
}} }}
on:change={async () => { on:drop={async (e) => {
initChatList(); const { type, id, item } = e.detail;
}}
/>
{/if}
{#if $pinnedChats.length > 0} if (type === 'chat') {
<div class="flex flex-col space-y-1 rounded-xl"> let chat = await getChatById(localStorage.token, id).catch((error) => {
<Folder return null;
className="" });
bind:open={showPinnedChat} if (!chat && item) {
on:change={(e) => { chat = await importChat(
localStorage.setItem('showPinnedChat', e.detail); localStorage.token,
console.log(e.detail); item.chat,
}} item?.meta ?? {},
on:import={(e) => { false,
importChatHandler(e.detail, true); null,
}} item?.created_at ?? null,
on:drop={async (e) => { item?.updated_at ?? null
const { type, id, item } = e.detail; );
if (type === 'chat') {
let chat = await getChatById(localStorage.token, id).catch((error) => {
return null;
});
if (!chat && item) {
chat = await importChat(
localStorage.token,
item.chat,
item?.meta ?? {},
false,
null,
item?.created_at ?? null,
item?.updated_at ?? null
);
}
if (chat) {
console.log(chat);
if (chat.folder_id) {
const res = await updateChatFolderIdById(
localStorage.token,
chat.id,
null
).catch((error) => {
toast.error(`${error}`);
return null;
});
}
if (!chat.pinned) {
const res = await toggleChatPinnedStatusById(
localStorage.token,
chat.id
);
}
initChatList();
}
} }
}}
name={$i18n.t('Pinned')} if (chat) {
console.log(chat);
if (chat.folder_id) {
const res = await updateChatFolderIdById(
localStorage.token,
chat.id,
null
).catch((error) => {
toast.error(`${error}`);
return null;
});
}
if (!chat.pinned) {
const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
}
initChatList();
}
}
}}
name={$i18n.t('Pinned')}
>
<div
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900 text-gray-900 dark:text-gray-200"
> >
<div {#each $pinnedChats as chat, idx (`pinned-chat-${chat?.id ?? idx}`)}
class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900" <ChatItem
> className=""
{#each $pinnedChats as chat, idx (`pinned-chat-${chat?.id ?? idx}`)} id={chat.id}
<ChatItem title={chat.title}
className="" {shiftKey}
id={chat.id} selected={selectedChatId === chat.id}
title={chat.title} on:select={() => {
{shiftKey} selectedChatId = chat.id;
selected={selectedChatId === chat.id} }}
on:select={() => { on:unselect={() => {
selectedChatId = chat.id; selectedChatId = null;
}} }}
on:unselect={() => { on:change={async () => {
selectedChatId = null; initChatList();
}} }}
on:change={async () => { on:tag={(e) => {
initChatList(); const { type, name } = e.detail;
}} tagEventHandler(type, name, chat.id);
on:tag={(e) => { }}
const { type, name } = e.detail; />
tagEventHandler(type, name, chat.id); {/each}
}} </div>
/> </Folder>
{/each} </div>
</div>
</Folder>
</div>
{/if}
</div> </div>
{/if} {/if}

View file

@ -464,7 +464,7 @@
}} }}
> >
<button <button
class="text-gray-500 dark:text-gray-500 transition-all p-[3px] hover:bg-gray-200 dark:hover:bg-gray-850 rounded-lg" class="text-gray-500 dark:text-gray-500 transition-all p-1 hover:bg-gray-200 dark:hover:bg-gray-850 rounded-lg"
on:click={(e) => { on:click={(e) => {
e.stopPropagation(); e.stopPropagation();
open = !open; open = !open;
@ -472,22 +472,22 @@
> >
{#if folders[folderId]?.meta?.icon} {#if folders[folderId]?.meta?.icon}
<div class="flex group-hover:hidden transition-all"> <div class="flex group-hover:hidden transition-all">
<Emoji className="size-4" shortCode={folders[folderId].meta.icon} /> <Emoji className="size-3.5" shortCode={folders[folderId].meta.icon} />
</div> </div>
<div class="hidden group-hover:flex transition-all p-[1px]"> <div class="hidden group-hover:flex transition-all p-[1px]">
{#if open} {#if open}
<ChevronDown className=" size-3.5" strokeWidth="2.5" /> <ChevronDown className=" size-3" strokeWidth="2.5" />
{:else} {:else}
<ChevronRight className=" size-3.5" strokeWidth="2.5" /> <ChevronRight className=" size-3" strokeWidth="2.5" />
{/if} {/if}
</div> </div>
{:else} {:else}
<div class="p-[1px]"> <div class="p-[1px]">
{#if open} {#if open}
<ChevronDown className=" size-3.5" strokeWidth="2.5" /> <ChevronDown className=" size-3" strokeWidth="2.5" />
{:else} {:else}
<ChevronRight className=" size-3.5" strokeWidth="2.5" /> <ChevronRight className=" size-3" strokeWidth="2.5" />
{/if} {/if}
</div> </div>
{/if} {/if}