chat room is scrollable
This commit is contained in:
		
							parent
							
								
									66c0fe57df
								
							
						
					
					
						commit
						87a8fdc1d4
					
				
							
								
								
									
										12
									
								
								src/app.scss
								
								
								
								
							
							
						
						
									
										12
									
								
								src/app.scss
								
								
								
								
							| 
						 | 
					@ -94,4 +94,16 @@ div.copy-prompt {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  top: 5px;
 | 
					  top: 5px;
 | 
				
			||||||
  right: 5px;
 | 
					  right: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.message-container{
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  height: 720px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.message-list{
 | 
				
			||||||
 | 
					  // max-height: 100%;
 | 
				
			||||||
 | 
					  overflow-y: auto;
 | 
				
			||||||
 | 
					  flex: 1;
 | 
				
			||||||
 | 
					  margin-bottom: 10px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -102,7 +102,7 @@
 | 
				
			||||||
    // Scroll to the bottom of the page after any updates to the messages array
 | 
					    // Scroll to the bottom of the page after any updates to the messages array
 | 
				
			||||||
    window.scrollTo(0, document.body.scrollHeight);
 | 
					    window.scrollTo(0, document.body.scrollHeight);
 | 
				
			||||||
    input.focus();
 | 
					    input.focus();
 | 
				
			||||||
    copyFunction()
 | 
					    copyFunction();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Marked options
 | 
					  // Marked options
 | 
				
			||||||
| 
						 | 
					@ -286,7 +286,13 @@
 | 
				
			||||||
      const copyPrompt = document.createElement("div");
 | 
					      const copyPrompt = document.createElement("div");
 | 
				
			||||||
      copyPrompt.className = "copy-prompt";
 | 
					      copyPrompt.className = "copy-prompt";
 | 
				
			||||||
      const copyPromptText = document.createElement("button");
 | 
					      const copyPromptText = document.createElement("button");
 | 
				
			||||||
      copyPromptText.classList.add("button", "is-light", "is-outlined", "is-small", "is-responsive");
 | 
					      copyPromptText.classList.add(
 | 
				
			||||||
 | 
					        "button",
 | 
				
			||||||
 | 
					        "is-light",
 | 
				
			||||||
 | 
					        "is-outlined",
 | 
				
			||||||
 | 
					        "is-small",
 | 
				
			||||||
 | 
					        "is-responsive"
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
      copyPromptText.innerHTML = showCopyMessage;
 | 
					      copyPromptText.innerHTML = showCopyMessage;
 | 
				
			||||||
      copyPrompt.appendChild(copyPromptText);
 | 
					      copyPrompt.appendChild(copyPromptText);
 | 
				
			||||||
      block.appendChild(copyPrompt);
 | 
					      block.appendChild(copyPrompt);
 | 
				
			||||||
| 
						 | 
					@ -301,7 +307,7 @@
 | 
				
			||||||
          }, 1000);
 | 
					          }, 1000);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  };
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<nav class="level chat-header">
 | 
					<nav class="level chat-header">
 | 
				
			||||||
| 
						 | 
					@ -357,118 +363,125 @@
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</nav>
 | 
					</nav>
 | 
				
			||||||
 | 
					<div class="is-fullhd message-container">
 | 
				
			||||||
{#each chat.messages as message}
 | 
					  <div class="message-list">
 | 
				
			||||||
  {#if message.role === "user"}
 | 
					    {#each chat.messages as message}
 | 
				
			||||||
    <article
 | 
					      {#if message.role === "user"}
 | 
				
			||||||
      class="message is-info user-message"
 | 
					        <article
 | 
				
			||||||
      class:has-text-right={message.content
 | 
					          class="message is-info user-message"
 | 
				
			||||||
        .split("\n")
 | 
					          class:has-text-right={message.content
 | 
				
			||||||
        .filter((line) => line.trim()).length === 1}
 | 
					            .split("\n")
 | 
				
			||||||
    >
 | 
					            .filter((line) => line.trim()).length === 1}
 | 
				
			||||||
      <div class="message-body content">
 | 
					 | 
				
			||||||
        <a
 | 
					 | 
				
			||||||
          href={"#"}
 | 
					 | 
				
			||||||
          class="greyscale is-pulled-right ml-2 is-hidden editbutton"
 | 
					 | 
				
			||||||
          on:click={() => {
 | 
					 | 
				
			||||||
            input.value = message.content;
 | 
					 | 
				
			||||||
            input.focus();
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          ✏️
 | 
					          <div class="message-body content">
 | 
				
			||||||
        </a>
 | 
					            <a
 | 
				
			||||||
        <SvelteMarkdown
 | 
					              href={"#"}
 | 
				
			||||||
          source={message.content}
 | 
					              class="greyscale is-pulled-right ml-2 is-hidden editbutton"
 | 
				
			||||||
          options={markedownOptions}
 | 
					              on:click={() => {
 | 
				
			||||||
          renderers={{
 | 
					                input.value = message.content;
 | 
				
			||||||
            code: Code,
 | 
					                input.focus();
 | 
				
			||||||
          }}
 | 
					              }}
 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </article>
 | 
					 | 
				
			||||||
  {:else if message.role === "system" || message.role === "error"}
 | 
					 | 
				
			||||||
    <article class="message is-danger">
 | 
					 | 
				
			||||||
      <div class="message-body content">
 | 
					 | 
				
			||||||
        <SvelteMarkdown
 | 
					 | 
				
			||||||
          source={message.content}
 | 
					 | 
				
			||||||
          options={markedownOptions}
 | 
					 | 
				
			||||||
          renderers={{
 | 
					 | 
				
			||||||
            code: Code,
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </article>
 | 
					 | 
				
			||||||
  {:else}
 | 
					 | 
				
			||||||
    <article class="message is-success">
 | 
					 | 
				
			||||||
      <div class="message-body content">
 | 
					 | 
				
			||||||
        <SvelteMarkdown
 | 
					 | 
				
			||||||
          source={message.content}
 | 
					 | 
				
			||||||
          options={markedownOptions}
 | 
					 | 
				
			||||||
          renderers={{
 | 
					 | 
				
			||||||
            code: Code,
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
        {#if message.usage}
 | 
					 | 
				
			||||||
          <p class="is-size-7">
 | 
					 | 
				
			||||||
            This message was generated using <span class="has-text-weight-bold"
 | 
					 | 
				
			||||||
              >{message.usage.total_tokens}</span
 | 
					 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
            tokens ~=
 | 
					              ✏️
 | 
				
			||||||
            <span class="has-text-weight-bold"
 | 
					            </a>
 | 
				
			||||||
              >${(message.usage.total_tokens * token_price).toFixed(6)}</span
 | 
					            <SvelteMarkdown
 | 
				
			||||||
            >
 | 
					              source={message.content}
 | 
				
			||||||
          </p>
 | 
					              options={markedownOptions}
 | 
				
			||||||
        {/if}
 | 
					              renderers={{
 | 
				
			||||||
      </div>
 | 
					                code: Code,
 | 
				
			||||||
    </article>
 | 
					              }}
 | 
				
			||||||
  {/if}
 | 
					            />
 | 
				
			||||||
{/each}
 | 
					          </div>
 | 
				
			||||||
 | 
					        </article>
 | 
				
			||||||
 | 
					      {:else if message.role === "system" || message.role === "error"}
 | 
				
			||||||
 | 
					        <article class="message is-danger">
 | 
				
			||||||
 | 
					          <div class="message-body content">
 | 
				
			||||||
 | 
					            <SvelteMarkdown
 | 
				
			||||||
 | 
					              source={message.content}
 | 
				
			||||||
 | 
					              options={markedownOptions}
 | 
				
			||||||
 | 
					              renderers={{
 | 
				
			||||||
 | 
					                code: Code,
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </article>
 | 
				
			||||||
 | 
					      {:else}
 | 
				
			||||||
 | 
					        <article class="message is-success">
 | 
				
			||||||
 | 
					          <div class="message-body content">
 | 
				
			||||||
 | 
					            <SvelteMarkdown
 | 
				
			||||||
 | 
					              source={message.content}
 | 
				
			||||||
 | 
					              options={markedownOptions}
 | 
				
			||||||
 | 
					              renderers={{
 | 
				
			||||||
 | 
					                code: Code,
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            {#if message.usage}
 | 
				
			||||||
 | 
					              <p class="is-size-7">
 | 
				
			||||||
 | 
					                This message was generated using <span
 | 
				
			||||||
 | 
					                  class="has-text-weight-bold"
 | 
				
			||||||
 | 
					                  >{message.usage.total_tokens}</span
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                tokens ~=
 | 
				
			||||||
 | 
					                <span class="has-text-weight-bold"
 | 
				
			||||||
 | 
					                  >${(message.usage.total_tokens * token_price).toFixed(
 | 
				
			||||||
 | 
					                    6
 | 
				
			||||||
 | 
					                  )}</span
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					              </p>
 | 
				
			||||||
 | 
					            {/if}
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </article>
 | 
				
			||||||
 | 
					      {/if}
 | 
				
			||||||
 | 
					    {/each}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div class="message-send">
 | 
				
			||||||
 | 
					    {#if updating}
 | 
				
			||||||
 | 
					      <progress class="progress is-small is-dark" max="100" />
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if updating}
 | 
					    <form
 | 
				
			||||||
  <progress class="progress is-small is-dark" max="100" />
 | 
					      class="field has-addons has-addons-right"
 | 
				
			||||||
{/if}
 | 
					      on:submit|preventDefault={() => submitForm()}
 | 
				
			||||||
 | 
					 | 
				
			||||||
<form
 | 
					 | 
				
			||||||
  class="field has-addons has-addons-right"
 | 
					 | 
				
			||||||
  on:submit|preventDefault={() => submitForm()}
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
  <p class="control is-expanded">
 | 
					 | 
				
			||||||
    <textarea
 | 
					 | 
				
			||||||
      class="input is-info is-focused chat-input"
 | 
					 | 
				
			||||||
      placeholder="Type your message here..."
 | 
					 | 
				
			||||||
      rows="1"
 | 
					 | 
				
			||||||
      on:keydown={(e) => {
 | 
					 | 
				
			||||||
        // Only send if Enter is pressed, not Shift+Enter
 | 
					 | 
				
			||||||
        if (e.key === "Enter" && !e.shiftKey) {
 | 
					 | 
				
			||||||
          submitForm();
 | 
					 | 
				
			||||||
          e.preventDefault();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      on:input={(e) => {
 | 
					 | 
				
			||||||
        // Resize the textarea to fit the content - auto is important to reset the height after deleting content
 | 
					 | 
				
			||||||
        input.style.height = "auto";
 | 
					 | 
				
			||||||
        input.style.height = input.scrollHeight + "px";
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      bind:this={input}
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
  </p>
 | 
					 | 
				
			||||||
  <p class="control" class:is-hidden={!recognition}>
 | 
					 | 
				
			||||||
    <button
 | 
					 | 
				
			||||||
      class="button"
 | 
					 | 
				
			||||||
      class:is-pulse={recording}
 | 
					 | 
				
			||||||
      on:click|preventDefault={recordToggle}
 | 
					 | 
				
			||||||
      ><span class="greyscale">🎤</span></button
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
  </p>
 | 
					      <p class="control is-expanded">
 | 
				
			||||||
  <p class="control">
 | 
					        <textarea
 | 
				
			||||||
    <button class="button" on:click|preventDefault={showSettings}
 | 
					          class="input is-info is-focused chat-input"
 | 
				
			||||||
      ><span class="greyscale">⚙️</span></button
 | 
					          placeholder="Type your message here..."
 | 
				
			||||||
    >
 | 
					          rows="1"
 | 
				
			||||||
  </p>
 | 
					          on:keydown={(e) => {
 | 
				
			||||||
  <p class="control">
 | 
					            // Only send if Enter is pressed, not Shift+Enter
 | 
				
			||||||
    <button class="button is-info" type="submit">Send</button>
 | 
					            if (e.key === "Enter" && !e.shiftKey) {
 | 
				
			||||||
  </p>
 | 
					              submitForm();
 | 
				
			||||||
</form>
 | 
					              e.preventDefault();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          on:input={(e) => {
 | 
				
			||||||
 | 
					            // Resize the textarea to fit the content - auto is important to reset the height after deleting content
 | 
				
			||||||
 | 
					            input.style.height = "auto";
 | 
				
			||||||
 | 
					            input.style.height = input.scrollHeight + "px";
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					          bind:this={input}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p class="control" class:is-hidden={!recognition}>
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          class="button"
 | 
				
			||||||
 | 
					          class:is-pulse={recording}
 | 
				
			||||||
 | 
					          on:click|preventDefault={recordToggle}
 | 
				
			||||||
 | 
					          ><span class="greyscale">🎤</span></button
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p class="control">
 | 
				
			||||||
 | 
					        <button class="button" on:click|preventDefault={showSettings}
 | 
				
			||||||
 | 
					          ><span class="greyscale">⚙️</span></button
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p class="control">
 | 
				
			||||||
 | 
					        <button class="button is-info" type="submit">Send</button>
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<svelte:window
 | 
					<svelte:window
 | 
				
			||||||
  on:keydown={(event) => {
 | 
					  on:keydown={(event) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue