annotate mrjunejune/src/jrpg/jrpg.js @ 261:b401627fc49e

Add JRPG mock flows and interactive previews Add scripted mock SSE commands, custom event forwarding, animated chat turns, full-height message navigation, and a cyberpunk resume dossier. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <mrjunejune@users.noreply.github.com>
date Wed, 05 Aug 2026 20:38:32 -0700
parents 1f9877b637e9
children 0f45474c1b1a
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",
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
23 works: [
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
24 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
25 detail: "Agentic execution",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
26 label: "Copilot Tasks",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
27 url: "https://www.microsoft.com/en-us/microsoft-copilot/blog/2026/02/26/copilot-tasks-from-answers-to-actions/",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
28 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
29 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
30 detail: "AI platform",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
31 label: "Copilot SuperApp",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
32 url: "https://www.cio.com/article/3977098/microsoft-doubles-down-on-multi-model-ai-as-it-builds-a-copilot-super-app.html",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
33 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
34 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
35 detail: "Build 2026",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
36 label: "Code",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
37 url: "https://news.microsoft.com/build-2026/",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
38 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
39 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
40 detail: "Personal agent",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
41 label: "Autopilot",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
42 url: "https://www.microsoft.com/en-us/microsoft-365/blog/2026/06/02/introducing-microsoft-scout-your-always-on-personal-agent/",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
43 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
44 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
45 detail: "Ads systems",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
46 label: "Meta",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
47 url: "https://www.meta.com/",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
48 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
49 {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
50 detail: "Workspace platform",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
51 label: "Google",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
52 url: "https://www.google.com/",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
53 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
54 ],
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
55 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
56 tools: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
57 copy: "Small, focused utilities for writing, media, and experimentation.",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
58 kicker: "ITEM INVENTORY",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
59 title: "Tools",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
60 type: "Utilities",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
61 url: "/tools",
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
62 works: [
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
63 { detail: "Writing", label: "Markdown", url: "/tools/markdown_to_html" },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
64 { detail: "Media", label: "Converter", url: "/tools/file_converter" },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
65 { detail: "Streaming", label: "HLS Player", url: "/tools/hls_player" },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
66 ],
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
67 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
68 blog: {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
69 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
70 kicker: "QUEST ARCHIVE",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
71 title: "Blogs",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
72 type: "Writing",
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
73 url: "/blog",
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
74 works: [
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
75 { detail: "Archive", label: "All posts", url: "/blog" },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
76 ],
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
77 },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
78 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
79
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
80 const CONVERSATION_STORAGE_KEY = "mjj-jrpg-conversation-id";
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
81 const TYPING_INTERVAL_MS = 18;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
82
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
83 class StreamingTextAnimator {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
84 constructor(render) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
85 this._render = render;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
86 this._displayed = "";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
87 this._target = "";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
88 this._timer = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
89 this._waiters = [];
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
90 this._reduceMotion = window.matchMedia(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
91 "(prefers-reduced-motion: reduce)",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
92 ).matches;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
93 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
94
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
95 append(text) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
96 if (typeof text !== "string" || !text) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
97 this._target += text;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
98 this._schedule();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
99 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
100
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
101 complete(text) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
102 if (typeof text === "string") {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
103 this._target = text;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
104 if (!this._target.startsWith(this._displayed)) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
105 let shared = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
106 while (
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
107 shared < this._target.length &&
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
108 shared < this._displayed.length &&
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
109 this._target[shared] === this._displayed[shared]
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
110 ) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
111 shared++;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
112 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
113 this._displayed = this._displayed.slice(0, shared);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
114 this._render(this._displayed);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
115 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
116 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
117 this._schedule();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
118 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
119
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
120 waitForIdle() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
121 if (!this._timer && this._displayed === this._target) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
122 return Promise.resolve();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
123 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
124 return new Promise(resolve => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
125 this._waiters.push(resolve);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
126 });
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
127 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
128
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
129 cancel() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
130 window.clearTimeout(this._timer);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
131 this._timer = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
132 this._target = this._displayed;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
133 this._settle();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
134 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
135
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
136 _schedule() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
137 if (this._reduceMotion) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
138 this._displayed = this._target;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
139 this._render(this._displayed);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
140 this._settle();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
141 return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
142 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
143 if (this._timer || this._displayed === this._target) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
144 this._timer = window.setTimeout(() => this._tick(), TYPING_INTERVAL_MS);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
145 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
146
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
147 _tick() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
148 this._timer = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
149 const remaining = this._target.length - this._displayed.length;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
150 if (remaining <= 0) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
151 this._settle();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
152 return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
153 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
154 const amount = Math.max(1, Math.ceil(remaining / 80));
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
155 this._displayed = this._target.slice(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
156 0,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
157 this._displayed.length + amount,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
158 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
159 this._render(this._displayed);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
160 if (this._displayed === this._target) this._settle();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
161 else this._schedule();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
162 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
163
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
164 _settle() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
165 if (this._timer || this._displayed !== this._target) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
166 const waiters = this._waiters.splice(0);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
167 for (const resolve of waiters) resolve();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
168 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
169 }
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
170
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 class MjjJrpgCharacter extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 static get observedAttributes() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 return ["state"];
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 this.render();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180 attributeChangedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 if (this.isConnected) this.render();
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
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184 render() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 const image = this.querySelector("[data-character]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 if (!image) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 const state = CHARACTER_STATES[this.getAttribute("state")] ||
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 CHARACTER_STATES.default;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189 image.src = state.src;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 image.alt = state.alt;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
193
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
194 class MjjJrpgChat extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
196 this._messages = this.querySelector("[data-messages]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
197 this._viewport = this.querySelector("[data-zen-viewport]");
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
198 this._previousButton = this.querySelector("[data-turn-previous]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
199 this._nextButton = this.querySelector("[data-turn-next]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
200 this._position = this.querySelector("[data-turn-position]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
201 this._initialMessages = [...(this._messages?.children || [])].map(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
202 item => item.cloneNode(true),
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
203 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
204 this._turnSequence = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
205 this._activeGroup = null;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
206 this._onNavigationClick = event => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
207 const button = event.target.closest("button");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
208 if (!button || !this.contains(button) || button.disabled) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
209 if (button === this._previousButton) this._moveTurn(-1);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
210 else if (button === this._nextButton) this._moveTurn(1);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
211 };
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
212 this.addEventListener("click", this._onNavigationClick);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
213 this._refreshTurnGroups("start");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
214 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
215
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
216 disconnectedCallback() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
217 this.removeEventListener("click", this._onNavigationClick);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 appendMessage(speaker, text) {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
221 let group = this._activeGroup || "start";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
222 if (speaker === "June") {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
223 this._turnSequence++;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
224 group = `turn-${this._turnSequence}`;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
225 this._activeGroup = group;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
226 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
227 return this._createMessage(speaker, text, group, true);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
228 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
229
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
230 _createMessage(speaker, text, group, select) {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 if (!this._messages) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 const item = document.createElement("li");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
233 const name = document.createElement("strong");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
234 const copy = document.createElement("p");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 item.className = "jrpg-message";
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 item.dataset.speaker = speaker;
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
237 item.dataset.turnGroup = group;
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238 name.textContent = speaker;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 copy.textContent = text;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 item.append(name, copy);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 this._messages.append(item);
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
242 this._refreshTurnGroups(select ? group : this._currentGroup);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
243 return item;
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
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
246 replaceMessages(turns) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
247 if (!this._messages) return;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
248 this._messages.replaceChildren();
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
249 this._turnSequence = 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
250 this._activeGroup = null;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
251 const visibleTurns = turns.filter(turn =>
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
252 ["user", "assistant"].includes(turn.role)
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
253 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
254 if (!visibleTurns.length) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
255 this._messages.append(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
256 ...this._initialMessages.map(item => item.cloneNode(true)),
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
257 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
258 this._refreshTurnGroups("start");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
259 return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
260 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
261 let group = null;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
262 for (const turn of visibleTurns) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
263 if (turn.role === "user" || !group) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
264 this._turnSequence++;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
265 group = `turn-${this._turnSequence}`;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
266 this._activeGroup = group;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
267 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
268 this._createMessage(
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
269 turn.role === "user" ? "June" : "Epi",
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
270 turn.content ||
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
271 (turn.status === "aborted"
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
272 ? "Quest cancelled."
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
273 : turn.status === "failed"
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
274 ? "The quest failed."
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
275 : ""),
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
276 group,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
277 false,
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
278 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
279 }
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
280 this._refreshTurnGroups(group);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
281 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
282
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
283 startAssistantMessage() {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
284 return this.appendMessage("Epi", "");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
285 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
286
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
287 setMessageText(item, text) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
288 const copy = item?.querySelector("p");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
289 if (copy) copy.textContent = text;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
290 this.scrollEnd();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
291 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
292
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
293 setMessageStreaming(item, streaming) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
294 if (!item) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
295 if (streaming) item.dataset.streaming = "true";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
296 else delete item.dataset.streaming;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
297 this._viewport?.setAttribute("aria-busy", String(streaming));
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
298 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
299
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
300 _refreshTurnGroups(preferredGroup) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
301 const groups = [];
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
302 for (const item of this._messages?.children || []) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
303 const group = item.dataset.turnGroup || "start";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
304 item.dataset.turnGroup = group;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
305 if (!groups.includes(group)) groups.push(group);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
306 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
307 this._turnGroups = groups;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
308 const preferredIndex = groups.indexOf(preferredGroup);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
309 const currentIndex = groups.indexOf(this._currentGroup);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
310 this._turnIndex = preferredIndex >= 0
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
311 ? preferredIndex
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
312 : currentIndex >= 0
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
313 ? currentIndex
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
314 : Math.max(0, groups.length - 1);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
315 this._showTurn();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
316 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
317
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
318 _moveTurn(offset) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
319 const nextIndex = Math.max(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
320 0,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
321 Math.min(this._turnGroups.length - 1, this._turnIndex + offset),
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
322 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
323 if (nextIndex === this._turnIndex) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
324 this._turnIndex = nextIndex;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
325 this._showTurn();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
326 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
327
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
328 _showTurn() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
329 const group = this._turnGroups?.[this._turnIndex];
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
330 this._currentGroup = group;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
331 for (const item of this._messages?.children || []) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
332 item.hidden = item.dataset.turnGroup !== group;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
333 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
334 if (this._previousButton) this._previousButton.disabled = this._turnIndex <= 0;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
335 if (this._nextButton) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
336 this._nextButton.disabled =
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
337 this._turnIndex >= (this._turnGroups?.length || 0) - 1;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
338 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
339 if (this._position) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
340 if (group === "start") {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
341 this._position.textContent = "START";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
342 } else {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
343 const turns = this._turnGroups.filter(item => item !== "start");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
344 this._position.textContent =
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
345 `TURN ${turns.indexOf(group) + 1} / ${turns.length}`;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
346 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
347 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
348 this.scrollEnd();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
349 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
350
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
351 scrollEnd() {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 requestAnimationFrame(() => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
353 if (this._viewport) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
354 this._viewport.scrollTop = this._viewport.scrollHeight;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
356 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
357 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360 class MjjJrpgComposer extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
361 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
362 this._form = this.querySelector("[data-composer]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 this._textarea = this.querySelector("textarea");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364 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
365 this._cancel = this.querySelector("[data-cancel]");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
366 this._cancelControl = this.querySelector("[data-cancel-control]");
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
367 this._onSubmit = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
368 event.preventDefault();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
369 const message = this._textarea?.value.trim();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
370 if (!message || this._button?.disabled) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
371 this.dispatchEvent(new CustomEvent("mjj-jrpg-submit", {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
372 bubbles: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
373 detail: { message },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
374 }));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
375 this._form.reset();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
376 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
377 this._onKeydown = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
378 if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
379 event.preventDefault();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
380 this._form?.requestSubmit();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382 this._form?.addEventListener("submit", this._onSubmit);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 this._textarea?.addEventListener("keydown", this._onKeydown);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
384 this._onCancel = () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
385 this.dispatchEvent(new CustomEvent("mjj-jrpg-cancel", {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
386 bubbles: true,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
387 }));
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
388 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
389 this._cancel?.addEventListener("click", this._onCancel);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
390 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 disconnectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 this._form?.removeEventListener("submit", this._onSubmit);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 this._textarea?.removeEventListener("keydown", this._onKeydown);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
395 this._cancel?.removeEventListener("click", this._onCancel);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
396 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
397
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 setBusy(busy) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399 if (this._textarea) this._textarea.disabled = busy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 if (this._button) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 this._button.disabled = busy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
402 this._button.setAttribute("aria-busy", String(busy));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
403 }
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
404 if (this._cancelControl) this._cancelControl.hidden = !busy;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
405 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
406
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
407 setCancellable(cancellable) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
408 if (this._cancelControl) this._cancelControl.hidden = !cancellable;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
409 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
410
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
411 setDisabled(disabled) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
412 if (this._textarea) this._textarea.disabled = disabled;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
413 if (this._button) this._button.disabled = disabled;
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 class MjjJrpgMenu extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
418 connectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
419 this._onClick = event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 const button = event.target.closest("button[data-preview]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
421 if (!button || !this.contains(button)) return;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
422 for (const item of this.querySelectorAll("button[data-preview]")) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 item.setAttribute("aria-pressed", String(item === button));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425 this.dispatchEvent(new CustomEvent("mjj-jrpg-preview-change", {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
426 bubbles: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
427 detail: { selection: button.dataset.preview },
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428 }));
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429 };
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430 this.addEventListener("click", this._onClick);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433 disconnectedCallback() {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434 this.removeEventListener("click", this._onClick);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 class MjjJrpgPreview extends HTMLElement {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 connectedCallback() {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
440 this._onClick = event => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
441 if (
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
442 event.target.closest("[data-zen-trigger]") &&
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
443 this.dataset.selection === "resume"
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
444 ) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
445 void this.loadResume();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
446 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
447 };
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
448 this.addEventListener("click", this._onClick);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
449 this.show(this.dataset.selection || "resume");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
450 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
451
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
452 disconnectedCallback() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
453 this.removeEventListener("click", this._onClick);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
454 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
455
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
456 show(selection) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
457 const preview = PREVIEWS[selection] || PREVIEWS.resume;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
458 this.dataset.selection = selection;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
459 this.querySelector("[data-preview-kicker]").textContent = preview.kicker;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
460 this.querySelector("[data-preview-title]").textContent = preview.title;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
461 this.querySelector("[data-preview-copy]").textContent = preview.copy;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
462 this.querySelector("[data-preview-type]").textContent = preview.type;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
463 this.querySelector("[data-dialog-title]").textContent = preview.title;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
464 this.querySelector("[data-dialog-copy]").textContent = preview.copy;
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
465 const showcase = this.querySelector("[data-work-showcase]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
466 showcase.replaceChildren(...preview.works.map(work => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
467 const item = document.createElement("li");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
468 const link = document.createElement("a");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
469 const label = document.createElement("span");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
470 const detail = document.createElement("small");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
471 link.href = work.url;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
472 if (new URL(work.url, window.location.href).origin !== window.location.origin) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
473 link.target = "_blank";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
474 link.rel = "noreferrer";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
475 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
476 label.textContent = work.label;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
477 detail.textContent = work.detail;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
478 link.append(label, detail);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
479 item.append(link);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
480 return item;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
481 }));
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
482 const link = this.querySelector("[data-preview-link]");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
483 link.href = preview.url;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
484 link.setAttribute("aria-label", `Open ${preview.title}`);
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
485 const resumeDossier = this.querySelector("[data-resume-dossier]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
486 const resumeDownload = this.querySelector("[data-resume-download]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
487 const isResume = selection === "resume";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
488 resumeDossier.hidden = !isResume;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
489 resumeDownload.hidden = !isResume;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
490 this.querySelector("[data-dialog-copy]").hidden = isResume;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
491 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
492
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
493 async loadResume() {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
494 if (this._resumeLoaded || this._resumeLoading) return;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
495 this._resumeLoading = true;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
496 const status = this.querySelector("[data-resume-status]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
497 const content = this.querySelector("[data-resume-content]");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
498 try {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
499 const response = await fetch("/resume", {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
500 headers: { Accept: "text/html" },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
501 });
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
502 if (!response.ok) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
503 throw new Error(`Resume request failed (${response.status})`);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
504 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
505 const documentCopy = new DOMParser().parseFromString(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
506 await response.text(),
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
507 "text/html",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
508 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
509 const resume = documentCopy.querySelector("main");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
510 if (!resume) throw new Error("Resume content is unavailable");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
511 for (const unsafe of resume.querySelectorAll(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
512 "embed, iframe, object, script, style, svg",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
513 )) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
514 unsafe.remove();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
515 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
516 for (const element of resume.querySelectorAll("*")) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
517 for (const attribute of [...element.attributes]) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
518 if (attribute.name.toLowerCase().startsWith("on")) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
519 element.removeAttribute(attribute.name);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
520 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
521 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
522 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
523 for (const anchor of resume.querySelectorAll("a")) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
524 const target = new URL(anchor.href, window.location.href);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
525 if (target.origin !== window.location.origin) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
526 anchor.target = "_blank";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
527 anchor.rel = "noreferrer";
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
528 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
529 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
530 content.replaceChildren(...resume.childNodes);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
531 status.hidden = true;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
532 this._resumeLoaded = true;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
533 } catch (error) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
534 status.textContent = `Unable to load resume: ${error.message}`;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
535 } finally {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
536 this._resumeLoading = false;
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
537 }
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
538 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
539 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
540
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
541 class MjjJrpgShell extends HTMLElement {}
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
542
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
543 for (const [name, constructor] of Object.entries({
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
544 "mjj-jrpg-character": MjjJrpgCharacter,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
545 "mjj-jrpg-chat": MjjJrpgChat,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
546 "mjj-jrpg-composer": MjjJrpgComposer,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
547 "mjj-jrpg-menu": MjjJrpgMenu,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
548 "mjj-jrpg-preview": MjjJrpgPreview,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
549 "mjj-jrpg-shell": MjjJrpgShell,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
550 })) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
551 if (!customElements.get(name)) customElements.define(name, constructor);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
552 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
553
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
554 async function requestJson(url, options = {}) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
555 const response = await fetch(url, {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
556 ...options,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
557 headers: {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
558 "Content-Type": "application/json",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
559 ...options.headers,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
560 },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
561 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
562 if (!response.ok) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
563 let message = `Request failed (${response.status})`;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
564 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
565 const body = await response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
566 message = body.error?.message || message;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
567 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
568 // Preserve the status-derived message.
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
569 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
570 throw new Error(message);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
571 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
572 return response.status === 204 ? null : response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
573 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
574
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
575 async function consumeSse(response, onEvent) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
576 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
577 const reader = response.body.getReader();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
578 const decoder = new TextDecoder();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
579 let buffer = "";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
580
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
581 const dispatch = block => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
582 let eventName = "message";
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
583 const data = [];
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
584 for (const line of block.split("\n")) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
585 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
586 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
587 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
588 if (!data.length) return;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
589 onEvent(eventName, JSON.parse(data.join("\n")));
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
590 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
591
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
592 while (true) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
593 const { done, value } = await reader.read();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
594 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
595 buffer = buffer.replaceAll("\r\n", "\n");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
596 let boundary;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
597 while ((boundary = buffer.indexOf("\n\n")) >= 0) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
598 const block = buffer.slice(0, boundary);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
599 buffer = buffer.slice(boundary + 2);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
600 if (block.trim()) dispatch(block);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
601 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
602 if (done) break;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
603 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
604 if (buffer.trim()) dispatch(buffer);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
605 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
606
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
607 async function initializeJrpgPage() {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
608 const colorProbe = document.createElement("span");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
609 colorProbe.style.background = "var(--zenbu-sys-color-surface-page)";
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
610 document.body.append(colorProbe);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
611 const themeColor = getComputedStyle(colorProbe).backgroundColor;
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
612 colorProbe.remove();
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
613 for (const meta of document.querySelectorAll('meta[name="theme-color"]')) {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
614 meta.setAttribute("content", themeColor);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
615 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
616
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
617 const shell = document.querySelector("mjj-jrpg-shell");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
618 const character = shell?.querySelector("mjj-jrpg-character");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
619 const chat = shell?.querySelector("mjj-jrpg-chat");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
620 const composer = shell?.querySelector("mjj-jrpg-composer");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
621 const preview = shell?.querySelector("mjj-jrpg-preview");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
622 let characterTimer = 0;
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
623 let conversationId = sessionStorage.getItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
624 let activeController = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
625 composer?.setDisabled(true);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
626
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
627 if (conversationId) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
628 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
629 const conversation = await requestJson(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
630 `/api/conversations/${encodeURIComponent(conversationId)}`,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
631 { headers: {} },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
632 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
633 chat?.replaceMessages(conversation.turns);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
634 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
635 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
636 conversationId = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
637 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
638 }
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
639
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
640 shell?.addEventListener("mjj-jrpg-preview-change", event => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
641 preview?.show(event.detail.selection);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
642 character?.setAttribute("state", "thinking");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
643 window.clearTimeout(characterTimer);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
644 characterTimer = window.setTimeout(() => {
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
645 character?.setAttribute("state", "default");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
646 }, 650);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
647 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
648
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
649 shell?.addEventListener("mjj-jrpg-cancel", () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
650 activeController?.abort();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
651 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
652
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
653 shell?.addEventListener("mjj-jrpg-submit", async event => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
654 if (activeController) return;
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
655 chat?.appendMessage("June", event.detail.message);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
656 composer?.setBusy(true);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
657 character?.setAttribute("state", "thinking");
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
658 window.clearTimeout(characterTimer);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
659 const assistantItem = chat?.startAssistantMessage();
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
660 chat?.setMessageStreaming(assistantItem, true);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
661 const assistantText = new StreamingTextAnimator(text => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
662 chat?.setMessageText(assistantItem, text);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
663 });
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
664 let turnFinished = false;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
665 activeController = new AbortController();
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
666 activeController.signal.addEventListener("abort", () => {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
667 assistantText.cancel();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
668 }, { once: true });
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
669
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
670 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
671 const createConversation = async () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
672 const conversation = await requestJson("/api/conversations", {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
673 method: "POST",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
674 body: JSON.stringify({ title: "Shiba Quest" }),
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
675 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
676 conversationId = conversation.id;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
677 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, conversationId);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
678 };
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
679 if (!conversationId) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
680 await createConversation();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
681 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
682
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
683 const postTurn = () => fetch(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
684 `/api/conversations/${encodeURIComponent(conversationId)}/turns`, {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
685 method: "POST",
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
686 headers: { "Content-Type": "application/json" },
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
687 body: JSON.stringify({ prompt: event.detail.message }),
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
688 signal: activeController.signal,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
689 },
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
690 );
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
691 let response = await postTurn();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
692 if (response.status === 404) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
693 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
694 conversationId = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
695 await createConversation();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
696 response = await postTurn();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
697 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
698 if (!response.ok) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
699 let message = `Turn failed (${response.status})`;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
700 try {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
701 const body = await response.json();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
702 message = body.error?.message || message;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
703 } catch {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
704 // Preserve the status-derived message.
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
705 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
706 throw new Error(message);
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
707 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
708 await consumeSse(response, (eventName, data) => {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
709 shell?.dispatchEvent(new CustomEvent("mjj-jrpg-stream-event", {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
710 bubbles: true,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
711 detail: {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
712 data,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
713 type: eventName,
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
714 },
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
715 }));
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
716 if (eventName === "assistant.delta") {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
717 assistantText.append(data.delta || "");
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
718 } else if (eventName === "assistant.completed") {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
719 assistantText.complete(data.content);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
720 } else if (eventName === "turn.error") {
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
721 throw new Error(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
722 data.error?.message || data.message || "Inference turn failed",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
723 );
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
724 } else if (eventName === "turn.done") {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
725 turnFinished = true;
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
726 composer?.setCancellable(false);
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
727 if (data.failed || data.aborted) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
728 throw new Error(
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
729 data.aborted ? "Inference turn aborted" : "Inference turn failed",
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
730 );
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
731 }
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
732 }
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
733 });
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
734 if (!turnFinished) throw new Error("Inference stream ended unexpectedly");
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
735 await assistantText.waitForIdle();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
736 if (activeController.signal.aborted) {
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
737 throw new DOMException("Quest cancelled", "AbortError");
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
738 }
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
739 chat?.setMessageStreaming(assistantItem, false);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
740 character?.setAttribute("state", "default");
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
741 } catch (error) {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
742 const aborted = error instanceof DOMException && error.name === "AbortError";
261
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
743 assistantText.cancel();
b401627fc49e Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 260
diff changeset
744 chat?.setMessageStreaming(assistantItem, false);
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
745 chat?.setMessageText(
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
746 assistantItem,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
747 aborted ? "Quest cancelled." : `System error: ${error.message}`,
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
748 );
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
749 character?.setAttribute("state", "sad");
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
750 } finally {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
751 composer?.setBusy(false);
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
752 composer?.querySelector("textarea")?.focus();
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
753 activeController = null;
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
754 }
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
755 });
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
756 composer?.setDisabled(false);
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
757 }
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
758
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
759 if (document.readyState === "loading") {
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
760 document.addEventListener("DOMContentLoaded", () => {
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
761 void initializeJrpgPage();
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
762 }, {
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
763 once: true,
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
764 });
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
765 } else {
260
1f9877b637e9 Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents: 259
diff changeset
766 void initializeJrpgPage();
259
667156fcd3e3 Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
767 }