2023-10-19 17:14:48 +03:00
const colorThemes = document . querySelectorAll ( '[name="theme"]' ) ;
const markdown = window . markdownit ( ) ;
const message _box = document . getElementById ( ` messages ` ) ;
const message _input = document . getElementById ( ` message-input ` ) ;
2023-10-06 21:52:17 +03:00
const box _conversations = document . querySelector ( ` .top ` ) ;
2023-10-19 17:14:48 +03:00
const spinner = box _conversations . querySelector ( ".spinner" ) ;
const stop _generating = document . querySelector ( ` .stop_generating ` ) ;
const send _button = document . querySelector ( ` #send-button ` ) ;
let prompt _lock = false ;
2023-10-06 21:52:17 +03:00
hljs . addPlugin ( new CopyButtonPlugin ( ) ) ;
const format = ( text ) => {
return text . replace ( /(?:\r\n|\r|\n)/g , "<br>" ) ;
} ;
message _input . addEventListener ( "blur" , ( ) => {
window . scrollTo ( 0 , 0 ) ;
} ) ;
message _input . addEventListener ( "focus" , ( ) => {
document . documentElement . scrollTop = document . documentElement . scrollHeight ;
} ) ;
const delete _conversations = async ( ) => {
localStorage . clear ( ) ;
await new _conversation ( ) ;
} ;
const handle _ask = async ( ) => {
message _input . style . height = ` 80px ` ;
message _input . focus ( ) ;
window . scrollTo ( 0 , 0 ) ;
let message = message _input . value ;
if ( message . length > 0 ) {
message _input . value = ` ` ;
await ask _gpt ( message ) ;
}
} ;
const remove _cancel _button = async ( ) => {
stop _generating . classList . add ( ` stop_generating-hiding ` ) ;
setTimeout ( ( ) => {
stop _generating . classList . remove ( ` stop_generating-hiding ` ) ;
stop _generating . classList . add ( ` stop_generating-hidden ` ) ;
} , 300 ) ;
} ;
const ask _gpt = async ( message ) => {
try {
2023-10-10 01:47:58 +03:00
message _input . value = ` ` ;
2023-10-06 21:52:17 +03:00
message _input . innerHTML = ` ` ;
message _input . innerText = ` ` ;
add _conversation ( window . conversation _id , message ) ;
window . scrollTo ( 0 , 0 ) ;
window . controller = new AbortController ( ) ;
2023-10-10 01:47:58 +03:00
jailbreak = document . getElementById ( "jailbreak" ) ;
provider = document . getElementById ( "provider" ) ;
model = document . getElementById ( "model" ) ;
prompt _lock = true ;
window . text = ` ` ;
2023-10-06 21:52:17 +03:00
window . token = message _id ( ) ;
stop _generating . classList . remove ( ` stop_generating-hidden ` ) ;
message _box . innerHTML += `
< div class = "message" >
< div class = "user" >
$ { user _image }
< i class = "fa-regular fa-phone-arrow-up-right" > < / i >
< / d i v >
< div class = "content" id = "user_${token}" >
$ { format ( message ) }
< / d i v >
< / d i v >
` ;
message _box . scrollTop = message _box . scrollHeight ;
window . scrollTo ( 0 , 0 ) ;
await new Promise ( ( r ) => setTimeout ( r , 500 ) ) ;
window . scrollTo ( 0 , 0 ) ;
message _box . innerHTML += `
< div class = "message" >
< div class = "user" >
$ { gpt _image } < i class = "fa-regular fa-phone-arrow-down-left" > < / i >
< / d i v >
< div class = "content" id = "gpt_${window.token}" >
< div id = "cursor" > < / d i v >
< / d i v >
< / d i v >
` ;
message _box . scrollTop = message _box . scrollHeight ;
window . scrollTo ( 0 , 0 ) ;
await new Promise ( ( r ) => setTimeout ( r , 1000 ) ) ;
window . scrollTo ( 0 , 0 ) ;
const response = await fetch ( ` /backend-api/v2/conversation ` , {
method : ` POST ` ,
signal : window . controller . signal ,
headers : {
2023-10-19 17:14:48 +03:00
'content-type' : ` application/json ` ,
2023-10-06 21:52:17 +03:00
accept : ` text/event-stream ` ,
} ,
body : JSON . stringify ( {
conversation _id : window . conversation _id ,
action : ` _ask ` ,
model : model . options [ model . selectedIndex ] . value ,
jailbreak : jailbreak . options [ jailbreak . selectedIndex ] . value ,
2023-10-10 01:47:58 +03:00
provider : provider . options [ provider . selectedIndex ] . value ,
2023-10-06 21:52:17 +03:00
meta : {
id : window . token ,
content : {
conversation : await get _conversation ( window . conversation _id ) ,
2023-10-19 17:14:48 +03:00
internet _access : document . getElementById ( ` switch ` ) . checked ,
content _type : ` text ` ,
2023-10-06 21:52:17 +03:00
parts : [
{
content : message ,
2023-10-19 17:14:48 +03:00
role : ` user ` ,
2023-10-06 21:52:17 +03:00
} ,
] ,
} ,
} ,
} ) ,
} ) ;
const reader = response . body . getReader ( ) ;
while ( true ) {
const { value , done } = await reader . read ( ) ;
if ( done ) break ;
chunk = new TextDecoder ( ) . decode ( value ) ;
text += chunk ;
2023-10-19 17:14:48 +03:00
document . getElementById ( ` gpt_ ${ window . token } ` ) . innerHTML = markdown . render ( text ) ;
2023-10-06 21:52:17 +03:00
document . querySelectorAll ( ` code ` ) . forEach ( ( el ) => {
hljs . highlightElement ( el ) ;
} ) ;
window . scrollTo ( 0 , 0 ) ;
message _box . scrollTo ( { top : message _box . scrollHeight , behavior : "auto" } ) ;
}
2023-10-10 17:48:56 +03:00
if ( text . includes ( ` G4F_ERROR ` ) ) {
2023-10-06 21:52:17 +03:00
document . getElementById ( ` gpt_ ${ window . token } ` ) . innerHTML = "An error occured, please try again, if the problem persists, please reload / refresh cache or use a differnet browser" ;
}
add _message ( window . conversation _id , "user" , message ) ;
add _message ( window . conversation _id , "assistant" , text ) ;
message _box . scrollTop = message _box . scrollHeight ;
await remove _cancel _button ( ) ;
prompt _lock = false ;
await load _conversations ( 20 , 0 ) ;
window . scrollTo ( 0 , 0 ) ;
2023-10-19 17:14:48 +03:00
2023-10-06 21:52:17 +03:00
} catch ( e ) {
add _message ( window . conversation _id , "user" , message ) ;
message _box . scrollTop = message _box . scrollHeight ;
await remove _cancel _button ( ) ;
prompt _lock = false ;
await load _conversations ( 20 , 0 ) ;
console . log ( e ) ;
let cursorDiv = document . getElementById ( ` cursor ` ) ;
if ( cursorDiv ) cursorDiv . parentNode . removeChild ( cursorDiv ) ;
if ( e . name != ` AbortError ` ) {
let error _message = ` oops ! something went wrong, please try again / reload. [stacktrace in console] ` ;
document . getElementById ( ` gpt_ ${ window . token } ` ) . innerHTML = error _message ;
add _message ( window . conversation _id , "assistant" , error _message ) ;
} else {
document . getElementById ( ` gpt_ ${ window . token } ` ) . innerHTML += ` [aborted] ` ;
add _message ( window . conversation _id , "assistant" , text + ` [aborted] ` ) ;
}
window . scrollTo ( 0 , 0 ) ;
}
} ;
const clear _conversations = async ( ) => {
const elements = box _conversations . childNodes ;
let index = elements . length ;
if ( index > 0 ) {
while ( index -- ) {
const element = elements [ index ] ;
if (
element . nodeType === Node . ELEMENT _NODE &&
element . tagName . toLowerCase ( ) !== ` button `
) {
box _conversations . removeChild ( element ) ;
}
}
}
} ;
const clear _conversation = async ( ) => {
let messages = message _box . getElementsByTagName ( ` div ` ) ;
while ( messages . length > 0 ) {
message _box . removeChild ( messages [ 0 ] ) ;
}
} ;
const show _option = async ( conversation _id ) => {
const conv = document . getElementById ( ` conv- ${ conversation _id } ` ) ;
const yes = document . getElementById ( ` yes- ${ conversation _id } ` ) ;
const not = document . getElementById ( ` not- ${ conversation _id } ` ) ;
2023-10-19 17:14:48 +03:00
conv . style . display = ` none ` ;
yes . style . display = ` block ` ;
not . style . display = ` block ` ;
2023-10-06 21:52:17 +03:00
} ;
const hide _option = async ( conversation _id ) => {
const conv = document . getElementById ( ` conv- ${ conversation _id } ` ) ;
2023-10-19 17:14:48 +03:00
const yes = document . getElementById ( ` yes- ${ conversation _id } ` ) ;
const not = document . getElementById ( ` not- ${ conversation _id } ` ) ;
2023-10-06 21:52:17 +03:00
2023-10-19 17:14:48 +03:00
conv . style . display = ` block ` ;
yes . style . display = ` none ` ;
not . style . display = ` none ` ;
2023-10-06 21:52:17 +03:00
} ;
const delete _conversation = async ( conversation _id ) => {
localStorage . removeItem ( ` conversation: ${ conversation _id } ` ) ;
const conversation = document . getElementById ( ` convo- ${ conversation _id } ` ) ;
conversation . remove ( ) ;
if ( window . conversation _id == conversation _id ) {
await new _conversation ( ) ;
}
await load _conversations ( 20 , 0 , true ) ;
} ;
const set _conversation = async ( conversation _id ) => {
history . pushState ( { } , null , ` /chat/ ${ conversation _id } ` ) ;
window . conversation _id = conversation _id ;
await clear _conversation ( ) ;
await load _conversation ( conversation _id ) ;
await load _conversations ( 20 , 0 , true ) ;
} ;
const new _conversation = async ( ) => {
history . pushState ( { } , null , ` /chat/ ` ) ;
window . conversation _id = uuid ( ) ;
await clear _conversation ( ) ;
await load _conversations ( 20 , 0 , true ) ;
2023-10-19 17:14:48 +03:00
await say _hello ( )
2023-10-06 21:52:17 +03:00
} ;
const load _conversation = async ( conversation _id ) => {
let conversation = await JSON . parse (
localStorage . getItem ( ` conversation: ${ conversation _id } ` )
) ;
console . log ( conversation , conversation _id ) ;
for ( item of conversation . items ) {
message _box . innerHTML += `
< div class = "message" >
< div class = "user" >
$ { item . role == "assistant" ? gpt _image : user _image }
$ { item . role == "assistant"
2023-10-19 17:14:48 +03:00
? ` <i class="fa-regular fa-phone-arrow-down-left"></i> `
: ` <i class="fa-regular fa-phone-arrow-up-right"></i> `
}
2023-10-06 21:52:17 +03:00
< / d i v >
< div class = "content" >
$ { item . role == "assistant"
2023-10-19 17:14:48 +03:00
? markdown . render ( item . content )
: item . content
}
2023-10-06 21:52:17 +03:00
< / d i v >
< / d i v >
` ;
}
document . querySelectorAll ( ` code ` ) . forEach ( ( el ) => {
hljs . highlightElement ( el ) ;
} ) ;
message _box . scrollTo ( { top : message _box . scrollHeight , behavior : "smooth" } ) ;
setTimeout ( ( ) => {
message _box . scrollTop = message _box . scrollHeight ;
} , 500 ) ;
} ;
const get _conversation = async ( conversation _id ) => {
let conversation = await JSON . parse (
localStorage . getItem ( ` conversation: ${ conversation _id } ` )
) ;
return conversation . items ;
} ;
const add _conversation = async ( conversation _id , content ) => {
if ( content . length > 17 ) {
title = content . substring ( 0 , 17 ) + '..'
} else {
title = content + ' ' . repeat ( 19 - content . length )
}
if ( localStorage . getItem ( ` conversation: ${ conversation _id } ` ) == null ) {
localStorage . setItem (
` conversation: ${ conversation _id } ` ,
JSON . stringify ( {
id : conversation _id ,
title : title ,
items : [ ] ,
} )
) ;
}
} ;
const add _message = async ( conversation _id , role , content ) => {
before _adding = JSON . parse (
localStorage . getItem ( ` conversation: ${ conversation _id } ` )
) ;
before _adding . items . push ( {
role : role ,
content : content ,
} ) ;
localStorage . setItem (
` conversation: ${ conversation _id } ` ,
JSON . stringify ( before _adding )
2023-10-19 17:14:48 +03:00
) ;
2023-10-06 21:52:17 +03:00
} ;
const load _conversations = async ( limit , offset , loader ) => {
let conversations = [ ] ;
for ( let i = 0 ; i < localStorage . length ; i ++ ) {
if ( localStorage . key ( i ) . startsWith ( "conversation:" ) ) {
let conversation = localStorage . getItem ( localStorage . key ( i ) ) ;
conversations . push ( JSON . parse ( conversation ) ) ;
}
}
await clear _conversations ( ) ;
for ( conversation of conversations ) {
box _conversations . innerHTML += `
< div class = "convo" id = "convo-${conversation.id}" >
< div class = "left" onclick = "set_conversation('${conversation.id}')" >
< i class = "fa-regular fa-comments" > < / i >
< span class = "convo-title" > $ { conversation . title } < / s p a n >
< / d i v >
< i onclick = "show_option('${conversation.id}')" class = "fa-regular fa-trash" id = "conv-${conversation.id}" > < / i >
< i onclick = "delete_conversation('${conversation.id}')" class = "fa-regular fa-check" id = "yes-${conversation.id}" style = "display:none;" > < / i >
< i onclick = "hide_option('${conversation.id}')" class = "fa-regular fa-x" id = "not-${conversation.id}" style = "display:none;" > < / i >
< / d i v >
` ;
}
document . querySelectorAll ( ` code ` ) . forEach ( ( el ) => {
hljs . highlightElement ( el ) ;
} ) ;
} ;
document . getElementById ( ` cancelButton ` ) . addEventListener ( ` click ` , async ( ) => {
window . controller . abort ( ) ;
console . log ( ` aborted ${ window . conversation _id } ` ) ;
} ) ;
const uuid = ( ) => {
return ` xxxxxxxx-xxxx-4xxx-yxxx- ${ Date . now ( ) . toString ( 16 ) } ` . replace (
/[xy]/g ,
function ( c ) {
var r = ( Math . random ( ) * 16 ) | 0 ,
v = c == "x" ? r : ( r & 0x3 ) | 0x8 ;
return v . toString ( 16 ) ;
}
) ;
} ;
const message _id = ( ) => {
random _bytes = ( Math . floor ( Math . random ( ) * 1338377565 ) + 2956589730 ) . toString (
2
) ;
unix = Math . floor ( Date . now ( ) / 1000 ) . toString ( 2 ) ;
return BigInt ( ` 0b ${ unix } ${ random _bytes } ` ) . toString ( ) ;
} ;
document . querySelector ( ".mobile-sidebar" ) . addEventListener ( "click" , ( event ) => {
const sidebar = document . querySelector ( ".conversations" ) ;
if ( sidebar . classList . contains ( "shown" ) ) {
sidebar . classList . remove ( "shown" ) ;
event . target . classList . remove ( "rotated" ) ;
} else {
sidebar . classList . add ( "shown" ) ;
event . target . classList . add ( "rotated" ) ;
}
window . scrollTo ( 0 , 0 ) ;
} ) ;
const register _settings _localstorage = async ( ) => {
settings _ids = [ "switch" , "model" , "jailbreak" ] ;
settings _elements = settings _ids . map ( ( id ) => document . getElementById ( id ) ) ;
settings _elements . map ( ( element ) =>
element . addEventListener ( ` change ` , async ( event ) => {
switch ( event . target . type ) {
case "checkbox" :
localStorage . setItem ( event . target . id , event . target . checked ) ;
break ;
case "select-one" :
localStorage . setItem ( event . target . id , event . target . selectedIndex ) ;
break ;
default :
console . warn ( "Unresolved element type" ) ;
}
} )
) ;
} ;
const load _settings _localstorage = async ( ) => {
settings _ids = [ "switch" , "model" , "jailbreak" ] ;
settings _elements = settings _ids . map ( ( id ) => document . getElementById ( id ) ) ;
settings _elements . map ( ( element ) => {
if ( localStorage . getItem ( element . id ) ) {
switch ( element . type ) {
case "checkbox" :
element . checked = localStorage . getItem ( element . id ) === "true" ;
break ;
case "select-one" :
element . selectedIndex = parseInt ( localStorage . getItem ( element . id ) ) ;
break ;
default :
console . warn ( "Unresolved element type" ) ;
}
}
} ) ;
} ;
2023-10-19 17:14:48 +03:00
const say _hello = async ( ) => {
2023-10-06 21:52:17 +03:00
tokens = [ ` Hello ` , ` ! ` , ` How ` , ` can ` , ` I ` , ` assist ` , ` you ` , ` today ` , ` ? ` ]
message _box . innerHTML += `
< div class = "message" >
< div class = "user" >
$ { gpt _image }
< i class = "fa-regular fa-phone-arrow-down-left" > < / i >
< / d i v >
< div class = "content welcome-message" >
< / d i v >
< / d i v >
` ;
content = ` `
to _modify = document . querySelector ( ` .welcome-message ` ) ;
for ( token of tokens ) {
await new Promise ( resolve => setTimeout ( resolve , ( Math . random ( ) * ( 100 - 200 ) + 100 ) ) )
content += token ;
to _modify . innerHTML = markdown . render ( content ) ;
}
}
// Theme storage for recurring viewers
const storeTheme = function ( theme ) {
localStorage . setItem ( "theme" , theme ) ;
} ;
// set theme when visitor returns
const setTheme = function ( ) {
const activeTheme = localStorage . getItem ( "theme" ) ;
colorThemes . forEach ( ( themeOption ) => {
if ( themeOption . id === activeTheme ) {
themeOption . checked = true ;
}
} ) ;
// fallback for no :has() support
document . documentElement . className = activeTheme ;
} ;
colorThemes . forEach ( ( themeOption ) => {
themeOption . addEventListener ( "click" , ( ) => {
storeTheme ( themeOption . id ) ;
// fallback for no :has() support
document . documentElement . className = themeOption . id ;
} ) ;
} ) ;
window . onload = async ( ) => {
load _settings _localstorage ( ) ;
setTheme ( ) ;
conversations = 0 ;
for ( let i = 0 ; i < localStorage . length ; i ++ ) {
if ( localStorage . key ( i ) . startsWith ( "conversation:" ) ) {
conversations += 1 ;
}
}
if ( conversations == 0 ) localStorage . clear ( ) ;
await setTimeout ( ( ) => {
load _conversations ( 20 , 0 ) ;
} , 1 ) ;
if ( ! window . location . href . endsWith ( ` # ` ) ) {
if ( /\/chat\/.+/ . test ( window . location . href ) ) {
await load _conversation ( window . conversation _id ) ;
}
}
2023-10-19 17:14:48 +03:00
2023-10-19 22:25:13 +03:00
// await load_models();
2023-10-19 17:14:48 +03:00
await say _hello ( )
2023-10-06 21:52:17 +03:00
message _input . addEventListener ( ` keydown ` , async ( evt ) => {
if ( prompt _lock ) return ;
if ( evt . keyCode === 13 && ! evt . shiftKey ) {
evt . preventDefault ( ) ;
console . log ( "pressed enter" ) ;
await handle _ask ( ) ;
} else {
message _input . style . removeProperty ( "height" ) ;
message _input . style . height = message _input . scrollHeight + "px" ;
}
} ) ;
send _button . addEventListener ( ` click ` , async ( ) => {
console . log ( "clicked send" ) ;
if ( prompt _lock ) return ;
await handle _ask ( ) ;
} ) ;
register _settings _localstorage ( ) ;
} ;
const observer = new MutationObserver ( ( mutationsList ) => {
for ( const mutation of mutationsList ) {
if ( mutation . type === 'attributes' && mutation . attributeName === 'style' ) {
const height = message _input . offsetHeight ;
let heightValues = {
81 : "20px" ,
82 : "20px" ,
100 : "30px" ,
119 : "39px" ,
138 : "49px" ,
150 : "55px"
}
send _button . style . top = heightValues [ height ] || '' ;
}
}
} ) ;
2023-10-19 17:14:48 +03:00
observer . observe ( message _input , { attributes : true } ) ;
const load _models = async ( ) => {
2023-10-19 22:25:13 +03:00
// models = localStorage.getItem('_models')
2023-10-19 17:14:48 +03:00
2023-10-19 22:25:13 +03:00
// if (models === null) {
// response = await fetch('/backend-api/v2/models')
// models = await response.json()
// localStorage.setItem('_models', JSON.stringify(models))
// } else {
// models = JSON.parse(models)
// }
models = [
"gpt-3.5-turbo" ,
"gpt-3.5-turbo-0613" ,
"gpt-3.5-turbo-16k" ,
"gpt-3.5-turbo-16k-0613" ,
"gpt-4" ,
"gpt-4-0613" ,
"gpt-4-32k" ,
"gpt-4-32k-0613" ,
"palm2" ,
"palm" ,
"google" ,
"google-bard" ,
"google-palm" ,
"bard" ,
"falcon-40b" ,
"falcon-7b" ,
"llama-13b" ,
"command-nightly" ,
"gpt-neox-20b" ,
"santacoder" ,
"bloom" ,
"flan-t5-xxl" ,
"code-davinci-002" ,
"text-ada-001" ,
"text-babbage-001" ,
"text-curie-001" ,
"text-davinci-002" ,
"text-davinci-003" ,
"llama70b-v2-chat" ,
"llama13b-v2-chat" ,
"llama7b-v2-chat" ,
"oasst-sft-1-pythia-12b" ,
"oasst-sft-4-pythia-12b-epoch-3.5" ,
"command-light-nightly"
]
2023-10-19 21:37:56 +03:00
let MODELS _SELECT = document . getElementById ( 'model' ) ;
2023-10-19 22:25:13 +03:00
2023-10-19 17:14:48 +03:00
2023-10-19 21:37:56 +03:00
for ( model of models ) {
let model _info = document . createElement ( 'option' ) ;
2023-10-19 17:14:48 +03:00
model _info . value = model
model _info . text = model
MODELS _SELECT . appendChild ( model _info ) ;
}
}