annotate mrjunejune/src/jrpg/jrpg.js @ 260:1f9877b637e9

Add Copilot-powered cyberpunk JRPG chat Integrate the production JRPG chat with Seobeo streaming, Deita persistence, and a Bazel-managed Copilot SDK and LiteLLM inference stack. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <mrjunejune@users.noreply.github.com>
date Wed, 05 Aug 2026 09:19:41 -0700
parents 667156fcd3e3
children b401627fc49e
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
1 const CHARACTER_STATES = Object.freeze({
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
2 default: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
3 alt: "Epi the Shiba Inu standing happily",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
4 src: "/public/jrpg/shiba-default.webp",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
5 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
6 sad: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
7 alt: "Epi the Shiba Inu sitting sadly",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
8 src: "/public/jrpg/shiba-sad.webp",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
9 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
10 thinking: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
11 alt: "Epi the Shiba Inu tilting her head in thought",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
12 src: "/public/jrpg/shiba-thinking.webp",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
13 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
14 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
15
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
16 const PREVIEWS = Object.freeze({
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
17 resume: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
18 copy: "Experience, projects, and the systems I have helped build.",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
19 kicker: "CHARACTER RECORD",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
20 title: "Resume",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
21 type: "Profile",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
22 url: "/resume",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
23 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
24 tools: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
25 copy: "Small, focused utilities for writing, media, and experimentation.",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
26 kicker: "ITEM INVENTORY",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
27 title: "Tools",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
28 type: "Utilities",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
29 url: "/tools",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
30 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
31 blog: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
32 copy: "Notes from building systems, games, web tools, and curious prototypes.",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
33 kicker: "QUEST ARCHIVE",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
34 title: "Blogs",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
35 type: "Writing",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
36 url: "/blog",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
37 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
38 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
39
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
40 const CONVERSATION_STORAGE_KEY = "mjj-jrpg-conversation-id";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
41
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
42 class MjjJrpgCharacter extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
43 static get observedAttributes() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
44 return ["state"];
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
45 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
46
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
47 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
48 this.render();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
49 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
50
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
51 attributeChangedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
52 if (this.isConnected) this.render();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
53 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
54
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 render() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 const image = this.querySelector("[data-character]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57 if (!image) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 const state = CHARACTER_STATES[this.getAttribute("state")] ||
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 CHARACTER_STATES.default;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 image.src = state.src;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 image.alt = state.alt;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
62 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
63 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
64
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
65 class MjjJrpgChat extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
66 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 this._messages = this.querySelector("[data-messages]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 this._viewport = this.querySelector("[data-zen-viewport]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
70
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 appendMessage(speaker, text) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 if (!this._messages) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73 const item = document.createElement("li");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
74 const name = document.createElement("strong");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
75 const copy = document.createElement("p");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
76 item.className = "jrpg-message";
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 item.dataset.speaker = speaker;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 name.textContent = speaker;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79 copy.textContent = text;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
80 item.append(name, copy);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
81 this._messages.append(item);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
82 this.scrollEnd();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
83 return item;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
84 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
85
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
86 replaceMessages(turns) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
87 if (!this._messages) return;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
88 this._messages.replaceChildren();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
89 for (const turn of turns) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
90 if (!["user", "assistant"].includes(turn.role)) continue;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
91 this.appendMessage(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
92 turn.role === "user" ? "June" : "Epi",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
93 turn.content || (turn.status === "failed" ? "The quest failed." : ""),
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
94 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
95 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
96 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
97
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
98 startAssistantMessage() {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
99 return this.appendMessage("Epi", "");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
100 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
101
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
102 setMessageText(item, text) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
103 const copy = item?.querySelector("p");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
104 if (copy) copy.textContent = text;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
105 this.scrollEnd();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
106 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
107
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
108 scrollEnd() {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 requestAnimationFrame(() => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 if (this._viewport) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 this._viewport.scrollTop = this._viewport.scrollHeight;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
114 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
115 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117 class MjjJrpgComposer extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 this._form = this.querySelector("[data-composer]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 this._textarea = this.querySelector("textarea");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 this._button = this.querySelector('button[type="submit"]');
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
122 this._cancel = this.querySelector("[data-cancel]");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
123 this._cancelControl = this.querySelector("[data-cancel-control]");
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
124 this._onSubmit = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 event.preventDefault();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126 const message = this._textarea?.value.trim();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 if (!message || this._button?.disabled) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 this.dispatchEvent(new CustomEvent("mjj-jrpg-submit", {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 bubbles: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130 detail: { message },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 }));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 this._form.reset();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 this._onKeydown = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 event.preventDefault();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 this._form?.requestSubmit();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 this._form?.addEventListener("submit", this._onSubmit);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
140 this._textarea?.addEventListener("keydown", this._onKeydown);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
141 this._onCancel = () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
142 this.dispatchEvent(new CustomEvent("mjj-jrpg-cancel", {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
143 bubbles: true,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
144 }));
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
145 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
146 this._cancel?.addEventListener("click", this._onCancel);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 disconnectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
150 this._form?.removeEventListener("submit", this._onSubmit);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
151 this._textarea?.removeEventListener("keydown", this._onKeydown);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
152 this._cancel?.removeEventListener("click", this._onCancel);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 setBusy(busy) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156 if (this._textarea) this._textarea.disabled = busy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 if (this._button) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 this._button.disabled = busy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 this._button.setAttribute("aria-busy", String(busy));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160 }
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
161 if (this._cancelControl) this._cancelControl.hidden = !busy;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
162 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
163
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
164 setDisabled(disabled) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
165 if (this._textarea) this._textarea.disabled = disabled;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
166 if (this._button) this._button.disabled = disabled;
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
168 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
169
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 class MjjJrpgMenu extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 this._onClick = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 const button = event.target.closest("button[data-preview]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 if (!button || !this.contains(button)) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175 for (const item of this.querySelectorAll("button[data-preview]")) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 item.setAttribute("aria-pressed", String(item === button));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 this.dispatchEvent(new CustomEvent("mjj-jrpg-preview-change", {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 bubbles: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180 detail: { selection: button.dataset.preview },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 }));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 this.addEventListener("click", this._onClick);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 disconnectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 this.removeEventListener("click", this._onClick);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 class MjjJrpgPreview extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193 this.show(this.dataset.selection || "resume");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 show(selection) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 const preview = PREVIEWS[selection] || PREVIEWS.resume;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 this.dataset.selection = selection;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 this.querySelector("[data-preview-kicker]").textContent = preview.kicker;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 this.querySelector("[data-preview-title]").textContent = preview.title;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
201 this.querySelector("[data-preview-copy]").textContent = preview.copy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
202 this.querySelector("[data-preview-type]").textContent = preview.type;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 this.querySelector("[data-dialog-title]").textContent = preview.title;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 this.querySelector("[data-dialog-copy]").textContent = preview.copy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 const link = this.querySelector("[data-preview-link]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
206 link.href = preview.url;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
207 link.setAttribute("aria-label", `Open ${preview.title}`);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
210
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
211 class MjjJrpgShell extends HTMLElement {}
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 for (const [name, constructor] of Object.entries({
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 "mjj-jrpg-character": MjjJrpgCharacter,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 "mjj-jrpg-chat": MjjJrpgChat,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 "mjj-jrpg-composer": MjjJrpgComposer,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 "mjj-jrpg-menu": MjjJrpgMenu,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 "mjj-jrpg-preview": MjjJrpgPreview,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 "mjj-jrpg-shell": MjjJrpgShell,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 })) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 if (!customElements.get(name)) customElements.define(name, constructor);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
224 async function requestJson(url, options = {}) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
225 const response = await fetch(url, {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
226 ...options,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
227 headers: {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
228 "Content-Type": "application/json",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
229 ...options.headers,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
230 },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
231 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
232 if (!response.ok) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
233 let message = `Request failed (${response.status})`;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
234 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
235 const body = await response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
236 message = body.error?.message || message;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
237 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
238 // Preserve the status-derived message.
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
239 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
240 throw new Error(message);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
241 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
242 return response.status === 204 ? null : response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
243 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
244
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
245 async function consumeSse(response, onEvent) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
246 if (!response.body) throw new Error("Streaming response body is unavailable");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
247 const reader = response.body.getReader();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
248 const decoder = new TextDecoder();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
249 let buffer = "";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
250
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
251 const dispatch = block => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
252 let eventName = "message";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
253 const data = [];
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
254 for (const line of block.split("\n")) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
255 if (line.startsWith("event:")) eventName = line.slice(6).trim();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
256 else if (line.startsWith("data:")) data.push(line.slice(5).trimStart());
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
257 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
258 if (!data.length) return;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
259 onEvent(eventName, JSON.parse(data.join("\n")));
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
260 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
261
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
262 while (true) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
263 const { done, value } = await reader.read();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
264 buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
265 buffer = buffer.replaceAll("\r\n", "\n");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
266 let boundary;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
267 while ((boundary = buffer.indexOf("\n\n")) >= 0) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
268 const block = buffer.slice(0, boundary);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
269 buffer = buffer.slice(boundary + 2);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
270 if (block.trim()) dispatch(block);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
271 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
272 if (done) break;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
273 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
274 if (buffer.trim()) dispatch(buffer);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
275 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
276
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
277 async function initializeJrpgPage() {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 const colorProbe = document.createElement("span");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 colorProbe.style.background = "var(--zenbu-sys-color-surface-page)";
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 document.body.append(colorProbe);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 const themeColor = getComputedStyle(colorProbe).backgroundColor;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
282 colorProbe.remove();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 for (const meta of document.querySelectorAll('meta[name="theme-color"]')) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
284 meta.setAttribute("content", themeColor);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
285 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287 const shell = document.querySelector("mjj-jrpg-shell");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 const character = shell?.querySelector("mjj-jrpg-character");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 const chat = shell?.querySelector("mjj-jrpg-chat");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 const composer = shell?.querySelector("mjj-jrpg-composer");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 const preview = shell?.querySelector("mjj-jrpg-preview");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
292 let characterTimer = 0;
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
293 let conversationId = sessionStorage.getItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
294 let activeController = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
295 composer?.setDisabled(true);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
296
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
297 if (conversationId) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
298 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
299 const conversation = await requestJson(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
300 `/api/conversations/${encodeURIComponent(conversationId)}`,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
301 { headers: {} },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
302 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
303 chat?.replaceMessages(conversation.turns);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
304 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
305 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
306 conversationId = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
307 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
308 }
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 shell?.addEventListener("mjj-jrpg-preview-change", event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 preview?.show(event.detail.selection);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 character?.setAttribute("state", "thinking");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 window.clearTimeout(characterTimer);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 characterTimer = window.setTimeout(() => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315 character?.setAttribute("state", "default");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 }, 650);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
319 shell?.addEventListener("mjj-jrpg-cancel", () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
320 activeController?.abort();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
321 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
322
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
323 shell?.addEventListener("mjj-jrpg-submit", async event => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
324 if (activeController) return;
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
325 chat?.appendMessage("June", event.detail.message);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 composer?.setBusy(true);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 character?.setAttribute("state", "thinking");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 window.clearTimeout(characterTimer);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
329 const assistantItem = chat?.startAssistantMessage();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
330 let assistantText = "";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
331 let turnFinished = false;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
332 activeController = new AbortController();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
333
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
334 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
335 const createConversation = async () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
336 const conversation = await requestJson("/api/conversations", {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
337 method: "POST",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
338 body: JSON.stringify({ title: "Shiba Quest" }),
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
339 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
340 conversationId = conversation.id;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
341 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, conversationId);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
342 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
343 if (!conversationId) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
344 await createConversation();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
345 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
346
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
347 const postTurn = () => fetch(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
348 `/api/conversations/${encodeURIComponent(conversationId)}/turns`, {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
349 method: "POST",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
350 headers: { "Content-Type": "application/json" },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
351 body: JSON.stringify({ prompt: event.detail.message }),
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
352 signal: activeController.signal,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
353 },
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 );
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
355 let response = await postTurn();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
356 if (response.status === 404) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
357 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
358 conversationId = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
359 await createConversation();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
360 response = await postTurn();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
361 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
362 if (!response.ok) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
363 let message = `Turn failed (${response.status})`;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
364 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
365 const body = await response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
366 message = body.error?.message || message;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
367 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
368 // Preserve the status-derived message.
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
369 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
370 throw new Error(message);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
371 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
372 await consumeSse(response, (eventName, data) => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
373 if (eventName === "assistant.delta") {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
374 assistantText += data.delta || "";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
375 chat?.setMessageText(assistantItem, assistantText);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
376 } else if (eventName === "assistant.completed") {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
377 assistantText = data.content || assistantText;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
378 chat?.setMessageText(assistantItem, assistantText);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
379 } else if (eventName === "turn.error") {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
380 throw new Error(data.message || "Inference turn failed");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
381 } else if (eventName === "turn.done") {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
382 turnFinished = true;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
383 if (data.failed) throw new Error("Inference turn failed");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
384 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
385 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
386 if (!turnFinished) throw new Error("Inference stream ended unexpectedly");
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 character?.setAttribute("state", "default");
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
388 } catch (error) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
389 const aborted = error instanceof DOMException && error.name === "AbortError";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
390 chat?.setMessageText(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
391 assistantItem,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
392 aborted ? "Quest cancelled." : `System error: ${error.message}`,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
393 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
394 character?.setAttribute("state", "sad");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
395 } finally {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396 composer?.setBusy(false);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397 composer?.querySelector("textarea")?.focus();
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
398 activeController = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
399 }
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 });
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
401 composer?.setDisabled(false);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 if (document.readyState === "loading") {
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
405 document.addEventListener("DOMContentLoaded", () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
406 void initializeJrpgPage();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
407 }, {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 once: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
410 } else {
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
411 void initializeJrpgPage();
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 }