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 NewChat from './lib/NewChat.svelte'
|
||||||
import { chatsStorage, apiKeyStorage } from './lib/Storage.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
|
// The definition of the routes with some conditions
|
||||||
const routes = {
|
const routes = {
|
||||||
'/': Home,
|
'/': Home,
|
||||||
|
@ -37,11 +30,11 @@
|
||||||
|
|
||||||
'*': Home
|
'*': Home
|
||||||
}
|
}
|
||||||
document.body.classList.add('has-navbar-fixed-top')
|
// document.body.classList.add('something')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<Navbar />
|
<!-- <Navbar /> -->
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container is-fullhd">
|
<div class="container is-fullhd">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
|
@ -57,4 +50,4 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Footer />
|
<!-- <Footer /> -->
|
||||||
|
|
162
src/app.scss
162
src/app.scss
|
@ -17,25 +17,6 @@
|
||||||
section.section {
|
section.section {
|
||||||
flex-grow: 1;
|
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 {
|
.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 {
|
.chat-menu-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -195,3 +197,125 @@ $modal-background-background-color-dark: rgba($dark, 0.86) !default; // remove t
|
||||||
right: .4em;
|
right: .4em;
|
||||||
z-index: 200;
|
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>
|
</script>
|
||||||
|
|
||||||
|
<div class="chat-content">
|
||||||
<nav class="level chat-header">
|
<nav class="level chat-header">
|
||||||
<div class="level-left">
|
<div class="level-left">
|
||||||
<div class="level-item">
|
<div class="level-item">
|
||||||
|
@ -515,7 +516,10 @@
|
||||||
{#if chat.messages.length === 0 || (chat.messages.length === 1 && chat.messages[0].role === 'system')}
|
{#if chat.messages.length === 0 || (chat.messages.length === 1 && chat.messages[0].role === 'system')}
|
||||||
<Prompts bind:input />
|
<Prompts bind:input />
|
||||||
{/if}
|
{/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()}>
|
<form class="field has-addons has-addons-right is-align-items-flex-end" on:submit|preventDefault={() => submitForm()}>
|
||||||
<p class="control is-expanded">
|
<p class="control is-expanded">
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -549,7 +553,7 @@
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<!-- a target to scroll to -->
|
<!-- 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]}
|
{#each Object.entries(chat.usage || {}) as [model, usage]}
|
||||||
<p class="is-size-7 running-totals">
|
<p class="is-size-7 running-totals">
|
||||||
<em>{model}</em> total <span class="has-text-weight-bold">{usage.total_tokens}</span>
|
<em>{model}</em> total <span class="has-text-weight-bold">{usage.total_tokens}</span>
|
||||||
|
@ -557,6 +561,7 @@
|
||||||
</p>
|
</p>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ChatSettingsModal chatId={chatId} bind:show={showSettingsModal} />
|
<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,
|
faGear,
|
||||||
faTrash,
|
faTrash,
|
||||||
faClone,
|
faClone,
|
||||||
faEllipsisVertical,
|
// faEllipsisVertical,
|
||||||
|
faEllipsis,
|
||||||
faDownload,
|
faDownload,
|
||||||
faUpload,
|
faUpload,
|
||||||
faEraser,
|
faEraser,
|
||||||
|
@ -23,9 +24,10 @@
|
||||||
import { clickOutside } from 'svelte-use-click-outside'
|
import { clickOutside } from 'svelte-use-click-outside'
|
||||||
|
|
||||||
export let chatId
|
export let chatId
|
||||||
export const show = () => {
|
export const show = (showHide:boolean = true) => {
|
||||||
showChatMenu = true
|
showChatMenu = showHide
|
||||||
}
|
}
|
||||||
|
export let style: string = 'is-right'
|
||||||
|
|
||||||
let showChatMenu = false
|
let showChatMenu = false
|
||||||
let chatFileInput
|
let chatFileInput
|
||||||
|
@ -74,13 +76,13 @@
|
||||||
|
|
||||||
</script>
|
</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">
|
<div class="dropdown-trigger">
|
||||||
<button class="button" aria-haspopup="true"
|
<button class="button is-ghost default-text" aria-haspopup="true"
|
||||||
aria-controls="dropdown-menu3"
|
aria-controls="dropdown-menu3"
|
||||||
on:click|preventDefault|stopPropagation={() => { showChatMenu = !showChatMenu }}
|
on:click|preventDefault|stopPropagation={() => { showChatMenu = !showChatMenu }}
|
||||||
>
|
>
|
||||||
<span><Fa icon={faEllipsisVertical}/></span>
|
<span class="icon "><Fa icon={faEllipsis}/></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
|
<div class="dropdown-menu" id="dropdown-menu3" role="menu">
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import { apiKeyStorage, chatsStorage } from './Storage.svelte'
|
import { apiKeyStorage, chatsStorage } 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 logo from '../assets/logo.svg'
|
||||||
|
|
||||||
$: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id)
|
$: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id)
|
||||||
|
|
||||||
|
@ -11,9 +13,15 @@
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<aside class="menu">
|
<aside class="menu main-menu">
|
||||||
<p class="menu-label">Chats</p>
|
<div class="menu-expanse">
|
||||||
<ul class="menu-list">
|
<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}
|
{#if sortedChats.length === 0}
|
||||||
<li><a href={'#'} class="is-disabled">No chats yet...</a></li>
|
<li><a href={'#'} class="is-disabled">No chats yet...</a></li>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -22,20 +30,27 @@
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
<p class="menu-label">Actions</p>
|
<!-- <p class="menu-label">Actions</p> -->
|
||||||
<ul class="menu-list">
|
<ul class="menu-list">
|
||||||
{#if !$apiKeyStorage}
|
{#if !$apiKeyStorage}
|
||||||
<li>
|
<li>
|
||||||
<a href={'#/'} class="panel-block" class:is-disabled={!$apiKeyStorage} class:is-active={!activeChatId}
|
<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
|
><span class="icon"><Fa icon={faKey} /></span> API key</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
{:else}
|
{:else}
|
||||||
<li>
|
<li>
|
||||||
|
<div class="level-right side-actions">
|
||||||
|
<div class="level-item">
|
||||||
<a href={'#/chat/new'} class="panel-block" class:is-disabled={!$apiKeyStorage}
|
<a href={'#/chat/new'} class="panel-block" class:is-disabled={!$apiKeyStorage}
|
||||||
><span class="greyscale mr-2"><Fa icon={faSquarePlus} /></span> New chat</a
|
><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>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
// Reference: https://openai.com/pricing#language-models
|
// Reference: https://openai.com/pricing#language-models
|
||||||
// TODO: Move to settings of some type
|
// TODO: Move to settings of some type
|
||||||
const modelDetails : Record<string, [number, number, number]> = {
|
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-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
|
'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)
|
'gpt-3.5': [0.000002, 0.000002, 4096] // $0.002 per 1000 tokens (both prompt and completion), max 4k
|
||||||
}
|
}
|
||||||
|
|
||||||
const tpCache = {}
|
const tpCache = {}
|
||||||
|
|
Loading…
Reference in New Issue