@@ -5151,6 +5151,25 @@ Current version indicated by LITEVER below.
51515151 };
51525152 fileReader.readAsArrayBuffer(blob);
51535153 }
5154+
5155+ function updateCharacterListFromAll()
5156+ {
5157+ indexeddb_save("characterList", JSON.stringify(allCharacterNames)).then(() => {
5158+ waitingToast.hide()
5159+ })
5160+ }
5161+
5162+ function debounce(func, delay) {
5163+ let timeout;
5164+ return function (...args) {
5165+ clearTimeout(timeout);
5166+ timeout = setTimeout(() => {
5167+ func.apply(this, args);
5168+ }, delay);
5169+ };
5170+ }
5171+ let updateCharacterListFromAllDe = debounce(updateCharacterListFromAll, 5000);
5172+
51545173 function convertTavernPng(data) //import tavern png data. adapted from png-chunks-extract under MIT license
51555174 {
51565175 //accepts png input data, and returns the extracted JSON
@@ -5278,21 +5297,18 @@ Current version indicated by LITEVER below.
52785297 return base64url.slice(base64url.indexOf(',') + 1);
52795298 }
52805299
5281- bufferToBase64(data).then(b64 => {
5300+ bufferToBase64(data).then(async ( b64) => {
52825301 b64Url = `data:image/png;base64,${b64}`
52835302 let charData = !!decoded?.data ? decoded.data : decoded, charName = charData.name;
5303+ let dataUrl = await generateThumbnail(b64Url, [256, 256])
52845304 let dataToSave = {
52855305 name: charName,
52865306 data: charData,
52875307 image: b64Url
52885308 }
52895309 indexeddb_save(`character_${charName}`, JSON.stringify(dataToSave)).then(() => {
5290- indexeddb_load("characterList", "[]").then(charListJson => {
5291- let charList = JSON.parse(charListJson);
5292- charList.push(charName);
5293- allCharacterNames = charList;
5294- indexeddb_save("characterList", JSON.stringify(charList));
5295- })
5310+ allCharacterNames.push({name: charName, thumbnail: dataUrl})
5311+ updateCharacterListFromAllDe();
52965312 })
52975313 })
52985314
@@ -7811,15 +7827,17 @@ Current version indicated by LITEVER below.
78117827 a.download = newfilename;
78127828 setTimeout(function(){a.click()},20);
78137829 }
7814- function promptUserForLocalFile(fileCallback, allowedTypes = "")
7830+ function promptUserForLocalFile(fileCallback, allowedTypes = "", multiple = false )
78157831 {
78167832 //allowedTypes should be a string, because then you can accept MIME types like audio/*
78177833 let picker = document.getElementById('tempfilepicker');
78187834 picker.accept = allowedTypes;
7835+ picker.multiple = !!multiple ? true : undefined
78197836 picker.onchange = function (event) {
78207837 let input = event.target;
7821- if (input.files.length > 0) {
7822- let file = input.files[0];
7838+ for (let i = 0; i < input.files.length; i++)
7839+ {
7840+ let file = input.files[i];
78237841 let fileNameWithExt = file.name;
78247842 let extPos = fileNameWithExt.lastIndexOf(".");
78257843 let fileName = fileNameWithExt.substring(0, extPos);
@@ -23353,6 +23371,10 @@ Current version indicated by LITEVER below.
2335323371 let arr = new Uint8Array(data)
2335423372
2335523373 let tavernData = convertTavernPng(arr)
23374+ return importCharacterCardAsWIInternal(tavernData)
23375+ }
23376+
23377+ function importCharacterCardAsWIInternal(tavernData) {
2335623378 let charData = !!tavernData?.data ? tavernData.data : tavernData
2335723379 let charName = charData.name
2335823380 let memory = charData.description
@@ -24073,23 +24095,28 @@ Current version indicated by LITEVER below.
2407324095 allCharacterNames = JSON.parse(await indexeddb_load("characterList", "[]"))
2407424096 })
2407524097
24098+ function getCharacterData(characterName)
24099+ {
24100+ return indexeddb_load(`character_${characterName}`, "{}").then(charData => {
24101+ return JSON.parse(charData);
24102+ });
24103+ }
24104+
2407624105 function getImageUrlFromCharacter(characterName)
2407724106 {
24078- if (allCharacterNames.includes(characterName))
24107+ let charData = allCharacterNames.find(c => c.name === characterName)
24108+ if (charData !== undefined)
2407924109 {
2408024110 let charName = characterName.replaceAll(/[^\w]/g, "_"), id = `style_image_${charName}`;
2408124111 let varName = `--img_character_${charName}`
2408224112 if (!document.getElementById(id))
2408324113 {
24084- indexeddb_load(`character_${characterName}`, "{}").then(charData => {
24085- let image = JSON.parse(charData)?.image;
24086- let styleElem = document.createElement("style");
24087- styleElem.id = id
24088- styleElem.innerHTML = `:root{
24089- ${varName}:url(${image})
24090- }`
24091- document.body.appendChild(styleElem)
24092- })
24114+ let styleElem = document.createElement("style");
24115+ styleElem.id = id
24116+ styleElem.innerHTML = `:root{
24117+ ${varName}:url(${charData?.thumbnail})
24118+ }`
24119+ document.body.appendChild(styleElem)
2409324120 }
2409424121 return `var(${varName})`
2409524122 }
@@ -33014,7 +33041,14 @@ class MarkdownWYSIWYG {
3301433041 ctx.drawImage(img, 0, 0, w, h)
3301533042 return resolve(canvas.toDataURL(file.type))
3301633043 }
33017- img.src = URL.createObjectURL(file)
33044+ if (typeof file === "string")
33045+ {
33046+ img.src = file
33047+ }
33048+ else
33049+ {
33050+ img.src = URL.createObjectURL(file)
33051+ }
3301833052 }
3301933053 finally {
3302033054 URL.revokeObjectURL(img.src)
@@ -36191,7 +36225,7 @@ let checkFinalThoughtsPrompt = `Action: {"command":{"name":"thought","args":{"me
3619136225 }
3619236226
3619336227 let getEmbeddingPresetFromModel = () => {
36194- let embeddingPreset = embeddingPresets[Object.keys(embeddingPresets).filter(presetName => get_kcpp_embedding_model().includes(presetName))]
36228+ let embeddingPreset = embeddingPresets[Object.keys(embeddingPresets).filter(presetName => !! get_kcpp_embedding_model()? .includes(presetName))]
3619536229 if (!!embeddingPreset && (!documentdb_sqPrefix && !documentdb_cPrefix))
3619636230 {
3619736231 documentdb_sqPrefix = embeddingPreset.query_prompt
@@ -36522,7 +36556,7 @@ let checkFinalThoughtsPrompt = `Action: {"command":{"name":"thought","args":{"me
3652236556 showOpenButton() {
3652336557 this.hideOpenButton()
3652436558
36525- let openButton = document.createElement("span")
36559+ let openButton = document.createElement("span"), container = document.createElement("li");
3652636560 openButton.id = "openTreeDiagram"
3652736561 openButton.title = "World tree"
3652836562 openButton.onclick = () => {
@@ -36536,6 +36570,7 @@ let checkFinalThoughtsPrompt = `Action: {"command":{"name":"thought","args":{"me
3653636570 }
3653736571 }
3653836572 openButton.style = `
36573+ display: block;
3653936574 background-color: black;
3654036575 border-radius: 10px;
3654136576 height: 20px;
@@ -36545,9 +36580,11 @@ let checkFinalThoughtsPrompt = `Action: {"command":{"name":"thought","args":{"me
3654536580 height: 50px;
3654636581 background-size: contain;
3654736582 background-repeat: no-repeat;
36548- background-position: center;
36549- float: right;`
36550- document.querySelector("#navbarNavDropdown > ul").appendChild(openButton)
36583+ background-position: center;`
36584+
36585+ container.classList.add("nav-item")
36586+ container.appendChild(openButton)
36587+ document.querySelector("#navbarNavDropdown > ul").appendChild(container)
3655136588 }
3655236589
3655336590 closeTreeView() {
@@ -36981,6 +37018,9 @@ flowchart TD\n${treeToViewOutput.outputText.trim()}`
3698137018 topButton += `<li class="nav-item" id="topbtn_server_saves">
3698237019 <a class="nav-link mainnav" href="#" onclick="showServerSavesPopup()" tabindex="0">Server saves</a>
3698337020 </li>`
37021+ topButton += `<li class="nav-item" id="topbtn_server_saves">
37022+ <a class="nav-link mainnav" href="#" onclick="showCharacterList()" tabindex="0">Characters</a>
37023+ </li>`
3698437024
3698537025 document.querySelector("#navbarNavDropdown > ul").innerHTML += topButton
3698637026 treeViewer.showOpenButton()
@@ -37102,5 +37142,202 @@ flowchart TD\n${treeToViewOutput.outputText.trim()}`
3710237142 background-origin: content-box;
3710337143 background-repeat: no-repeat;
3710437144 }
37145+
37146+ #popupContainer > .nspopup
37147+ {
37148+ max-height: 80%;
37149+ max-width: 80%;
37150+ }
37151+
37152+ #popupContainer .popuptitlebar
37153+ {
37154+ height: 40px;
37155+ }
37156+
37157+ #popupContainer .popupContent
37158+ {
37159+ overflow-y: auto;
37160+ height: calc(100% - 40px);
37161+ }
37162+
37163+ #popupContainer .popupfooter button
37164+ {
37165+ width: unset;
37166+ }
37167+
37168+ .containAndScaleImage.tile
37169+ {
37170+ height: 128px;
37171+ aspect-ratio: 1;
37172+ border-radius: 25px;
37173+ text-align: center;
37174+ }
37175+
37176+ .containAndScaleImage.tile > b
37177+ {
37178+ text-shadow: 2px 2px 5px #ffffff;
37179+ color: white;
37180+ background-color: black;
37181+ border-radius: 4px;
37182+ font-weight: bold;
37183+ padding: 2px;
37184+ }
37185+
37186+ .fillContainer
37187+ {
37188+ height: 100%;
37189+ width: 100%;
37190+ }
37191+
37192+ .autoGrid
37193+ {
37194+ display: grid;
37195+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
37196+ padding: 12px;
37197+ gap: 12px;
37198+ }
37199+
37200+ .autoGrid > *
37201+ {
37202+ display: block;
37203+ }
3710537204</style>
37205+ <script>
37206+ class PopupUtils
37207+ {
37208+ popupElem
37209+ titleElem
37210+ contentElem
37211+ buttonsElem
37212+ constructor()
37213+ {
37214+ let popupElem = document.createElement("div");
37215+ popupElem.classList.add("popupcontainer", "flex", "hidden");
37216+ popupElem.id = "popupContainer";
37217+ popupElem.innerHTML = `<div class="popupbg flex"></div>
37218+ <div class="nspopup higher">
37219+ <div class="popuptitlebar">
37220+ <div class="popuptitletext" id="popupTitle"></div>
37221+ </div>
37222+ <div class="popupContent">
37223+
37224+ </div>
37225+ <div class="popupfooter">
37226+
37227+ </div>
37228+ </div>`
37229+ document.body.appendChild(popupElem)
37230+
37231+ this.popupElem = document.getElementById("popupContainer")
37232+ this.titleElem = popupElem.querySelector(".popuptitletext")
37233+ this.contentElem = popupElem.querySelector(".popupContent")
37234+ this.buttonsElem = popupElem.querySelector(".popupfooter")
37235+ }
37236+
37237+ _createButtonForPopup(text, onClick) {
37238+ let button = document.createElement("button")
37239+ button.type = "button"
37240+ button.classList.add("btn", "btn-primary")
37241+ button.innerText = text
37242+ button.onclick = onClick
37243+ return button
37244+ }
37245+
37246+ reset()
37247+ {
37248+ this.popupElem.classList.add("hidden");
37249+ this.titleElem.innerText = "";
37250+ this.contentElem.innerHTML = "";
37251+ this.buttonsElem.innerHTML = "";
37252+ return this;
37253+ }
37254+
37255+ show()
37256+ {
37257+ this.popupElem.classList.remove("hidden")
37258+ return this;
37259+ }
37260+
37261+ title(title)
37262+ {
37263+ this.titleElem.innerText = title
37264+ return this;
37265+ }
37266+
37267+ content(elem)
37268+ {
37269+ this.contentElem.appendChild(elem)
37270+ return this;
37271+ }
37272+
37273+ button(text, onClick)
37274+ {
37275+ this.buttonsElem.appendChild(this._createButtonForPopup(text, onClick))
37276+ return this;
37277+ }
37278+ }
37279+
37280+ window.addEventListener("load", () => {
37281+ window.popupUtils = new PopupUtils()
37282+ })
37283+
37284+ let showCharacterList = async () => {
37285+ let container = document.createElement("div");
37286+ container.classList.add("fillContainer", "autoGrid")
37287+ container.style.width = "70vw"
37288+ container.style.overflowX = "hidden"
37289+ if (allCharacterNames.length === 0)
37290+ {
37291+ let charIcon = document.createElement("span");
37292+ charIcon.innerText = "No characters added yet";
37293+ charIcon.style.color = "white";
37294+ container.appendChild(charIcon);
37295+ }
37296+ else
37297+ {
37298+ for (let i = 0; i < allCharacterNames.length; i++)
37299+ {
37300+ let {name, thumbnail} = allCharacterNames[i];
37301+ let charIcon = document.createElement("span");
37302+ let charText = document.createElement("b");
37303+ charIcon.classList.add("containAndScaleImage", "tile")
37304+ charIcon.style.backgroundImage = `url(${thumbnail})`
37305+ charIcon.title = name
37306+ charText.innerText = name
37307+ charIcon.onclick = () => {
37308+ popupUtils.reset().title("Character Options").button("Load character", async () => {
37309+ popupUtils.reset()
37310+ let charData = await getCharacterData(name)
37311+ load_tavern_obj(charData.data);
37312+ }).button("Add character to WI", async () => {
37313+ popupUtils.reset()
37314+ let charData = await getCharacterData(name)
37315+ let wiToAdd = importCharacterCardAsWIInternal(charData.data);
37316+ current_wi = current_wi.filter(wi => wi?.folder !== name)
37317+ current_wi.push(...wiToAdd)
37318+ }).button("Delete character", async () => {
37319+ popupUtils.reset()
37320+ allCharacterNames = allCharacterNames.filter(c => c.name !== name)
37321+ updateCharacterListFromAll()
37322+ }).button("Close", () => popupUtils.reset()).show();
37323+ }
37324+ charIcon.appendChild(charText)
37325+ container.appendChild(charIcon)
37326+ }
37327+ }
37328+ popupUtils.reset().title("Character List").content(container).button("Upload characters", () => {
37329+ popupUtils.reset()
37330+ promptUserForLocalFile(async (result) => {
37331+ let { file, fileName, ext, content, plaintext, dataArr } = result;
37332+ waitingToast.show()
37333+ waitingToast.setText(`Loading character ${fileName}`)
37334+ if (ext === ".png")
37335+ {
37336+ let arr = new Uint8Array(dataArr)
37337+ convertTavernPng(arr)
37338+ }
37339+ }, [".png"], true)
37340+ }).button("Close", () => popupUtils.reset()).show();
37341+ }
37342+ </script>
3710637343</html>
0 commit comments