Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 4,237 Bytes
b2ecf7d 76d4920 b2ecf7d 49cc62d b2ecf7d 76d4920 b2ecf7d da081e4 76d4920 b2ecf7d 76d4920 da081e4 b2ecf7d da081e4 b2ecf7d da081e4 b2ecf7d da081e4 b2ecf7d 76d4920 b2ecf7d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<script lang="ts">
import { createEventDispatcher, tick } from "svelte";
import { delay, onCmdEnter } from "../../../../utils/ViewUtils.js";
import WidgetLabel from "../WidgetLabel/WidgetLabel.svelte";
import LogInPopover from "../../../LogInPopover/LogInPopover.svelte";
import { isLoggedIn } from "../../stores.js";
export let label: string = "";
export let placeholder: string = "Your sentence here...";
export let value: string;
export let isLoading = false;
export let isDisabled = false;
export let size: "small" | "big" = "small";
let containerSpanEl: HTMLSpanElement;
let isOnFocus = false;
let popOverOpen = false;
const typingEffectSpeedMs = 12;
const classNamesInput = "whitespace-pre-wrap inline font-normal text-black dark:text-white";
const classNamesOutput = "whitespace-pre-wrap inline text-blue-600 dark:text-blue-400";
const dispatch = createEventDispatcher<{ cmdEnter: void }>();
export async function renderTextOutput(outputTxt: string, typingEffect = true): Promise<void> {
const spanEl = document.createElement("span");
spanEl.contentEditable = isDisabled ? "false" : "true";
spanEl.className = classNamesOutput;
containerSpanEl?.appendChild(spanEl);
await tick();
// fix Chrome bug that adds `<br>` els on contentedtiable el
const brElts = containerSpanEl?.querySelectorAll("br");
for (const brEl of brElts) {
brEl.remove();
}
await tick();
// split on whitespace or any other character to correctly render newlines \n
if (typingEffect) {
for (const char of outputTxt.split(/(\s|.)/g)) {
await delay(typingEffectSpeedMs);
spanEl.textContent += char;
if (isOnFocus) {
moveCaretToEnd();
}
}
} else {
spanEl.textContent = outputTxt;
}
updateInnerTextValue();
}
function moveCaretToEnd() {
if (containerSpanEl) {
const range = document.createRange();
range.selectNodeContents(containerSpanEl);
range.collapse(false);
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
}
}
// handle FireFox contenteditable paste bug
function handlePaste(e: ClipboardEvent) {
if (isLoading) {
return e.preventDefault();
}
const copiedTxt = e.clipboardData?.getData("text/plain");
const selection = window.getSelection();
if (selection?.rangeCount && !!copiedTxt?.length) {
const range = selection.getRangeAt(0);
range.deleteContents();
const spanEl = document.createElement("span");
spanEl.contentEditable = "true";
spanEl.className = classNamesInput;
spanEl.textContent = copiedTxt;
range.insertNode(spanEl);
}
window.getSelection()?.collapseToEnd();
updateInnerTextValue();
}
function updateInnerTextValue() {
value = containerSpanEl?.textContent ?? "";
}
function onFocus() {
isOnFocus = true;
moveCaretToEnd();
}
export function setValue(text: string): void {
containerSpanEl.textContent = text;
updateInnerTextValue();
}
</script>
<LogInPopover bind:open={popOverOpen}>
<WidgetLabel {label}>
<svelte:fragment slot="after">
<!-- `whitespace-pre-wrap inline-block` are needed to get correct newlines from `el.textContent` on Chrome -->
<span
class="{label ? 'mt-1.5' : ''} block w-full resize-y overflow-auto py-2 px-3 {size === 'small'
? 'min-h-[42px]'
: 'min-h-[144px]'} inline-block max-h-[500px] whitespace-pre-wrap rounded-lg border border-gray-200 shadow-inner outline-none focus:shadow-inner focus:ring focus:ring-blue-200 dark:bg-gray-925"
role="textbox"
style="--placeholder: '{isDisabled ? '' : placeholder}'"
spellcheck="false"
dir="auto"
contenteditable
class:pointer-events-none={isLoading || isDisabled}
use:onCmdEnter={{ disabled: isLoading || isDisabled }}
on:cmdEnter={() => {
if (!$isLoggedIn) {
popOverOpen = true;
return;
}
dispatch("cmdEnter");
}}
bind:this={containerSpanEl}
on:paste|preventDefault={handlePaste}
on:input={updateInnerTextValue}
on:focus={onFocus}
on:blur={() => (isOnFocus = false)}
/>
</svelte:fragment>
</WidgetLabel>
</LogInPopover>
<style>
span[contenteditable]:empty::before {
content: var(--placeholder);
color: rgba(156, 163, 175);
}
</style>
|