Spaces:
Runtime error
Runtime error
| const App = { | |
| persona: { | |
| slug: "", | |
| text: "", | |
| }, | |
| messages: [] as Message[], | |
| /// HTMLElements | |
| messagesRoot: document.querySelector('div.messages') as HTMLDivElement, | |
| divPersona: document.querySelector('div.persona') as HTMLDivElement, | |
| linkSuggest: document.querySelector('.chat-suggestion .js-suggestion') as HTMLLinkElement, | |
| sliders: Array.from( | |
| document.querySelectorAll('.decoder-settings input.slider') | |
| ) as HTMLInputElement[], | |
| }; | |
| document.addEventListener('DOMContentLoaded', () => { | |
| /** | |
| * Persona | |
| * - in simple mode, we just re-format the text slightly to make it prettier. | |
| * - in attention mode, we tokenize and add spans around every token. | |
| */ | |
| const simplePersona = () => { | |
| const text = App.persona.text; | |
| const html = text.split('.').map(x => Utils.capitalize(x)).join(`.<br>`); | |
| App.divPersona.innerHTML = html; | |
| }; | |
| const tokenizePersona = () => { | |
| const text = App.persona.text; | |
| const tokens = text.split(/\b/).filter(x => ! /\s/.test(x)); | |
| let html = ``; | |
| for (let [i, tok] of tokens.entries()) { | |
| if (i === 0) { | |
| html += `<span data-idx="${i}">${ Utils.capitalize(tok) }</span>`; | |
| } else if (i > 0 && tokens[i-1] === '.') { | |
| html += `<br><span data-idx="${i}">${ Utils.capitalize(tok) }</span>`; | |
| } else if (/[.,']/.test(tok)) { | |
| html += `<span data-idx="${i}">${ tok }</span>`; | |
| } else { | |
| html += ` <span data-idx="${i}">${ tok }</span>`; | |
| } | |
| } | |
| App.divPersona.innerHTML = html; | |
| }; | |
| App.persona = (<any>window).PERSONA_ATLOAD; | |
| // ^^ tokenizePersona(); | |
| simplePersona(); | |
| document.querySelector('.js-shuffle')!.addEventListener('click', async (evt) => { | |
| evt.preventDefault(); | |
| App.persona = await Api.shared.getShuffle(); | |
| tokenizePersona(); | |
| history.replaceState(null, "", `/persona/${App.persona.slug}`); | |
| /// Also reset messages and reload suggestion. | |
| App.messages = []; | |
| App.messagesRoot.innerHTML = ""; | |
| loadSuggestion(); | |
| }); | |
| document.querySelector('.js-share')!.addEventListener('click', async (evt) => { | |
| evt.preventDefault(); | |
| history.replaceState(null, "", `/persona/${App.persona.slug}`); | |
| const text = `Chat with me: ${ App.divPersona.innerText.replace(/\n/g, " ") }`; | |
| window.open(`https://twitter.com/share?url=${ encodeURIComponent(window.location.href) }&text=${ encodeURIComponent(text) }`); | |
| }); | |
| /** | |
| * Settings | |
| */ | |
| const handleSliderChange = (slider: HTMLInputElement) => { | |
| const div = slider.parentNode as HTMLDivElement; | |
| const spanVal = div.querySelector('.js-val') as HTMLSpanElement; | |
| const value = Number.isInteger(slider.valueAsNumber) | |
| ? slider.valueAsNumber | |
| : Number(slider.valueAsNumber.toFixed(2)) | |
| ; | |
| spanVal.innerText = value.toString(); | |
| const min = Number(slider.getAttribute('min')); | |
| const max = Number(slider.getAttribute('max')); | |
| if (value < min + (max - min) / 3) { | |
| spanVal.className = "js-val green"; | |
| } else if (value < min + 2 * (max - min) / 3) { | |
| spanVal.className = "js-val orange"; | |
| } else { | |
| spanVal.className = "js-val red"; | |
| } | |
| const isInverted = slider.classList.contains('js-inverted'); | |
| if (isInverted) { | |
| if (spanVal.classList.contains('green')) { | |
| spanVal.classList.remove('green'); | |
| spanVal.classList.add('red'); | |
| } else if (spanVal.classList.contains('red')) { | |
| spanVal.classList.remove('red'); | |
| spanVal.classList.add('green'); | |
| } | |
| } | |
| }; | |
| for (const slider of App.sliders) { | |
| handleSliderChange(slider); | |
| slider.addEventListener('input', () => { | |
| handleSliderChange(slider); | |
| }); | |
| } | |
| const gauge = document.querySelector('div.gauge') as HTMLDivElement; | |
| gauge.addEventListener('click', () => { | |
| gauge.classList.add('active'); | |
| }); | |
| const gaugeEls = Array.from( | |
| document.querySelectorAll('.gauge .gauge-el-wrapper') | |
| ); | |
| for (const gaugeEl of gaugeEls) { | |
| gaugeEl.addEventListener('click', () => { | |
| const i = gaugeEls.indexOf(gaugeEl); | |
| if (i === 0) { | |
| App.sliders[0].value = `180`; | |
| App.sliders[1].value = `0.1`; | |
| App.sliders[2].value = `1.9`; | |
| } else if (i === 1) { | |
| App.sliders[0].value = `70`; | |
| App.sliders[1].value = `0.5`; | |
| App.sliders[2].value = `1.2`; | |
| } else { | |
| App.sliders[0].value = `0`; | |
| App.sliders[1].value = `0.9`; | |
| App.sliders[2].value = `0.6`; | |
| } | |
| for (const slider of App.sliders) { | |
| handleSliderChange(slider); | |
| } | |
| }); | |
| } | |
| /** | |
| * Chat input | |
| */ | |
| const form = document.querySelector('form.js-form') as HTMLFormElement; | |
| const input = document.querySelector('input.input-message') as HTMLInputElement; | |
| form.addEventListener('submit', async (evt) => { | |
| evt.preventDefault(); | |
| document.querySelector<HTMLDivElement>('.placeholder-start')!.style.display = 'none'; | |
| const content = input.value; | |
| if (content.trim() === "") { | |
| c.debug(`Empty input`); | |
| return ; | |
| } | |
| input.value = ""; | |
| const um: Message = { | |
| incoming: false, | |
| content: content, | |
| }; | |
| Markup.append(um); | |
| const o = await Api.shared.postWithSettings({ text: content }, { | |
| context: App.messages, | |
| persona: App.persona.text, | |
| }); | |
| c.log(o); | |
| // c.log(o.attention[0]); | |
| // c.log(o.attention[1]); | |
| // c.log(o.attention[2]); | |
| App.messages.push(um); | |
| /// ^^ not before the API call because it shouldn't be included in context. | |
| /** | |
| * Visualize Attention | |
| */ | |
| // for (const [idx, [token, att]] of o.attention[0].entries()) { | |
| // if (att === 0) { | |
| // continue; | |
| // } | |
| // const span = document.querySelector(`.persona span[data-idx="${idx}"]`); | |
| // if (!span) { | |
| // c.error(`span not found`); | |
| // return ; | |
| // } | |
| // span.className = `attention-level level${ Markup.attentionThreshold(att) }`; | |
| // } | |
| ////// | |
| const m: Message = { | |
| incoming: true, | |
| content: o.text, | |
| }; | |
| App.messages.push(m); | |
| Markup.append(m); | |
| /// Finally, launch an auto-complete: | |
| loadSuggestion(); | |
| }); | |
| /** | |
| * Suggestion box | |
| */ | |
| const loadSuggestion = async () => { | |
| const spanLoading = document.querySelector('.chat-suggestion .js-loading') as HTMLSpanElement; | |
| spanLoading.classList.remove('hide'); | |
| App.linkSuggest.classList.add('hide'); | |
| const o = await Api.shared.postWithSettings({ completion: "" }, { | |
| context: App.messages, | |
| persona: App.persona.text, | |
| }); | |
| c.log(o); | |
| App.linkSuggest.innerText = o.text; | |
| spanLoading.classList.add('hide'); | |
| App.linkSuggest.classList.remove('hide'); | |
| }; | |
| loadSuggestion(); | |
| App.linkSuggest.addEventListener('click', async (evt) => { | |
| evt.preventDefault(); | |
| App.linkSuggest.classList.add('hide'); | |
| input.value = App.linkSuggest.innerText; | |
| await Utils.delay(500); | |
| const sendBtn = form.querySelector('button.input-button') as HTMLButtonElement; | |
| sendBtn.click(); | |
| /// ^^ do not do `form.submit()` as we want to trigger our handler. | |
| }); | |
| }); | |