Begin testing new UI layout
This commit is contained in:
parent
6b24d99022
commit
df89356dc0
|
@ -10,13 +10,6 @@
|
|||
import NewChat from './lib/NewChat.svelte'
|
||||
import { chatsStorage, apiKeyStorage } from './lib/Storage.svelte'
|
||||
|
||||
// Check if the API key is passed in as a "key" query parameter - if so, save it
|
||||
// Example: https://niek.github.io/chatgpt-web/#/?key=sk-...
|
||||
const urlParams: URLSearchParams = new URLSearchParams($querystring)
|
||||
if (urlParams.has('key')) {
|
||||
apiKeyStorage.set(urlParams.get('key') as string)
|
||||
}
|
||||
|
||||
// The definition of the routes with some conditions
|
||||
const routes = {
|
||||
'/': Home,
|
||||
|
@ -37,11 +30,11 @@
|
|||
|
||||
'*': Home
|
||||
}
|
||||
document.body.classList.add('has-navbar-fixed-top')
|
||||
// document.body.classList.add('something')
|
||||
</script>
|
||||
|
||||
|
||||
<Navbar />
|
||||
<!-- <Navbar /> -->
|
||||
<section class="section">
|
||||
<div class="container is-fullhd">
|
||||
<div class="columns">
|
||||
|
@ -57,4 +50,4 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<Footer />
|
||||
<!-- <Footer /> -->
|
||||
|
|
164
src/app.scss
164
src/app.scss
|
@ -17,25 +17,6 @@
|
|||
section.section {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.chat-option-menu.navbar-item {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* temp. fix to keep navbar from getting huge on small screen devices
|
||||
if the right menu is put in the proper navbar-end container */
|
||||
.navbar-brand {
|
||||
/* margin-right: 0; */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-item .menu-icon {
|
||||
padding-right: .5em;
|
||||
}
|
||||
.dropdown-menu .dropdown-content {
|
||||
max-height: calc(100vh - 60px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.is-disabled {
|
||||
|
@ -180,6 +161,27 @@ $modal-background-background-color-dark: rgba($dark, 0.86) !default; // remove t
|
|||
}
|
||||
}
|
||||
|
||||
/* Bulma layout hacks */
|
||||
|
||||
.chat-option-menu.navbar-item {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* temp. fix to keep navbar from getting huge on small screen devices
|
||||
if the right menu is put in the proper navbar-end container */
|
||||
.navbar-brand {
|
||||
/* margin-right: 0; */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-item .menu-icon {
|
||||
padding-right: .5em;
|
||||
}
|
||||
.dropdown-menu .dropdown-content {
|
||||
max-height: calc(100vh - 60px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.chat-menu-item {
|
||||
position: relative;
|
||||
}
|
||||
|
@ -194,4 +196,126 @@ $modal-background-background-color-dark: rgba($dark, 0.86) !default; // remove t
|
|||
position: absolute;
|
||||
right: .4em;
|
||||
z-index: 200;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overrides for main layout */
|
||||
|
||||
section.section {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
aside.menu.main-menu {
|
||||
position: fixed;
|
||||
width: 20%;
|
||||
padding-right: 20px;
|
||||
top: 0px;
|
||||
bottom:0px;
|
||||
}
|
||||
|
||||
aside.menu.main-menu .menu-expanse {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
background-color: hsla(0, 0%, 60%, 0.208);
|
||||
box-shadow: 5px 0px 1px hsla(0, 0%, 60%, 0.208);
|
||||
}
|
||||
|
||||
.menu-expanse
|
||||
.menu-label, .menu-expanse
|
||||
.menu-list {
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.menu-expanse
|
||||
.menu-expansion-list {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.default-text {
|
||||
color: hsl(0, 0%, 21%) !important;
|
||||
}
|
||||
|
||||
html {
|
||||
--scrollbarBG: $body-background-color;
|
||||
--thumbBG: #999999;
|
||||
}
|
||||
|
||||
.lower-mask {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
height: 100px;
|
||||
width: 100%;
|
||||
background-image: linear-gradient(180deg,hsla(0,0%,100%,0) 13.94%,#fff 54.73%);
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
--thumbBG: #3f3f3f;
|
||||
}
|
||||
.default-text {
|
||||
color: rgb(181, 181, 181) !important;
|
||||
}
|
||||
.lower-mask {
|
||||
background-image: linear-gradient(180deg,hsla(0,0%,100%,0) 13.94%,#17181c 54.73%);
|
||||
}
|
||||
aside.menu.main-menu .menu-expanse {
|
||||
background-color: hsla(0, 0%, 19%, 0.371);
|
||||
box-shadow: 5px 0px 1px hsla(0, 0%, 19%, 0.371);
|
||||
}
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
width: 11px;
|
||||
}
|
||||
*::-webkit-scrollbar-track {
|
||||
background: var(--scrollbarBG);
|
||||
}
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: var(--thumbBG) ;
|
||||
border-radius: 6px;
|
||||
border: 3px solid var(--scrollbarBG);
|
||||
}
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
padding-left: 20px;
|
||||
padding-top: 40px;
|
||||
padding-right: 40px;
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
|
||||
.chat-focus-point {
|
||||
width:100%;
|
||||
height: 1px;
|
||||
margin-bottom: -.75rem;;
|
||||
}
|
||||
|
||||
.prompt-input-container {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
width: 80%;
|
||||
padding: 0px 40px 10px 40px;
|
||||
}
|
||||
|
||||
.running-total-container {
|
||||
min-height:2em;
|
||||
// padding-bottom:.6em;
|
||||
// /* padding-left: 1.9em; */
|
||||
// margin-bottom:- 1em
|
||||
}
|
||||
|
||||
.side-actions {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.main-menu .gpt-logo .icon {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
|
|
|
@ -481,6 +481,7 @@
|
|||
|
||||
</script>
|
||||
|
||||
<div class="chat-content">
|
||||
<nav class="level chat-header">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
|
@ -515,7 +516,10 @@
|
|||
{#if chat.messages.length === 0 || (chat.messages.length === 1 && chat.messages[0].role === 'system')}
|
||||
<Prompts bind:input />
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<div class="lower-mask"/>
|
||||
<div class="chat-focus-point"></div>
|
||||
<div class="prompt-input-container">
|
||||
<form class="field has-addons has-addons-right is-align-items-flex-end" on:submit|preventDefault={() => submitForm()}>
|
||||
<p class="control is-expanded">
|
||||
<textarea
|
||||
|
@ -549,7 +553,7 @@
|
|||
</p>
|
||||
</form>
|
||||
<!-- a target to scroll to -->
|
||||
<div class="chat-focus-point running-total-container">
|
||||
<div class="running-total-container">
|
||||
{#each Object.entries(chat.usage || {}) as [model, usage]}
|
||||
<p class="is-size-7 running-totals">
|
||||
<em>{model}</em> total <span class="has-text-weight-bold">{usage.total_tokens}</span>
|
||||
|
@ -557,6 +561,7 @@
|
|||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatSettingsModal chatId={chatId} bind:show={showSettingsModal} />
|
||||
|
||||
|
@ -600,12 +605,3 @@
|
|||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.running-total-container {
|
||||
min-height:2em;
|
||||
padding-bottom:.6em;
|
||||
/* padding-left: 1.9em; */
|
||||
margin-bottom:-2.6em
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
faGear,
|
||||
faTrash,
|
||||
faClone,
|
||||
faEllipsisVertical,
|
||||
// faEllipsisVertical,
|
||||
faEllipsis,
|
||||
faDownload,
|
||||
faUpload,
|
||||
faEraser,
|
||||
|
@ -23,9 +24,10 @@
|
|||
import { clickOutside } from 'svelte-use-click-outside'
|
||||
|
||||
export let chatId
|
||||
export const show = () => {
|
||||
showChatMenu = true
|
||||
export const show = (showHide:boolean = true) => {
|
||||
showChatMenu = showHide
|
||||
}
|
||||
export let style: string = 'is-right'
|
||||
|
||||
let showChatMenu = false
|
||||
let chatFileInput
|
||||
|
@ -74,13 +76,13 @@
|
|||
|
||||
</script>
|
||||
|
||||
<div class="dropdown is-right" class:is-active={showChatMenu} use:clickOutside={() => { showChatMenu = false }}>
|
||||
<div class="dropdown {style}" class:is-active={showChatMenu} use:clickOutside={() => { showChatMenu = false }}>
|
||||
<div class="dropdown-trigger">
|
||||
<button class="button" aria-haspopup="true"
|
||||
<button class="button is-ghost default-text" aria-haspopup="true"
|
||||
aria-controls="dropdown-menu3"
|
||||
on:click|preventDefault|stopPropagation={() => { showChatMenu = !showChatMenu }}
|
||||
>
|
||||
<span><Fa icon={faEllipsisVertical}/></span>
|
||||
<span class="icon "><Fa icon={faEllipsis}/></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
import { apiKeyStorage, chatsStorage } from './Storage.svelte'
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import { faSquarePlus, faKey } from '@fortawesome/free-solid-svg-icons/index'
|
||||
import ChatOptionMenu from './ChatOptionMenu.svelte';
|
||||
import logo from '../assets/logo.svg'
|
||||
|
||||
$: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id)
|
||||
|
||||
|
@ -11,31 +13,44 @@
|
|||
|
||||
</script>
|
||||
|
||||
<aside class="menu">
|
||||
<p class="menu-label">Chats</p>
|
||||
<ul class="menu-list">
|
||||
{#if sortedChats.length === 0}
|
||||
<li><a href={'#'} class="is-disabled">No chats yet...</a></li>
|
||||
{:else}
|
||||
{#each sortedChats as chat, i}
|
||||
<ChatMenuItem activeChatId={activeChatId} chat={chat} prevChat={sortedChats[i - 1]} nextChat={sortedChats[i + 1]} />
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
<p class="menu-label">Actions</p>
|
||||
<ul class="menu-list">
|
||||
{#if !$apiKeyStorage}
|
||||
<li>
|
||||
<a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage} class:is-active={!activeChatId}
|
||||
><span class="greyscale mr-2"><Fa icon={faKey} /></span> API key</a
|
||||
>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<a href={'#/chat/new'} class="panel-block" class:is-disabled={!$apiKeyStorage}
|
||||
><span class="greyscale mr-2"><Fa icon={faSquarePlus} /></span> New chat</a
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
<aside class="menu main-menu">
|
||||
<div class="menu-expanse">
|
||||
<div class="menu-label gpt-logo navbar-brand">
|
||||
<a class="navbar-item" href={'#/'}>
|
||||
<img src={logo} alt="ChatGPT-web" width="28" height="28" />
|
||||
<p class="ml-2 is-size-4 has-text-weight-bold">ChatGPT-web</p>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="menu-list menu-expansion-list">
|
||||
{#if sortedChats.length === 0}
|
||||
<li><a href={'#'} class="is-disabled">No chats yet...</a></li>
|
||||
{:else}
|
||||
{#each sortedChats as chat, i}
|
||||
<ChatMenuItem activeChatId={activeChatId} chat={chat} prevChat={sortedChats[i - 1]} nextChat={sortedChats[i + 1]} />
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
<!-- <p class="menu-label">Actions</p> -->
|
||||
<ul class="menu-list">
|
||||
{#if !$apiKeyStorage}
|
||||
<li>
|
||||
<a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage} class:is-active={!activeChatId}
|
||||
><span class="icon"><Fa icon={faKey} /></span> API key</a
|
||||
>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<div class="level-right side-actions">
|
||||
<div class="level-item">
|
||||
<a href={'#/chat/new'} class="panel-block" class:is-disabled={!$apiKeyStorage}
|
||||
><span class="greyscale mr-2"><Fa icon={faSquarePlus} /></span> New chat</a
|
||||
></div>
|
||||
<div class="level-item">
|
||||
<ChatOptionMenu bind:chatId={activeChatId} style="is-right is-up" />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
// Reference: https://openai.com/pricing#language-models
|
||||
// TODO: Move to settings of some type
|
||||
const modelDetails : Record<string, [number, number, number]> = {
|
||||
'gpt-4-32k': [0.00006, 0.00012, 32768], // $0.06 per 1000 tokens prompt, $0.12 per 1000 tokens completion, max tokens
|
||||
'gpt-4': [0.00003, 0.00006, 8192], // $0.03 per 1000 tokens prompt, $0.06 per 1000 tokens completion
|
||||
'gpt-3.5': [0.000002, 0.000002, 4096] // $0.002 per 1000 tokens (both prompt and completion)
|
||||
'gpt-4-32k': [0.00006, 0.00012, 32768], // $0.06 per 1000 tokens prompt, $0.12 per 1000 tokens completion, max 32k
|
||||
'gpt-4': [0.00003, 0.00006, 8192], // $0.03 per 1000 tokens prompt, $0.06 per 1000 tokens completion, max 8k
|
||||
'gpt-3.5': [0.000002, 0.000002, 4096] // $0.002 per 1000 tokens (both prompt and completion), max 4k
|
||||
}
|
||||
|
||||
const tpCache = {}
|
||||
|
|
Loading…
Reference in New Issue