Add chat sorting

This commit is contained in:
Webifi 2023-06-24 15:51:06 -05:00
parent 8835ee1d6e
commit 52affeb83f
7 changed files with 134 additions and 73 deletions

View File

@ -363,7 +363,8 @@ aside.menu.main-menu .menu-expanse {
.menu-expanse .menu-expanse
.menu-label, .menu-expanse .menu-label, .menu-expanse
.menu-list { .menu-list,
.menu-expanse .bottom-buttons {
flex: 0 1 auto; flex: 0 1 auto;
} }

View File

@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
// This beast needs to be broken down into multiple components before it gets any worse.
import { import {
saveChatStore, saveChatStore,
chatsStorage, chatsStorage,
@ -118,6 +117,7 @@
chat.lastAccess = Date.now() chat.lastAccess = Date.now()
saveChatStore() saveChatStore()
$checkStateChange++
// Focus the input on mount // Focus the input on mount
focusInput() focusInput()

View File

@ -9,7 +9,7 @@ $: apiKey = $apiKeyStorage
onMount(() => { onMount(() => {
if (!$started) { if (!$started) {
$started = true $started = true
console.log('started', apiKey, $lastChatId, getChat($lastChatId)) // console.log('started', apiKey, $lastChatId, getChat($lastChatId))
if (apiKey && getChat($lastChatId)) { if (apiKey && getChat($lastChatId)) {
// const chatId = $lastChatId // const chatId = $lastChatId
$lastChatId = 0 $lastChatId = 0

View File

@ -2,7 +2,7 @@
import { applyProfile } from './Profiles.svelte' import { applyProfile } from './Profiles.svelte'
import { getChatSettings, getGlobalSettings, setGlobalSettingValueByKey } from './Storage.svelte' import { getChatSettings, getGlobalSettings, setGlobalSettingValueByKey } from './Storage.svelte'
import { encode } from 'gpt-tokenizer' import { encode } from 'gpt-tokenizer'
import { faCheck, faThumbTack } from '@fortawesome/free-solid-svg-icons/index' import { faArrowDown91, faArrowDownAZ, faCheck, faThumbTack } from '@fortawesome/free-solid-svg-icons/index'
// Setting definitions // Setting definitions
import { import {
@ -13,7 +13,10 @@ import {
type GlobalSettings, type GlobalSettings,
type Request, type Request,
type Model, type Model,
type ControlAction type ControlAction,
type ChatSortOption
} from './Types.svelte' } from './Types.svelte'
export const defaultModel:Model = 'gpt-3.5-turbo' export const defaultModel:Model = 'gpt-3.5-turbo'
@ -93,6 +96,14 @@ const defaults:ChatSettings = {
isDirty: false isDirty: false
} }
export const globalDefaults: GlobalSettings = {
profiles: {} as Record<string, ChatSettings>,
lastProfile: 'default',
defaultProfile: 'default',
hideSummarized: false,
chatSort: 'created'
}
const excludeFromProfile = { const excludeFromProfile = {
messages: true, messages: true,
user: true, user: true,
@ -105,14 +116,14 @@ export const imageGenerationSizes = [
export const imageGenerationSizeTypes = ['', ...imageGenerationSizes] export const imageGenerationSizeTypes = ['', ...imageGenerationSizes]
export const chatSortOptions = [ export const chatSortOptions = {
{ value: 'name', text: 'Name' }, name: { text: 'Name', icon: faArrowDownAZ, value: '', sortFn: (a, b) => { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0 } },
{ value: 'created', text: 'Created' }, created: { text: 'Created', icon: faArrowDown91, value: '', sortFn: (a, b) => { return ((b.created || 0) - (a.created || 0)) || (b.id - a.id) } },
{ value: 'updated', text: 'Last Use' }, lastUse: { text: 'Last Use', icon: faArrowDown91, value: '', sortFn: (a, b) => { return ((b.lastUse || 0) - (a.lastUse || 0)) || (b.id - a.id) } },
{ value: 'lastAccess', text: 'Last View' } lastAccess: { text: 'Last View', icon: faArrowDown91, value: '', sortFn: (a, b) => { return ((b.lastAccess || 0) - (a.lastAccess || 0)) || (b.id - a.id) } }
] } as Record<string, ChatSortOption>
export const chatSortOptionsKeys = chatSortOptions.map(o => o.value, [] as string[]) Object.entries(chatSortOptions).forEach(([k, o]) => { o.value = k })
const profileSetting: ChatSetting & SettingSelect = { const profileSetting: ChatSetting & SettingSelect = {
key: 'profile', key: 'profile',

View File

@ -1,17 +1,30 @@
<script lang="ts"> <script lang="ts">
import { params } from 'svelte-spa-router' import { params } from 'svelte-spa-router'
import ChatMenuItem from './ChatMenuItem.svelte' import ChatMenuItem from './ChatMenuItem.svelte'
import { apiKeyStorage, chatsStorage, pinMainMenu, checkStateChange } from './Storage.svelte' import { apiKeyStorage, chatsStorage, pinMainMenu, checkStateChange, getChatSortOption, setChatSortOption } from './Storage.svelte'
import Fa from 'svelte-fa/src/fa.svelte' import Fa from 'svelte-fa/src/fa.svelte'
import { faSquarePlus, faKey } from '@fortawesome/free-solid-svg-icons/index' import { faSquarePlus, faKey } from '@fortawesome/free-solid-svg-icons/index'
import ChatOptionMenu from './ChatOptionMenu.svelte' import ChatOptionMenu from './ChatOptionMenu.svelte'
import logo from '../assets/logo.svg' import logo from '../assets/logo.svg'
import { clickOutside } from 'svelte-use-click-outside' import { clickOutside } from 'svelte-use-click-outside'
import { startNewChatWithWarning } from './Util.svelte' import { startNewChatWithWarning } from './Util.svelte'
import { chatSortOptions } from './Settings.svelte'
$: sortedChats = $chatsStorage.sort((a, b) => ((b.created || 0) - (a.created || 0)) || (b.id - a.id)) $: sortedChats = $chatsStorage.sort(getChatSortOption().sortFn)
$: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined $: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined
let sortOption = getChatSortOption()
const onStateChange = (...args:any) => {
sortOption = getChatSortOption()
sortedChats = $chatsStorage.sort(sortOption.sortFn)
// console.log('Sorting', sortOption, sortedChats)
}
$: onStateChange($checkStateChange)
let showSortMenu = false
</script> </script>
<aside class="menu main-menu" class:pinned={$pinMainMenu} use:clickOutside={() => { $pinMainMenu = false }}> <aside class="menu main-menu" class:pinned={$pinMainMenu} use:clickOutside={() => { $pinMainMenu = false }}>
@ -37,22 +50,39 @@
{/if} {/if}
</ul> </ul>
<!-- <p class="menu-label">Actions</p> --> <!-- <p class="menu-label">Actions</p> -->
<ul class="menu-list"> <div class="level is-mobile bottom-buttons p-1">
<li> <div class="level-left">
<div class="level-right side-actions"> <div class="dropdown is-left is-up" class:is-active={showSortMenu}>
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu3" on:click|preventDefault|stopPropagation={() => { showSortMenu = !showSortMenu }}>
<span class="icon"><Fa icon={sortOption.icon}/></span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
<div class="dropdown-content">
{#each Object.values(chatSortOptions) as opt}
<a href={'#'} class="dropdown-item" class:is-active={sortOption === opt} on:click|preventDefault={() => { showSortMenu = false; setChatSortOption(opt.value) }}>
<span class="menu-icon"><Fa icon={opt.icon}/></span>
{opt.text}
</a>
{/each}
</div>
</div>
</div>
</div>
<div class="level-right">
{#if !$apiKeyStorage} {#if !$apiKeyStorage}
<div class="level-item"> <div class="level-item">
<a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage} <a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage}
><span class="greyscale mr-2"><Fa icon={faKey} /></span> API key</a ><span class="greyscale mr-1"><Fa icon={faKey} /></span> API key</a
></div> ></div>
{:else} {:else}
<div class="level-item"> <div class="level-item">
<button on:click={() => { $pinMainMenu = false; startNewChatWithWarning(activeChatId) }} class="panel-block button" title="Start new chat with default profile" class:is-disabled={!$apiKeyStorage} <button on:click={() => { $pinMainMenu = false; startNewChatWithWarning(activeChatId) }} class="panel-block button" title="Start new chat with default profile" class:is-disabled={!$apiKeyStorage}
><span class="greyscale mr-2"><Fa icon={faSquarePlus} /></span> New chat</button> ><span class="greyscale mr-1"><Fa icon={faSquarePlus} /></span> New chat</button>
</div> </div>
{/if} {/if}
</div> </div>
</li> </div>
</ul>
</div> </div>
</aside> </aside>

View File

@ -1,8 +1,8 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import { persisted } from 'svelte-local-storage-store' import { persisted } from 'svelte-local-storage-store'
import { get, writable } from 'svelte/store' import { get, writable } from 'svelte/store'
import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model } from './Types.svelte' import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model, ChatSortOption } from './Types.svelte'
import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte' import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile, chatSortOptions, globalDefaults } from './Settings.svelte'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte' import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte'
import { errorNotice } from './Util.svelte' import { errorNotice } from './Util.svelte'
@ -54,7 +54,7 @@
startSession: false, startSession: false,
sessionStarted: false, sessionStarted: false,
created: Date.now(), created: Date.now(),
updated: Date.now(), lastUse: Date.now(),
lastAccess: Date.now() lastAccess: Date.now()
}) })
chatsStorage.set(chats) chatsStorage.set(chats)
@ -257,7 +257,7 @@
} else { } else {
clearTimeout(setMessagesTimers[chatId]) clearTimeout(setMessagesTimers[chatId])
const chat = getChat(chatId) const chat = getChat(chatId)
chat.updated = Date.now() chat.lastUse = Date.now()
chat.messages = messages chat.messages = messages
saveChatStore() saveChatStore()
} }
@ -387,6 +387,7 @@
// Set the ID // Set the ID
chatCopy.id = newChatID() chatCopy.id = newChatID()
chatCopy.created = Date.now()
// Set new name // Set new name
chatCopy.name = cname chatCopy.name = cname
@ -532,6 +533,18 @@
getProfiles(true) // force update profile cache getProfiles(true) // force update profile cache
} }
export const getChatSortOption = (): ChatSortOption => {
const store = get(globalStorage)
return (chatSortOptions[store.chatSort] || chatSortOptions[globalDefaults.chatSort])
}
export const setChatSortOption = (sortName: any) => {
const store = get(globalStorage)
store.chatSort = chatSortOptions[sortName] ? sortName : globalDefaults.chatSort
globalStorage.set(store)
checkStateChange.set(get(checkStateChange) + 1)
}
export const newName = (name:string, nameMap:Record<string, any>):string => { export const newName = (name:string, nameMap:Record<string, any>):string => {
if (!nameMap[name]) return name if (!nameMap[name]) return name
let i:number = 1 let i:number = 1

View File

@ -1,13 +1,12 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons'
import { supportedModelKeys } from './Models.svelte' import { supportedModelKeys } from './Models.svelte'
import { chatSortOptionsKeys, imageGenerationSizeTypes } from './Settings.svelte' import { imageGenerationSizeTypes } from './Settings.svelte'
export type Model = typeof supportedModelKeys[number]; export type Model = typeof supportedModelKeys[number];
export type ImageGenerationSizes = typeof imageGenerationSizeTypes[number]; export type ImageGenerationSizes = typeof imageGenerationSizeTypes[number];
export type ChatSortTypes = typeof chatSortOptionsKeys[number];
export type ModelDetail = { export type ModelDetail = {
prompt: number; prompt: number;
completion: number; completion: number;
@ -113,7 +112,7 @@
startSession: boolean; startSession: boolean;
sessionStarted: boolean; sessionStarted: boolean;
created: number; created: number;
updated: number; lastUse: number;
lastAccess: number; lastAccess: number;
}; };
@ -160,12 +159,14 @@
fillMessage?:Message, fillMessage?:Message,
}; };
export type ChatSortOptions = 'name'|'created'|'lastUse'|'lastAccess';
export type GlobalSettings = { export type GlobalSettings = {
profiles: Record<string, ChatSettings>; profiles: Record<string, ChatSettings>;
lastProfile?: string; lastProfile: string|null;
defaultProfile?: string; defaultProfile: string;
hideSummarized?: boolean; hideSummarized: boolean;
chatSort: keyof ChatSortTypes; chatSort: ChatSortOptions;
}; };
type SettingNumber = { type SettingNumber = {
@ -180,6 +181,11 @@
text: string; text: string;
}; };
export type ChatSortOption = SelectOption & {
sortFn: (a: Chat, b: Chat) => number;
icon: IconDefinition;
};
type SettingBoolean = { type SettingBoolean = {
type: 'boolean'; type: 'boolean';
}; };