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()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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')
 | 
			
		||||
  function promptRename () {
 | 
			
		||||
    openModal(PromptInput, {
 | 
			
		||||
      title: 'Enter Name for Chat',
 | 
			
		||||
      label: 'Name',
 | 
			
		||||
      value: chat.name,
 | 
			
		||||
      onSubmit: (value) => {
 | 
			
		||||
        chat.name = (value || '').trim() || chat.name
 | 
			
		||||
        $checkStateChange++
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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