Skip to content

Commit 4c7f076

Browse files
author
prima
committed
feat: Addition of character loading menu
1 parent 603bd92 commit 4c7f076

File tree

1 file changed

+263
-26
lines changed

1 file changed

+263
-26
lines changed

klite.embd

Lines changed: 263 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)