Use separate Svelte components
This commit is contained in:
		
							parent
							
								
									6656240361
								
							
						
					
					
						commit
						06a383227a
					
				
							
								
								
									
										132
									
								
								src/App.svelte
								
								
								
								
							
							
						
						
									
										132
									
								
								src/App.svelte
								
								
								
								
							| 
						 | 
					@ -1,12 +1,11 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import logo from "./assets/logo.svg";
 | 
					  import Navbar from "./lib/Navbar.svelte";
 | 
				
			||||||
 | 
					  import Sidebar from "./lib/Sidebar.svelte";
 | 
				
			||||||
 | 
					  import Home from "./lib/Home.svelte";
 | 
				
			||||||
  import Chat from "./lib/Chat.svelte";
 | 
					  import Chat from "./lib/Chat.svelte";
 | 
				
			||||||
  import {
 | 
					  import Footer from "./lib/Footer.svelte";
 | 
				
			||||||
    addChat,
 | 
					
 | 
				
			||||||
    apiKeyStorage,
 | 
					  import { apiKeyStorage, chatsStorage } from "./lib/Storage.svelte";
 | 
				
			||||||
    chatsStorage,
 | 
					 | 
				
			||||||
    clearChats,
 | 
					 | 
				
			||||||
  } from "./lib/Storage.svelte";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  $: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id);
 | 
					  $: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id);
 | 
				
			||||||
  $: apiKey = $apiKeyStorage;
 | 
					  $: apiKey = $apiKeyStorage;
 | 
				
			||||||
| 
						 | 
					@ -14,132 +13,23 @@
 | 
				
			||||||
  let activeChatId: number;
 | 
					  let activeChatId: number;
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<nav class="navbar" aria-label="main navigation">
 | 
					<Navbar />
 | 
				
			||||||
  <div class="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>
 | 
					 | 
				
			||||||
</nav>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- svelte-ignore a11y-missing-attribute a11y-click-events-have-key-events -->
 | 
					 | 
				
			||||||
<section class="section">
 | 
					<section class="section">
 | 
				
			||||||
  <div class="container is-fluid">
 | 
					  <div class="container is-fluid">
 | 
				
			||||||
    <div class="columns">
 | 
					    <div class="columns">
 | 
				
			||||||
      <div class="column is-one-fifth">
 | 
					      <div class="column is-one-fifth">
 | 
				
			||||||
        <article class="panel is-link">
 | 
					        <Sidebar bind:apiKey bind:sortedChats bind:activeChatId />
 | 
				
			||||||
          <p class="panel-heading">Chats</p>
 | 
					 | 
				
			||||||
          {#if sortedChats.length === 0}
 | 
					 | 
				
			||||||
            <a class="panel-block">No chats...</a>
 | 
					 | 
				
			||||||
          {:else}
 | 
					 | 
				
			||||||
            {#each sortedChats as chat}
 | 
					 | 
				
			||||||
              <a
 | 
					 | 
				
			||||||
                class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
					 | 
				
			||||||
                on:click={() => (activeChatId = chat.id)}>Chat {chat.id}</a
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
            {/each}
 | 
					 | 
				
			||||||
          {/if}
 | 
					 | 
				
			||||||
        </article>
 | 
					 | 
				
			||||||
        <article class="panel is-link">
 | 
					 | 
				
			||||||
          <a
 | 
					 | 
				
			||||||
            class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
					 | 
				
			||||||
            on:click={() => {
 | 
					 | 
				
			||||||
              activeChatId = addChat();
 | 
					 | 
				
			||||||
            }}>➕ New chat</a
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
          <a
 | 
					 | 
				
			||||||
            class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
					 | 
				
			||||||
            on:click={() => {
 | 
					 | 
				
			||||||
              clearChats();
 | 
					 | 
				
			||||||
              activeChatId = null;
 | 
					 | 
				
			||||||
            }}>🗑️ Clear chats</a
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          <a
 | 
					 | 
				
			||||||
            class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
					 | 
				
			||||||
            on:click={() => {
 | 
					 | 
				
			||||||
              activeChatId = null;
 | 
					 | 
				
			||||||
            }}>🔙 Back to home</a
 | 
					 | 
				
			||||||
          >
 | 
					 | 
				
			||||||
        </article>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="column">
 | 
					      <div class="column">
 | 
				
			||||||
        {#if activeChatId}
 | 
					        {#if activeChatId}
 | 
				
			||||||
          <Chat chatId={activeChatId} />
 | 
					          <Chat bind:chatId={activeChatId} />
 | 
				
			||||||
        {:else}
 | 
					        {:else}
 | 
				
			||||||
          <article class="message">
 | 
					          <Home bind:activeChatId />
 | 
				
			||||||
            <div class="message-body">
 | 
					 | 
				
			||||||
              <strong
 | 
					 | 
				
			||||||
                ><a href="https://github.com/Niek/chatgpt-web">ChatGPT-web</a
 | 
					 | 
				
			||||||
                ></strong
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
              is a simple one-page web interface to the OpenAI ChatGPT API. To use
 | 
					 | 
				
			||||||
              it, you need to register for
 | 
					 | 
				
			||||||
              <a
 | 
					 | 
				
			||||||
                href="https://platform.openai.com/account/api-key"
 | 
					 | 
				
			||||||
                target="_blank"
 | 
					 | 
				
			||||||
                rel="noreferrer">an OpenAI API key</a
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
              first. All messages are stored in your browser's local storage, so
 | 
					 | 
				
			||||||
              everything is <strong>private</strong>. You can also close the
 | 
					 | 
				
			||||||
              browser tab and come back later to continue the conversation.
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </article>
 | 
					 | 
				
			||||||
          <article class="message {!apiKey ? 'is-danger' : 'is-warning'}">
 | 
					 | 
				
			||||||
            <div class="message-body">
 | 
					 | 
				
			||||||
              Set your OpenAI API key below:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <form
 | 
					 | 
				
			||||||
                class="field has-addons has-addons-right"
 | 
					 | 
				
			||||||
                on:submit|preventDefault={(event) => {
 | 
					 | 
				
			||||||
                  apiKeyStorage.set(event.target[0].value);
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <p class="control is-expanded">
 | 
					 | 
				
			||||||
                  <input
 | 
					 | 
				
			||||||
                    type="text"
 | 
					 | 
				
			||||||
                    class="input {!apiKey ? 'is-danger' : ''}"
 | 
					 | 
				
			||||||
                    value={apiKey}
 | 
					 | 
				
			||||||
                  />
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                <p class="control">
 | 
					 | 
				
			||||||
                  <button class="button is-info" type="submit">Save</button>
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
              </form>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              {#if !apiKey}
 | 
					 | 
				
			||||||
                <p class="help is-danger">
 | 
					 | 
				
			||||||
                  Please enter your OpenAI API key above to use ChatGPT-web
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
              {/if}
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </article>
 | 
					 | 
				
			||||||
          <article class="message is-info">
 | 
					 | 
				
			||||||
            <div class="message-body">
 | 
					 | 
				
			||||||
              Select an existing chat on the sidebar, or <a
 | 
					 | 
				
			||||||
                class={!apiKey ? "is-disabled" : ""}
 | 
					 | 
				
			||||||
                on:click={() => {
 | 
					 | 
				
			||||||
                  activeChatId = addChat();
 | 
					 | 
				
			||||||
                }}>create a new chat</a
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
          </article>
 | 
					 | 
				
			||||||
        {/if}
 | 
					        {/if}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<footer class="footer">
 | 
					<Footer />
 | 
				
			||||||
  <div class="content has-text-centered">
 | 
					 | 
				
			||||||
    <p>
 | 
					 | 
				
			||||||
      <strong>ChatGPT-web</strong>
 | 
					 | 
				
			||||||
      by
 | 
					 | 
				
			||||||
      <a href="https://niekvandermaas.nl/">Niek van der Maas</a>
 | 
					 | 
				
			||||||
      — see
 | 
					 | 
				
			||||||
      <a href="https://github.com/Niek/chatgpt-web">GitHub</a>
 | 
					 | 
				
			||||||
      for source code.
 | 
					 | 
				
			||||||
    </p>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
</footer>
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,10 +112,10 @@
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<button
 | 
					<button
 | 
				
			||||||
  class="button is-danger is-pulled-right"
 | 
					  class="button is-warning is-pulled-right"
 | 
				
			||||||
  on:click={() => {
 | 
					  on:click={() => {
 | 
				
			||||||
    clearMessages(chatId);
 | 
					    clearMessages(chatId);
 | 
				
			||||||
  }}>Clear messages</button
 | 
					  }}>🗑️ Clear messages</button
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<p class="subtitle">Chat {chatId}</p>
 | 
					<p class="subtitle">Chat {chatId}</p>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					<footer class="footer">
 | 
				
			||||||
 | 
					  <div class="content has-text-centered">
 | 
				
			||||||
 | 
					    <p>
 | 
				
			||||||
 | 
					      <strong>ChatGPT-web</strong>
 | 
				
			||||||
 | 
					      by
 | 
				
			||||||
 | 
					      <a href="https://niekvandermaas.nl/">Niek van der Maas</a>
 | 
				
			||||||
 | 
					      — see
 | 
				
			||||||
 | 
					      <a href="https://github.com/Niek/chatgpt-web">GitHub</a>
 | 
				
			||||||
 | 
					      for source code.
 | 
				
			||||||
 | 
					    </p>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</footer>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,66 @@
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import { addChat, apiKeyStorage } from "./Storage.svelte";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  $: apiKey = $apiKeyStorage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  export let activeChatId: number;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<article class="message">
 | 
				
			||||||
 | 
					  <div class="message-body">
 | 
				
			||||||
 | 
					    <strong
 | 
				
			||||||
 | 
					      ><a href="https://github.com/Niek/chatgpt-web">ChatGPT-web</a></strong
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					    is a simple one-page web interface to the OpenAI ChatGPT API. To use it, you
 | 
				
			||||||
 | 
					    need to register for
 | 
				
			||||||
 | 
					    <a
 | 
				
			||||||
 | 
					      href="https://platform.openai.com/account/api-key"
 | 
				
			||||||
 | 
					      target="_blank"
 | 
				
			||||||
 | 
					      rel="noreferrer">an OpenAI API key</a
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					    first. All messages are stored in your browser's local storage, so everything
 | 
				
			||||||
 | 
					    is <strong>private</strong>. You can also close the browser tab and come
 | 
				
			||||||
 | 
					    back later to continue the conversation.
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</article>
 | 
				
			||||||
 | 
					<article class="message {!apiKey ? 'is-danger' : 'is-warning'}">
 | 
				
			||||||
 | 
					  <div class="message-body">
 | 
				
			||||||
 | 
					    Set your OpenAI API key below:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <form
 | 
				
			||||||
 | 
					      class="field has-addons has-addons-right"
 | 
				
			||||||
 | 
					      on:submit|preventDefault={(event) => {
 | 
				
			||||||
 | 
					        apiKeyStorage.set(event.target[0].value);
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <p class="control is-expanded">
 | 
				
			||||||
 | 
					        <input
 | 
				
			||||||
 | 
					          type="text"
 | 
				
			||||||
 | 
					          class="input {!apiKey ? 'is-danger' : ''}"
 | 
				
			||||||
 | 
					          value={apiKey}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p class="control">
 | 
				
			||||||
 | 
					        <button class="button is-info" type="submit">Save</button>
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {#if !apiKey}
 | 
				
			||||||
 | 
					      <p class="help is-danger">
 | 
				
			||||||
 | 
					        Please enter your OpenAI API key above to use ChatGPT-web
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</article>
 | 
				
			||||||
 | 
					<article class="message is-info">
 | 
				
			||||||
 | 
					  <div class="message-body">
 | 
				
			||||||
 | 
					    <!-- svelte-ignore a11y-missing-attribute a11y-click-events-have-key-events -->
 | 
				
			||||||
 | 
					    Select an existing chat on the sidebar, or
 | 
				
			||||||
 | 
					    <a
 | 
				
			||||||
 | 
					      class={!apiKey ? "is-disabled" : ""}
 | 
				
			||||||
 | 
					      on:click={() => {
 | 
				
			||||||
 | 
					        activeChatId = addChat();
 | 
				
			||||||
 | 
					      }}>create a new chat</a
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</article>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import logo from "../assets/logo.svg";
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<nav class="navbar" aria-label="main navigation">
 | 
				
			||||||
 | 
					  <div class="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>
 | 
				
			||||||
 | 
					</nav>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import { addChat, clearChats } from "./Storage.svelte";
 | 
				
			||||||
 | 
					  import type { Chat } from "./Types.svelte";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  export let activeChatId: number;
 | 
				
			||||||
 | 
					  export let sortedChats: Chat[];
 | 
				
			||||||
 | 
					  export let apiKey: string;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- svelte-ignore a11y-missing-attribute a11y-click-events-have-key-events -->
 | 
				
			||||||
 | 
					<aside class="menu">
 | 
				
			||||||
 | 
					  <p class="menu-label">Chats</p>
 | 
				
			||||||
 | 
					  <ul class="menu-list">
 | 
				
			||||||
 | 
					    {#if sortedChats.length === 0}
 | 
				
			||||||
 | 
					      <a class="panel-block">No chats...</a>
 | 
				
			||||||
 | 
					    {:else}
 | 
				
			||||||
 | 
					      <li>
 | 
				
			||||||
 | 
					        <ul>
 | 
				
			||||||
 | 
					          {#each sortedChats as chat}
 | 
				
			||||||
 | 
					            <li>
 | 
				
			||||||
 | 
					              <a
 | 
				
			||||||
 | 
					                class="panel-block {!apiKey
 | 
				
			||||||
 | 
					                  ? 'is-disabled'
 | 
				
			||||||
 | 
					                  : ''} {activeChatId === chat.id
 | 
				
			||||||
 | 
					                  ? 'has-background-light'
 | 
				
			||||||
 | 
					                  : ''}"
 | 
				
			||||||
 | 
					                on:click={() => (activeChatId = chat.id)}>Chat {chat.id}</a
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					          {/each}
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      </li>
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
 | 
					  </ul>
 | 
				
			||||||
 | 
					  <p class="menu-label">Actions</p>
 | 
				
			||||||
 | 
					  <ul class="menu-list">
 | 
				
			||||||
 | 
					    <li>
 | 
				
			||||||
 | 
					      <a
 | 
				
			||||||
 | 
					        class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
				
			||||||
 | 
					        on:click={() => {
 | 
				
			||||||
 | 
					          activeChatId = addChat();
 | 
				
			||||||
 | 
					        }}>➕ New chat</a
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </li>
 | 
				
			||||||
 | 
					    <li>
 | 
				
			||||||
 | 
					      <a
 | 
				
			||||||
 | 
					        class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
				
			||||||
 | 
					        on:click={() => {
 | 
				
			||||||
 | 
					          clearChats();
 | 
				
			||||||
 | 
					          activeChatId = null;
 | 
				
			||||||
 | 
					        }}>🗑️ Clear chats</a
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </li>
 | 
				
			||||||
 | 
					    <li>
 | 
				
			||||||
 | 
					      <a
 | 
				
			||||||
 | 
					        class="panel-block {!apiKey ? 'is-disabled' : ''}"
 | 
				
			||||||
 | 
					        on:click={() => {
 | 
				
			||||||
 | 
					          activeChatId = null;
 | 
				
			||||||
 | 
					        }}>🔙 Back to home</a
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </li>
 | 
				
			||||||
 | 
					  </ul>
 | 
				
			||||||
 | 
					</aside>
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import "./app.scss";
 | 
					import "./app.scss";
 | 
				
			||||||
import App from "./App.svelte";
 | 
					import App from "./App.svelte";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// @ts-ignore
 | 
					 | 
				
			||||||
const app = new App({
 | 
					const app = new App({
 | 
				
			||||||
  target: document.getElementById("app"),
 | 
					  target: document.getElementById("app"),
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue