Mercurial
annotate mrjunejune/src/jrpg/jrpg.js @ 273:e02e2036ef84 default tip
add Layer 2 JRPG component system
Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sat, 08 Aug 2026 02:08:08 -0700 |
| parents | 41a49c29a28f |
| children |
| 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: { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
18 copy: "Member of Technical Staff and engineering leader with 10+ years building AI agent platforms and production systems across Microsoft, Meta, Google, and growth-stage companies.", |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
19 title: "Resume", |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
20 url: "/resume", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
21 works: [ |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
22 { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
23 detail: "Career dossier", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
24 label: "Full resume", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
25 modal: "resume", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
26 url: "/resume", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
27 }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
28 { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
29 detail: "Led engineering", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
30 label: "Copilot Tasks", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
31 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
|
32 }, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
33 { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
34 detail: "Foundational platform", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
35 label: "AIX Harness / Copilot", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
36 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
|
37 }, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
38 { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
39 detail: "Build 2026 products", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
40 label: "Code & Autopilot", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
41 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
|
42 }, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
43 { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
44 detail: "Full-stack ads systems", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
45 label: "Meta", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
46 url: "https://www.meta.com/", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
47 }, |
|
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 detail: "Workspace platform", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
50 label: "Google", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
51 url: "https://www.google.com/", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
52 }, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
53 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
54 detail: "Technical lead", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
55 label: "Warner Music Group", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
56 url: "https://www.wmg.com/", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
57 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
58 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
59 detail: "Personal agent launch", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
60 label: "Microsoft Scout", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
61 url: "https://www.microsoft.com/en-us/microsoft-365/blog/2026/06/02/introducing-microsoft-scout-your-always-on-personal-agent/", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
62 }, |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
63 ], |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
64 }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
65 tools: { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
66 copy: "Useful browser tools backed by first-party C, WASM, media, and document-processing systems.", |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
67 title: "Tools", |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
68 url: "/tools", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
69 works: [ |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
70 { 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
|
71 { 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
|
72 { 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
|
73 ], |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
74 }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
75 blog: { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
76 copy: "Technical writing about networking, rendering, performance, developer tooling, and experiments.", |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
77 title: "Blogs", |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
78 url: "/blog", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
79 works: [ |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
80 { 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
|
81 ], |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
82 }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
83 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
84 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
85 const CONVERSATION_STORAGE_KEY = "mjj-jrpg-conversation-id"; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
86 const CONVERSATION_LEGACY_KEY = "mjj-jrpg-legacy-claim"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
87 const ARCHIVE_PAGE_SIZE = 20; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
88 const TYPING_INTERVAL_MS = 18; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
89 const SCRIPT_LOADS = new Map(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
90 const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
91 const VALID_PANELS = Object.freeze(["resume", "tools", "blog", "conversations"]); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
92 const ROLE_GREETINGS = Object.freeze({ |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
93 guest: "Hi, my name is Epi. June usually calls me Epi-chan. I’m here to help you navigate his personal website and learn about his work, projects, and writing.", |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
94 invited: "Hi! I’m Epi, but June usually calls me Epi-chan. Since June invited you, I can be a little more casual. I can help you explore his site, talk about his projects, or brainstorm simple ideas with you.", |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
95 admin: "Hi June! I’m Epi-chan. I’m here to help you review your personal website, think through your projects, and draft or refine public-facing ideas.", |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
96 }); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
97 const TOOL_ICON_NAMES = Object.freeze({ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
98 "/notes": "repository", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
99 "/tools/file_converter": "retry", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
100 "/tools/hls_player": "play", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
101 "/tools/latex_editor": "file", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
102 "/tools/markdown_to_html": "code", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
103 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
104 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
105 function loadScript(source) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
106 if (SCRIPT_LOADS.has(source)) return SCRIPT_LOADS.get(source); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
107 const loading = new Promise((resolve, reject) => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
108 const script = document.createElement("script"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
109 script.src = source; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
110 script.addEventListener("load", resolve, { once: true }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
111 script.addEventListener("error", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
112 SCRIPT_LOADS.delete(source); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
113 script.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
114 reject(new Error(`Unable to load ${source}`)); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
115 }, { once: true }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
116 document.head.append(script); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
117 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
118 SCRIPT_LOADS.set(source, loading); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
119 return loading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
120 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
121 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
122 function greetingForSession(sessionData, hasConversations = false) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
123 const greeting = sessionData?.kind !== "user" |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
124 ? ROLE_GREETINGS.guest |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
125 : sessionData.role === "admin" |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
126 ? ROLE_GREETINGS.admin |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
127 : ROLE_GREETINGS.invited; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
128 return hasConversations |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
129 ? `${greeting} We’ve talked before. Open Conversations if you’d like to continue one.` |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
130 : greeting; |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
131 } |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
132 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
133 class StreamingTextAnimator { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
134 constructor(render) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
135 this._render = render; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
136 this._displayed = ""; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
137 this._target = ""; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
138 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
139 this._waiters = []; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
140 this._reduceMotion = window.matchMedia( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
141 "(prefers-reduced-motion: reduce)", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
142 ).matches; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
143 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
144 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
145 append(text) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
146 if (typeof text !== "string" || !text) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
147 this._target += text; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
148 this._schedule(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
149 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
150 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
151 complete(text) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
152 if (typeof text === "string") { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
153 this._target = text; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
154 if (!this._target.startsWith(this._displayed)) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
155 let shared = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
156 while ( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
157 shared < this._target.length && |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
158 shared < this._displayed.length && |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
159 this._target[shared] === this._displayed[shared] |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
160 ) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
161 shared++; |
|
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 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
|
164 this._render(this._displayed); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
165 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
166 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
167 this._schedule(); |
|
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 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
170 waitForIdle() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
171 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
|
172 return Promise.resolve(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
173 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
174 return new Promise(resolve => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
175 this._waiters.push(resolve); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
176 }); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
177 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
178 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
179 cancel() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
180 window.clearTimeout(this._timer); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
181 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
182 this._target = this._displayed; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
183 this._settle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
184 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
185 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
186 _schedule() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
187 if (this._reduceMotion) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
188 this._displayed = this._target; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
189 this._render(this._displayed); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
190 this._settle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
191 return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
192 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
193 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
|
194 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
|
195 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
196 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
197 _tick() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
198 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
199 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
|
200 if (remaining <= 0) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
201 this._settle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
202 return; |
|
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 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
|
205 this._displayed = this._target.slice( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
206 0, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
207 this._displayed.length + amount, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
208 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
209 this._render(this._displayed); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
210 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
|
211 else this._schedule(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
212 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
213 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
214 _settle() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
215 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
|
216 const waiters = this._waiters.splice(0); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
217 for (const resolve of waiters) resolve(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
218 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
219 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
220 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
221 class MjjJrpgCharacter extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
222 static get observedAttributes() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
223 return ["state"]; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
224 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
225 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
226 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
227 this.render(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
228 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
229 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
230 attributeChangedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
231 if (this.isConnected) this.render(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
232 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
233 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
234 render() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
235 const image = this.querySelector("[data-character]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
236 if (!image) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
237 const state = CHARACTER_STATES[this.getAttribute("state")] || |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
238 CHARACTER_STATES.default; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
239 image.src = state.src; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
240 image.alt = state.alt; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
241 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
242 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
243 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
244 class MjjJrpgChat extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
245 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
246 this._messages = this.querySelector("[data-messages]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
247 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
|
248 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
|
249 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
|
250 this._position = this.querySelector("[data-turn-position]"); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
251 this._greetingText = ROLE_GREETINGS.guest; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
252 this._greetingAnimator = null; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
253 this._turnSequence = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
254 this._activeGroup = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
255 this._onNavigationClick = event => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
256 const button = event.target.closest("button"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
257 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
|
258 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
|
259 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
|
260 }; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
261 this.addEventListener("click", this._onNavigationClick); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
262 this._refreshTurnGroups("start"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
263 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
264 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
265 disconnectedCallback() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
266 this.removeEventListener("click", this._onNavigationClick); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
267 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
268 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
269 appendMessage(speaker, text) { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
270 let group = this._activeGroup || "start"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
271 if (speaker === "June") { |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
272 this._greetingAnimator?.cancel(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
273 this._greetingAnimator = null; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
274 const greeting = this._messages?.querySelector("[data-greeting]"); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
275 if (greeting) this.setMessageStreaming(greeting, false); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
276 this._turnSequence++; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
277 group = `turn-${this._turnSequence}`; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
278 this._activeGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
279 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
280 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
|
281 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
282 |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
283 setGreeting(text, animate = true) { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
284 this._greetingText = typeof text === "string" && text |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
285 ? text |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
286 : ROLE_GREETINGS.guest; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
287 this._showGreeting(animate); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
288 } |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
289 |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
290 _showGreeting(animate) { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
291 if (!this._messages) return; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
292 this._greetingAnimator?.cancel(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
293 this._greetingAnimator = null; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
294 this._messages.replaceChildren(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
295 this._turnSequence = 0; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
296 this._activeGroup = null; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
297 const item = this._createMessage("Epi", "", "start", false); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
298 if (!item) return; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
299 item.dataset.greeting = "true"; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
300 this._refreshTurnGroups("start"); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
301 if (!animate) { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
302 this.setMessageText(item, this._greetingText); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
303 this.setMessageStreaming(item, false); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
304 return; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
305 } |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
306 this.setMessageStreaming(item, true); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
307 const animator = new StreamingTextAnimator(value => { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
308 this.setMessageText(item, value); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
309 }); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
310 this._greetingAnimator = animator; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
311 animator.complete(this._greetingText); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
312 void animator.waitForIdle().then(() => { |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
313 if (this._greetingAnimator !== animator) return; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
314 this.setMessageStreaming(item, false); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
315 this._greetingAnimator = null; |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
316 }); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
317 } |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
318 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
319 _createMessage(speaker, text, group, select) { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
320 if (!this._messages) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
321 const item = document.createElement("li"); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
322 const speakerCell = document.createElement("span"); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
323 const name = document.createElement("strong"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
324 const copy = document.createElement("p"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
325 item.className = "jrpg-message"; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
326 item.dataset.speaker = speaker; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
327 item.dataset.turnGroup = group; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
328 speakerCell.className = "jrpg-message-speaker"; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
329 if (speaker === "Epi") { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
330 const avatar = document.createElement("img"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
331 avatar.className = "jrpg-message-avatar"; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
332 avatar.src = "/public/jrpg/shiba-default.webp"; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
333 avatar.alt = ""; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
334 avatar.setAttribute("aria-hidden", "true"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
335 speakerCell.append(avatar); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
336 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
337 name.textContent = speaker; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
338 copy.textContent = text; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
339 speakerCell.append(name); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
340 item.append(speakerCell, copy); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
341 this._messages.append(item); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
342 this._refreshTurnGroups(select ? group : this._currentGroup); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
343 return item; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
344 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
345 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
346 replaceMessages(turns) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
347 if (!this._messages) return; |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
348 this._greetingAnimator?.cancel(); |
|
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
349 this._greetingAnimator = null; |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
350 this._messages.replaceChildren(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
351 this._turnSequence = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
352 this._activeGroup = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
353 const visibleTurns = turns.filter(turn => |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
354 ["user", "assistant"].includes(turn.role) |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
355 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
356 if (!visibleTurns.length) { |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
357 this._showGreeting(true); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
358 return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
359 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
360 let group = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
361 for (const turn of visibleTurns) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
362 if (turn.role === "user" || !group) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
363 this._turnSequence++; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
364 group = `turn-${this._turnSequence}`; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
365 this._activeGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
366 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
367 this._createMessage( |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
368 turn.role === "user" ? "June" : "Epi", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
369 turn.content || |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
370 (turn.status === "aborted" |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
371 ? "Quest cancelled." |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
372 : turn.status === "failed" |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
373 ? "The quest failed." |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
374 : ""), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
375 group, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
376 false, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
377 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
378 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
379 this._refreshTurnGroups(group); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
380 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
381 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
382 startAssistantMessage() { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
383 return this.appendMessage("Epi", ""); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
384 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
385 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
386 setMessageText(item, text) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
387 const copy = item?.querySelector("p"); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
388 if (copy) copy.textContent = text; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
389 this.scrollEnd(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
390 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
391 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
392 setMessageStreaming(item, streaming) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
393 if (!item) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
394 if (streaming) item.dataset.streaming = "true"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
395 else delete item.dataset.streaming; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
396 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
|
397 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
398 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
399 _refreshTurnGroups(preferredGroup) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
400 const groups = []; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
401 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
|
402 const group = item.dataset.turnGroup || "start"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
403 item.dataset.turnGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
404 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
|
405 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
406 this._turnGroups = groups; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
407 const preferredIndex = groups.indexOf(preferredGroup); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
408 const currentIndex = groups.indexOf(this._currentGroup); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
409 this._turnIndex = preferredIndex >= 0 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
410 ? preferredIndex |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
411 : currentIndex >= 0 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
412 ? currentIndex |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
413 : Math.max(0, groups.length - 1); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
414 this._showTurn(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
415 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
416 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
417 _moveTurn(offset) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
418 const nextIndex = Math.max( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
419 0, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
420 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
|
421 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
422 if (nextIndex === this._turnIndex) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
423 this._turnIndex = nextIndex; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
424 this._showTurn(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
425 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
426 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
427 _showTurn() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
428 const group = this._turnGroups?.[this._turnIndex]; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
429 this._currentGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
430 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
|
431 item.hidden = item.dataset.turnGroup !== group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
432 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
433 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
|
434 if (this._nextButton) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
435 this._nextButton.disabled = |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
436 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
|
437 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
438 if (this._position) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
439 if (group === "start") { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
440 this._position.textContent = "START"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
441 } else { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
442 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
|
443 this._position.textContent = |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
444 `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
|
445 } |
|
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 this.scrollEnd(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
448 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
449 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
450 scrollEnd() { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
451 requestAnimationFrame(() => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
452 if (this._viewport) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
453 this._viewport.scrollTop = this._viewport.scrollHeight; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
454 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
455 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
456 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
457 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
458 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
459 class MjjJrpgMenu extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
460 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
461 this._onClick = event => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
462 const button = event.target.closest("button[data-preview]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
463 if (!button || !this.contains(button)) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
464 for (const item of this.querySelectorAll("button[data-preview]")) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
465 item.setAttribute("aria-pressed", String(item === button)); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
466 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
467 this.dispatchEvent(new CustomEvent("mjj-jrpg-preview-change", { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
468 bubbles: true, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
469 detail: { selection: button.dataset.preview }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
470 })); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
471 }; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
472 this.addEventListener("click", this._onClick); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
473 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
474 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
475 disconnectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
476 this.removeEventListener("click", this._onClick); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
477 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
478 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
479 |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
480 function sanitizeFetchedMain(main) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
481 for (const unsafe of main.querySelectorAll( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
482 "embed, form, iframe, object, script, style, svg", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
483 )) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
484 unsafe.remove(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
485 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
486 for (const element of main.querySelectorAll("*")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
487 for (const attribute of [...element.attributes]) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
488 const name = attribute.name.toLowerCase(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
489 if (name.startsWith("on") || name === "id" || name === "style") { |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
490 element.removeAttribute(attribute.name); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
491 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
492 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
493 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
494 for (const anchor of main.querySelectorAll("a")) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
495 const href = anchor.getAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
496 if (!href || anchor.getAttribute("aria-disabled") === "true") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
497 anchor.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
498 continue; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
499 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
500 const target = new URL( |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
501 href, |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
502 window.location.href, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
503 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
504 anchor.href = target.href; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
505 if (target.origin !== window.location.origin) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
506 anchor.target = "_blank"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
507 anchor.rel = "noreferrer"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
508 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
509 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
510 for (const image of main.querySelectorAll("img")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
511 const source = image.getAttribute("src") || ""; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
512 if (source.startsWith("/public/") && source.endsWith(".png")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
513 image.src = source.replace(/\.png$/i, ".webp"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
514 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
515 image.loading = "lazy"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
516 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
517 return main; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
518 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
519 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
520 function sanitizeToolDetail(main) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
521 sanitizeFetchedMain(main); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
522 for (const textarea of main.querySelectorAll("textarea")) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
523 const sample = document.createElement("pre"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
524 sample.className = "jrpg-tool-source-sample"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
525 sample.textContent = textarea.value || textarea.textContent; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
526 textarea.replaceWith(sample); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
527 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
528 for (const inactive of main.querySelectorAll( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
529 "button, canvas, dialog, input, select, video, [hidden]", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
530 )) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
531 inactive.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
532 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
533 for (const owner of main.querySelectorAll( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
534 "zen-button, zen-checkbox, zen-field, zen-input, zen-textarea", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
535 )) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
536 owner.replaceWith(...owner.childNodes); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
537 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
538 for (const label of main.querySelectorAll("label[for]")) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
539 label.removeAttribute("for"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
540 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
541 return main; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
542 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
543 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
544 class MjjJrpgPreview extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
545 connectedCallback() { |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
546 this._modalOwner = this.querySelector("[data-detail-modal-owner]"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
547 this._dialog = this.querySelector("dialog"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
548 this._onDialogClose = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
549 this._toolRequest = (this._toolRequest || 0) + 1; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
550 this.cleanupTool(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
551 }; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
552 this._onClick = event => { |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
553 const trigger = event.target.closest("[data-zen-trigger]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
554 if (trigger) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
555 if (this.dataset.selection === "resume") void this.loadResume(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
556 if (this.dataset.selection === "blog") { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
557 this.show("blog"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
558 void this.loadBlogs(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
559 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
560 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
561 this.show("tools"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
562 void this.openToolWorkspace(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
563 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
564 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
565 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
566 const blogEntry = event.target.closest("[data-blog-entry]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
567 if (blogEntry) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
568 void this.loadBlogDetail(blogEntry.dataset.blogUrl); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
569 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
570 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
571 const latestBlog = event.target.closest("[data-latest-blog]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
572 if (latestBlog && !event.metaKey && !event.ctrlKey && !event.shiftKey) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
573 event.preventDefault(); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
574 this.show("blog"); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
575 this._modalOwner?.open(); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
576 void this.loadBlogDetail(latestBlog.dataset.blogUrl); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
577 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
578 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
579 const blogArchive = event.target.closest("[data-blog-archive]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
580 if (blogArchive) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
581 event.preventDefault(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
582 this.show("blog"); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
583 this._modalOwner?.open(); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
584 void this.loadBlogs(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
585 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
586 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
587 const resumeModal = event.target.closest("[data-resume-modal]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
588 if (resumeModal) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
589 event.preventDefault(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
590 this.show("resume"); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
591 this._modalOwner?.open(); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
592 void this.loadResume(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
593 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
594 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
595 const latestTool = event.target.closest("[data-latest-tool]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
596 if (latestTool && !event.metaKey && !event.ctrlKey && !event.shiftKey) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
597 event.preventDefault(); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
598 this.show("tools"); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
599 this._modalOwner?.open(); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
600 void this.loadToolDetail(latestTool.dataset.toolUrl); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
601 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
602 }; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
603 this.addEventListener("click", this._onClick); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
604 this._dialog.addEventListener("close", this._onDialogClose); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
605 this.show(this.dataset.selection || "resume"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
606 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
607 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
608 disconnectedCallback() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
609 this.removeEventListener("click", this._onClick); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
610 this._dialog?.removeEventListener("close", this._onDialogClose); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
611 this.cleanupTool(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
612 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
613 |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
614 _setDetailModalType(useWindow) { |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
615 const targetName = useWindow ? "mjj-window-modal" : "mjj-content-modal"; |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
616 if (!this._modalOwner || this._modalOwner.localName === targetName) return; |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
617 const replacement = document.createElement(targetName); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
618 for (const attribute of [...this._modalOwner.attributes]) { |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
619 replacement.setAttribute(attribute.name, attribute.value); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
620 } |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
621 replacement.append(...this._modalOwner.childNodes); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
622 this._modalOwner.replaceWith(replacement); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
623 this._modalOwner = replacement; |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
624 } |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
625 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
626 show(selection) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
627 this.cleanupTool(); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
628 const preview = PREVIEWS[selection] || PREVIEWS.resume; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
629 this.dataset.selection = selection; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
630 this.querySelector("[data-preview-title]").textContent = preview.title; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
631 this.querySelector("[data-preview-copy]").textContent = preview.copy; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
632 this.querySelector("[data-dialog-title]").textContent = preview.title; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
633 this.querySelector("[data-dialog-copy]").textContent = preview.copy; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
634 this.renderShowcase(preview.works); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
635 const resumeDossier = this.querySelector("[data-resume-dossier]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
636 const resumeDownload = this.querySelector("[data-resume-download]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
637 const blogBrowser = this.querySelector("[data-blog-browser]"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
638 const toolBrowser = this.querySelector("[data-tool-browser]"); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
639 const isResume = selection === "resume"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
640 const isBlog = selection === "blog"; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
641 const isTools = selection === "tools"; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
642 this._setDetailModalType(isTools); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
643 this._dialog.dataset.detailMode = isTools ? "tools" : "content"; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
644 resumeDossier.hidden = !isResume; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
645 resumeDownload.hidden = !isResume; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
646 blogBrowser.hidden = !isBlog; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
647 toolBrowser.hidden = !isTools; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
648 this.querySelector("[data-dialog-copy]").hidden = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
649 isResume || isBlog || isTools; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
650 if (!isBlog) this._blogRequest = (this._blogRequest || 0) + 1; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
651 if (isBlog) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
652 const status = this.querySelector("[data-blog-status]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
653 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
654 status.textContent = "Select a blog to inspect."; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
655 this.querySelector("[data-blog-content]").replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
656 for (const button of this.querySelectorAll("[data-blog-entry]")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
657 button.setAttribute("aria-pressed", "false"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
658 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
659 void this.loadBlogs(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
660 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
661 if (!isTools) this._toolRequest = (this._toolRequest || 0) + 1; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
662 if (isTools) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
663 const status = this.querySelector("[data-tool-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
664 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
665 status.textContent = "Select a tool to inspect."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
666 this.querySelector("[data-tool-content]").replaceChildren(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
667 void this.loadTools(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
668 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
669 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
670 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
671 renderShowcase(works, latestBlogs = false, latestKind = null) { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
672 const showcase = this.querySelector("[data-work-showcase]"); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
673 showcase.replaceChildren(...works.map(work => { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
674 const item = document.createElement("li"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
675 const link = document.createElement("a"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
676 const label = document.createElement("span"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
677 const detail = document.createElement("small"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
678 link.href = work.url; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
679 if (latestBlogs) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
680 if (work.modal === "blog") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
681 link.dataset.blogArchive = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
682 } else { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
683 link.dataset.latestBlog = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
684 link.dataset.blogUrl = work.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
685 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
686 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
687 if (work.modal === "resume") link.dataset.resumeModal = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
688 if (latestKind === "tool") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
689 link.dataset.latestTool = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
690 link.dataset.toolUrl = work.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
691 const icon = document.createElement("zen-icon"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
692 icon.setAttribute("name", TOOL_ICON_NAMES[work.url] || "settings"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
693 link.prepend(icon); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
694 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
695 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
|
696 link.target = "_blank"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
697 link.rel = "noreferrer"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
698 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
699 label.textContent = work.label; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
700 detail.textContent = work.detail; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
701 link.append(label, detail); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
702 item.append(link); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
703 return item; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
704 })); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
705 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
706 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
707 async loadResume() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
708 if (this._resumeLoaded || this._resumeLoading) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
709 this._resumeLoading = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
710 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
|
711 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
|
712 try { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
713 const response = await fetch("/resume", { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
714 headers: { Accept: "text/html" }, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
715 }); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
716 if (!response.ok) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
717 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
|
718 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
719 const documentCopy = new DOMParser().parseFromString( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
720 await response.text(), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
721 "text/html", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
722 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
723 const resume = documentCopy.querySelector("main"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
724 if (!resume) throw new Error("Resume content is unavailable"); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
725 sanitizeFetchedMain(resume); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
726 for (const duplicateDownload of resume.querySelectorAll( |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
727 'a[href="/public/resume.pdf"]', |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
728 )) { |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
729 duplicateDownload.closest("zen-button")?.remove(); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
730 duplicateDownload.remove(); |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
731 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
732 content.replaceChildren(...resume.childNodes); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
733 status.hidden = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
734 this._resumeLoaded = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
735 } catch (error) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
736 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
|
737 } finally { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
738 this._resumeLoading = false; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
739 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
740 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
741 |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
742 async loadBlogs() { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
743 if (this._blogs) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
744 if (this.dataset.selection === "blog") { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
745 this.renderShowcase([ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
746 ...this._blogs.slice(0, 4), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
747 { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
748 detail: "Full archive", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
749 label: "All blogs", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
750 modal: "blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
751 url: "/blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
752 }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
753 ], true); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
754 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
755 return true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
756 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
757 if (this._blogsLoading) return this._blogsLoading; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
758 const list = this.querySelector("[data-blog-list]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
759 this._blogsLoading = (async () => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
760 const response = await fetch("/blog", { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
761 headers: { Accept: "text/html" }, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
762 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
763 if (!response.ok) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
764 throw new Error(`Blog archive request failed (${response.status})`); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
765 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
766 const documentCopy = new DOMParser().parseFromString( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
767 await response.text(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
768 "text/html", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
769 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
770 this._blogs = [...documentCopy.querySelectorAll( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
771 'main li a[href^="/blog/"]', |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
772 )].map(anchor => ({ |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
773 date: anchor.closest("li")?.querySelector("span")?.textContent.trim() || |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
774 "Archive", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
775 detail: anchor.closest("li")?.querySelector("span")?.textContent.trim() || |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
776 "Archive", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
777 label: anchor.textContent.trim(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
778 url: anchor.getAttribute("href"), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
779 })); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
780 if (!this._blogs.length) throw new Error("No blog entries were found"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
781 this.renderBlogList(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
782 if (this.dataset.selection === "blog") { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
783 this.renderShowcase([ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
784 ...this._blogs.slice(0, 4), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
785 { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
786 detail: "Full archive", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
787 label: "All blogs", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
788 modal: "blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
789 url: "/blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
790 }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
791 ], true); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
792 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
793 return true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
794 })().catch(error => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
795 list.replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
796 const item = document.createElement("li"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
797 item.textContent = `Unable to load blogs: ${error.message}`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
798 list.append(item); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
799 return false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
800 }).finally(() => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
801 this._blogsLoading = null; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
802 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
803 return this._blogsLoading; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
804 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
805 |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
806 renderBlogList() { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
807 const list = this.querySelector("[data-blog-list]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
808 list.replaceChildren(...this._blogs.map(blog => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
809 const item = document.createElement("li"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
810 const owner = document.createElement("zen-button"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
811 const button = document.createElement("button"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
812 const title = document.createElement("span"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
813 const date = document.createElement("small"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
814 owner.setAttribute("appearance", "plain"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
815 owner.setAttribute("size", "sm"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
816 button.type = "button"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
817 button.dataset.blogEntry = ""; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
818 button.dataset.blogUrl = blog.url; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
819 title.textContent = blog.label; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
820 date.textContent = blog.date; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
821 button.append(title, date); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
822 owner.append(button); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
823 item.append(owner); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
824 return item; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
825 })); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
826 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
827 |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
828 async loadBlogDetail(url) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
829 if (!await this.loadBlogs()) return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
830 const blog = this._blogs.find(item => item.url === url); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
831 const status = this.querySelector("[data-blog-status]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
832 const content = this.querySelector("[data-blog-content]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
833 if (!blog) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
834 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
835 status.textContent = "Unable to load blog: unknown entry."; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
836 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
837 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
838 const request = (this._blogRequest || 0) + 1; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
839 this._blogRequest = request; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
840 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
841 status.textContent = `Loading ${blog.label}...`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
842 content.replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
843 this.querySelector("[data-dialog-title]").textContent = blog.label; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
844 for (const button of this.querySelectorAll("[data-blog-entry]")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
845 button.setAttribute( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
846 "aria-pressed", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
847 String(button.dataset.blogUrl === blog.url), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
848 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
849 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
850 try { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
851 const response = await fetch(blog.url, { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
852 headers: { Accept: "text/html" }, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
853 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
854 if (!response.ok) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
855 throw new Error(`Blog request failed (${response.status})`); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
856 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
857 const documentCopy = new DOMParser().parseFromString( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
858 await response.text(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
859 "text/html", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
860 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
861 const article = documentCopy.querySelector("main"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
862 if (!article) throw new Error("Blog content is unavailable"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
863 sanitizeFetchedMain(article); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
864 if (request !== this._blogRequest || this.dataset.selection !== "blog") { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
865 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
866 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
867 content.replaceChildren(...article.childNodes); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
868 status.hidden = true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
869 } catch (error) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
870 if (request !== this._blogRequest) return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
871 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
872 status.textContent = `Unable to load blog: ${error.message}`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
873 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
874 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
875 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
876 async loadTools() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
877 if (this._tools) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
878 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
879 this.renderShowcase(this._tools, false, "tool"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
880 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
881 return true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
882 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
883 if (this._toolsLoading) return this._toolsLoading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
884 this._toolsLoading = (async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
885 const response = await fetch("/tools", { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
886 headers: { Accept: "text/html" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
887 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
888 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
889 throw new Error(`Tools request failed (${response.status})`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
890 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
891 const documentCopy = new DOMParser().parseFromString( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
892 await response.text(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
893 "text/html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
894 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
895 this._tools = [...documentCopy.querySelectorAll("main li a[href]")].map( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
896 anchor => ({ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
897 detail: anchor.getAttribute("href").startsWith("/notes") |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
898 ? "Writing" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
899 : "Utility", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
900 label: anchor.textContent.trim(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
901 url: anchor.getAttribute("href"), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
902 }), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
903 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
904 if (!this._tools.length) throw new Error("No tools were found"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
905 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
906 this.renderShowcase(this._tools, false, "tool"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
907 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
908 return true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
909 })().catch(error => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
910 const status = this.querySelector("[data-tool-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
911 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
912 status.textContent = `Unable to load tools: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
913 return false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
914 }).finally(() => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
915 this._toolsLoading = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
916 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
917 return this._toolsLoading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
918 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
919 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
920 async openToolWorkspace() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
921 if (!await this.loadTools() || this.dataset.selection !== "tools") return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
922 await this.loadToolDetail(this._activeToolUrl || this._tools[0].url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
923 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
924 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
925 async loadToolDetail(url) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
926 const request = (this._toolRequest || 0) + 1; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
927 this._toolRequest = request; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
928 if (!await this.loadTools()) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
929 if (request !== this._toolRequest || this.dataset.selection !== "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
930 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
931 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
932 const tool = this._tools.find(item => item.url === url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
933 const status = this.querySelector("[data-tool-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
934 const content = this.querySelector("[data-tool-content]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
935 if (!tool) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
936 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
937 status.textContent = "Unable to load tool: unknown entry."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
938 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
939 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
940 this._activeToolUrl = tool.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
941 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
942 status.textContent = `Loading ${tool.label}...`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
943 this.cleanupTool(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
944 content.replaceChildren(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
945 this.querySelector("[data-dialog-title]").textContent = tool.label; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
946 if (tool.url === "/tools/markdown_to_html") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
947 await this.renderMarkdownTool(content, status, request); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
948 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
949 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
950 if (tool.url === "/tools/file_converter") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
951 this.renderFileConverterTool(content, status); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
952 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
953 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
954 if (tool.url === "/tools/hls_player") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
955 await this.renderHlsTool(content, status, request); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
956 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
957 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
958 if (tool.url === "/tools/latex_editor") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
959 this.renderLatexTool(content, status); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
960 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
961 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
962 if (tool.url === "/notes") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
963 const heading = document.createElement("h1"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
964 const description = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
965 const privacy = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
966 heading.textContent = "Personal Notes"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
967 description.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
968 "A private browser-based writing workspace with rich-text editing and server-backed persistence."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
969 privacy.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
970 "The interactive editor and authentication flow are available on the full Notes page."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
971 content.replaceChildren( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
972 this.createReadonlyToolNote(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
973 heading, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
974 description, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
975 privacy, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
976 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
977 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
978 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
979 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
980 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
981 const response = await fetch(tool.url, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
982 headers: { Accept: "text/html" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
983 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
984 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
985 throw new Error(`Tool request failed (${response.status})`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
986 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
987 const documentCopy = new DOMParser().parseFromString( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
988 await response.text(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
989 "text/html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
990 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
991 const detail = documentCopy.querySelector("main, .container") || |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
992 documentCopy.body; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
993 if (!detail) throw new Error("Tool content is unavailable"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
994 sanitizeToolDetail(detail); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
995 if (request !== this._toolRequest || this.dataset.selection !== "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
996 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
997 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
998 content.replaceChildren( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
999 this.createReadonlyToolNote(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1000 ...detail.childNodes, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1001 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1002 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1003 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1004 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1005 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1006 status.textContent = `Unable to load tool: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1007 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1008 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1009 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1010 async renderMarkdownTool(content, status, request) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1011 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1012 <section class="jrpg-functional-tool" data-functional-tool="markdown"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1013 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1014 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1015 <h2><zen-icon name="code"></zen-icon> Markdown source</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1016 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1017 <label for="jrpgMarkdownSource">Markdown</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1018 <textarea id="jrpgMarkdownSource" data-markdown-source></textarea> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1019 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1020 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1021 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1022 <h2><zen-icon name="check"></zen-icon> Converted HTML</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1023 <div class="jrpg-markdown-output" data-markdown-output></div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1024 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1025 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1026 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1027 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1028 const source = content.querySelector("[data-markdown-source]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1029 const output = content.querySelector("[data-markdown-output]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1030 source.value = [ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1031 "# JRPG Markdown", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1032 "", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1033 "Edit this **Markdown** to update the converted panel.", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1034 "", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1035 "- Bazel-built WASM", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1036 "- Sanitized HTML output", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1037 ].join("\n"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1038 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1039 status.textContent = "Loading the Markdown converter..."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1040 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1041 this._markdownModule ||= import("/markdown_to_html_bin.js") |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1042 .then(module => module.default()); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1043 const module = await this._markdownModule; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1044 if (request !== this._toolRequest || !source.isConnected) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1045 const convertMarkdown = module.cwrap( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1046 "markdown_to_html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1047 "number", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1048 ["string"], |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1049 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1050 const freeMarkdown = module.cwrap("markdown_free", null, ["number"]); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1051 const convert = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1052 const pointer = convertMarkdown(source.value); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1053 const html = module.UTF8ToString(pointer); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1054 freeMarkdown(pointer); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1055 const documentCopy = new DOMParser().parseFromString(html, "text/html"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1056 sanitizeFetchedMain(documentCopy.body); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1057 output.replaceChildren(...documentCopy.body.childNodes); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1058 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1059 source.addEventListener("input", convert); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1060 convert(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1061 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1062 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1063 this._markdownModule = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1064 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1065 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1066 status.textContent = `Unable to start Markdown: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1067 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1068 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1069 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1070 renderFileConverterTool(content, status) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1071 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1072 <section class="jrpg-functional-tool" data-functional-tool="converter"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1073 <p class="jrpg-tool-status" data-converter-status> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1074 Choose a file in either panel. |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1075 </p> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1076 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1077 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1078 <h2><zen-icon name="file"></zen-icon> Image to WebP</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1079 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1080 <label for="jrpgImageInput">Image file</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1081 <input id="jrpgImageInput" data-converter-input="image" type="file" accept="image/*"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1082 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1083 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1084 <button type="button" data-convert-kind="image"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1085 Convert image <zen-icon name="arrow-right"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1086 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1087 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1088 <a data-converter-download="image" hidden>Download WebP</a> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1089 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1090 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1091 <h2><zen-icon name="play"></zen-icon> Video to MP4</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1092 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1093 <label for="jrpgVideoInput">Video file</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1094 <input id="jrpgVideoInput" data-converter-input="video" type="file" accept="video/*"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1095 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1096 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1097 <button type="button" data-convert-kind="video"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1098 Convert video <zen-icon name="arrow-right"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1099 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1100 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1101 <a data-converter-download="video" hidden>Download MP4</a> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1102 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1103 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1104 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1105 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1106 const toolStatus = content.querySelector("[data-converter-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1107 let disposed = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1108 const downloads = new Set(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1109 const discardDownload = url => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1110 if (!url) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1111 downloads.delete(url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1112 void fetch(url, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1113 method: "DELETE", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1114 keepalive: true, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1115 }).then(response => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1116 if (!response.ok && response.status !== 404) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1117 console.warn(`Unable to discard converted file (${response.status})`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1118 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1119 }).catch(error => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1120 console.warn(`Unable to discard converted file: ${error.message}`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1121 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1122 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1123 const convert = async kind => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1124 const input = content.querySelector(`[data-converter-input="${kind}"]`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1125 const button = content.querySelector(`[data-convert-kind="${kind}"]`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1126 const download = content.querySelector( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1127 `[data-converter-download="${kind}"]`, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1128 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1129 const file = input.files?.[0]; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1130 if (!file) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1131 toolStatus.textContent = `Choose a ${kind} file first.`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1132 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1133 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1134 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1135 button.disabled = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1136 discardDownload(download.dataset.cleanupUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1137 delete download.dataset.cleanupUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1138 download.removeAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1139 download.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1140 toolStatus.textContent = `Converting ${file.name}...`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1141 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1142 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1143 const endpoint = kind === "image" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1144 ? "/api/convert/image-to-webp" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1145 : "/api/convert/video-to-mp4"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1146 const response = await fetch(endpoint, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1147 method: "POST", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1148 body: file, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1149 headers: { "Content-Type": file.type || "application/octet-stream" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1150 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1151 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1152 throw new Error(await response.text() || `Conversion failed (${response.status})`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1153 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1154 const result = await response.json(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1155 if (disposed || !download.isConnected) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1156 discardDownload(result.download_url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1157 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1158 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1159 download.href = result.download_url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1160 download.dataset.cleanupUrl = result.download_url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1161 downloads.add(result.download_url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1162 download.download = file.name.replace(/\.[^/.]+$/, "") + |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1163 (kind === "image" ? ".webp" : ".mp4"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1164 download.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1165 toolStatus.textContent = "Conversion complete."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1166 toolStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1167 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1168 if (!disposed) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1169 toolStatus.textContent = `Conversion failed: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1170 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1171 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1172 } finally { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1173 if (button.isConnected) button.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1174 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1175 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1176 for (const button of content.querySelectorAll("[data-convert-kind]")) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1177 button.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1178 void convert(button.dataset.convertKind); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1179 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1180 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1181 for (const download of content.querySelectorAll("[data-converter-download]")) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1182 download.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1183 downloads.delete(download.dataset.cleanupUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1184 delete download.dataset.cleanupUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1185 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1186 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1187 this._toolCleanup = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1188 disposed = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1189 for (const download of [...downloads]) discardDownload(download); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1190 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1191 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1192 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1193 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1194 async renderHlsTool(content, status, request) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1195 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1196 <section class="jrpg-functional-tool" data-functional-tool="hls"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1197 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1198 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1199 <h2><zen-icon name="file"></zen-icon> HLS source</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1200 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1201 <label for="jrpgHlsUrl">Playlist URL</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1202 <input |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1203 id="jrpgHlsUrl" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1204 data-hls-url |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1205 type="url" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1206 value="/public/hls-sample/h264-ts-stream.m3u8" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1207 > |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1208 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1209 <div class="jrpg-tool-actions"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1210 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1211 <button type="button" data-hls-load> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1212 Load URL <zen-icon name="arrow-right"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1213 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1214 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1215 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1216 <button type="button" data-hls-sample> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1217 Load sample <zen-icon name="play"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1218 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1219 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1220 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1221 <p class="jrpg-tool-status" data-hls-status>Loading player engine...</p> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1222 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1223 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1224 <h2><zen-icon name="play"></zen-icon> Playback</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1225 <video data-hls-video controls playsinline preload="metadata"></video> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1226 <dl class="jrpg-tool-details" data-hls-details hidden> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1227 <div><dt>Playback</dt><dd data-detail="mode">-</dd></div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1228 <div><dt>Segments</dt><dd data-detail="segments">-</dd></div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1229 <div><dt>Duration</dt><dd data-detail="duration">-</dd></div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1230 <div><dt>Codec</dt><dd data-detail="codecs">-</dd></div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1231 </dl> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1232 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1233 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1234 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1235 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1236 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1237 await loadScript("/public/hls.min.js"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1238 await loadScript("/tools/hls_player/hls-player.js"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1239 if (request !== this._toolRequest || !content.isConnected) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1240 const video = content.querySelector("[data-hls-video]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1241 const playerStatus = content.querySelector("[data-hls-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1242 const details = content.querySelector("[data-hls-details]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1243 const url = content.querySelector("[data-hls-url]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1244 const player = new window.HlsPlayerModule.HlsPlayer(video, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1245 statusElement: playerStatus, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1246 detailsElement: details, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1247 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1248 const load = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1249 void player.load(url.value.trim()).catch(() => {}); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1250 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1251 content.querySelector("[data-hls-load]").addEventListener("click", load); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1252 content.querySelector("[data-hls-sample]").addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1253 url.value = "/public/hls-sample/h264-ts-stream.m3u8"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1254 load(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1255 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1256 playerStatus.textContent = "Ready to load a playlist."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1257 playerStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1258 this._toolCleanup = () => player.destroy(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1259 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1260 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1261 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1262 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1263 status.textContent = `Unable to start HLS: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1264 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1265 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1266 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1267 renderLatexTool(content, status) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1268 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1269 <section class="jrpg-functional-tool" data-functional-tool="latex"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1270 <p class="jrpg-tool-status" data-latex-status> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1271 Edit the source, then compile the PDF. |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1272 </p> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1273 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1274 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1275 <div class="jrpg-tool-pane-heading"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1276 <h2><zen-icon name="code"></zen-icon> LaTeX source</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1277 <span data-latex-size></span> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1278 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1279 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1280 <label for="jrpgLatexSource">document.tex</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1281 <textarea id="jrpgLatexSource" data-latex-source></textarea> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1282 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1283 <div class="jrpg-tool-actions"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1284 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1285 <button type="button" data-latex-compile> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1286 Compile PDF <zen-icon name="file"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1287 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1288 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1289 <zen-button appearance="plain" size="md" data-latex-download hidden> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1290 <a download="document.pdf"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1291 Download PDF <zen-icon name="download"></zen-icon> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1292 </a> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1293 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1294 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1295 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1296 <section class="jrpg-tool-pane jrpg-tool-window"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1297 <h2><zen-icon name="file"></zen-icon> PDF preview</h2> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1298 <iframe data-latex-preview title="Compiled LaTeX PDF preview"></iframe> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1299 <pre data-latex-diagnostics hidden></pre> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1300 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1301 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1302 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1303 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1304 const source = content.querySelector("[data-latex-source]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1305 const compileButton = content.querySelector("[data-latex-compile]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1306 const toolStatus = content.querySelector("[data-latex-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1307 const size = content.querySelector("[data-latex-size]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1308 const preview = content.querySelector("[data-latex-preview]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1309 const diagnostics = content.querySelector("[data-latex-diagnostics]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1310 const downloadOwner = content.querySelector("[data-latex-download]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1311 const download = downloadOwner.querySelector("a"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1312 source.value = String.raw`\documentclass[11pt]{article} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1313 \usepackage[margin=1in]{geometry} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1314 \title{JRPG LaTeX} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1315 \author{MrJuneJune} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1316 \begin{document} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1317 \maketitle |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1318 This PDF was compiled from the cyberpunk tool modal. |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1319 \end{document}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1320 let controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1321 let pdfUrl = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1322 let compileGeneration = 0; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1323 const clearPdf = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1324 if (pdfUrl) URL.revokeObjectURL(pdfUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1325 pdfUrl = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1326 preview.src = "about:blank"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1327 preview.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1328 diagnostics.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1329 download.removeAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1330 downloadOwner.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1331 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1332 const updateSize = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1333 const bytes = new TextEncoder().encode(source.value).byteLength; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1334 size.textContent = `${bytes.toLocaleString()} / 65,536 bytes`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1335 return bytes; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1336 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1337 const compile = async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1338 const generation = ++compileGeneration; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1339 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1340 const bytes = updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1341 if (!source.value.trim() || bytes > 64 * 1024) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1342 toolStatus.textContent = bytes > 64 * 1024 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1343 ? "Source exceeds 64 KiB." |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1344 : "Write LaTeX before compiling."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1345 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1346 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1347 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1348 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1349 const requestController = new AbortController(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1350 controller = requestController; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1351 compileButton.disabled = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1352 toolStatus.textContent = "Compiling on the server..."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1353 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1354 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1355 const response = await fetch("/api/latex/render", { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1356 method: "POST", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1357 headers: { "Content-Type": "text/plain; charset=utf-8" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1358 body: source.value, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1359 cache: "no-store", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1360 signal: requestController.signal, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1361 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1362 if (generation !== compileGeneration) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1363 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1364 throw new Error(await response.text() || `Compilation failed (${response.status})`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1365 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1366 const blob = await response.blob(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1367 if (generation !== compileGeneration) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1368 if (blob.type !== "application/pdf" || blob.size < 5) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1369 throw new Error("The server returned an invalid PDF."); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1370 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1371 if (pdfUrl) URL.revokeObjectURL(pdfUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1372 pdfUrl = URL.createObjectURL(blob); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1373 preview.src = pdfUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1374 preview.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1375 diagnostics.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1376 download.href = pdfUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1377 downloadOwner.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1378 toolStatus.textContent = `PDF ready (${Math.ceil(blob.size / 1024)} KiB).`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1379 toolStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1380 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1381 if (error.name !== "AbortError" && generation === compileGeneration) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1382 diagnostics.textContent = error.message; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1383 diagnostics.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1384 preview.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1385 toolStatus.textContent = "Compilation failed."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1386 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1387 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1388 } finally { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1389 if (controller === requestController) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1390 controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1391 compileButton.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1392 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1393 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1394 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1395 source.addEventListener("input", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1396 compileGeneration++; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1397 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1398 controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1399 compileButton.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1400 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1401 updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1402 toolStatus.textContent = "Changes ready to compile."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1403 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1404 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1405 compileButton.addEventListener("click", () => void compile()); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1406 updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1407 this._toolCleanup = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1408 compileGeneration++; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1409 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1410 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1411 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1412 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1413 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1414 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1415 cleanupTool() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1416 this._toolCleanup?.(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1417 this._toolCleanup = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1418 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1419 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1420 createReadonlyToolNote() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1421 const introduction = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1422 introduction.className = "jrpg-tool-readonly-note"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1423 introduction.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1424 "Read-only preview. Open the full page to use this tool."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1425 return introduction; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1426 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1427 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1428 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1429 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1430 MjjConversationArchive — archive panel within the scene |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1431 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1432 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1433 class MjjConversationArchive extends HTMLElement { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1434 connectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1435 this._list = this.querySelector("[data-archive-list]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1436 this._status = this.querySelector("[data-archive-status]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1437 this._loadMoreOwner = this.querySelector("[data-archive-load-more-owner]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1438 this._loadMoreButton = this.querySelector("[data-archive-load-more]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1439 this._newButton = this.querySelector("[data-archive-new]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1440 this._closeButton = this.querySelector("[data-archive-close]"); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1441 this._deleteModal = this.querySelector("[data-archive-delete-owner]"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1442 this._deleteDialog = this.querySelector("[data-archive-delete-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1443 this._deleteNameEl = this.querySelector("[data-archive-delete-name]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1444 this._deleteCancelButton = this.querySelector("[data-archive-delete-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1445 this._deleteConfirmButton = this.querySelector("[data-archive-delete-confirm]"); |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1446 this._renameModal = this.querySelector("[data-archive-rename-owner]"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1447 this._renameDialog = this.querySelector("[data-archive-rename-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1448 this._renameInput = this.querySelector("[data-archive-rename-input]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1449 this._renameForm = this.querySelector("[data-archive-rename-form]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1450 this._renameCancelButton = this.querySelector("[data-archive-rename-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1451 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1452 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1453 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1454 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1455 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1456 this._closeButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1457 this.dispatchEvent(new CustomEvent("mjj-archive-toggle", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1458 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1459 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1460 this._newButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1461 this.dispatchEvent(new CustomEvent("mjj-archive-new", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1462 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1463 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1464 this._loadMoreButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1465 this.dispatchEvent(new CustomEvent("mjj-archive-load-more", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1466 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1467 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1468 this._deleteCancelButton?.addEventListener("click", () => { |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1469 this._deleteModal?.close(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1470 const t = this._pendingDeleteTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1471 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1472 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1473 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1474 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1475 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1476 this._deleteConfirmButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1477 const id = this._pendingDeleteId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1478 const trigger = this._pendingDeleteTrigger; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1479 this._deleteModal?.close(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1480 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1481 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1482 if (id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1483 this.dispatchEvent(new CustomEvent("mjj-archive-delete", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1484 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1485 detail: { id, trigger }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1486 })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1487 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1488 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1489 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1490 this._deleteDialog?.addEventListener("close", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1491 const t = this._pendingDeleteTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1492 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1493 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1494 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1495 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1496 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1497 this._renameCancelButton?.addEventListener("click", () => { |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1498 this._renameModal?.close(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1499 const t = this._pendingRenameTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1500 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1501 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1502 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1503 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1504 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1505 this._renameForm?.addEventListener("submit", event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1506 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1507 const id = this._pendingRenameId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1508 const trigger = this._pendingRenameTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1509 const newTitle = this._renameInput?.value.trim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1510 if (!id || !newTitle) return; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1511 this._renameModal?.close(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1512 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1513 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1514 this.dispatchEvent(new CustomEvent("mjj-archive-rename", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1515 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1516 detail: { id, newTitle, trigger }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1517 })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1518 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1519 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1520 this._renameDialog?.addEventListener("close", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1521 const t = this._pendingRenameTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1522 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1523 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1524 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1525 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1526 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1527 this._onListClick = event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1528 const openButton = event.target.closest("[data-conv-open]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1529 if (openButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1530 const id = openButton.closest("[data-conv-item]")?._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1531 if (id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1532 this.dispatchEvent(new CustomEvent("mjj-archive-open", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1533 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1534 detail: { id }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1535 })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1536 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1537 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1538 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1539 const renameButton = event.target.closest("[data-conv-rename]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1540 if (renameButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1541 const item = renameButton.closest("[data-conv-item]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1542 if (item?._convId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1543 this._pendingRenameId = item._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1544 this._pendingRenameTrigger = renameButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1545 if (this._renameInput) this._renameInput.value = item._convTitle || ""; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1546 this._renameModal?.open(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1547 requestAnimationFrame(() => { this._renameInput?.select(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1548 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1549 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1550 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1551 const deleteButton = event.target.closest("[data-conv-delete]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1552 if (deleteButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1553 const item = deleteButton.closest("[data-conv-item]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1554 if (item?._convId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1555 this._pendingDeleteId = item._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1556 this._pendingDeleteTrigger = deleteButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1557 if (this._deleteNameEl) this._deleteNameEl.textContent = item._convTitle || ""; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
1558 this._deleteModal?.open(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1559 requestAnimationFrame(() => { this._deleteConfirmButton?.focus(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1560 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1561 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1562 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1563 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1564 this._list?.addEventListener("click", this._onListClick); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1565 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1566 this._onListKeydown = event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1567 if (!["ArrowUp", "ArrowDown"].includes(event.key)) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1568 const items = [...(this._list?.querySelectorAll("[data-conv-open]") || [])]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1569 const index = items.indexOf(document.activeElement); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1570 if (index < 0) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1571 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1572 const next = event.key === "ArrowDown" |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1573 ? items[Math.min(index + 1, items.length - 1)] |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1574 : items[Math.max(index - 1, 0)]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1575 next?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1576 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1577 this._list?.addEventListener("keydown", this._onListKeydown); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1578 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1579 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1580 disconnectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1581 this._list?.removeEventListener("click", this._onListClick); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1582 this._list?.removeEventListener("keydown", this._onListKeydown); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1583 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1584 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1585 setConversations(conversations, cursor, append = false) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1586 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1587 if (!append) this._list.replaceChildren(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1588 for (const conv of conversations) this._list.append(this._createItem(conv)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1589 if (this._loadMoreOwner) this._loadMoreOwner.hidden = !cursor; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1590 if (!conversations.length && !append) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1591 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1592 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1593 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1594 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1595 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1596 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1597 setCurrentId(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1598 for (const button of (this._list?.querySelectorAll("[data-conv-open]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1599 const isCurrent = id && button.closest("[data-conv-item]")?._convId === id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1600 button.setAttribute("aria-current", String(Boolean(isCurrent))); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1601 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1602 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1603 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1604 addConversation(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1605 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1606 this._list.prepend(this._createItem(conv)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1607 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1608 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1609 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1610 updateConversation(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1611 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1612 for (const item of this._list.querySelectorAll("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1613 if (item._convId === conv.id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1614 const titleEl = item.querySelector("[data-conv-title]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1615 const metaEl = item.querySelector("[data-conv-meta]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1616 if (titleEl) titleEl.textContent = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1617 if (metaEl) metaEl.textContent = this._formatMeta(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1618 item._convTitle = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1619 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1620 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1621 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1622 this.addConversation(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1623 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1624 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1625 removeConversation(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1626 for (const item of (this._list?.querySelectorAll("[data-conv-item]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1627 if (item._convId === id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1628 item.remove(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1629 break; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1630 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1631 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1632 if (!this._list?.querySelector("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1633 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1634 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1635 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1636 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1637 setLoading(loading) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1638 if (loading) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1639 this._showStatus("Loading conversations..."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1640 } else if (!this._list?.querySelector("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1641 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1642 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1643 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1644 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1645 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1646 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1647 setError(message) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1648 this._showStatus(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1649 if (this._status) this._status.dataset.state = "error"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1650 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1651 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1652 setLoadMoreDisabled(disabled) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1653 if (this._loadMoreButton) this._loadMoreButton.disabled = disabled; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1654 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1655 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1656 /* Disable/enable destructive archive controls while a stream is active. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1657 setStreamActive(active) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1658 const controls = this._list?.querySelectorAll( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1659 "[data-conv-delete], [data-conv-rename]", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1660 ) || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1661 for (const el of controls) el.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1662 if (this._newButton) this._newButton.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1663 if (this._deleteConfirmButton) this._deleteConfirmButton.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1664 this.dataset.streamActive = active ? "true" : ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1665 if (!active) delete this.dataset.streamActive; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1666 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1667 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1668 /* Return the open-button of the adjacent surviving item, or New button. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1669 adjacentItemOrNewButton(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1670 const items = [...(this._list?.querySelectorAll("[data-conv-item]") || [])]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1671 const idx = items.findIndex(el => el._convId === id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1672 if (idx < 0) return this._newButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1673 const sibling = items[idx + 1] || items[idx - 1]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1674 return sibling?.querySelector("[data-conv-open]") || this._newButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1675 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1676 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1677 showLegacyClaim(statusMessage = null, showButton = true) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1678 if (!this._status) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1679 this._status.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1680 delete this._status.dataset.state; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1681 this._status.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1682 const msg = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1683 msg.textContent = statusMessage ?? "You have a prior conversation. "; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1684 this._status.append(msg); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1685 if (showButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1686 const btn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1687 btn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1688 btn.textContent = "Claim it"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1689 btn.dataset.archiveClaim = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1690 btn.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1691 this.dispatchEvent(new CustomEvent("mjj-archive-claim", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1692 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1693 this._status.append(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1694 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1695 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1696 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1697 _showStatus(text) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1698 if (this._status) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1699 this._status.textContent = text; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1700 this._status.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1701 delete this._status.dataset.state; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1702 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1703 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1704 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1705 _hideStatus() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1706 if (this._status) this._status.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1707 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1708 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1709 _createItem(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1710 const item = document.createElement("li"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1711 item.dataset.convItem = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1712 item._convId = conv.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1713 item._convTitle = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1714 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1715 const openButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1716 openButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1717 openButton.dataset.convOpen = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1718 openButton.setAttribute("aria-current", "false"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1719 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1720 const titleEl = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1721 titleEl.dataset.convTitle = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1722 titleEl.textContent = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1723 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1724 const metaEl = document.createElement("small"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1725 metaEl.dataset.convMeta = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1726 metaEl.textContent = this._formatMeta(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1727 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1728 openButton.append(titleEl, metaEl); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1729 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1730 const actions = document.createElement("div"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1731 actions.className = "jrpg-archive-item-actions"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1732 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1733 const renameButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1734 renameButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1735 renameButton.dataset.convRename = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1736 renameButton.setAttribute("aria-label", `Rename "${conv.title}"`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1737 renameButton.textContent = "Rename"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1738 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1739 const deleteButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1740 deleteButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1741 deleteButton.dataset.convDelete = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1742 deleteButton.setAttribute("aria-label", `Delete "${conv.title}"`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1743 deleteButton.textContent = "Delete"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1744 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1745 actions.append(renameButton, deleteButton); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1746 item.append(openButton, actions); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1747 return item; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1748 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1749 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1750 _formatMeta(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1751 const count = conv.turn_count ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1752 const turns = count === 1 ? "1 turn" : `${count} turns`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1753 if (conv.last_message_preview) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1754 return `${turns} · ${conv.last_message_preview.slice(0, 28)}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1755 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1756 return turns; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1757 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1758 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1759 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1760 class MjjJrpgShell extends HTMLElement { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1761 connectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1762 this._mobileMenuQuery = window.matchMedia( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1763 "(max-width: 52rem) and (orientation: portrait)", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1764 ); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1765 this._mobileDestinationOwner = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1766 "[data-mobile-destination-owner]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1767 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1768 this._mobileDestinationDialog = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1769 "[data-mobile-destination-dialog]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1770 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1771 this._mobileDestinationContent = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1772 "[data-mobile-destination-content]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1773 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1774 this._mobileDestinationTitle = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1775 "[data-mobile-destination-title]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1776 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1777 this._mobileDestinationBackOwner = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1778 "[data-mobile-destination-back-owner]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1779 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1780 this._mobileDestinationBack = this.querySelector( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1781 "[data-mobile-destination-back]", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1782 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1783 this._utility = this.querySelector(".jrpg-utility"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1784 this._menu = this.querySelector("mjj-jrpg-menu"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1785 this._utilityPlaceholder = document.createComment("jrpg-utility"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1786 this._menuPlaceholder = document.createComment("jrpg-menu"); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1787 this._utility?.before(this._utilityPlaceholder); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1788 this._menu?.before(this._menuPlaceholder); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1789 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1790 this._syncMobileLayout = () => { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1791 if (!this._utility || !this._menu || !this._mobileDestinationContent) { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1792 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1793 } |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1794 if (this._mobileMenuQuery.matches) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1795 this._mobileDestinationContent.append(this._menu, this._utility); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1796 this.showMobileMenu(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1797 return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1798 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1799 this._mobileDestinationOwner?.close(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1800 this._utilityPlaceholder.after(this._utility); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1801 this._menuPlaceholder.after(this._menu); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1802 this._utility.hidden = false; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1803 this._menu.hidden = false; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1804 }; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1805 this._onMobileMenuMediaChange = () => this._syncMobileLayout(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1806 this._onMobileDestinationBack = () => this.showMobileMenu(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1807 this._onMobileDestinationClose = () => this.showMobileMenu(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1808 this._mobileDestinationBack?.addEventListener( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1809 "click", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1810 this._onMobileDestinationBack, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1811 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1812 this._mobileDestinationDialog?.addEventListener( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1813 "close", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1814 this._onMobileDestinationClose, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1815 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1816 this._mobileMenuQuery.addEventListener( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1817 "change", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1818 this._onMobileMenuMediaChange, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1819 ); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1820 this._syncMobileLayout(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1821 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1822 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1823 isMobileDestinationModal() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1824 return Boolean(this._mobileMenuQuery?.matches); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1825 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1826 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1827 isMobileDestinationOpen() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1828 return this.isMobileDestinationModal() && |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1829 Boolean(this._mobileDestinationDialog?.open); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1830 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1831 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1832 showMobileMenu() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1833 if (!this.isMobileDestinationModal() || !this._menu || !this._utility) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1834 return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1835 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1836 this._menu.hidden = false; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1837 this._utility.hidden = true; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1838 if (this._mobileDestinationTitle) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1839 this._mobileDestinationTitle.textContent = "Menu"; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1840 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1841 if (this._mobileDestinationBackOwner) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1842 this._mobileDestinationBackOwner.hidden = true; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1843 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1844 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1845 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1846 showMobileDestination(selection) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1847 if (!this.isMobileDestinationModal() || !this._menu || !this._utility) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1848 return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1849 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1850 const labels = { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1851 blog: "Blogs", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1852 conversations: "Conversations", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1853 resume: "Resume", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1854 tools: "Tools", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1855 }; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1856 this._menu.hidden = true; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1857 this._utility.hidden = false; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1858 if (this._mobileDestinationTitle) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1859 this._mobileDestinationTitle.textContent = labels[selection] || "Destination"; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1860 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1861 if (this._mobileDestinationBackOwner) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1862 this._mobileDestinationBackOwner.hidden = false; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1863 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1864 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1865 |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1866 closeMobileDestinations() { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1867 if (this.isMobileDestinationModal()) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1868 this._mobileDestinationOwner?.close(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1869 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1870 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1871 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1872 disconnectedCallback() { |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1873 this._mobileDestinationBack?.removeEventListener( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1874 "click", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1875 this._onMobileDestinationBack, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1876 ); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1877 this._mobileDestinationDialog?.removeEventListener( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1878 "close", |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1879 this._onMobileDestinationClose, |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
1880 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1881 this._mobileMenuQuery?.removeEventListener( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1882 "change", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1883 this._onMobileMenuMediaChange, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1884 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1885 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1886 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1887 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1888 for (const [name, constructor] of Object.entries({ |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1889 "mjj-conversation-archive": MjjConversationArchive, |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1890 "mjj-jrpg-character": MjjJrpgCharacter, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1891 "mjj-jrpg-chat": MjjJrpgChat, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1892 "mjj-jrpg-menu": MjjJrpgMenu, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1893 "mjj-jrpg-preview": MjjJrpgPreview, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1894 "mjj-jrpg-shell": MjjJrpgShell, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1895 })) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1896 if (!customElements.get(name)) customElements.define(name, constructor); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1897 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1898 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1899 async function requestJson(url, options = {}) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1900 const response = await fetch(url, { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1901 ...options, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1902 credentials: "same-origin", |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1903 headers: { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1904 "Content-Type": "application/json", |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1905 ...options.headers, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1906 }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1907 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1908 if (!response.ok) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1909 let message = `Request failed (${response.status})`; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1910 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1911 const body = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1912 message = body.error?.message || message; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1913 } catch { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1914 // Preserve the status-derived message. |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1915 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1916 throw new Error(message); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1917 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1918 return response.status === 204 ? null : response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1919 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1920 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1921 /* Session state for CSRF: bootstrapped once, refreshed at most once on 401/403 */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1922 let _csrfToken = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1923 let _sessionData = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1924 let _sessionRefreshInFlight = null; /* single-flight guard */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1925 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1926 function _principalFingerprint(data) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1927 if (!data) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1928 if (data.kind === "user") return `user:${data.username || ""}:${data.role || ""}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1929 if (data.kind === "guest") return `guest:${data.csrfToken || ""}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1930 return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1931 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1932 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1933 async function _fetchSession() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1934 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1935 const response = await fetch("/api/auth/session", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1936 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1937 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1938 if (!response.ok) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1939 const data = await response.json().catch(() => null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1940 if (!data) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1941 _sessionData = data; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1942 return data.csrfToken || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1943 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1944 return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1945 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1946 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1947 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1948 /* Single-flight session refresh: dedupe concurrent callers. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1949 async function _refreshSessionOnce() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1950 if (_sessionRefreshInFlight) return _sessionRefreshInFlight; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1951 _sessionRefreshInFlight = (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1952 const prev = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1953 const token = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1954 const next = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1955 return { token, prev, next }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1956 })(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1957 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1958 return await _sessionRefreshInFlight; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1959 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1960 _sessionRefreshInFlight = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1961 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1962 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1963 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1964 /* |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1965 * Perform a state-changing (mutating) fetch with CSRF token and same-origin |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1966 * credentials. On 401 or CSRF-specific 403 (csrf_invalid), refreshes the |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1967 * session once and retries — but only if the refreshed principal matches the |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1968 * original. If the user becomes a guest or a different account, rejects and |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1969 * triggers the session-changed callback. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1970 */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1971 let _sessionRefreshCallback = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1972 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1973 async function mutatingFetch(url, options = {}) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1974 const buildHeaders = () => ({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1975 ...(options.headers || {}), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1976 Origin: window.location.origin, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1977 ...(_csrfToken ? { "X-CSRF-Token": _csrfToken } : {}), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1978 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1979 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1980 const response = await fetch(url, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1981 ...options, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1982 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1983 headers: buildHeaders(), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1984 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1985 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1986 const shouldRefresh = response.status === 401 || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1987 (response.status === 403 && await (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1988 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1989 const clone = response.clone(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1990 const body = await clone.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1991 return body?.error?.code === "csrf_invalid"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1992 } catch { return false; } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1993 })()); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1994 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1995 if (shouldRefresh) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1996 const prevFingerprint = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1997 const { token, next } = await _refreshSessionOnce(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1998 _csrfToken = token; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1999 if (next !== prevFingerprint) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2000 if (_sessionRefreshCallback) _sessionRefreshCallback(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2001 const err = Object.assign(new Error("Session changed"), { status: 401 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2002 throw err; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2003 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2004 const retried = await fetch(url, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2005 ...options, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2006 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2007 headers: buildHeaders(), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2008 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2009 if (retried.status === 401 && _sessionRefreshCallback) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2010 _sessionRefreshCallback(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2011 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2012 return retried; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2013 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2014 return response; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2015 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2016 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2017 async function consumeSse(response, onEvent) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2018 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
|
2019 const reader = response.body.getReader(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2020 const decoder = new TextDecoder(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2021 let buffer = ""; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2022 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2023 const dispatch = block => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2024 let eventName = "message"; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2025 const data = []; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2026 for (const line of block.split("\n")) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2027 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
|
2028 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
|
2029 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2030 if (!data.length) return; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2031 onEvent(eventName, JSON.parse(data.join("\n"))); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2032 }; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2033 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2034 while (true) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2035 const { done, value } = await reader.read(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2036 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
|
2037 buffer = buffer.replaceAll("\r\n", "\n"); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2038 let boundary; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2039 while ((boundary = buffer.indexOf("\n\n")) >= 0) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2040 const block = buffer.slice(0, boundary); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2041 buffer = buffer.slice(boundary + 2); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2042 if (block.trim()) dispatch(block); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2043 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2044 if (done) break; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2045 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2046 if (buffer.trim()) dispatch(buffer); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2047 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2048 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2049 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2050 Account slot rendering |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2051 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2052 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2053 function renderAccountSlot(slotEl, sessionData) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2054 if (!slotEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2055 slotEl.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2056 if (!sessionData || sessionData.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2057 const owner = document.createElement("zen-button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2058 const btn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2059 owner.setAttribute("appearance", "plain"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2060 owner.setAttribute("size", "xs"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2061 btn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2062 btn.dataset.loginOpen = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2063 btn.textContent = "LOGIN"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2064 owner.append(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2065 slotEl.setAttribute("aria-label", "Not signed in"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2066 slotEl.append(owner); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2067 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2068 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2069 const username = (sessionData.username || "").slice(0, 8).toUpperCase(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2070 if (sessionData.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2071 const link = document.createElement("a"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2072 link.href = "/account/password"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2073 link.textContent = username ? `${username}\u26A0` : "PASSWD"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2074 link.title = "Password change required"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2075 slotEl.setAttribute("aria-label", `${sessionData.username}: password change required`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2076 slotEl.append(link); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2077 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2078 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2079 const nameSpan = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2080 nameSpan.dataset.accountUser = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2081 nameSpan.textContent = username; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2082 slotEl.append(nameSpan); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2083 if (sessionData.role === "admin") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2084 const adminLink = document.createElement("a"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2085 adminLink.href = "/admin/users"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2086 adminLink.textContent = "\u00A0ADM"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2087 slotEl.append(adminLink); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2088 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2089 const logoutBtn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2090 logoutBtn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2091 logoutBtn.textContent = "\u00A0LGOUT"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2092 logoutBtn.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2093 logoutBtn.disabled = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2094 mutatingFetch("/api/auth/logout", { method: "POST" }).then(response => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2095 if (!response.ok) throw new Error(`Logout failed (${response.status})`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2096 window.dispatchEvent(new CustomEvent("mjj-logout")); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2097 }).catch(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2098 logoutBtn.disabled = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2099 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2100 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2101 slotEl.append(logoutBtn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2102 slotEl.setAttribute("aria-label", `Signed in as ${sessionData.username}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2103 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2104 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2105 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2106 Quota display helpers |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2107 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2108 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2109 function formatQuotaText(quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2110 if (!quota) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2111 const turns = quota.turnsRemaining ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2112 const tokens = quota.outputTokensRemaining ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2113 if (turns <= 0) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2114 const resetAt = quota.resetsAt ? new Date(quota.resetsAt * 1000) : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2115 const resetStr = resetAt |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2116 ? ` Resets ${resetAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}` |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2117 : ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2118 return { text: `Quota exhausted.${resetStr}`, state: "exhausted" }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2119 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2120 if (turns <= 2) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2121 return { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2122 text: `${turns} turn${turns === 1 ? "" : "s"} remaining (${tokens.toLocaleString()} tokens)`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2123 state: "warning", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2124 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2125 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2126 return { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2127 text: `${turns} turns remaining (${tokens.toLocaleString()} tokens)`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2128 state: "ok", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2129 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2130 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2131 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2132 function updateQuotaDisplay(quotaEl, quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2133 if (!quotaEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2134 if (!quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2135 quotaEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2136 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2137 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2138 const result = formatQuotaText(quota); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2139 /* Only show the bar for low or exhausted quota; hide when turns are plentiful */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2140 if (!result || result.state === "ok") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2141 quotaEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2142 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2143 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2144 quotaEl.textContent = result.text; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2145 if (result.state === "exhausted") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2146 quotaEl.dataset.state = "exhausted"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2147 } else if (result.state === "warning") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2148 quotaEl.dataset.state = "warning"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2149 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2150 delete quotaEl.dataset.state; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2151 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2152 quotaEl.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2153 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2154 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2155 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2156 Conversation archive helpers |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2157 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2158 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2159 async function fetchConversationList(cursor) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2160 const url = cursor |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2161 ? `/api/conversations?limit=${ARCHIVE_PAGE_SIZE}&cursor=${encodeURIComponent(cursor)}` |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2162 : `/api/conversations?limit=${ARCHIVE_PAGE_SIZE}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2163 const response = await fetch(url, { credentials: "same-origin" }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2164 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2165 let message = `Failed to load conversations (${response.status})`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2166 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2167 const body = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2168 message = body.error?.message || message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2169 } catch { /* keep status message */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2170 throw new Error(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2171 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2172 return response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2173 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2174 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2175 async function initializeJrpgPage() { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2176 const colorProbe = document.createElement("span"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2177 colorProbe.style.background = "var(--zenbu-sys-color-surface-page)"; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2178 document.body.append(colorProbe); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2179 const themeColor = getComputedStyle(colorProbe).backgroundColor; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2180 colorProbe.remove(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2181 for (const meta of document.querySelectorAll('meta[name="theme-color"]')) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2182 meta.setAttribute("content", themeColor); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2183 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2184 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2185 /* Bootstrap session to obtain CSRF token and session data */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2186 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2187 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2188 const shell = document.querySelector("mjj-jrpg-shell"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2189 const character = shell?.querySelector("mjj-jrpg-character"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2190 const chat = shell?.querySelector("mjj-jrpg-chat"); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2191 const composer = shell?.querySelector("mjj-composer"); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2192 const preview = shell?.querySelector("mjj-jrpg-preview"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2193 const archive = shell?.querySelector("mjj-conversation-archive"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2194 const accountSlot = shell?.querySelector("[data-frame-account]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2195 const quotaEl = shell?.querySelector("[data-quota]"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2196 const frameNetwork = shell?.querySelector("[data-frame-network]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2197 const frameUptime = shell?.querySelector("[data-frame-uptime]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2198 const minimizeButton = shell?.querySelector("[data-frame-minimize]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2199 const fullscreenButton = shell?.querySelector("[data-frame-fullscreen]"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2200 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2201 let characterTimer = 0; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2202 let currentConversationId = null; |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
2203 const serverInitialPanel = VALID_PANELS.includes(shell?.dataset.initialPanel) |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
2204 ? shell.dataset.initialPanel |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
2205 : "resume"; |
|
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
2206 let currentPanel = serverInitialPanel; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2207 let archiveCursor = null; |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2208 let activeController = null; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2209 let archiveLoadInFlight = false; /* serialize pagination requests */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2210 let openSeq = 0; /* sequence guard for conversation open */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2211 let popSeq = 0; /* sequence guard for popstate */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2212 let _pendingPopState = null; /* queued popstate received during active stream */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2213 let _claimInFlight = false; /* guard against duplicate concurrent claims */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2214 let legacyClaimId = sessionStorage.getItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2215 let principalEpoch = 0; /* rejects responses from a prior owner */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2216 /* Dedupe conversation IDs: set of IDs currently rendered in archive */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2217 const _renderedIds = new Set(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2218 const invalidatePrincipalRequests = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2219 principalEpoch += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2220 openSeq += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2221 popSeq += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2222 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2223 }; |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2224 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2225 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2226 /* Disable composer until fully initialized */ |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2227 composer?.setDisabled(true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2228 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2229 const setFrameStatus = (label, state, title = "") => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2230 if (frameNetwork) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2231 frameNetwork.textContent = label; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2232 frameNetwork.setAttribute("aria-label", `Status ${label.toLowerCase()}`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2233 frameNetwork.dataset.state = state; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2234 frameNetwork.title = title; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2235 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2236 }; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2237 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2238 /* Bootstrap failure: keep controls disabled and expose retry */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2239 if (!_csrfToken) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2240 setFrameStatus("OFFLINE", "offline", "Bootstrap failed"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2241 archive?.setError("Connection failed. Reload to retry."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2242 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2243 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2244 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2245 setFrameStatus("ONLINE", "online"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2246 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2247 const startedAt = Date.now(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2248 const updateUptime = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2249 const elapsed = Math.floor((Date.now() - startedAt) / 1000); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2250 const hours = String(Math.floor(elapsed / 3600)).padStart(2, "0"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2251 const minutes = String(Math.floor((elapsed % 3600) / 60)).padStart(2, "0"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2252 const seconds = String(elapsed % 60).padStart(2, "0"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2253 if (frameUptime) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2254 frameUptime.textContent = `${hours}:${minutes}:${seconds}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2255 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2256 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2257 updateUptime(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2258 window.setInterval(updateUptime, 1000); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2259 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2260 minimizeButton?.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2261 const minimized = shell.dataset.minimized !== "true"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2262 shell.dataset.minimized = String(minimized); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2263 minimizeButton.setAttribute("aria-pressed", String(minimized)); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2264 minimizeButton.setAttribute( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2265 "aria-label", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2266 minimized ? "Restore interface" : "Minimize interface", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2267 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2268 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2269 fullscreenButton?.addEventListener("click", async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2270 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2271 if (document.fullscreenElement) await document.exitFullscreen(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2272 else await shell.requestFullscreen(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2273 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2274 setFrameStatus("DENIED", "offline", error.message); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2275 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2276 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2277 document.addEventListener("fullscreenchange", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2278 fullscreenButton?.setAttribute( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2279 "aria-pressed", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2280 String(Boolean(document.fullscreenElement)), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2281 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2282 }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2283 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2284 /* Render account slot */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2285 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2286 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2287 /* ---- Login modal ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2288 const loginDialogOwner = shell?.querySelector("[data-login-dialog-owner]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2289 const loginDialog = loginDialogOwner?.querySelector("[data-login-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2290 const loginForm = loginDialog?.querySelector("[data-login-form]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2291 const loginSubmitBtn = loginDialog?.querySelector("[data-login-submit]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2292 const loginCancelBtn = loginDialog?.querySelector("[data-login-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2293 const loginErrorEl = loginDialog?.querySelector("[data-login-error]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2294 const loginUsernameInput = loginDialog?.querySelector("#jrpg-login-username"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2295 const loginPasswordInput = loginDialog?.querySelector("#jrpg-login-password"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2296 let _loginTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2297 const setAccountActionsDisabled = disabled => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2298 for (const control of accountSlot?.querySelectorAll("button") || []) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2299 control.disabled = disabled; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2300 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2301 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2302 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2303 const _clearLoginError = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2304 if (loginErrorEl) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2305 loginErrorEl.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2306 loginErrorEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2307 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2308 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2309 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2310 const _showLoginError = (message) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2311 if (!loginErrorEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2312 loginErrorEl.textContent = message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2313 loginErrorEl.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2314 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2315 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2316 const openLoginModal = (triggerEl) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2317 if (!loginDialogOwner || !loginDialog) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2318 _loginTrigger = triggerEl || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2319 _clearLoginError(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2320 loginForm?.reset(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2321 loginDialogOwner.open(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2322 requestAnimationFrame(() => { loginUsernameInput?.focus(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2323 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2324 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2325 /* Delegate clicks on data-login-open in the account slot */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2326 accountSlot?.addEventListener("click", event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2327 const btn = event.target.closest("[data-login-open]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2328 if (!btn || activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2329 openLoginModal(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2330 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2331 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2332 /* Cancel button */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2333 loginCancelBtn?.addEventListener("click", () => { loginDialog?.close(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2334 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2335 /* Cleanup on any close (Escape, X button, cancel) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2336 loginDialog?.addEventListener("close", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2337 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2338 _clearLoginError(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2339 const trigger = _loginTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2340 _loginTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2341 requestAnimationFrame(() => { trigger?.focus(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2342 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2343 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2344 /* Login form submission */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2345 loginForm?.addEventListener("submit", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2346 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2347 if (loginSubmitBtn?.disabled) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2348 const username = loginUsernameInput?.value.trim() || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2349 const password = loginPasswordInput?.value || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2350 if (!username || !password) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2351 _showLoginError("Please enter your username and password."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2352 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2353 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2354 if (loginSubmitBtn) loginSubmitBtn.disabled = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2355 _clearLoginError(); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2356 try { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2357 const response = await fetch("/api/auth/login", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2358 method: "POST", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2359 headers: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2360 "Content-Type": "application/json", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2361 "Origin": window.location.origin, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2362 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2363 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2364 body: JSON.stringify({ username, password, csrfToken: _csrfToken }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2365 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2366 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2367 if (response.status === 429) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2368 _showLoginError("Too many attempts. Please try again later."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2369 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2370 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2371 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2372 _showLoginError("Invalid username or password."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2373 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2374 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2375 let data = await response.json().catch(() => null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2376 if (!data) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2377 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2378 if (_sessionData?.kind !== "user") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2379 loginDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2380 window.location.reload(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2381 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2382 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2383 data = _sessionData; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2384 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2385 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2386 loginDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2387 _sessionData = { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2388 kind: "user", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2389 username: data.username || username, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2390 role: data.role || "member", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2391 mustChangePassword: Boolean(data.mustChangePassword), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2392 csrfToken: data.csrfToken, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2393 quota: null, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2394 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2395 _csrfToken = data.csrfToken || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2396 if (data.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2397 window.location.href = "/account/password"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2398 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2399 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2400 /* Commit the returned principal before optional UI refresh work. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2401 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2402 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2403 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2404 /* Clear conversation state — guest convs may transfer to user */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2405 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2406 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2407 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2408 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2409 _replaceUrlState(currentPanel, null); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2410 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2411 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2412 await refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2413 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2414 archive?.setError(`Signed in, but archive refresh failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2415 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2416 requestAnimationFrame(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2417 accountSlot?.querySelector("button, a")?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2418 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2419 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2420 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2421 _showLoginError("Sign-in failed. Please try again."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2422 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2423 if (loginSubmitBtn) loginSubmitBtn.disabled = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2424 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2425 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2426 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2427 /* Handle logout */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2428 _sessionRefreshCallback = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2429 /* Account changed or session lost — clear active state */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2430 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2431 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2432 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2433 activeController = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2434 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2435 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2436 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2437 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2438 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2439 _replaceUrlState(currentPanel, null); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2440 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2441 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2442 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2443 archive?.setConversations([], null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2444 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2445 archive?.setError("Session changed. Loading conversations..."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2446 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2447 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2448 window.addEventListener("mjj-logout", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2449 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2450 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2451 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2452 activeController = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2453 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2454 archive?.setStreamActive(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2455 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2456 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2457 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2458 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2459 _replaceUrlState(currentPanel, null); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2460 chat?.setGreeting(ROLE_GREETINGS.guest, true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2461 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2462 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2463 archive?.setConversations([], null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2464 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2465 void (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2466 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2467 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2468 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2469 if (_sessionData?.kind === "user" && _sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2470 composer?.setDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2471 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2472 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2473 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2474 })(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2475 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2476 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2477 /* Session quota display */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2478 if (_sessionData?.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2479 updateQuotaDisplay(quotaEl, _sessionData.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2480 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2481 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2482 /* Disable composer for forced-password-change */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2483 const isForced = _sessionData?.kind === "user" && _sessionData?.mustChangePassword; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2484 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2485 /* ---- URL state helpers ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2486 const _buildStateUrl = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2487 const url = new URL(window.location.href); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2488 if (panel && VALID_PANELS.includes(panel)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2489 url.searchParams.set("panel", panel); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2490 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2491 url.searchParams.delete("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2492 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2493 if (convId && UUID_REGEX.test(convId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2494 url.searchParams.set("conversation", convId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2495 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2496 url.searchParams.delete("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2497 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2498 return url; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2499 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2500 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2501 const _pushUrlState = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2502 history.pushState({ panel, conversation: convId || null }, "", _buildStateUrl(panel, convId)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2503 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2504 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2505 const _replaceUrlState = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2506 history.replaceState({ panel, conversation: convId || null }, "", _buildStateUrl(panel, convId)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2507 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2508 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2509 /* ---- Panel selection: shows archive or preview in utility ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2510 const utilityEl = shell?.querySelector(".jrpg-utility"); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2511 const previewContent = preview?.querySelector("[data-preview-content]"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2512 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2513 const selectPanel = (panel, pushHistory = true) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2514 const validPanel = VALID_PANELS.includes(panel) ? panel : "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2515 currentPanel = validPanel; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2516 for (const btn of (shell?.querySelectorAll("button[data-preview]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2517 btn.setAttribute("aria-pressed", String(btn.dataset.preview === validPanel)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2518 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2519 if (validPanel === "conversations") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2520 if (archive) archive.hidden = false; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2521 if (previewContent) previewContent.hidden = true; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2522 if (utilityEl) utilityEl.setAttribute("aria-label", "Conversations"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2523 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2524 if (archive) archive.hidden = true; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2525 if (previewContent) previewContent.hidden = false; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2526 preview?.show(validPanel); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2527 if (utilityEl) utilityEl.setAttribute("aria-label", "Selected destination"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2528 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2529 if (pushHistory) _pushUrlState(validPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2530 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2531 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2532 /* ---- Archive close button: navigate back to resume panel ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2533 shell?.addEventListener("mjj-archive-toggle", () => { |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2534 if (shell?.isMobileDestinationModal()) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2535 shell.showMobileMenu(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2536 return; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2537 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2538 selectPanel("resume"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2539 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2540 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2541 shell?.addEventListener("mjj-archive-new", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2542 if (activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2543 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2544 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2545 _pushUrlState(currentPanel, null); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2546 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2547 archive?.setCurrentId(null); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2548 shell?.closeMobileDestinations(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2549 composer?.querySelector("textarea")?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2550 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2551 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2552 /* ---- Popstate: re-apply panel and conversation on back/forward ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2553 /* _applyPopState always increments openSeq so any pending archive-open |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2554 fetch (including a transition to no conversation) cannot overwrite the |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2555 state the user navigated to (Fix 2). */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2556 const _applyPopState = async (seq, popPanel, popConvId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2557 const localSeq = ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2558 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2559 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2560 selectPanel(popPanel, false); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2561 if (shell?.isMobileDestinationOpen()) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2562 shell.showMobileDestination(popPanel); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2563 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2564 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2565 if (popConvId && popConvId !== currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2566 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2567 const conversation = await requestJson(`/api/conversations/${encodeURIComponent(popConvId)}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2568 if (seq !== popSeq || localSeq !== openSeq || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2569 epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2570 currentConversationId = popConvId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2571 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2572 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2573 archive?.setCurrentId(popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2574 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2575 if (seq !== popSeq || localSeq !== openSeq || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2576 epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2577 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2578 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2579 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2580 archive?.setCurrentId(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2581 history.replaceState( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2582 { panel: popPanel, conversation: null }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2583 "", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2584 _buildStateUrl(popPanel, null), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2585 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2586 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2587 } else if (!popConvId && currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2588 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2589 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2590 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2591 archive?.setCurrentId(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2592 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2593 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2594 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2595 /* When popstate fires while a stream is active the URL has already changed. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2596 Queue the parsed destination, abort the stream, and apply exactly once in |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2597 the stream's finally block so URL and UI always converge (Fix 1). */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2598 window.addEventListener("popstate", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2599 const seq = ++popSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2600 ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2601 const state = event.state || {}; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2602 const popPanel = VALID_PANELS.includes(state.panel) ? state.panel : "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2603 const popConvId = (state.conversation && UUID_REGEX.test(state.conversation)) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2604 ? state.conversation : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2605 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2606 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2607 _pendingPopState = { seq, popPanel, popConvId }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2608 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2609 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2610 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2611 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2612 await _applyPopState(seq, popPanel, popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2613 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2614 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2615 shell?.addEventListener("mjj-archive-rename", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2616 const { id, newTitle, trigger } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2617 if (activeController) { trigger?.focus(); return; } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2618 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2619 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2620 `/api/conversations/${encodeURIComponent(id)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2621 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2622 method: "PATCH", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2623 headers: { "Content-Type": "application/json" }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2624 body: JSON.stringify({ title: newTitle }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2625 }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2626 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2627 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2628 const body = await response.json().catch(() => ({})); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2629 throw new Error(body.error?.message || `Rename failed (${response.status})`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2630 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2631 archive?.updateConversation({ id, title: newTitle, turn_count: null }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2632 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2633 archive?.setError(`Rename failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2634 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2635 trigger?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2636 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2637 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2638 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2639 shell?.addEventListener("mjj-archive-open", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2640 const { id } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2641 if (id === currentConversationId) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2642 if (activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2643 const seq = ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2644 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2645 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2646 const conversation = await requestJson(`/api/conversations/${encodeURIComponent(id)}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2647 if (seq !== openSeq || epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2648 currentConversationId = id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2649 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2650 _pushUrlState(currentPanel, id); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2651 chat?.replaceMessages(conversation.turns); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2652 archive?.setCurrentId(id); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2653 shell?.closeMobileDestinations(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2654 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2655 if (seq !== openSeq || epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2656 archive?.setError(`Unable to open: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2657 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2658 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2659 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2660 shell?.addEventListener("mjj-archive-delete", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2661 const { id, trigger } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2662 if (activeController) { trigger?.focus(); return; } /* block while streaming */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2663 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2664 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2665 `/api/conversations/${encodeURIComponent(id)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2666 { method: "DELETE" }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2667 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2668 if (response.ok || response.status === 404) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2669 /* Focus adjacent archive item or New button before removing the node */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2670 const focusTarget = archive?.adjacentItemOrNewButton(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2671 _renderedIds.delete(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2672 archive?.removeConversation(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2673 if (id === currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2674 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2675 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2676 _replaceUrlState(currentPanel, null); |
|
265
056790c4fb0d
add role-aware Epi assistant prompts
MrJuneJune <me@mrjunejune.com>
parents:
264
diff
changeset
|
2677 chat?.setGreeting(greetingForSession(_sessionData), true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2678 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2679 /* Focus surviving element — never the detached trigger */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2680 requestAnimationFrame(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2681 (focusTarget || archive?.querySelector("[data-archive-new]"))?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2682 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2683 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2684 const body = await response.json().catch(() => ({})); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2685 archive?.setError(`Delete failed: ${body.error?.message || response.status}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2686 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2687 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2688 archive?.setError(`Delete failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2689 /* On error, the trigger is still attached — focus it */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2690 trigger?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2691 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2692 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2693 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2694 shell?.addEventListener("mjj-archive-load-more", async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2695 if (!archiveCursor || archiveLoadInFlight) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2696 archiveLoadInFlight = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2697 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2698 archive?.setLoadMoreDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2699 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2700 const data = await fetchConversationList(archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2701 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2702 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2703 const newConvs = (data.conversations || []).filter(c => !_renderedIds.has(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2704 newConvs.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2705 archive?.setConversations(newConvs, archiveCursor, true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2706 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2707 archive?.setError(`Load failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2708 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2709 archiveLoadInFlight = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2710 archive?.setLoadMoreDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2711 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2712 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2713 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2714 shell?.addEventListener("mjj-archive-claim", async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2715 const legacyId = |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2716 legacyClaimId || sessionStorage.getItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2717 if (!legacyId || _sessionData?.kind !== "user" || _claimInFlight) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2718 _claimInFlight = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2719 archive?.showLegacyClaim("Claiming prior conversation...", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2720 let _claimOutcome = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2721 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2722 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2723 "/api/conversations/claim", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2724 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2725 method: "POST", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2726 headers: { "Content-Type": "application/json" }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2727 body: JSON.stringify({ conversationId: legacyId }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2728 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2729 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2730 if (response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2731 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2732 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2733 _claimOutcome = "success"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2734 } else if (response.status === 409) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2735 /* Definitive non-legacy conflict: clear key, no retry needed */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2736 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2737 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2738 _claimOutcome = "conflict"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2739 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2740 /* Retryable (401, 403, 429, 5xx): retain key */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2741 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, legacyId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2742 legacyClaimId = legacyId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2743 _claimOutcome = "retry"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2744 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2745 } catch { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2746 /* Network error: retain key */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2747 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, legacyId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2748 legacyClaimId = legacyId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2749 _claimOutcome = "network-error"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2750 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2751 /* Reset guard BEFORE re-rendering so the retry button is immediately usable */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2752 _claimInFlight = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2753 if (_claimOutcome === "success") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2754 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2755 } else if (_claimOutcome === "conflict") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2756 archive?.showLegacyClaim("Prior conversation already claimed.", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2757 } else if (_claimOutcome === "retry") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2758 selectPanel("conversations", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2759 archive?.showLegacyClaim("Claim failed. Try again. "); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2760 } else if (_claimOutcome === "network-error") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2761 selectPanel("conversations", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2762 archive?.showLegacyClaim("Claim failed. Check connection. "); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2763 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2764 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2765 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2766 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2767 async function refreshArchiveFromScratch() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2768 if (!archive) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2769 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2770 archive.setLoading(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2771 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2772 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2773 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2774 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2775 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2776 const convs = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2777 convs.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2778 archive.setConversations(convs, archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2779 archive.setCurrentId(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2780 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2781 archive.setError(`Archive error: ${error.message}`); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2782 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2783 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2784 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2785 async function refreshArchiveHead() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2786 if (!archive) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2787 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2788 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2789 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2790 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2791 const fresh = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2792 for (const conv of fresh) archive.updateConversation(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2793 if (fresh.length > 0) archive.setCurrentId(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2794 } catch { /* silent refresh failure */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2795 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2796 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2797 /* ---- Parse and validate URL params at startup ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2798 /* Track whether ?conversation was explicitly present (even if malformed) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2799 so we never migrate sessionStorage when the param was given but invalid. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2800 let _convParamWasExplicit = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2801 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2802 const initParams = new URLSearchParams(window.location.search); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2803 const rawPanel = initParams.get("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2804 const rawConvId = initParams.get("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2805 _convParamWasExplicit = rawConvId !== null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2806 const validPanel = VALID_PANELS.includes(rawPanel) ? rawPanel : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2807 const validConvId = (rawConvId && UUID_REGEX.test(rawConvId)) ? rawConvId : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2808 /* Remove invalid params with replaceState */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2809 if ((rawPanel && !validPanel) || (rawConvId && !validConvId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2810 const normalUrl = new URL(window.location.href); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2811 if (!validPanel) normalUrl.searchParams.delete("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2812 if (!validConvId) normalUrl.searchParams.delete("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2813 history.replaceState( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2814 { panel: validPanel, conversation: validConvId }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2815 "", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2816 normalUrl, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2817 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2818 } |
|
273
e02e2036ef84
add Layer 2 JRPG component system
MrJuneJune <me@mrjunejune.com>
parents:
272
diff
changeset
|
2819 currentPanel = validPanel || serverInitialPanel; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2820 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2821 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2822 /* ---- Load archive; only an explicit URL restores a conversation ---- */ |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2823 const startupEpoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2824 const ensureStartupPrincipal = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2825 if (startupEpoch !== principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2826 const error = new Error("Principal changed during startup"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2827 error.name = "StalePrincipalError"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2828 throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2829 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2830 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2831 archive?.setLoading(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2832 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2833 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2834 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2835 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2836 const conversations = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2837 conversations.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2838 archive?.setConversations(conversations, archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2839 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2840 /* A conversation opens only through an explicit URL or a user archive action. */ |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2841 const initParams2 = new URLSearchParams(window.location.search); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2842 const urlConvId = initParams2.get("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2843 const urlConvValid = urlConvId && UUID_REGEX.test(urlConvId) ? urlConvId : null; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2844 const storedConvId = !_convParamWasExplicit |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2845 ? sessionStorage.getItem(CONVERSATION_STORAGE_KEY) |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2846 : null; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2847 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2848 if (urlConvValid) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2849 const inArchive = conversations.find(c => c.id === urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2850 if (inArchive) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2851 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2852 const conversation = await requestJson( |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2853 `/api/conversations/${encodeURIComponent(urlConvValid)}`, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2854 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2855 ensureStartupPrincipal(); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2856 currentConversationId = urlConvValid; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2857 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2858 chat?.replaceMessages(conversation.turns); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2859 archive?.setCurrentId(urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2860 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2861 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2862 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2863 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2864 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2865 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2866 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2867 /* Not on first page — fetch directly to verify ownership */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2868 let ownedConversation = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2869 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2870 ownedConversation = await requestJson( |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2871 `/api/conversations/${encodeURIComponent(urlConvValid)}`, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2872 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2873 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2874 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2875 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2876 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2877 ownedConversation = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2878 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2879 if (ownedConversation) { |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2880 currentConversationId = urlConvValid; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2881 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2882 chat?.replaceMessages(ownedConversation.turns); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2883 if (!_renderedIds.has(urlConvValid)) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2884 _renderedIds.add(urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2885 archive?.addConversation({ |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2886 id: urlConvValid, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2887 title: ownedConversation.title || "Conversation", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2888 turn_count: ownedConversation.turns?.length || 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2889 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2890 } |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2891 archive?.setCurrentId(urlConvValid); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2892 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2893 currentConversationId = null; |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2894 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2895 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2896 } |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2897 } else { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2898 currentConversationId = null; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2899 archive?.setCurrentId(null); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2900 if (conversations.length > 0) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2901 chat?.setGreeting(greetingForSession(_sessionData, true), true); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2902 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2903 if (storedConvId && UUID_REGEX.test(storedConvId)) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2904 const storedConversationIsOwned = conversations.some( |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2905 conversation => conversation.id === storedConvId, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2906 ); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2907 if (_sessionData?.kind === "user" && !storedConversationIsOwned) { |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2908 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, storedConvId); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2909 legacyClaimId = storedConvId; |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2910 archive?.showLegacyClaim(); |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2911 } |
|
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2912 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2913 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2914 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2915 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2916 if (error.name !== "StalePrincipalError" && |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2917 startupEpoch === principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2918 archive?.setError(`Archive error: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2919 } |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2920 /* Fall back only for an explicit deep-linked conversation. */ |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2921 const initParams3 = new URLSearchParams(window.location.search); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2922 const fallbackId = initParams3.get("conversation"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2923 if (startupEpoch === principalEpoch && |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2924 fallbackId && UUID_REGEX.test(fallbackId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2925 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2926 const conversation = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2927 `/api/conversations/${encodeURIComponent(fallbackId)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2928 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2929 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2930 currentConversationId = fallbackId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2931 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2932 } catch (fallbackError) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2933 if (fallbackError.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2934 startupEpoch !== principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2935 // The new principal owns subsequent rendering. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2936 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2937 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2938 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2939 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2940 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2941 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2942 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2943 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2944 /* Apply startup panel and normalize URL (replaceState — not a user navigation) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2945 if (startupEpoch === principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2946 selectPanel(currentPanel, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2947 _replaceUrlState(currentPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2948 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2949 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2950 shell?.addEventListener("mjj-jrpg-preview-change", event => { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2951 selectPanel(event.detail.selection); |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2952 shell?.showMobileDestination(event.detail.selection); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2953 character?.setAttribute("state", "thinking"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2954 window.clearTimeout(characterTimer); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2955 characterTimer = window.setTimeout(() => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2956 character?.setAttribute("state", "default"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2957 }, 650); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2958 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2959 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2960 shell?.addEventListener("mjj-composer-cancel", () => { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2961 activeController?.abort(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2962 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2963 |
|
272
41a49c29a28f
polish JRPG conversation experience
MrJuneJune <me@mrjunejune.com>
parents:
265
diff
changeset
|
2964 shell?.addEventListener("mjj-composer-submit", async event => { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2965 if (activeController) return; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2966 /* Block sends when password change is required */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2967 if (_sessionData?.kind === "user" && _sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2968 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2969 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2970 chat?.appendMessage("June", event.detail.message); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2971 composer?.setBusy(true); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2972 character?.setAttribute("state", "thinking"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2973 window.clearTimeout(characterTimer); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2974 const assistantItem = chat?.startAssistantMessage(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2975 chat?.setMessageStreaming(assistantItem, true); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2976 const assistantText = new StreamingTextAnimator(text => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2977 chat?.setMessageText(assistantItem, text); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2978 }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2979 let turnFinished = false; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2980 activeController = new AbortController(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2981 activeController.signal.addEventListener("abort", () => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2982 assistantText.cancel(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2983 }, { once: true }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2984 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2985 /* Disable archive/account mutations while stream is active (finding #7) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2986 archive?.setStreamActive(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2987 setAccountActionsDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2988 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2989 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2990 const createConversation = async () => { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2991 const resp = await mutatingFetch("/api/conversations", { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2992 method: "POST", |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2993 headers: { "Content-Type": "application/json" }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2994 body: JSON.stringify({ title: "Shiba Quest" }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2995 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2996 if (!resp.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2997 let message = `Create failed (${resp.status})`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2998 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2999 const body = await resp.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3000 message = body.error?.message || message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3001 } catch { /* keep */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3002 throw new Error(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3003 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3004 const conversation = await resp.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3005 currentConversationId = conversation.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3006 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3007 _pushUrlState(currentPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3008 if (!_renderedIds.has(currentConversationId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3009 _renderedIds.add(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3010 archive?.addConversation({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3011 id: currentConversationId, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3012 title: "Shiba Quest", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3013 turn_count: 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3014 last_message_preview: event.detail.message.slice(0, 32), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3015 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3016 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3017 archive?.setCurrentId(currentConversationId); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3018 }; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3019 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3020 if (!currentConversationId) { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3021 await createConversation(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3022 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3023 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3024 const postTurn = () => mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3025 `/api/conversations/${encodeURIComponent(currentConversationId)}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3026 { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3027 method: "POST", |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3028 headers: { "Content-Type": "application/json" }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3029 body: JSON.stringify({ prompt: event.detail.message }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3030 signal: activeController.signal, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3031 }, |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3032 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3033 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3034 let response = await postTurn(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3035 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3036 /* Handle 404: conversation may have expired — recreate it */ |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3037 if (response.status === 404) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3038 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3039 currentConversationId = null; |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3040 await createConversation(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3041 response = await postTurn(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3042 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3043 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3044 /* Handle 429: distinguish permanent quota from temporary capacity */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3045 if (response.status === 429) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3046 let errorBody = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3047 try { errorBody = await response.json(); } catch { /* keep */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3048 const code = errorBody?.error?.code || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3049 const isQuotaExhausted = |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3050 code === "guest_quota_turns_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3051 code === "guest_quota_tokens_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3052 code === "guest_turn_quota_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3053 code === "guest_token_quota_exhausted"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3054 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3055 if (isQuotaExhausted) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3056 const quotaState = errorBody?.error?.quota || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3057 updateQuotaDisplay(quotaEl, quotaState); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3058 composer?.setDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3059 chat?.setMessageStreaming(assistantItem, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3060 chat?.setMessageText( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3061 assistantItem, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3062 quotaState |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3063 ? `Quota exhausted. ${formatQuotaText(quotaState)?.text || ""}` |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3064 : "Quota exhausted. Try again later.", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3065 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3066 character?.setAttribute("state", "sad"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3067 setFrameStatus("QUOTA", "offline"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3068 /* Re-enable when quota resets */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3069 const resetsAt = quotaState?.resetsAt; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3070 if (resetsAt) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3071 const delay = Math.max(0, resetsAt * 1000 - Date.now()); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3072 window.setTimeout(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3073 if (_sessionData?.kind !== "user" || !_sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3074 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3075 setFrameStatus("ONLINE", "online"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3076 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3077 }, Math.min(delay + 1000, 24 * 3600 * 1000)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3078 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3079 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3080 /* Temporary capacity 429 — use Retry-After or default 5 s */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3081 const retryAfter = parseInt(response.headers.get("Retry-After") || "5", 10); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3082 chat?.setMessageStreaming(assistantItem, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3083 chat?.setMessageText(assistantItem, "Server busy. Retrying shortly…"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3084 character?.setAttribute("state", "sad"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3085 setFrameStatus("BUSY", "offline"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3086 window.setTimeout(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3087 if (!activeController?.signal.aborted) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3088 setFrameStatus("ONLINE", "online"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3089 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3090 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3091 }, retryAfter * 1000); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3092 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3093 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3094 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3095 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3096 if (!response.ok) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3097 let message = `Turn failed (${response.status})`; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3098 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3099 const body = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3100 message = body.error?.message || message; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3101 } catch { /* keep */ } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3102 throw new Error(message); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3103 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3104 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3105 await consumeSse(response, (eventName, data) => { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3106 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
|
3107 bubbles: true, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3108 detail: { data, type: eventName }, |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3109 })); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3110 if (eventName === "assistant.delta") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3111 assistantText.append(data.delta || ""); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3112 } else if (eventName === "assistant.completed") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3113 assistantText.complete(data.content); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3114 } else if (eventName === "turn.error") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3115 throw new Error( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3116 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
|
3117 ); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3118 } else if (eventName === "turn.done") { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3119 turnFinished = true; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3120 composer?.setCancellable(false); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3121 if (data.failed || data.aborted) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3122 throw new Error( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3123 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
|
3124 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3125 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3126 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3127 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3128 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3129 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
|
3130 await assistantText.waitForIdle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3131 if (activeController.signal.aborted) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3132 throw new DOMException("Quest cancelled", "AbortError"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3133 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3134 chat?.setMessageStreaming(assistantItem, false); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3135 character?.setAttribute("state", "default"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
3136 setFrameStatus("ONLINE", "online"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3137 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3138 /* Refresh archive and quota after successful turn */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3139 void refreshArchiveHead(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3140 if (_sessionData?.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3141 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3142 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3143 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3144 } catch (error) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3145 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
|
3146 assistantText.cancel(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3147 chat?.setMessageStreaming(assistantItem, false); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3148 chat?.setMessageText( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3149 assistantItem, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3150 aborted ? "Quest cancelled." : `System error: ${error.message}`, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3151 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3152 character?.setAttribute("state", "sad"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
3153 if (!aborted) setFrameStatus("OFFLINE", "offline"); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3154 } finally { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3155 composer?.setBusy(false); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3156 composer?.querySelector("textarea")?.focus(); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3157 activeController = null; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3158 /* Re-enable archive/account mutations exactly once */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3159 archive?.setStreamActive(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3160 setAccountActionsDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3161 /* Apply popstate that was queued during the stream (Fix 1) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3162 if (_pendingPopState) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3163 const pending = _pendingPopState; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3164 _pendingPopState = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3165 if (pending.seq === popSeq) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3166 await _applyPopState(pending.seq, pending.popPanel, pending.popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3167 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3168 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3169 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3170 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3171 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3172 /* Enable composer unless forced password change */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3173 if (!isForced) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3174 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3175 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3176 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3177 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3178 if (document.readyState === "loading") { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3179 document.addEventListener("DOMContentLoaded", () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3180 void initializeJrpgPage(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3181 }, { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3182 once: true, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3183 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3184 } else { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3185 void initializeJrpgPage(); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3186 } |