Lay groundwork for dynamic modals
This commit is contained in:
parent
49c7570952
commit
39233051da
|
@ -34,6 +34,7 @@
|
|||
"svelte-highlight": "^7.2.1",
|
||||
"svelte-local-storage-store": "^0.4.0",
|
||||
"svelte-markdown": "^0.2.3",
|
||||
"svelte-modals": "^1.2.1",
|
||||
"svelte-spa-router": "^3.3.0",
|
||||
"svelte-use-click-outside": "^1.0.0",
|
||||
"tslib": "^2.5.0",
|
||||
|
@ -4186,6 +4187,15 @@
|
|||
"integrity": "sha512-vSSbKZFbNktrQ15v7o1EaH78EbWV+sPQbPjHG+Cp8CaNcPFUEfjZ0Iml/V0bFDwsTlYe8o6XC5Hfdp91cqPV2g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svelte-modals": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-modals/-/svelte-modals-1.2.1.tgz",
|
||||
"integrity": "sha512-7MEKUx5wb5YppkXWFGeRlYM5FMGEnpix39u9Y6GtCNTMXRDZ7DB2Z50IYLMRTMW5lOsCdtJgFbB0E3iZMKmsAA==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-preprocess": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.0.3.tgz",
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"svelte-highlight": "^7.2.1",
|
||||
"svelte-local-storage-store": "^0.4.0",
|
||||
"svelte-markdown": "^0.2.3",
|
||||
"svelte-modals": "^1.2.1",
|
||||
"svelte-spa-router": "^3.3.0",
|
||||
"svelte-use-click-outside": "^1.0.0",
|
||||
"tslib": "^2.5.0",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import Chat from './lib/Chat.svelte'
|
||||
import NewChat from './lib/NewChat.svelte'
|
||||
import { chatsStorage, apiKeyStorage } from './lib/Storage.svelte'
|
||||
import { Modals, closeModal } from 'svelte-modals'
|
||||
|
||||
// The definition of the routes with some conditions
|
||||
const routes = {
|
||||
|
@ -31,7 +32,6 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
|
||||
<Navbar />
|
||||
<div class="side-bar-column">
|
||||
<Sidebar />
|
||||
|
@ -41,3 +41,23 @@
|
|||
<Router {routes} on:conditionsFailed={() => replace('/')}/>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<Modals>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
slot="backdrop"
|
||||
class="backdrop"
|
||||
on:click={closeModal}
|
||||
/>
|
||||
</Modals>
|
||||
|
||||
<style>
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: transparent
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -60,7 +60,7 @@ html {
|
|||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
.modal-card {
|
||||
.modal-card.wide {
|
||||
width: 960px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
import { autoGrowInputOnEvent, sizeTextElements } from './Util.svelte'
|
||||
import ChatSettingsModal from './ChatSettingsModal.svelte'
|
||||
import Footer from './Footer.svelte'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptInput from './PromptInput.svelte'
|
||||
|
||||
// This makes it possible to override the OpenAI API base URL in the .env file
|
||||
const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com'
|
||||
|
@ -51,7 +53,7 @@
|
|||
let updatingMessage: string = ''
|
||||
let input: HTMLTextAreaElement
|
||||
// let settings: HTMLDivElement
|
||||
let chatNameSettings: HTMLFormElement
|
||||
// let chatNameSettings: HTMLFormElement
|
||||
let recognition: any = null
|
||||
let recording = false
|
||||
|
||||
|
@ -469,24 +471,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
const showChatNameSettings = () => {
|
||||
chatNameSettings.classList.add('is-active');
|
||||
(chatNameSettings.querySelector('#settings-chat-name') as HTMLInputElement).focus();
|
||||
(chatNameSettings.querySelector('#settings-chat-name') as HTMLInputElement).select()
|
||||
function promptRename () {
|
||||
openModal(PromptInput, {
|
||||
title: 'Enter Name for Chat',
|
||||
label: 'Name',
|
||||
value: chat.name,
|
||||
onSubmit: (value) => {
|
||||
chat.name = (value || '').trim() || chat.name
|
||||
$checkStateChange++
|
||||
}
|
||||
|
||||
const saveChatNameSettings = () => {
|
||||
const newChatName = (chatNameSettings.querySelector('#settings-chat-name') as HTMLInputElement).value
|
||||
// save if changed
|
||||
if (newChatName && newChatName !== chat.name) {
|
||||
chat.name = newChatName
|
||||
chatsStorage.set($chatsStorage)
|
||||
}
|
||||
closeChatNameSettings()
|
||||
}
|
||||
|
||||
const closeChatNameSettings = () => {
|
||||
chatNameSettings.classList.remove('is-active')
|
||||
})
|
||||
}
|
||||
|
||||
const recordToggle = () => {
|
||||
|
@ -503,48 +497,13 @@
|
|||
|
||||
<ChatSettingsModal chatId={chatId} bind:show={showSettingsModal} />
|
||||
|
||||
|
||||
<!-- rename modal -->
|
||||
<form class="modal" bind:this={chatNameSettings} on:submit={saveChatNameSettings}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="modal-background" on:click={closeChatNameSettings} />
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Enter a new name for this chat</p>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label is-normal">
|
||||
<label class="label" for="settings-chat-name">New name:</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
id="settings-chat-name"
|
||||
value={chat.name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<input type="submit" class="button is-info" value="Save" />
|
||||
<button class="button" on:click={closeChatNameSettings}>Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</form>
|
||||
<!-- end -->
|
||||
|
||||
|
||||
<div class="chat-content">
|
||||
<nav class="level chat-header">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<p class="subtitle is-5">
|
||||
<span>{chat.name || `Chat ${chat.id}`}</span>
|
||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Rename chat" on:click|preventDefault={showChatNameSettings}><Fa icon={faPenToSquare} /></a>
|
||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Rename chat" on:click|preventDefault={promptRename}><Fa icon={faPenToSquare} /></a>
|
||||
<a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Suggest a chat name" on:click|preventDefault={suggestName}><Fa icon={faLightbulb} /></a>
|
||||
<!-- <a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Copy this chat" on:click|preventDefault={() => { copyChat(chatId) }}><Fa icon={faClone} /></a> -->
|
||||
<!-- <a href={'#'} class="greyscale ml-2 is-hidden has-text-weight-bold editbutton" title="Delete this chat" on:click|preventDefault={deleteChat}><Fa icon={faTrash} /></a> -->
|
||||
|
@ -617,11 +576,3 @@
|
|||
{/each}
|
||||
</div>
|
||||
</Footer>
|
||||
|
||||
<svelte:window
|
||||
on:keydown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
closeChatNameSettings()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -255,7 +255,7 @@
|
|||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="modal" class:is-active={showSettingsModal}>
|
||||
<div class="modal-background" on:click={closeSettings} />
|
||||
<div class="modal-card" on:click={() => { showProfileMenu = false }}>
|
||||
<div class="modal-card wide" on:click={() => { showProfileMenu = false }}>
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">Chat Settings</p>
|
||||
<button class="delete" aria-label="close" on:click={closeSettings}></button>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts">
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import { closeModal } from 'svelte-modals'
|
||||
import {
|
||||
faExclamation
|
||||
} from '@fortawesome/free-solid-svg-icons/index'
|
||||
import { onMount } from 'svelte'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
// provided by <Modals />
|
||||
export let isOpen:boolean
|
||||
|
||||
export let title:string
|
||||
export let label:string
|
||||
export let value:any
|
||||
|
||||
export let onSubmit:(value:any)=>boolean|void
|
||||
export let onClose:(()=>boolean|void) = () => {}
|
||||
export let saveButton:string = 'Save'
|
||||
export let closeButton:string = 'Cancel'
|
||||
export let placeholder:string = ''
|
||||
export let error:string = ''
|
||||
export let icon:Fa|null = null
|
||||
|
||||
const id = uuidv4()
|
||||
|
||||
onMount(async () => {
|
||||
const input = document.getElementById(id)
|
||||
input && input.focus()
|
||||
})
|
||||
|
||||
const doClose = () => {
|
||||
if (!onClose || !onClose()) closeModal()
|
||||
}
|
||||
|
||||
const doSubmit = (value) => {
|
||||
onSubmit(value)
|
||||
closeModal()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<div class="modal is-active">
|
||||
<form action="{'#'}" on:submit|preventDefault={() => { doSubmit(value) }}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="modal-background" on:click={doClose} />
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{title}</p>
|
||||
<button class="delete" aria-label="close" type="button" on:click={doClose}></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="field">
|
||||
<label class="label" for="text-input">{label}</label>
|
||||
<div class="control" class:has-icons-left={icon} class:has-icons-right={error} >
|
||||
<input id={id} name="text-input" class="input" class:is-danger={error} type="text" placeholder={placeholder} bind:value={value}>
|
||||
{#if icon}
|
||||
<span class="icon is-small is-left">
|
||||
<Fa icon={icon}/>
|
||||
</span>
|
||||
{/if}
|
||||
{#if error}
|
||||
<span class="icon is-small is-right">
|
||||
<Fa icon={faExclamation}/>
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if error}
|
||||
<p class="help is-danger">{error}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<input type="submit" class="button is-info" value="{saveButton}" />
|
||||
<button class="button" type="button" on:click={doClose} >{closeButton}</button>
|
||||
</footer>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{/if}
|
||||
<!--
|
||||
<svelte:window
|
||||
on:keydown={(event) => {
|
||||
if (event.key === 'Escape') {
|
||||
closeModal()
|
||||
}
|
||||
}}
|
||||
/> -->
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { params } from 'svelte-spa-router'
|
||||
import ChatMenuItem from './ChatMenuItem.svelte'
|
||||
import { apiKeyStorage, chatsStorage, pinMainMenu } from './Storage.svelte'
|
||||
import { apiKeyStorage, chatsStorage, pinMainMenu, checkStateChange } 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'
|
||||
|
@ -9,7 +9,6 @@
|
|||
import { clickOutside } from 'svelte-use-click-outside'
|
||||
|
||||
$: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id)
|
||||
|
||||
$: activeChatId = $params && $params.chatId ? parseInt($params.chatId) : undefined
|
||||
|
||||
</script>
|
||||
|
@ -29,9 +28,11 @@
|
|||
{#if sortedChats.length === 0}
|
||||
<li><a href={'#'} class="is-disabled">No chats yet...</a></li>
|
||||
{:else}
|
||||
{#key $checkStateChange}
|
||||
{#each sortedChats as chat, i}
|
||||
<ChatMenuItem activeChatId={activeChatId} chat={chat} prevChat={sortedChats[i - 1]} nextChat={sortedChats[i + 1]} />
|
||||
{/each}
|
||||
{/key}
|
||||
{/if}
|
||||
</ul>
|
||||
<!-- <p class="menu-label">Actions</p> -->
|
||||
|
|
Loading…
Reference in New Issue