@kevin5251984/guild 0.2.18 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/catalog/subagents.ts +2 -1
- package/src/chat-parts.ts +6 -1
- package/src/compact.ts +27 -0
- package/src/db.ts +199 -64
- package/src/generate.ts +58 -27
- package/src/handlers.ts +341 -54
- package/src/harness.ts +21 -8
- package/src/image-gen.ts +2 -1
- package/src/llm.ts +400 -58
- package/src/memory.ts +69 -0
- package/src/mention.ts +119 -12
- package/src/oauth.ts +202 -25
- package/src/opencode-free.ts +247 -0
- package/src/public/buddy.js +432 -0
- package/src/public/chat.css +174 -72
- package/src/public/chat.html +212 -43
- package/src/public/i18n.js +41 -6
- package/src/public/index.html +1 -46
- package/src/public/mobile.css +491 -0
- package/src/public/mobile.html +784 -0
- package/src/public/settings.html +188 -27
- package/src/public/skills-add.html +8 -0
- package/src/public/studio.html +171 -117
- package/src/public/style.css +220 -26
- package/src/public/subagents-add.html +8 -0
- package/src/router.ts +76 -5
- package/src/store.ts +161 -5
- package/src/subagent.ts +233 -6
- package/src/tools.ts +124 -18
- package/src/trajectory.ts +72 -9
- package/src/usage.ts +10 -0
- package/vendor/protocol/src/index.ts +14 -1
package/src/public/studio.html
CHANGED
|
@@ -51,24 +51,32 @@
|
|
|
51
51
|
</div>
|
|
52
52
|
<div class="rpg-scene is-inn" id="scene-inn" hidden>
|
|
53
53
|
<div class="inn-map">
|
|
54
|
-
<div class="inn-wall"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<div class="inn-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
<div class="inn-wall">
|
|
55
|
+
<span class="inn-window"></span>
|
|
56
|
+
<span class="inn-window"></span>
|
|
57
|
+
<span class="inn-window"></span>
|
|
58
|
+
<span class="inn-window"></span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="inn-nook inn-nook-l">
|
|
61
|
+
<span class="inn-prop plant"></span>
|
|
62
|
+
<span class="inn-prop plant"></span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="inn-nook inn-nook-r">
|
|
65
|
+
<div class="inn-bar"></div>
|
|
66
|
+
<div class="inn-bottles" id="inn-bottles"></div>
|
|
67
|
+
</div>
|
|
63
68
|
<div class="inn-actors" id="inn-actors"></div>
|
|
64
69
|
</div>
|
|
65
70
|
<button type="button" class="rpg-exit" id="leave-inn" data-i18n="studio.leave">出門</button>
|
|
66
71
|
</div>
|
|
67
72
|
<div class="rpg-dialog" id="rpg-dialog">
|
|
68
73
|
<div class="rpg-dialog-inner">
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
74
|
+
<img class="rpg-face" id="rpg-face" alt="" hidden />
|
|
75
|
+
<div class="rpg-copy">
|
|
76
|
+
<div class="rpg-name" id="rpg-name">酒館</div>
|
|
77
|
+
<p class="rpg-line" id="rpg-line">夜晚的編制還開著。點那扇門進去。</p>
|
|
78
|
+
<div class="rpg-acts" id="rpg-acts"></div>
|
|
79
|
+
</div>
|
|
72
80
|
</div>
|
|
73
81
|
</div>
|
|
74
82
|
</div>
|
|
@@ -88,6 +96,7 @@
|
|
|
88
96
|
<input name="oneLiner" placeholder="這名冒險者在編制裡做什麼" data-i18n-placeholder="studio.oneLinerPh" />
|
|
89
97
|
<div class="card-foot">
|
|
90
98
|
<button type="button" class="ghost danger" id="delete-bot" hidden data-i18n="bot.delete">請這名冒險者離席</button>
|
|
99
|
+
<button type="button" class="ghost" id="gen-look" hidden data-i18n="studio.genLook">生成形象</button>
|
|
91
100
|
<div class="save-row">
|
|
92
101
|
<p class="block-flash" hidden></p>
|
|
93
102
|
<button type="button" class="save-md" data-save="identity" hidden>儲存名字</button>
|
|
@@ -183,7 +192,7 @@
|
|
|
183
192
|
let openId = "";
|
|
184
193
|
let activeTags = new Set();
|
|
185
194
|
let showAllSkills = false;
|
|
186
|
-
let rpgTalk = { name: "", line: "", acts: "" };
|
|
195
|
+
let rpgTalk = { name: "", line: "", acts: "", face: "" };
|
|
187
196
|
const PICKED = "__picked__";
|
|
188
197
|
const REST_CAP = 12;
|
|
189
198
|
|
|
@@ -197,6 +206,8 @@
|
|
|
197
206
|
document.title = t("studio.editDocTitle");
|
|
198
207
|
submit.hidden = true;
|
|
199
208
|
document.getElementById("delete-bot").hidden = false;
|
|
209
|
+
document.getElementById("gen-look").hidden = false;
|
|
210
|
+
document.getElementById("gen-look").textContent = t("studio.genLook");
|
|
200
211
|
mdSaves.forEach((btn) => {
|
|
201
212
|
btn.hidden = false;
|
|
202
213
|
const kind = btn.getAttribute("data-save");
|
|
@@ -212,6 +223,7 @@
|
|
|
212
223
|
submit.hidden = false;
|
|
213
224
|
submit.textContent = t("studio.seat");
|
|
214
225
|
document.getElementById("delete-bot").hidden = true;
|
|
226
|
+
document.getElementById("gen-look").hidden = true;
|
|
215
227
|
document.title = t("title.studio");
|
|
216
228
|
mdSaves.forEach((btn) => {
|
|
217
229
|
btn.hidden = true;
|
|
@@ -238,6 +250,24 @@
|
|
|
238
250
|
function setBlockFlash(block, kind, text) {
|
|
239
251
|
setNodeFlash(block && block.querySelector(".gen-row .block-flash"), kind, text);
|
|
240
252
|
}
|
|
253
|
+
function beginBusy(button, label) {
|
|
254
|
+
if (!button || button.disabled || button.getAttribute("aria-busy") === "true") {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
button.dataset.idleLabel = button.textContent;
|
|
258
|
+
button.disabled = true;
|
|
259
|
+
button.setAttribute("aria-busy", "true");
|
|
260
|
+
button.classList.add("is-busy");
|
|
261
|
+
if (label) button.textContent = label;
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
function endBusy(button, label) {
|
|
265
|
+
if (!button) return;
|
|
266
|
+
button.disabled = false;
|
|
267
|
+
button.removeAttribute("aria-busy");
|
|
268
|
+
button.classList.remove("is-busy");
|
|
269
|
+
button.textContent = label || button.dataset.idleLabel || button.textContent;
|
|
270
|
+
}
|
|
241
271
|
function saveFlashEl(kind) {
|
|
242
272
|
const btn = document.querySelector('.save-md[data-save="' + kind + '"]');
|
|
243
273
|
return btn && btn.parentElement ? btn.parentElement.querySelector(".block-flash") : null;
|
|
@@ -547,8 +577,7 @@
|
|
|
547
577
|
setNodeFlash(flashEl, "error", t("studio.emptyLib"));
|
|
548
578
|
return;
|
|
549
579
|
}
|
|
550
|
-
button.
|
|
551
|
-
button.textContent = t("studio.picking");
|
|
580
|
+
if (!beginBusy(button, t("studio.picking"))) return;
|
|
552
581
|
try {
|
|
553
582
|
const response = await fetch("/generate/skills", {
|
|
554
583
|
method: "POST",
|
|
@@ -597,8 +626,7 @@
|
|
|
597
626
|
} catch (err) {
|
|
598
627
|
setNodeFlash(flashEl, "error", err.message);
|
|
599
628
|
} finally {
|
|
600
|
-
button.
|
|
601
|
-
button.textContent = t("studio.pickSkills");
|
|
629
|
+
endBusy(button, t("studio.pickSkills"));
|
|
602
630
|
}
|
|
603
631
|
});
|
|
604
632
|
document.getElementById("skill-search").addEventListener("input", () => {
|
|
@@ -688,8 +716,7 @@
|
|
|
688
716
|
setBlockFlash(block, "error", t("studio.needPrompt", { kind: kind }));
|
|
689
717
|
return;
|
|
690
718
|
}
|
|
691
|
-
button.
|
|
692
|
-
button.textContent = t("studio.generating");
|
|
719
|
+
if (!beginBusy(button, t("studio.generating"))) return;
|
|
693
720
|
try {
|
|
694
721
|
const response = await fetch("/generate", {
|
|
695
722
|
method: "POST",
|
|
@@ -729,8 +756,7 @@
|
|
|
729
756
|
} catch (err) {
|
|
730
757
|
setBlockFlash(block, "error", err.message);
|
|
731
758
|
} finally {
|
|
732
|
-
button.
|
|
733
|
-
button.textContent = t("studio.genMd");
|
|
759
|
+
endBusy(button, t("studio.genMd"));
|
|
734
760
|
}
|
|
735
761
|
});
|
|
736
762
|
});
|
|
@@ -887,80 +913,29 @@
|
|
|
887
913
|
const roster = await fetch("/bots").then((r) => r.json());
|
|
888
914
|
renderBar(Array.isArray(roster) ? roster : []);
|
|
889
915
|
setScene("inn");
|
|
890
|
-
talk(body.name, t("studio.hireToast"), npcActs(body));
|
|
916
|
+
talk(body.name, t("studio.hireToast"), npcActs(body), body.portrait);
|
|
891
917
|
});
|
|
892
918
|
|
|
893
|
-
function seedOf(key) {
|
|
894
|
-
let n = 0;
|
|
895
|
-
for (const ch of key) n = (n + ch.charCodeAt(0) * 17) % 997;
|
|
896
|
-
return n;
|
|
897
|
-
}
|
|
898
|
-
function shirtColor(key) {
|
|
899
|
-
return COLORS[seedOf(key) % COLORS.length];
|
|
900
|
-
}
|
|
901
|
-
function px(ctx, x, y, w, h, color) {
|
|
902
|
-
ctx.fillStyle = color;
|
|
903
|
-
ctx.fillRect(x, y, w, h);
|
|
904
|
-
}
|
|
905
|
-
function drawBuddy(canvas, shirt, seed) {
|
|
906
|
-
const ctx = canvas.getContext("2d");
|
|
907
|
-
canvas.width = 16;
|
|
908
|
-
canvas.height = 24;
|
|
909
|
-
ctx.imageSmoothingEnabled = false;
|
|
910
|
-
const skin = ["#f0c8a0", "#e2b184", "#c68642", "#8d5524"][seed % 4];
|
|
911
|
-
const hair = ["#2a1c12", "#c4a35a", "#1a1a1a", "#6b2d3c", "#3c4a6e"][seed % 5];
|
|
912
|
-
const outline = "#1b1510";
|
|
913
|
-
const pants = "#2a3140";
|
|
914
|
-
const style = seed % 3;
|
|
915
|
-
px(ctx, 4, 12, 8, 7, outline);
|
|
916
|
-
px(ctx, 5, 13, 6, 5, shirt);
|
|
917
|
-
px(ctx, 3, 13, 2, 5, shirt);
|
|
918
|
-
px(ctx, 11, 13, 2, 5, shirt);
|
|
919
|
-
px(ctx, 3, 18, 2, 1, skin);
|
|
920
|
-
px(ctx, 11, 18, 2, 1, skin);
|
|
921
|
-
px(ctx, 4, 4, 8, 8, outline);
|
|
922
|
-
px(ctx, 5, 5, 6, 6, skin);
|
|
923
|
-
px(ctx, 6, 7, 1, 1, outline);
|
|
924
|
-
px(ctx, 9, 7, 1, 1, outline);
|
|
925
|
-
if (style === 0) {
|
|
926
|
-
px(ctx, 4, 3, 8, 3, hair);
|
|
927
|
-
px(ctx, 4, 5, 2, 3, hair);
|
|
928
|
-
px(ctx, 10, 5, 2, 3, hair);
|
|
929
|
-
} else if (style === 1) {
|
|
930
|
-
px(ctx, 4, 2, 8, 4, hair);
|
|
931
|
-
px(ctx, 3, 5, 2, 4, hair);
|
|
932
|
-
px(ctx, 11, 5, 2, 4, hair);
|
|
933
|
-
} else {
|
|
934
|
-
px(ctx, 5, 2, 6, 2, hair);
|
|
935
|
-
px(ctx, 4, 4, 8, 2, hair);
|
|
936
|
-
px(ctx, 7, 1, 2, 2, hair);
|
|
937
|
-
}
|
|
938
|
-
px(ctx, 5, 19, 3, 4, pants);
|
|
939
|
-
px(ctx, 8, 19, 3, 4, pants);
|
|
940
|
-
px(ctx, 5, 23, 3, 1, outline);
|
|
941
|
-
px(ctx, 8, 23, 3, 1, outline);
|
|
942
|
-
}
|
|
943
|
-
const SPOTS = [
|
|
944
|
-
[22, 38],
|
|
945
|
-
[36, 34],
|
|
946
|
-
[50, 40],
|
|
947
|
-
[64, 36],
|
|
948
|
-
[26, 54],
|
|
949
|
-
[42, 58],
|
|
950
|
-
[58, 52],
|
|
951
|
-
[74, 56],
|
|
952
|
-
];
|
|
953
919
|
let barBots = [];
|
|
954
920
|
function setScene(name) {
|
|
955
921
|
document.getElementById("scene-street").hidden = name !== "street";
|
|
956
922
|
document.getElementById("scene-inn").hidden = name !== "inn";
|
|
957
923
|
document.getElementById("rpg-screen").setAttribute("data-scene", name);
|
|
958
924
|
}
|
|
959
|
-
function talk(name, line, acts) {
|
|
960
|
-
rpgTalk = { name: name, line: line, acts: acts || "" };
|
|
925
|
+
function talk(name, line, acts, face) {
|
|
926
|
+
rpgTalk = { name: name, line: line, acts: acts || "", face: face || "" };
|
|
961
927
|
document.getElementById("rpg-name").textContent = name;
|
|
962
928
|
document.getElementById("rpg-line").textContent = line;
|
|
963
929
|
document.getElementById("rpg-acts").innerHTML = acts || "";
|
|
930
|
+
const img = document.getElementById("rpg-face");
|
|
931
|
+
const url = String(face || "").trim();
|
|
932
|
+
if (url) {
|
|
933
|
+
img.hidden = false;
|
|
934
|
+
img.src = url;
|
|
935
|
+
} else {
|
|
936
|
+
img.removeAttribute("src");
|
|
937
|
+
img.hidden = true;
|
|
938
|
+
}
|
|
964
939
|
}
|
|
965
940
|
function npcActs(bot) {
|
|
966
941
|
return (
|
|
@@ -975,6 +950,69 @@
|
|
|
975
950
|
"</a>"
|
|
976
951
|
);
|
|
977
952
|
}
|
|
953
|
+
function npcSprite(bot) {
|
|
954
|
+
if (bot.portrait) {
|
|
955
|
+
return (
|
|
956
|
+
'<img class="npc-bust" alt="" src="' +
|
|
957
|
+
esc(bot.portrait) +
|
|
958
|
+
'">'
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
return (
|
|
962
|
+
'<span class="npc-blank" aria-hidden="true">' +
|
|
963
|
+
esc(initials(bot.name || bot.handle)) +
|
|
964
|
+
"</span>"
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
const lookBusy = new Set();
|
|
968
|
+
async function requestLook(id) {
|
|
969
|
+
const bot = barBots.find((item) => item.id === id);
|
|
970
|
+
if (!bot || lookBusy.has(id)) return;
|
|
971
|
+
const formBtn = document.getElementById("gen-look");
|
|
972
|
+
if (formBtn && EDIT_ID === id && !beginBusy(formBtn, t("studio.genLookBusy"))) {
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
lookBusy.add(id);
|
|
976
|
+
const npc = document.querySelector('.rpg-npc[data-id="' + id + '"]');
|
|
977
|
+
if (npc) npc.classList.add("drawing");
|
|
978
|
+
talk(bot.name, t("studio.genLookBusy"), npcActs(bot), bot.portrait);
|
|
979
|
+
const idFlash = document.querySelector("#create-bot > .card .block-flash");
|
|
980
|
+
if (idFlash && EDIT_ID === id) {
|
|
981
|
+
setNodeFlash(idFlash, "muted", t("studio.genLookBusy"));
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
const response = await fetch("/bots/" + encodeURIComponent(id) + "/look", {
|
|
985
|
+
method: "POST",
|
|
986
|
+
});
|
|
987
|
+
const body = await response.json();
|
|
988
|
+
if (!response.ok) {
|
|
989
|
+
talk(bot.name, body.error || t("studio.genLookFail"), npcActs(bot), bot.portrait);
|
|
990
|
+
if (idFlash && EDIT_ID === id) {
|
|
991
|
+
setNodeFlash(idFlash, "error", body.error || t("studio.genLookFail"));
|
|
992
|
+
}
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const i = barBots.findIndex((item) => item.id === id);
|
|
996
|
+
if (i >= 0) barBots[i] = Object.assign({}, barBots[i], body);
|
|
997
|
+
await renderBar(barBots);
|
|
998
|
+
document.querySelectorAll(".rpg-npc").forEach((el) => {
|
|
999
|
+
el.classList.toggle("on", el.getAttribute("data-id") === id);
|
|
1000
|
+
});
|
|
1001
|
+
talk(body.name || bot.name, t("studio.genLookDone"), npcActs(body), body.portrait);
|
|
1002
|
+
if (idFlash && EDIT_ID === id) {
|
|
1003
|
+
setNodeFlash(idFlash, "health", t("studio.genLookDone"));
|
|
1004
|
+
}
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
talk(bot.name, t("studio.genLookFail"), npcActs(bot), bot.portrait);
|
|
1007
|
+
if (idFlash && EDIT_ID === id) {
|
|
1008
|
+
setNodeFlash(idFlash, "error", t("studio.genLookFail"));
|
|
1009
|
+
}
|
|
1010
|
+
} finally {
|
|
1011
|
+
lookBusy.delete(id);
|
|
1012
|
+
if (npc) npc.classList.remove("drawing");
|
|
1013
|
+
if (formBtn && EDIT_ID === id) endBusy(formBtn, t("studio.genLook"));
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
978
1016
|
function renderSideRoster(bots) {
|
|
979
1017
|
const hire = document.getElementById("side-hire");
|
|
980
1018
|
const box = document.getElementById("side-roster");
|
|
@@ -997,48 +1035,55 @@
|
|
|
997
1035
|
})
|
|
998
1036
|
.join("");
|
|
999
1037
|
}
|
|
1000
|
-
function renderBar(bots) {
|
|
1038
|
+
async function renderBar(bots) {
|
|
1001
1039
|
barBots = bots || [];
|
|
1002
1040
|
renderSideRoster(barBots);
|
|
1003
1041
|
const bottles = ["#c44", "#4a8", "#c84", "#48c", "#a4c", "#cc8", "#844", "#4cc"];
|
|
1004
1042
|
document.getElementById("inn-bottles").innerHTML = bottles
|
|
1005
1043
|
.map((c) => '<span style="background:' + c + '"></span>')
|
|
1006
1044
|
.join("");
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1009
|
-
const
|
|
1045
|
+
const diving = {};
|
|
1046
|
+
try {
|
|
1047
|
+
const space = await fetch("/workspace").then((r) => r.json());
|
|
1048
|
+
(space.live || []).forEach((row) => {
|
|
1049
|
+
if (row && row.botId) diving[row.botId] = true;
|
|
1050
|
+
});
|
|
1051
|
+
} catch {
|
|
1052
|
+
/* roster still renders if live is down */
|
|
1053
|
+
}
|
|
1054
|
+
const station = (inner) =>
|
|
1055
|
+
'<div class="inn-station"><span class="inn-desk" aria-hidden="true"><span class="inn-monitor"></span></span>' +
|
|
1056
|
+
inner +
|
|
1057
|
+
"</div>";
|
|
1058
|
+
const nodes = barBots.map((bot) => {
|
|
1010
1059
|
const on = EDIT_ID === bot.id ? " on" : "";
|
|
1011
|
-
|
|
1060
|
+
const live = Boolean(diving[bot.id]);
|
|
1061
|
+
return station(
|
|
1012
1062
|
'<button type="button" class="rpg-npc' +
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1063
|
+
on +
|
|
1064
|
+
'" data-id="' +
|
|
1065
|
+
bot.id +
|
|
1066
|
+
'"><span class="npc-state' +
|
|
1067
|
+
(live ? " live" : "") +
|
|
1068
|
+
'">' +
|
|
1069
|
+
esc(live ? t("studio.diving") : t("studio.idle")) +
|
|
1070
|
+
"</span>" +
|
|
1071
|
+
npcSprite(bot) +
|
|
1072
|
+
'<span class="tag">' +
|
|
1073
|
+
esc(bot.name) +
|
|
1074
|
+
"</span></button>",
|
|
1025
1075
|
);
|
|
1026
1076
|
});
|
|
1027
|
-
const hireSpot = SPOTS[Math.min(roster.length, SPOTS.length - 1)];
|
|
1028
1077
|
nodes.push(
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1078
|
+
station(
|
|
1079
|
+
'<button type="button" class="rpg-npc vacant" data-id="hire"><span class="npc-state">' +
|
|
1080
|
+
esc(t("studio.idle")) +
|
|
1081
|
+
'</span><span class="npc-blank" aria-hidden="true">?</span><span class="tag">' +
|
|
1082
|
+
t("studio.hireChip") +
|
|
1083
|
+
"</span></button>",
|
|
1084
|
+
),
|
|
1036
1085
|
);
|
|
1037
1086
|
document.getElementById("inn-actors").innerHTML = nodes.join("");
|
|
1038
|
-
document.querySelectorAll("#inn-actors canvas.buddy").forEach((canvas) => {
|
|
1039
|
-
const handle = canvas.getAttribute("data-handle") || "";
|
|
1040
|
-
drawBuddy(canvas, shirtColor(handle), seedOf(handle));
|
|
1041
|
-
});
|
|
1042
1087
|
}
|
|
1043
1088
|
document.getElementById("enter-inn").addEventListener("click", () => {
|
|
1044
1089
|
setScene("inn");
|
|
@@ -1062,11 +1107,16 @@
|
|
|
1062
1107
|
}
|
|
1063
1108
|
const bot = barBots.find((item) => item.id === id);
|
|
1064
1109
|
if (!bot) return;
|
|
1065
|
-
talk(bot.name, bot.oneLiner || t("studio.drink"), npcActs(bot));
|
|
1110
|
+
talk(bot.name, bot.oneLiner || t("studio.drink"), npcActs(bot), bot.portrait);
|
|
1066
1111
|
});
|
|
1067
1112
|
document.getElementById("rpg-acts").addEventListener("click", (event) => {
|
|
1068
1113
|
const link = event.target.closest('a[href="#hire"]');
|
|
1069
1114
|
if (link) document.getElementById("hire").hidden = false;
|
|
1115
|
+
const look = event.target.closest("[data-look]");
|
|
1116
|
+
if (look) requestLook(look.getAttribute("data-look"));
|
|
1117
|
+
});
|
|
1118
|
+
document.getElementById("gen-look").addEventListener("click", () => {
|
|
1119
|
+
if (EDIT_ID) requestLook(EDIT_ID);
|
|
1070
1120
|
});
|
|
1071
1121
|
|
|
1072
1122
|
async function boot() {
|
|
@@ -1087,7 +1137,11 @@
|
|
|
1087
1137
|
}
|
|
1088
1138
|
allSkills = mergeSkills(guild, host);
|
|
1089
1139
|
const bots = await fetch("/bots").then((r) => r.json());
|
|
1090
|
-
renderBar(Array.isArray(bots) ? bots : []);
|
|
1140
|
+
await renderBar(Array.isArray(bots) ? bots : []);
|
|
1141
|
+
if (location.hash === "#inn") {
|
|
1142
|
+
setScene("inn");
|
|
1143
|
+
talk(t("studio.keeper"), t("studio.keeperLine"), "");
|
|
1144
|
+
}
|
|
1091
1145
|
if (EDIT_ID) {
|
|
1092
1146
|
const response = await fetch("/bots/" + encodeURIComponent(EDIT_ID));
|
|
1093
1147
|
const bot = await response.json();
|
|
@@ -1107,7 +1161,7 @@
|
|
|
1107
1161
|
selected = new Set(bot.skillIds || []);
|
|
1108
1162
|
setFlash("muted", t("studio.editing", { handle: bot.handle }));
|
|
1109
1163
|
setScene("inn");
|
|
1110
|
-
talk(bot.name, bot.oneLiner || t("studio.drink"), npcActs(bot));
|
|
1164
|
+
talk(bot.name, bot.oneLiner || t("studio.drink"), npcActs(bot), bot.portrait);
|
|
1111
1165
|
} else {
|
|
1112
1166
|
setScene("street");
|
|
1113
1167
|
document.getElementById("hire").hidden = true;
|
|
@@ -1119,7 +1173,7 @@
|
|
|
1119
1173
|
applyStudioCopy();
|
|
1120
1174
|
render();
|
|
1121
1175
|
renderBar(barBots);
|
|
1122
|
-
if (rpgTalk.name) talk(rpgTalk.name, rpgTalk.line, rpgTalk.acts);
|
|
1176
|
+
if (rpgTalk.name) talk(rpgTalk.name, rpgTalk.line, rpgTalk.acts, rpgTalk.face);
|
|
1123
1177
|
});
|
|
1124
1178
|
boot().catch((err) => setFlash("error", err.message));
|
|
1125
1179
|
</script>
|