Mercurial
annotate mrjunejune/src/jrpg/jrpg.js @ 264:04fee26ecce0
add authenticated JRPG conversation platform
Add reusable auth/session storage, owned conversation recovery, guest quotas, admin workflows, URL-routed conversation UI, mobile frame support, and parallel browser acceptance.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 07:34:12 -0700 |
| parents | ee04e4e69fed |
| children | 056790c4fb0d |
| 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"]); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
92 const TOOL_ICON_NAMES = Object.freeze({ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
93 "/notes": "repository", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
94 "/tools/file_converter": "retry", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
95 "/tools/hls_player": "play", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
96 "/tools/latex_editor": "file", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
97 "/tools/markdown_to_html": "code", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
98 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
99 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
100 function loadScript(source) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
101 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
|
102 const loading = new Promise((resolve, reject) => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
103 const script = document.createElement("script"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
104 script.src = source; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
105 script.addEventListener("load", resolve, { once: true }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
106 script.addEventListener("error", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
107 SCRIPT_LOADS.delete(source); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
108 script.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
109 reject(new Error(`Unable to load ${source}`)); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
110 }, { once: true }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
111 document.head.append(script); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
112 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
113 SCRIPT_LOADS.set(source, loading); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
114 return loading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
115 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
116 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
117 class StreamingTextAnimator { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
118 constructor(render) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
119 this._render = render; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
120 this._displayed = ""; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
121 this._target = ""; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
122 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
123 this._waiters = []; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
124 this._reduceMotion = window.matchMedia( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
125 "(prefers-reduced-motion: reduce)", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
126 ).matches; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
127 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
128 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
129 append(text) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
130 if (typeof text !== "string" || !text) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
131 this._target += text; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
132 this._schedule(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
133 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
134 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
135 complete(text) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
136 if (typeof text === "string") { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
137 this._target = text; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
138 if (!this._target.startsWith(this._displayed)) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
139 let shared = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
140 while ( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
141 shared < this._target.length && |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
142 shared < this._displayed.length && |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
143 this._target[shared] === this._displayed[shared] |
|
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 shared++; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
146 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
147 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
|
148 this._render(this._displayed); |
|
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 this._schedule(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
152 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
153 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
154 waitForIdle() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
155 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
|
156 return Promise.resolve(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
157 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
158 return new Promise(resolve => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
159 this._waiters.push(resolve); |
|
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 } |
|
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 cancel() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
164 window.clearTimeout(this._timer); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
165 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
166 this._target = this._displayed; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
167 this._settle(); |
|
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 _schedule() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
171 if (this._reduceMotion) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
172 this._displayed = this._target; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
173 this._render(this._displayed); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
174 this._settle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
175 return; |
|
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 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
|
178 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
|
179 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
180 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
181 _tick() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
182 this._timer = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
183 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
|
184 if (remaining <= 0) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
185 this._settle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
186 return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
187 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
188 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
|
189 this._displayed = this._target.slice( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
190 0, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
191 this._displayed.length + amount, |
|
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 this._render(this._displayed); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
194 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
|
195 else this._schedule(); |
|
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 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
198 _settle() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
199 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
|
200 const waiters = this._waiters.splice(0); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
201 for (const resolve of waiters) resolve(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
202 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
203 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
204 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
205 class MjjJrpgCharacter extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
206 static get observedAttributes() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
207 return ["state"]; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
208 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
209 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
210 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
211 this.render(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
212 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
213 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
214 attributeChangedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
215 if (this.isConnected) this.render(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
216 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
217 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
218 render() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
219 const image = this.querySelector("[data-character]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
220 if (!image) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
221 const state = CHARACTER_STATES[this.getAttribute("state")] || |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
222 CHARACTER_STATES.default; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
223 image.src = state.src; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
224 image.alt = state.alt; |
|
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 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
227 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
228 class MjjJrpgChat extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
229 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
230 this._messages = this.querySelector("[data-messages]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
231 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
|
232 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
|
233 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
|
234 this._position = this.querySelector("[data-turn-position]"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
235 this._initialMessages = [...(this._messages?.children || [])].map( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
236 item => item.cloneNode(true), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
237 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
238 this._turnSequence = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
239 this._activeGroup = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
240 this._onNavigationClick = event => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
241 const button = event.target.closest("button"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
242 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
|
243 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
|
244 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
|
245 }; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
246 this.addEventListener("click", this._onNavigationClick); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
247 this._refreshTurnGroups("start"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
248 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
249 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
250 disconnectedCallback() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
251 this.removeEventListener("click", this._onNavigationClick); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
252 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
253 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
254 appendMessage(speaker, text) { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
255 let group = this._activeGroup || "start"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
256 if (speaker === "June") { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
257 this._turnSequence++; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
258 group = `turn-${this._turnSequence}`; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
259 this._activeGroup = group; |
|
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 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
|
262 } |
|
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 _createMessage(speaker, text, group, select) { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
265 if (!this._messages) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
266 const item = document.createElement("li"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
267 const name = document.createElement("strong"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
268 const copy = document.createElement("p"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
269 item.className = "jrpg-message"; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
270 item.dataset.speaker = speaker; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
271 item.dataset.turnGroup = group; |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
272 name.textContent = speaker; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
273 copy.textContent = text; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
274 item.append(name, copy); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
275 this._messages.append(item); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
276 this._refreshTurnGroups(select ? group : this._currentGroup); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
277 return item; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
278 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
279 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
280 replaceMessages(turns) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
281 if (!this._messages) return; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
282 this._messages.replaceChildren(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
283 this._turnSequence = 0; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
284 this._activeGroup = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
285 const visibleTurns = turns.filter(turn => |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
286 ["user", "assistant"].includes(turn.role) |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
287 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
288 if (!visibleTurns.length) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
289 this._messages.append( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
290 ...this._initialMessages.map(item => item.cloneNode(true)), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
291 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
292 this._refreshTurnGroups("start"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
293 return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
294 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
295 let group = null; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
296 for (const turn of visibleTurns) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
297 if (turn.role === "user" || !group) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
298 this._turnSequence++; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
299 group = `turn-${this._turnSequence}`; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
300 this._activeGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
301 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
302 this._createMessage( |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
303 turn.role === "user" ? "June" : "Epi", |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
304 turn.content || |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
305 (turn.status === "aborted" |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
306 ? "Quest cancelled." |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
307 : turn.status === "failed" |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
308 ? "The quest failed." |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
309 : ""), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
310 group, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
311 false, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
312 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
313 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
314 this._refreshTurnGroups(group); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
315 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
316 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
317 startAssistantMessage() { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
318 return this.appendMessage("Epi", ""); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
319 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
320 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
321 setMessageText(item, text) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
322 const copy = item?.querySelector("p"); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
323 if (copy) copy.textContent = text; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
324 this.scrollEnd(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
325 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
326 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
327 setMessageStreaming(item, streaming) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
328 if (!item) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
329 if (streaming) item.dataset.streaming = "true"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
330 else delete item.dataset.streaming; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
331 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
|
332 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
333 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
334 _refreshTurnGroups(preferredGroup) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
335 const groups = []; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
336 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
|
337 const group = item.dataset.turnGroup || "start"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
338 item.dataset.turnGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
339 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
|
340 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
341 this._turnGroups = groups; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
342 const preferredIndex = groups.indexOf(preferredGroup); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
343 const currentIndex = groups.indexOf(this._currentGroup); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
344 this._turnIndex = preferredIndex >= 0 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
345 ? preferredIndex |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
346 : currentIndex >= 0 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
347 ? currentIndex |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
348 : Math.max(0, groups.length - 1); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
349 this._showTurn(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
350 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
351 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
352 _moveTurn(offset) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
353 const nextIndex = Math.max( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
354 0, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
355 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
|
356 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
357 if (nextIndex === this._turnIndex) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
358 this._turnIndex = nextIndex; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
359 this._showTurn(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
360 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
361 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
362 _showTurn() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
363 const group = this._turnGroups?.[this._turnIndex]; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
364 this._currentGroup = group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
365 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
|
366 item.hidden = item.dataset.turnGroup !== group; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
367 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
368 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
|
369 if (this._nextButton) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
370 this._nextButton.disabled = |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
371 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
|
372 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
373 if (this._position) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
374 if (group === "start") { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
375 this._position.textContent = "START"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
376 } else { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
377 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
|
378 this._position.textContent = |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
379 `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
|
380 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
381 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
382 this.scrollEnd(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
383 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
384 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
385 scrollEnd() { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
386 requestAnimationFrame(() => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
387 if (this._viewport) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
388 this._viewport.scrollTop = this._viewport.scrollHeight; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
389 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
390 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
391 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
392 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
393 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
394 class MjjJrpgComposer extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
395 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
396 this._form = this.querySelector("[data-composer]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
397 this._textarea = this.querySelector("textarea"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
398 this._button = this.querySelector('button[type="submit"]'); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
399 this._cancel = this.querySelector("[data-cancel]"); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
400 this._cancelControl = this.querySelector("[data-cancel-control]"); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
401 this._onSubmit = event => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
402 event.preventDefault(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
403 const message = this._textarea?.value.trim(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
404 if (!message || this._button?.disabled) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
405 this.dispatchEvent(new CustomEvent("mjj-jrpg-submit", { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
406 bubbles: true, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
407 detail: { message }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
408 })); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
409 this._form.reset(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
410 }; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
411 this._onKeydown = event => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
412 if (event.key !== "Enter" || event.shiftKey || event.isComposing) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
413 event.preventDefault(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
414 this._form?.requestSubmit(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
415 }; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
416 this._form?.addEventListener("submit", this._onSubmit); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
417 this._textarea?.addEventListener("keydown", this._onKeydown); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
418 this._onCancel = () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
419 this.dispatchEvent(new CustomEvent("mjj-jrpg-cancel", { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
420 bubbles: true, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
421 })); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
422 }; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
423 this._cancel?.addEventListener("click", this._onCancel); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
424 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
425 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
426 disconnectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
427 this._form?.removeEventListener("submit", this._onSubmit); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
428 this._textarea?.removeEventListener("keydown", this._onKeydown); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
429 this._cancel?.removeEventListener("click", this._onCancel); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
430 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
431 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
432 setBusy(busy) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
433 if (this._textarea) this._textarea.disabled = busy; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
434 if (this._button) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
435 this._button.disabled = busy; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
436 this._button.setAttribute("aria-busy", String(busy)); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
437 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
438 if (this._cancelControl) this._cancelControl.hidden = !busy; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
439 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
440 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
441 setCancellable(cancellable) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
442 if (this._cancelControl) this._cancelControl.hidden = !cancellable; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
443 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
444 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
445 setDisabled(disabled) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
446 if (this._textarea) this._textarea.disabled = disabled; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
447 if (this._button) this._button.disabled = disabled; |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
448 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
449 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
450 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
451 class MjjJrpgMenu extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
452 connectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
453 this._onClick = event => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
454 const button = event.target.closest("button[data-preview]"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
455 if (!button || !this.contains(button)) return; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
456 for (const item of this.querySelectorAll("button[data-preview]")) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
457 item.setAttribute("aria-pressed", String(item === button)); |
|
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 this.dispatchEvent(new CustomEvent("mjj-jrpg-preview-change", { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
460 bubbles: true, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
461 detail: { selection: button.dataset.preview }, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
462 })); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
463 }; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
464 this.addEventListener("click", this._onClick); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
465 } |
|
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 disconnectedCallback() { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
468 this.removeEventListener("click", this._onClick); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
469 } |
|
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 |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
472 function sanitizeFetchedMain(main) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
473 for (const unsafe of main.querySelectorAll( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
474 "embed, form, iframe, object, script, style, svg", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
475 )) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
476 unsafe.remove(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
477 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
478 for (const element of main.querySelectorAll("*")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
479 for (const attribute of [...element.attributes]) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
480 const name = attribute.name.toLowerCase(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
481 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
|
482 element.removeAttribute(attribute.name); |
|
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 } |
|
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 anchor of main.querySelectorAll("a")) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
487 const href = anchor.getAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
488 if (!href || anchor.getAttribute("aria-disabled") === "true") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
489 anchor.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
490 continue; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
491 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
492 const target = new URL( |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
493 href, |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
494 window.location.href, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
495 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
496 anchor.href = target.href; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
497 if (target.origin !== window.location.origin) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
498 anchor.target = "_blank"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
499 anchor.rel = "noreferrer"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
500 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
501 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
502 for (const image of main.querySelectorAll("img")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
503 const source = image.getAttribute("src") || ""; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
504 if (source.startsWith("/public/") && source.endsWith(".png")) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
505 image.src = source.replace(/\.png$/i, ".webp"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
506 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
507 image.loading = "lazy"; |
|
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 return main; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
510 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
511 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
512 function sanitizeToolDetail(main) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
513 sanitizeFetchedMain(main); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
514 for (const textarea of main.querySelectorAll("textarea")) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
515 const sample = document.createElement("pre"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
516 sample.className = "jrpg-tool-source-sample"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
517 sample.textContent = textarea.value || textarea.textContent; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
518 textarea.replaceWith(sample); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
519 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
520 for (const inactive of main.querySelectorAll( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
521 "button, canvas, dialog, input, select, video, [hidden]", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
522 )) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
523 inactive.remove(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
524 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
525 for (const owner of main.querySelectorAll( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
526 "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
|
527 )) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
528 owner.replaceWith(...owner.childNodes); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
529 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
530 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
|
531 label.removeAttribute("for"); |
|
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 return main; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
534 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
535 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
536 class MjjJrpgPreview extends HTMLElement { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
537 connectedCallback() { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
538 this._dialog = this.querySelector("dialog"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
539 this._onDialogClose = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
540 this._toolRequest = (this._toolRequest || 0) + 1; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
541 this.cleanupTool(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
542 }; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
543 this._onClick = event => { |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
544 const trigger = event.target.closest("[data-zen-trigger]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
545 if (trigger) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
546 if (this.dataset.selection === "resume") void this.loadResume(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
547 if (this.dataset.selection === "blog") { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
548 this.show("blog"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
549 void this.loadBlogs(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
550 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
551 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
552 this.show("tools"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
553 void this.openToolWorkspace(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
554 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
555 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
556 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
557 const blogEntry = event.target.closest("[data-blog-entry]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
558 if (blogEntry) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
559 void this.loadBlogDetail(blogEntry.dataset.blogUrl); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
560 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
561 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
562 const latestBlog = event.target.closest("[data-latest-blog]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
563 if (latestBlog && !event.metaKey && !event.ctrlKey && !event.shiftKey) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
564 event.preventDefault(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
565 this.querySelector("zen-dialog").open(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
566 void this.loadBlogDetail(latestBlog.dataset.blogUrl); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
567 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
568 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
569 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
|
570 if (blogArchive) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
571 event.preventDefault(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
572 this.querySelector("zen-dialog").open(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
573 this.show("blog"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
574 void this.loadBlogs(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
575 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
576 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
577 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
|
578 if (resumeModal) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
579 event.preventDefault(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
580 this.querySelector("zen-dialog").open(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
581 this.show("resume"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
582 void this.loadResume(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
583 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
584 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
585 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
|
586 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
|
587 event.preventDefault(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
588 this.querySelector("zen-dialog").open(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
589 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
|
590 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
591 }; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
592 this.addEventListener("click", this._onClick); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
593 this._dialog.addEventListener("close", this._onDialogClose); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
594 this.show(this.dataset.selection || "resume"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
595 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
596 |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
597 disconnectedCallback() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
598 this.removeEventListener("click", this._onClick); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
599 this._dialog?.removeEventListener("close", this._onDialogClose); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
600 this.cleanupTool(); |
|
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 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
603 show(selection) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
604 this.cleanupTool(); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
605 const preview = PREVIEWS[selection] || PREVIEWS.resume; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
606 this.dataset.selection = selection; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
607 this.querySelector("[data-preview-title]").textContent = preview.title; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
608 this.querySelector("[data-preview-copy]").textContent = preview.copy; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
609 this.querySelector("[data-dialog-title]").textContent = preview.title; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
610 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
|
611 this.renderShowcase(preview.works); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
612 const link = this.querySelector("[data-preview-link]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
613 link.href = preview.url; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
614 link.setAttribute("aria-label", `Open ${preview.title}`); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
615 const resumeDossier = this.querySelector("[data-resume-dossier]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
616 const resumeDownload = this.querySelector("[data-resume-download]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
617 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
|
618 const toolBrowser = this.querySelector("[data-tool-browser]"); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
619 const isResume = selection === "resume"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
620 const isBlog = selection === "blog"; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
621 const isTools = selection === "tools"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
622 this._dialog.dataset.detailMode = isTools ? "tools" : "content"; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
623 resumeDossier.hidden = !isResume; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
624 resumeDownload.hidden = !isResume; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
625 this.querySelector("[data-dialog-actions]").hidden = !isResume; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
626 blogBrowser.hidden = !isBlog; |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
627 toolBrowser.hidden = !isTools; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
628 this.querySelector("[data-dialog-copy]").hidden = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
629 isResume || isBlog || isTools; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
630 if (!isBlog) this._blogRequest = (this._blogRequest || 0) + 1; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
631 if (isBlog) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
632 const status = this.querySelector("[data-blog-status]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
633 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
634 status.textContent = "Select a blog to inspect."; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
635 this.querySelector("[data-blog-content]").replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
636 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
|
637 button.setAttribute("aria-pressed", "false"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
638 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
639 void this.loadBlogs(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
640 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
641 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
|
642 if (isTools) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
643 const status = this.querySelector("[data-tool-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
644 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
645 status.textContent = "Select a tool to inspect."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
646 this.querySelector("[data-tool-content]").replaceChildren(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
647 void this.loadTools(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
648 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
649 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
650 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
651 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
|
652 const showcase = this.querySelector("[data-work-showcase]"); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
653 showcase.replaceChildren(...works.map(work => { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
654 const item = document.createElement("li"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
655 const link = document.createElement("a"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
656 const label = document.createElement("span"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
657 const detail = document.createElement("small"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
658 link.href = work.url; |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
659 if (latestBlogs) { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
660 if (work.modal === "blog") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
661 link.dataset.blogArchive = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
662 } else { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
663 link.dataset.latestBlog = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
664 link.dataset.blogUrl = work.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
665 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
666 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
667 if (work.modal === "resume") link.dataset.resumeModal = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
668 if (latestKind === "tool") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
669 link.dataset.latestTool = ""; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
670 link.dataset.toolUrl = work.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
671 const icon = document.createElement("zen-icon"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
672 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
|
673 link.prepend(icon); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
674 } |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
675 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
|
676 link.target = "_blank"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
677 link.rel = "noreferrer"; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
678 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
679 label.textContent = work.label; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
680 detail.textContent = work.detail; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
681 link.append(label, detail); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
682 item.append(link); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
683 return item; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
684 })); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
685 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
686 |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
687 async loadResume() { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
688 if (this._resumeLoaded || this._resumeLoading) return; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
689 this._resumeLoading = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
690 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
|
691 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
|
692 try { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
693 const response = await fetch("/resume", { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
694 headers: { Accept: "text/html" }, |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
695 }); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
696 if (!response.ok) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
697 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
|
698 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
699 const documentCopy = new DOMParser().parseFromString( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
700 await response.text(), |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
701 "text/html", |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
702 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
703 const resume = documentCopy.querySelector("main"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
704 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
|
705 sanitizeFetchedMain(resume); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
706 content.replaceChildren(...resume.childNodes); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
707 status.hidden = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
708 this._resumeLoaded = true; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
709 } catch (error) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
710 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
|
711 } finally { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
712 this._resumeLoading = false; |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
713 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
714 } |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
715 |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
716 async loadBlogs() { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
717 if (this._blogs) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
718 if (this.dataset.selection === "blog") { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
719 this.renderShowcase([ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
720 ...this._blogs.slice(0, 4), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
721 { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
722 detail: "Full archive", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
723 label: "All blogs", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
724 modal: "blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
725 url: "/blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
726 }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
727 ], true); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
728 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
729 return true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
730 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
731 if (this._blogsLoading) return this._blogsLoading; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
732 const list = this.querySelector("[data-blog-list]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
733 this._blogsLoading = (async () => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
734 const response = await fetch("/blog", { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
735 headers: { Accept: "text/html" }, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
736 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
737 if (!response.ok) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
738 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
|
739 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
740 const documentCopy = new DOMParser().parseFromString( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
741 await response.text(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
742 "text/html", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
743 ); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
744 this._blogs = [...documentCopy.querySelectorAll( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
745 'main li a[href^="/blog/"]', |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
746 )].map(anchor => ({ |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
747 date: anchor.closest("li")?.querySelector("span")?.textContent.trim() || |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
748 "Archive", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
749 detail: anchor.closest("li")?.querySelector("span")?.textContent.trim() || |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
750 "Archive", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
751 label: anchor.textContent.trim(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
752 url: anchor.getAttribute("href"), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
753 })); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
754 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
|
755 this.renderBlogList(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
756 if (this.dataset.selection === "blog") { |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
757 this.renderShowcase([ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
758 ...this._blogs.slice(0, 4), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
759 { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
760 detail: "Full archive", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
761 label: "All blogs", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
762 modal: "blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
763 url: "/blog", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
764 }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
765 ], true); |
|
262
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
766 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
767 return true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
768 })().catch(error => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
769 list.replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
770 const item = document.createElement("li"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
771 item.textContent = `Unable to load blogs: ${error.message}`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
772 list.append(item); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
773 return false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
774 }).finally(() => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
775 this._blogsLoading = null; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
776 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
777 return this._blogsLoading; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
778 } |
|
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 renderBlogList() { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
781 const list = this.querySelector("[data-blog-list]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
782 list.replaceChildren(...this._blogs.map(blog => { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
783 const item = document.createElement("li"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
784 const owner = document.createElement("zen-button"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
785 const button = document.createElement("button"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
786 const title = document.createElement("span"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
787 const date = document.createElement("small"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
788 owner.setAttribute("appearance", "plain"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
789 owner.setAttribute("size", "sm"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
790 button.type = "button"; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
791 button.dataset.blogEntry = ""; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
792 button.dataset.blogUrl = blog.url; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
793 title.textContent = blog.label; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
794 date.textContent = blog.date; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
795 button.append(title, date); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
796 owner.append(button); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
797 item.append(owner); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
798 return item; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
799 })); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
800 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
801 |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
802 async loadBlogDetail(url) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
803 if (!await this.loadBlogs()) return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
804 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
|
805 const status = this.querySelector("[data-blog-status]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
806 const content = this.querySelector("[data-blog-content]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
807 if (!blog) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
808 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
809 status.textContent = "Unable to load blog: unknown entry."; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
810 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
811 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
812 const request = (this._blogRequest || 0) + 1; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
813 this._blogRequest = request; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
814 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
815 status.textContent = `Loading ${blog.label}...`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
816 content.replaceChildren(); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
817 this.querySelector("[data-dialog-title]").textContent = blog.label; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
818 const fullPage = this.querySelector("[data-preview-link]"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
819 fullPage.href = blog.url; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
820 fullPage.setAttribute("aria-label", `Open ${blog.label}`); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
821 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
|
822 button.setAttribute( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
823 "aria-pressed", |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
824 String(button.dataset.blogUrl === blog.url), |
|
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 try { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
828 const response = await fetch(blog.url, { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
829 headers: { Accept: "text/html" }, |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
830 }); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
831 if (!response.ok) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
832 throw new Error(`Blog request failed (${response.status})`); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
833 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
834 const documentCopy = new DOMParser().parseFromString( |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
835 await response.text(), |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
836 "text/html", |
|
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 article = documentCopy.querySelector("main"); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
839 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
|
840 sanitizeFetchedMain(article); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
841 if (request !== this._blogRequest || this.dataset.selection !== "blog") { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
842 return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
843 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
844 content.replaceChildren(...article.childNodes); |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
845 status.hidden = true; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
846 } catch (error) { |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
847 if (request !== this._blogRequest) return; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
848 status.hidden = false; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
849 status.textContent = `Unable to load blog: ${error.message}`; |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
850 } |
|
0f45474c1b1a
Add cyberpunk JRPG blog browser
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
261
diff
changeset
|
851 } |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
852 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
853 async loadTools() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
854 if (this._tools) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
855 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
856 this.renderShowcase(this._tools, false, "tool"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
857 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
858 return true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
859 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
860 if (this._toolsLoading) return this._toolsLoading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
861 this._toolsLoading = (async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
862 const response = await fetch("/tools", { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
863 headers: { Accept: "text/html" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
864 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
865 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
866 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
|
867 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
868 const documentCopy = new DOMParser().parseFromString( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
869 await response.text(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
870 "text/html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
871 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
872 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
|
873 anchor => ({ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
874 detail: anchor.getAttribute("href").startsWith("/notes") |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
875 ? "Writing" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
876 : "Utility", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
877 label: anchor.textContent.trim(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
878 url: anchor.getAttribute("href"), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
879 }), |
|
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 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
|
882 if (this.dataset.selection === "tools") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
883 this.renderShowcase(this._tools, false, "tool"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
884 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
885 return true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
886 })().catch(error => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
887 const status = this.querySelector("[data-tool-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
888 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
889 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
|
890 return false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
891 }).finally(() => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
892 this._toolsLoading = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
893 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
894 return this._toolsLoading; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
895 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
896 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
897 async openToolWorkspace() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
898 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
|
899 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
|
900 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
901 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
902 async loadToolDetail(url) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
903 const request = (this._toolRequest || 0) + 1; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
904 this._toolRequest = request; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
905 if (!await this.loadTools()) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
906 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
|
907 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
908 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
909 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
|
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 const content = this.querySelector("[data-tool-content]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
912 if (!tool) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
913 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
914 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
|
915 return; |
|
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 this._activeToolUrl = tool.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
918 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
919 status.textContent = `Loading ${tool.label}...`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
920 this.cleanupTool(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
921 content.replaceChildren(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
922 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
|
923 const fullPage = this.querySelector("[data-preview-link]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
924 fullPage.href = tool.url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
925 fullPage.setAttribute("aria-label", `Open ${tool.label}`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
926 if (tool.url === "/tools/markdown_to_html") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
927 await this.renderMarkdownTool(content, status, request); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
928 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
929 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
930 if (tool.url === "/tools/file_converter") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
931 this.renderFileConverterTool(content, status); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
932 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
933 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
934 if (tool.url === "/tools/hls_player") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
935 await this.renderHlsTool(content, status, request); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
936 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
937 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
938 if (tool.url === "/tools/latex_editor") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
939 this.renderLatexTool(content, status); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
940 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
941 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
942 if (tool.url === "/notes") { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
943 const heading = document.createElement("h1"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
944 const description = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
945 const privacy = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
946 heading.textContent = "Personal Notes"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
947 description.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
948 "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
|
949 privacy.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
950 "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
|
951 content.replaceChildren( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
952 this.createReadonlyToolNote(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
953 heading, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
954 description, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
955 privacy, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
956 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
957 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
958 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
959 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
960 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
961 const response = await fetch(tool.url, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
962 headers: { Accept: "text/html" }, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
963 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
964 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
965 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
|
966 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
967 const documentCopy = new DOMParser().parseFromString( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
968 await response.text(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
969 "text/html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
970 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
971 const detail = documentCopy.querySelector("main, .container") || |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
972 documentCopy.body; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
973 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
|
974 sanitizeToolDetail(detail); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
975 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
|
976 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
977 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
978 content.replaceChildren( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
979 this.createReadonlyToolNote(), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
980 ...detail.childNodes, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
981 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
982 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
983 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
984 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
985 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
986 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
|
987 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
988 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
989 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
990 async renderMarkdownTool(content, status, request) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
991 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
992 <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
|
993 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
994 <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
|
995 <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
|
996 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
997 <label for="jrpgMarkdownSource">Markdown</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
998 <textarea id="jrpgMarkdownSource" data-markdown-source></textarea> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
999 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1000 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1001 <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
|
1002 <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
|
1003 <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
|
1004 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1005 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1006 </section> |
|
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 const source = content.querySelector("[data-markdown-source]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1009 const output = content.querySelector("[data-markdown-output]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1010 source.value = [ |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1011 "# JRPG Markdown", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1012 "", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1013 "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
|
1014 "", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1015 "- Bazel-built WASM", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1016 "- Sanitized HTML output", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1017 ].join("\n"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1018 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1019 status.textContent = "Loading the Markdown converter..."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1020 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1021 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
|
1022 .then(module => module.default()); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1023 const module = await this._markdownModule; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1024 if (request !== this._toolRequest || !source.isConnected) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1025 const convertMarkdown = module.cwrap( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1026 "markdown_to_html", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1027 "number", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1028 ["string"], |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1029 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1030 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
|
1031 const convert = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1032 const pointer = convertMarkdown(source.value); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1033 const html = module.UTF8ToString(pointer); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1034 freeMarkdown(pointer); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1035 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
|
1036 sanitizeFetchedMain(documentCopy.body); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1037 output.replaceChildren(...documentCopy.body.childNodes); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1038 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1039 source.addEventListener("input", convert); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1040 convert(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1041 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1042 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1043 this._markdownModule = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1044 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1045 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1046 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
|
1047 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1048 } |
|
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 renderFileConverterTool(content, status) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1051 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1052 <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
|
1053 <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
|
1054 Choose a file in either panel. |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1055 </p> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1056 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1057 <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
|
1058 <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
|
1059 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1060 <label for="jrpgImageInput">Image file</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1061 <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
|
1062 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1063 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1064 <button type="button" data-convert-kind="image"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1065 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
|
1066 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1067 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1068 <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
|
1069 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1070 <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
|
1071 <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
|
1072 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1073 <label for="jrpgVideoInput">Video file</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1074 <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
|
1075 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1076 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1077 <button type="button" data-convert-kind="video"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1078 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
|
1079 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1080 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1081 <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
|
1082 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1083 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1084 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1085 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1086 const toolStatus = content.querySelector("[data-converter-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1087 let disposed = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1088 const downloads = new Set(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1089 const discardDownload = url => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1090 if (!url) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1091 downloads.delete(url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1092 void fetch(url, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1093 method: "DELETE", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1094 keepalive: true, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1095 }).then(response => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1096 if (!response.ok && response.status !== 404) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1097 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
|
1098 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1099 }).catch(error => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1100 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
|
1101 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1102 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1103 const convert = async kind => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1104 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
|
1105 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
|
1106 const download = content.querySelector( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1107 `[data-converter-download="${kind}"]`, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1108 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1109 const file = input.files?.[0]; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1110 if (!file) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1111 toolStatus.textContent = `Choose a ${kind} file first.`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1112 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1113 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1114 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1115 button.disabled = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1116 discardDownload(download.dataset.cleanupUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1117 delete download.dataset.cleanupUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1118 download.removeAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1119 download.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1120 toolStatus.textContent = `Converting ${file.name}...`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1121 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1122 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1123 const endpoint = kind === "image" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1124 ? "/api/convert/image-to-webp" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1125 : "/api/convert/video-to-mp4"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1126 const response = await fetch(endpoint, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1127 method: "POST", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1128 body: file, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1129 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
|
1130 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1131 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1132 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
|
1133 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1134 const result = await response.json(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1135 if (disposed || !download.isConnected) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1136 discardDownload(result.download_url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1137 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1138 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1139 download.href = result.download_url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1140 download.dataset.cleanupUrl = result.download_url; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1141 downloads.add(result.download_url); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1142 download.download = file.name.replace(/\.[^/.]+$/, "") + |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1143 (kind === "image" ? ".webp" : ".mp4"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1144 download.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1145 toolStatus.textContent = "Conversion complete."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1146 toolStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1147 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1148 if (!disposed) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1149 toolStatus.textContent = `Conversion failed: ${error.message}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1150 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1151 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1152 } finally { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1153 if (button.isConnected) button.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1154 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1155 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1156 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
|
1157 button.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1158 void convert(button.dataset.convertKind); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1159 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1160 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1161 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
|
1162 download.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1163 downloads.delete(download.dataset.cleanupUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1164 delete download.dataset.cleanupUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1165 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1166 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1167 this._toolCleanup = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1168 disposed = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1169 for (const download of [...downloads]) discardDownload(download); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1170 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1171 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1172 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1173 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1174 async renderHlsTool(content, status, request) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1175 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1176 <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
|
1177 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1178 <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
|
1179 <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
|
1180 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1181 <label for="jrpgHlsUrl">Playlist URL</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1182 <input |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1183 id="jrpgHlsUrl" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1184 data-hls-url |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1185 type="url" |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1186 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
|
1187 > |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1188 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1189 <div class="jrpg-tool-actions"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1190 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1191 <button type="button" data-hls-load> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1192 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
|
1193 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1194 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1195 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1196 <button type="button" data-hls-sample> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1197 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
|
1198 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1199 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1200 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1201 <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
|
1202 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1203 <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
|
1204 <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
|
1205 <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
|
1206 <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
|
1207 <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
|
1208 <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
|
1209 <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
|
1210 <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
|
1211 </dl> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1212 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1213 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1214 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1215 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1216 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1217 await loadScript("/public/hls.min.js"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1218 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
|
1219 if (request !== this._toolRequest || !content.isConnected) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1220 const video = content.querySelector("[data-hls-video]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1221 const playerStatus = content.querySelector("[data-hls-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1222 const details = content.querySelector("[data-hls-details]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1223 const url = content.querySelector("[data-hls-url]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1224 const player = new window.HlsPlayerModule.HlsPlayer(video, { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1225 statusElement: playerStatus, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1226 detailsElement: details, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1227 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1228 const load = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1229 void player.load(url.value.trim()).catch(() => {}); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1230 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1231 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
|
1232 content.querySelector("[data-hls-sample]").addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1233 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
|
1234 load(); |
|
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 playerStatus.textContent = "Ready to load a playlist."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1237 playerStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1238 this._toolCleanup = () => player.destroy(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1239 status.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1240 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1241 if (request !== this._toolRequest) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1242 status.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1243 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
|
1244 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1245 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1246 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1247 renderLatexTool(content, status) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1248 content.innerHTML = ` |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1249 <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
|
1250 <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
|
1251 Edit the source, then compile the PDF. |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1252 </p> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1253 <div class="jrpg-tool-panes"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1254 <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
|
1255 <div class="jrpg-tool-pane-heading"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1256 <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
|
1257 <span data-latex-size></span> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1258 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1259 <zen-field appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1260 <label for="jrpgLatexSource">document.tex</label> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1261 <textarea id="jrpgLatexSource" data-latex-source></textarea> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1262 </zen-field> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1263 <div class="jrpg-tool-actions"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1264 <zen-button appearance="plain" size="md"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1265 <button type="button" data-latex-compile> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1266 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
|
1267 </button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1268 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1269 <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
|
1270 <a download="document.pdf"> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1271 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
|
1272 </a> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1273 </zen-button> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1274 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1275 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1276 <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
|
1277 <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
|
1278 <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
|
1279 <pre data-latex-diagnostics hidden></pre> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1280 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1281 </div> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1282 </section> |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1283 `; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1284 const source = content.querySelector("[data-latex-source]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1285 const compileButton = content.querySelector("[data-latex-compile]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1286 const toolStatus = content.querySelector("[data-latex-status]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1287 const size = content.querySelector("[data-latex-size]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1288 const preview = content.querySelector("[data-latex-preview]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1289 const diagnostics = content.querySelector("[data-latex-diagnostics]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1290 const downloadOwner = content.querySelector("[data-latex-download]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1291 const download = downloadOwner.querySelector("a"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1292 source.value = String.raw`\documentclass[11pt]{article} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1293 \usepackage[margin=1in]{geometry} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1294 \title{JRPG LaTeX} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1295 \author{MrJuneJune} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1296 \begin{document} |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1297 \maketitle |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1298 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
|
1299 \end{document}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1300 let controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1301 let pdfUrl = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1302 let compileGeneration = 0; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1303 const clearPdf = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1304 if (pdfUrl) URL.revokeObjectURL(pdfUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1305 pdfUrl = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1306 preview.src = "about:blank"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1307 preview.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1308 diagnostics.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1309 download.removeAttribute("href"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1310 downloadOwner.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1311 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1312 const updateSize = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1313 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
|
1314 size.textContent = `${bytes.toLocaleString()} / 65,536 bytes`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1315 return bytes; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1316 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1317 const compile = async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1318 const generation = ++compileGeneration; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1319 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1320 const bytes = updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1321 if (!source.value.trim() || bytes > 64 * 1024) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1322 toolStatus.textContent = bytes > 64 * 1024 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1323 ? "Source exceeds 64 KiB." |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1324 : "Write LaTeX before compiling."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1325 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1326 return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1327 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1328 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1329 const requestController = new AbortController(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1330 controller = requestController; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1331 compileButton.disabled = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1332 toolStatus.textContent = "Compiling on the server..."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1333 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1334 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1335 const response = await fetch("/api/latex/render", { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1336 method: "POST", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1337 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
|
1338 body: source.value, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1339 cache: "no-store", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1340 signal: requestController.signal, |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1341 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1342 if (generation !== compileGeneration) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1343 if (!response.ok) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1344 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
|
1345 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1346 const blob = await response.blob(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1347 if (generation !== compileGeneration) return; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1348 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
|
1349 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
|
1350 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1351 if (pdfUrl) URL.revokeObjectURL(pdfUrl); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1352 pdfUrl = URL.createObjectURL(blob); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1353 preview.src = pdfUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1354 preview.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1355 diagnostics.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1356 download.href = pdfUrl; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1357 downloadOwner.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1358 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
|
1359 toolStatus.dataset.state = "ready"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1360 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1361 if (error.name !== "AbortError" && generation === compileGeneration) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1362 diagnostics.textContent = error.message; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1363 diagnostics.hidden = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1364 preview.hidden = true; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1365 toolStatus.textContent = "Compilation failed."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1366 toolStatus.dataset.state = "error"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1367 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1368 } finally { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1369 if (controller === requestController) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1370 controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1371 compileButton.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1372 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1373 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1374 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1375 source.addEventListener("input", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1376 compileGeneration++; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1377 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1378 controller = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1379 compileButton.disabled = false; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1380 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1381 updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1382 toolStatus.textContent = "Changes ready to compile."; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1383 toolStatus.dataset.state = "working"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1384 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1385 compileButton.addEventListener("click", () => void compile()); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1386 updateSize(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1387 this._toolCleanup = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1388 compileGeneration++; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1389 controller?.abort(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1390 clearPdf(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1391 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1392 status.hidden = true; |
|
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 cleanupTool() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1396 this._toolCleanup?.(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1397 this._toolCleanup = null; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1398 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1399 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1400 createReadonlyToolNote() { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1401 const introduction = document.createElement("p"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1402 introduction.className = "jrpg-tool-readonly-note"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1403 introduction.textContent = |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1404 "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
|
1405 return introduction; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
1406 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1407 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1408 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1409 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1410 MjjConversationArchive — archive panel within the scene |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1411 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1412 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1413 class MjjConversationArchive extends HTMLElement { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1414 connectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1415 this._list = this.querySelector("[data-archive-list]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1416 this._status = this.querySelector("[data-archive-status]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1417 this._loadMoreOwner = this.querySelector("[data-archive-load-more-owner]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1418 this._loadMoreButton = this.querySelector("[data-archive-load-more]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1419 this._newButton = this.querySelector("[data-archive-new]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1420 this._closeButton = this.querySelector("[data-archive-close]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1421 this._deleteDialog = this.querySelector("[data-archive-delete-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1422 this._deleteNameEl = this.querySelector("[data-archive-delete-name]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1423 this._deleteCancelButton = this.querySelector("[data-archive-delete-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1424 this._deleteConfirmButton = this.querySelector("[data-archive-delete-confirm]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1425 this._renameDialog = this.querySelector("[data-archive-rename-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1426 this._renameInput = this.querySelector("[data-archive-rename-input]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1427 this._renameForm = this.querySelector("[data-archive-rename-form]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1428 this._renameCancelButton = this.querySelector("[data-archive-rename-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1429 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1430 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1431 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1432 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1433 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1434 this._closeButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1435 this.dispatchEvent(new CustomEvent("mjj-archive-toggle", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1436 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1437 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1438 this._newButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1439 this.dispatchEvent(new CustomEvent("mjj-archive-new", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1440 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1441 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1442 this._loadMoreButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1443 this.dispatchEvent(new CustomEvent("mjj-archive-load-more", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1444 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1445 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1446 this._deleteCancelButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1447 this._deleteDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1448 const t = this._pendingDeleteTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1449 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1450 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1451 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1452 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1453 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1454 this._deleteConfirmButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1455 const id = this._pendingDeleteId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1456 const trigger = this._pendingDeleteTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1457 this._deleteDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1458 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1459 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1460 if (id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1461 this.dispatchEvent(new CustomEvent("mjj-archive-delete", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1462 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1463 detail: { id, trigger }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1464 })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1465 } |
|
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._deleteDialog?.addEventListener("close", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1469 const t = this._pendingDeleteTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1470 this._pendingDeleteId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1471 this._pendingDeleteTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1472 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1473 }); |
|
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 this._renameCancelButton?.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1476 this._renameDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1477 const t = this._pendingRenameTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1478 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1479 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1480 t?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1481 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1482 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1483 this._renameForm?.addEventListener("submit", event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1484 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1485 const id = this._pendingRenameId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1486 const trigger = this._pendingRenameTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1487 const newTitle = this._renameInput?.value.trim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1488 if (!id || !newTitle) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1489 this._renameDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1490 this._pendingRenameId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1491 this._pendingRenameTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1492 this.dispatchEvent(new CustomEvent("mjj-archive-rename", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1493 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1494 detail: { id, newTitle, trigger }, |
|
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 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1498 this._renameDialog?.addEventListener("close", () => { |
|
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._onListClick = event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1506 const openButton = event.target.closest("[data-conv-open]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1507 if (openButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1508 const id = openButton.closest("[data-conv-item]")?._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1509 if (id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1510 this.dispatchEvent(new CustomEvent("mjj-archive-open", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1511 bubbles: true, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1512 detail: { id }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1513 })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1514 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1515 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1516 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1517 const renameButton = event.target.closest("[data-conv-rename]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1518 if (renameButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1519 const item = renameButton.closest("[data-conv-item]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1520 if (item?._convId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1521 this._pendingRenameId = item._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1522 this._pendingRenameTrigger = renameButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1523 if (this._renameInput) this._renameInput.value = item._convTitle || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1524 this._renameDialog?.showModal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1525 requestAnimationFrame(() => { this._renameInput?.select(); }); |
|
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 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1528 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1529 const deleteButton = event.target.closest("[data-conv-delete]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1530 if (deleteButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1531 const item = deleteButton.closest("[data-conv-item]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1532 if (item?._convId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1533 this._pendingDeleteId = item._convId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1534 this._pendingDeleteTrigger = deleteButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1535 if (this._deleteNameEl) this._deleteNameEl.textContent = item._convTitle || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1536 this._deleteDialog?.showModal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1537 requestAnimationFrame(() => { this._deleteConfirmButton?.focus(); }); |
|
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 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1540 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1541 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1542 this._list?.addEventListener("click", this._onListClick); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1543 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1544 this._onListKeydown = event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1545 if (!["ArrowUp", "ArrowDown"].includes(event.key)) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1546 const items = [...(this._list?.querySelectorAll("[data-conv-open]") || [])]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1547 const index = items.indexOf(document.activeElement); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1548 if (index < 0) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1549 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1550 const next = event.key === "ArrowDown" |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1551 ? items[Math.min(index + 1, items.length - 1)] |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1552 : items[Math.max(index - 1, 0)]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1553 next?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1554 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1555 this._list?.addEventListener("keydown", this._onListKeydown); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1556 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1557 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1558 disconnectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1559 this._list?.removeEventListener("click", this._onListClick); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1560 this._list?.removeEventListener("keydown", this._onListKeydown); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1561 } |
|
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 setConversations(conversations, cursor, append = false) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1564 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1565 if (!append) this._list.replaceChildren(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1566 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
|
1567 if (this._loadMoreOwner) this._loadMoreOwner.hidden = !cursor; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1568 if (!conversations.length && !append) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1569 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1570 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1571 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1572 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1573 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1574 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1575 setCurrentId(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1576 for (const button of (this._list?.querySelectorAll("[data-conv-open]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1577 const isCurrent = id && button.closest("[data-conv-item]")?._convId === id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1578 button.setAttribute("aria-current", String(Boolean(isCurrent))); |
|
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 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1581 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1582 addConversation(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1583 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1584 this._list.prepend(this._createItem(conv)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1585 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1586 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1587 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1588 updateConversation(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1589 if (!this._list) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1590 for (const item of this._list.querySelectorAll("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1591 if (item._convId === conv.id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1592 const titleEl = item.querySelector("[data-conv-title]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1593 const metaEl = item.querySelector("[data-conv-meta]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1594 if (titleEl) titleEl.textContent = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1595 if (metaEl) metaEl.textContent = this._formatMeta(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1596 item._convTitle = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1597 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1598 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1599 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1600 this.addConversation(conv); |
|
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 removeConversation(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1604 for (const item of (this._list?.querySelectorAll("[data-conv-item]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1605 if (item._convId === id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1606 item.remove(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1607 break; |
|
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 if (!this._list?.querySelector("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1611 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1612 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1613 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1614 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1615 setLoading(loading) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1616 if (loading) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1617 this._showStatus("Loading conversations..."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1618 } else if (!this._list?.querySelector("[data-conv-item]")) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1619 this._showStatus("No conversations yet. Start a new one!"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1620 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1621 this._hideStatus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1622 } |
|
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 setError(message) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1626 this._showStatus(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1627 if (this._status) this._status.dataset.state = "error"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1628 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1629 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1630 setLoadMoreDisabled(disabled) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1631 if (this._loadMoreButton) this._loadMoreButton.disabled = disabled; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1632 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1633 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1634 /* Disable/enable destructive archive controls while a stream is active. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1635 setStreamActive(active) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1636 const controls = this._list?.querySelectorAll( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1637 "[data-conv-delete], [data-conv-rename]", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1638 ) || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1639 for (const el of controls) el.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1640 if (this._newButton) this._newButton.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1641 if (this._deleteConfirmButton) this._deleteConfirmButton.disabled = active; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1642 this.dataset.streamActive = active ? "true" : ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1643 if (!active) delete this.dataset.streamActive; |
|
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 /* 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
|
1647 adjacentItemOrNewButton(id) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1648 const items = [...(this._list?.querySelectorAll("[data-conv-item]") || [])]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1649 const idx = items.findIndex(el => el._convId === id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1650 if (idx < 0) return this._newButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1651 const sibling = items[idx + 1] || items[idx - 1]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1652 return sibling?.querySelector("[data-conv-open]") || this._newButton; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1653 } |
|
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 showLegacyClaim(statusMessage = null, showButton = true) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1656 if (!this._status) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1657 this._status.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1658 delete this._status.dataset.state; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1659 this._status.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1660 const msg = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1661 msg.textContent = statusMessage ?? "You have a prior conversation. "; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1662 this._status.append(msg); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1663 if (showButton) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1664 const btn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1665 btn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1666 btn.textContent = "Claim it"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1667 btn.dataset.archiveClaim = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1668 btn.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1669 this.dispatchEvent(new CustomEvent("mjj-archive-claim", { bubbles: true })); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1670 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1671 this._status.append(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1672 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1673 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1674 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1675 _showStatus(text) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1676 if (this._status) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1677 this._status.textContent = text; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1678 this._status.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1679 delete this._status.dataset.state; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1680 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1681 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1682 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1683 _hideStatus() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1684 if (this._status) this._status.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1685 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1686 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1687 _createItem(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1688 const item = document.createElement("li"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1689 item.dataset.convItem = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1690 item._convId = conv.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1691 item._convTitle = conv.title; |
|
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 const openButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1694 openButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1695 openButton.dataset.convOpen = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1696 openButton.setAttribute("aria-current", "false"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1697 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1698 const titleEl = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1699 titleEl.dataset.convTitle = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1700 titleEl.textContent = conv.title; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1701 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1702 const metaEl = document.createElement("small"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1703 metaEl.dataset.convMeta = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1704 metaEl.textContent = this._formatMeta(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1705 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1706 openButton.append(titleEl, metaEl); |
|
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 const actions = document.createElement("div"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1709 actions.className = "jrpg-archive-item-actions"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1710 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1711 const renameButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1712 renameButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1713 renameButton.dataset.convRename = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1714 renameButton.setAttribute("aria-label", `Rename "${conv.title}"`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1715 renameButton.textContent = "Rename"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1716 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1717 const deleteButton = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1718 deleteButton.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1719 deleteButton.dataset.convDelete = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1720 deleteButton.setAttribute("aria-label", `Delete "${conv.title}"`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1721 deleteButton.textContent = "Delete"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1722 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1723 actions.append(renameButton, deleteButton); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1724 item.append(openButton, actions); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1725 return item; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1726 } |
|
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 _formatMeta(conv) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1729 const count = conv.turn_count ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1730 const turns = count === 1 ? "1 turn" : `${count} turns`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1731 if (conv.last_message_preview) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1732 return `${turns} · ${conv.last_message_preview.slice(0, 28)}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1733 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1734 return turns; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1735 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1736 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1737 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1738 class MjjJrpgShell extends HTMLElement { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1739 connectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1740 this._mobileMenuQuery = window.matchMedia( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1741 "(max-width: 52rem) and (orientation: portrait)", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1742 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1743 this._syncMobileMenu = expanded => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1744 const button = this.querySelector("[data-mobile-menu-toggle]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1745 const menu = this.querySelector("mjj-jrpg-menu"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1746 if (!button || !menu) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1747 if (!this._mobileMenuQuery.matches) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1748 button.setAttribute("aria-expanded", "true"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1749 menu.removeAttribute("data-mobile-menu-collapsed"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1750 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1751 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1752 button.setAttribute("aria-expanded", String(expanded)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1753 menu.toggleAttribute("data-mobile-menu-collapsed", !expanded); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1754 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1755 this._onMenuToggle = event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1756 const button = event.target.closest("[data-mobile-menu-toggle]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1757 if (!button || !this.contains(button)) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1758 const expanded = button.getAttribute("aria-expanded") === "true"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1759 this._syncMobileMenu(!expanded); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1760 if (!expanded) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1761 const firstItem = this.querySelector( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1762 "mjj-jrpg-menu button[data-preview]", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1763 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1764 if (firstItem) firstItem.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1765 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1766 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1767 this._onMobileMenuMediaChange = () => this._syncMobileMenu(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1768 this.addEventListener("click", this._onMenuToggle); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1769 this._mobileMenuQuery.addEventListener( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1770 "change", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1771 this._onMobileMenuMediaChange, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1772 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1773 this._syncMobileMenu(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1774 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1775 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1776 disconnectedCallback() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1777 this.removeEventListener("click", this._onMenuToggle); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1778 this._mobileMenuQuery?.removeEventListener( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1779 "change", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1780 this._onMobileMenuMediaChange, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1781 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1782 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1783 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1784 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1785 for (const [name, constructor] of Object.entries({ |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1786 "mjj-conversation-archive": MjjConversationArchive, |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1787 "mjj-jrpg-character": MjjJrpgCharacter, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1788 "mjj-jrpg-chat": MjjJrpgChat, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1789 "mjj-jrpg-composer": MjjJrpgComposer, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1790 "mjj-jrpg-menu": MjjJrpgMenu, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1791 "mjj-jrpg-preview": MjjJrpgPreview, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1792 "mjj-jrpg-shell": MjjJrpgShell, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1793 })) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1794 if (!customElements.get(name)) customElements.define(name, constructor); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1795 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1796 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1797 async function requestJson(url, options = {}) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1798 const response = await fetch(url, { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1799 ...options, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1800 credentials: "same-origin", |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1801 headers: { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1802 "Content-Type": "application/json", |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1803 ...options.headers, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1804 }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1805 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1806 if (!response.ok) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1807 let message = `Request failed (${response.status})`; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1808 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1809 const body = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1810 message = body.error?.message || message; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1811 } catch { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1812 // Preserve the status-derived message. |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1813 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1814 throw new Error(message); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1815 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1816 return response.status === 204 ? null : response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1817 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1818 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1819 /* 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
|
1820 let _csrfToken = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1821 let _sessionData = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1822 let _sessionRefreshInFlight = null; /* single-flight guard */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1823 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1824 function _principalFingerprint(data) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1825 if (!data) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1826 if (data.kind === "user") return `user:${data.username || ""}:${data.role || ""}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1827 if (data.kind === "guest") return `guest:${data.csrfToken || ""}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1828 return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1829 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1830 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1831 async function _fetchSession() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1832 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1833 const response = await fetch("/api/auth/session", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1834 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1835 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1836 if (!response.ok) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1837 const data = await response.json().catch(() => null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1838 if (!data) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1839 _sessionData = data; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1840 return data.csrfToken || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1841 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1842 return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1843 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1844 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1845 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1846 /* Single-flight session refresh: dedupe concurrent callers. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1847 async function _refreshSessionOnce() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1848 if (_sessionRefreshInFlight) return _sessionRefreshInFlight; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1849 _sessionRefreshInFlight = (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1850 const prev = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1851 const token = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1852 const next = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1853 return { token, prev, next }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1854 })(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1855 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1856 return await _sessionRefreshInFlight; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1857 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1858 _sessionRefreshInFlight = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1859 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1860 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1861 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1862 /* |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1863 * 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
|
1864 * 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
|
1865 * 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
|
1866 * 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
|
1867 * triggers the session-changed callback. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1868 */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1869 let _sessionRefreshCallback = null; |
|
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 async function mutatingFetch(url, options = {}) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1872 const buildHeaders = () => ({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1873 ...(options.headers || {}), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1874 Origin: window.location.origin, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1875 ...(_csrfToken ? { "X-CSRF-Token": _csrfToken } : {}), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1876 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1877 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1878 const response = await fetch(url, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1879 ...options, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1880 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1881 headers: buildHeaders(), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1882 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1883 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1884 const shouldRefresh = response.status === 401 || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1885 (response.status === 403 && await (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1886 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1887 const clone = response.clone(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1888 const body = await clone.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1889 return body?.error?.code === "csrf_invalid"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1890 } catch { return false; } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1891 })()); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1892 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1893 if (shouldRefresh) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1894 const prevFingerprint = _principalFingerprint(_sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1895 const { token, next } = await _refreshSessionOnce(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1896 _csrfToken = token; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1897 if (next !== prevFingerprint) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1898 if (_sessionRefreshCallback) _sessionRefreshCallback(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1899 const err = Object.assign(new Error("Session changed"), { status: 401 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1900 throw err; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1901 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1902 const retried = await fetch(url, { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1903 ...options, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1904 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1905 headers: buildHeaders(), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1906 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1907 if (retried.status === 401 && _sessionRefreshCallback) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1908 _sessionRefreshCallback(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1909 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1910 return retried; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1911 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1912 return response; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1913 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1914 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1915 async function consumeSse(response, onEvent) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1916 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
|
1917 const reader = response.body.getReader(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1918 const decoder = new TextDecoder(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1919 let buffer = ""; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1920 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1921 const dispatch = block => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1922 let eventName = "message"; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1923 const data = []; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1924 for (const line of block.split("\n")) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1925 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
|
1926 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
|
1927 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1928 if (!data.length) return; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1929 onEvent(eventName, JSON.parse(data.join("\n"))); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1930 }; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1931 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1932 while (true) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1933 const { done, value } = await reader.read(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1934 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
|
1935 buffer = buffer.replaceAll("\r\n", "\n"); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1936 let boundary; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1937 while ((boundary = buffer.indexOf("\n\n")) >= 0) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1938 const block = buffer.slice(0, boundary); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1939 buffer = buffer.slice(boundary + 2); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1940 if (block.trim()) dispatch(block); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1941 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1942 if (done) break; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1943 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1944 if (buffer.trim()) dispatch(buffer); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1945 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
1946 |
|
264
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 Account slot rendering |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1949 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1950 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1951 function renderAccountSlot(slotEl, sessionData) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1952 if (!slotEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1953 slotEl.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1954 if (!sessionData || sessionData.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1955 const owner = document.createElement("zen-button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1956 const btn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1957 owner.setAttribute("appearance", "plain"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1958 owner.setAttribute("size", "xs"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1959 btn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1960 btn.dataset.loginOpen = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1961 btn.textContent = "LOGIN"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1962 owner.append(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1963 slotEl.setAttribute("aria-label", "Not signed in"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1964 slotEl.append(owner); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1965 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1966 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1967 const username = (sessionData.username || "").slice(0, 8).toUpperCase(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1968 if (sessionData.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1969 const link = document.createElement("a"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1970 link.href = "/account/password"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1971 link.textContent = username ? `${username}\u26A0` : "PASSWD"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1972 link.title = "Password change required"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1973 slotEl.setAttribute("aria-label", `${sessionData.username}: password change required`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1974 slotEl.append(link); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1975 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1976 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1977 const nameSpan = document.createElement("span"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1978 nameSpan.dataset.accountUser = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1979 nameSpan.textContent = username; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1980 slotEl.append(nameSpan); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1981 if (sessionData.role === "admin") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1982 const adminLink = document.createElement("a"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1983 adminLink.href = "/admin/users"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1984 adminLink.textContent = "\u00A0ADM"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1985 slotEl.append(adminLink); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1986 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1987 const logoutBtn = document.createElement("button"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1988 logoutBtn.type = "button"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1989 logoutBtn.textContent = "\u00A0LGOUT"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1990 logoutBtn.addEventListener("click", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1991 logoutBtn.disabled = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1992 mutatingFetch("/api/auth/logout", { method: "POST" }).then(response => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1993 if (!response.ok) throw new Error(`Logout failed (${response.status})`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1994 window.dispatchEvent(new CustomEvent("mjj-logout")); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1995 }).catch(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1996 logoutBtn.disabled = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1997 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1998 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
1999 slotEl.append(logoutBtn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2000 slotEl.setAttribute("aria-label", `Signed in as ${sessionData.username}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2001 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2002 |
|
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 Quota display helpers |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2005 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2006 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2007 function formatQuotaText(quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2008 if (!quota) return null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2009 const turns = quota.turnsRemaining ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2010 const tokens = quota.outputTokensRemaining ?? 0; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2011 if (turns <= 0) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2012 const resetAt = quota.resetsAt ? new Date(quota.resetsAt * 1000) : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2013 const resetStr = resetAt |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2014 ? ` Resets ${resetAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}` |
|
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 return { text: `Quota exhausted.${resetStr}`, state: "exhausted" }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2017 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2018 if (turns <= 2) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2019 return { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2020 text: `${turns} turn${turns === 1 ? "" : "s"} remaining (${tokens.toLocaleString()} tokens)`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2021 state: "warning", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2022 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2023 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2024 return { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2025 text: `${turns} turns remaining (${tokens.toLocaleString()} tokens)`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2026 state: "ok", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2027 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2028 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2029 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2030 function updateQuotaDisplay(quotaEl, quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2031 if (!quotaEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2032 if (!quota) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2033 quotaEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2034 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2035 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2036 const result = formatQuotaText(quota); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2037 /* 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
|
2038 if (!result || result.state === "ok") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2039 quotaEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2040 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2041 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2042 quotaEl.textContent = result.text; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2043 if (result.state === "exhausted") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2044 quotaEl.dataset.state = "exhausted"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2045 } else if (result.state === "warning") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2046 quotaEl.dataset.state = "warning"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2047 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2048 delete quotaEl.dataset.state; |
|
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 quotaEl.hidden = false; |
|
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 /* ===================================================================== |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2054 Conversation archive helpers |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2055 ===================================================================== */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2056 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2057 async function fetchConversationList(cursor) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2058 const url = cursor |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2059 ? `/api/conversations?limit=${ARCHIVE_PAGE_SIZE}&cursor=${encodeURIComponent(cursor)}` |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2060 : `/api/conversations?limit=${ARCHIVE_PAGE_SIZE}`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2061 const response = await fetch(url, { credentials: "same-origin" }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2062 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2063 let message = `Failed to load conversations (${response.status})`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2064 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2065 const body = await response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2066 message = body.error?.message || message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2067 } catch { /* keep status message */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2068 throw new Error(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2069 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2070 return response.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2071 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2072 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2073 async function initializeJrpgPage() { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2074 const colorProbe = document.createElement("span"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2075 colorProbe.style.background = "var(--zenbu-sys-color-surface-page)"; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2076 document.body.append(colorProbe); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2077 const themeColor = getComputedStyle(colorProbe).backgroundColor; |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2078 colorProbe.remove(); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2079 for (const meta of document.querySelectorAll('meta[name="theme-color"]')) { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2080 meta.setAttribute("content", themeColor); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2081 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2082 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2083 /* Bootstrap session to obtain CSRF token and session data */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2084 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2085 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2086 const shell = document.querySelector("mjj-jrpg-shell"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2087 const character = shell?.querySelector("mjj-jrpg-character"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2088 const chat = shell?.querySelector("mjj-jrpg-chat"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2089 const composer = shell?.querySelector("mjj-jrpg-composer"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2090 const preview = shell?.querySelector("mjj-jrpg-preview"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2091 const archive = shell?.querySelector("mjj-conversation-archive"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2092 const accountSlot = shell?.querySelector("[data-frame-account]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2093 const quotaEl = shell?.querySelector("[data-quota]"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2094 const frameNetwork = shell?.querySelector("[data-frame-network]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2095 const frameUptime = shell?.querySelector("[data-frame-uptime]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2096 const minimizeButton = shell?.querySelector("[data-frame-minimize]"); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2097 const fullscreenButton = shell?.querySelector("[data-frame-fullscreen]"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2098 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2099 let characterTimer = 0; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2100 let currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2101 let currentPanel = "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2102 let archiveCursor = null; |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2103 let activeController = null; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2104 let archiveLoadInFlight = false; /* serialize pagination requests */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2105 let openSeq = 0; /* sequence guard for conversation open */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2106 let popSeq = 0; /* sequence guard for popstate */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2107 let _pendingPopState = null; /* queued popstate received during active stream */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2108 let _claimInFlight = false; /* guard against duplicate concurrent claims */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2109 let legacyClaimId = sessionStorage.getItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2110 let principalEpoch = 0; /* rejects responses from a prior owner */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2111 /* Dedupe conversation IDs: set of IDs currently rendered in archive */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2112 const _renderedIds = new Set(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2113 const invalidatePrincipalRequests = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2114 principalEpoch += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2115 openSeq += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2116 popSeq += 1; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2117 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2118 }; |
|
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 /* Disable composer until fully initialized */ |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2121 composer?.setDisabled(true); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2122 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2123 const setFrameStatus = (label, state, title = "") => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2124 if (frameNetwork) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2125 frameNetwork.textContent = label; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2126 frameNetwork.setAttribute("aria-label", `Status ${label.toLowerCase()}`); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2127 frameNetwork.dataset.state = state; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2128 frameNetwork.title = title; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2129 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2130 }; |
|
264
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 /* Bootstrap failure: keep controls disabled and expose retry */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2133 if (!_csrfToken) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2134 setFrameStatus("OFFLINE", "offline", "Bootstrap failed"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2135 archive?.setError("Connection failed. Reload to retry."); |
|
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 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2139 setFrameStatus("ONLINE", "online"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2140 |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2141 const startedAt = Date.now(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2142 const updateUptime = () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2143 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
|
2144 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
|
2145 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
|
2146 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
|
2147 if (frameUptime) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2148 frameUptime.textContent = `${hours}:${minutes}:${seconds}`; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2149 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2150 }; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2151 updateUptime(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2152 window.setInterval(updateUptime, 1000); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2153 |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2154 minimizeButton?.addEventListener("click", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2155 const minimized = shell.dataset.minimized !== "true"; |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2156 shell.dataset.minimized = String(minimized); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2157 minimizeButton.setAttribute("aria-pressed", String(minimized)); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2158 minimizeButton.setAttribute( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2159 "aria-label", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2160 minimized ? "Restore interface" : "Minimize interface", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2161 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2162 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2163 fullscreenButton?.addEventListener("click", async () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2164 try { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2165 if (document.fullscreenElement) await document.exitFullscreen(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2166 else await shell.requestFullscreen(); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2167 } catch (error) { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2168 setFrameStatus("DENIED", "offline", error.message); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2169 } |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2170 }); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2171 document.addEventListener("fullscreenchange", () => { |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2172 fullscreenButton?.setAttribute( |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2173 "aria-pressed", |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2174 String(Boolean(document.fullscreenElement)), |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2175 ); |
|
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
2176 }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2177 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2178 /* Render account slot */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2179 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2180 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2181 /* ---- Login modal ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2182 const loginDialogOwner = shell?.querySelector("[data-login-dialog-owner]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2183 const loginDialog = loginDialogOwner?.querySelector("[data-login-dialog]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2184 const loginForm = loginDialog?.querySelector("[data-login-form]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2185 const loginSubmitBtn = loginDialog?.querySelector("[data-login-submit]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2186 const loginCancelBtn = loginDialog?.querySelector("[data-login-cancel]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2187 const loginErrorEl = loginDialog?.querySelector("[data-login-error]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2188 const loginUsernameInput = loginDialog?.querySelector("#jrpg-login-username"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2189 const loginPasswordInput = loginDialog?.querySelector("#jrpg-login-password"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2190 let _loginTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2191 const setAccountActionsDisabled = disabled => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2192 for (const control of accountSlot?.querySelectorAll("button") || []) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2193 control.disabled = disabled; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2194 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2195 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2196 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2197 const _clearLoginError = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2198 if (loginErrorEl) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2199 loginErrorEl.textContent = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2200 loginErrorEl.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2201 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2202 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2203 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2204 const _showLoginError = (message) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2205 if (!loginErrorEl) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2206 loginErrorEl.textContent = message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2207 loginErrorEl.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2208 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2209 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2210 const openLoginModal = (triggerEl) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2211 if (!loginDialogOwner || !loginDialog) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2212 _loginTrigger = triggerEl || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2213 _clearLoginError(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2214 loginForm?.reset(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2215 loginDialogOwner.open(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2216 requestAnimationFrame(() => { loginUsernameInput?.focus(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2217 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2218 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2219 /* Delegate clicks on data-login-open in the account slot */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2220 accountSlot?.addEventListener("click", event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2221 const btn = event.target.closest("[data-login-open]"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2222 if (!btn || activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2223 openLoginModal(btn); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2224 }); |
|
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 /* Cancel button */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2227 loginCancelBtn?.addEventListener("click", () => { loginDialog?.close(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2228 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2229 /* Cleanup on any close (Escape, X button, cancel) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2230 loginDialog?.addEventListener("close", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2231 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2232 _clearLoginError(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2233 const trigger = _loginTrigger; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2234 _loginTrigger = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2235 requestAnimationFrame(() => { trigger?.focus(); }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2236 }); |
|
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 /* Login form submission */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2239 loginForm?.addEventListener("submit", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2240 event.preventDefault(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2241 if (loginSubmitBtn?.disabled) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2242 const username = loginUsernameInput?.value.trim() || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2243 const password = loginPasswordInput?.value || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2244 if (!username || !password) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2245 _showLoginError("Please enter your username and password."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2246 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2247 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2248 if (loginSubmitBtn) loginSubmitBtn.disabled = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2249 _clearLoginError(); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2250 try { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2251 const response = await fetch("/api/auth/login", { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2252 method: "POST", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2253 headers: { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2254 "Content-Type": "application/json", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2255 "Origin": window.location.origin, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2256 }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2257 credentials: "same-origin", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2258 body: JSON.stringify({ username, password, csrfToken: _csrfToken }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2259 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2260 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2261 if (response.status === 429) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2262 _showLoginError("Too many attempts. Please try again later."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2263 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2264 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2265 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2266 _showLoginError("Invalid username or password."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2267 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2268 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2269 let data = await response.json().catch(() => null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2270 if (!data) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2271 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2272 if (_sessionData?.kind !== "user") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2273 loginDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2274 window.location.reload(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2275 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2276 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2277 data = _sessionData; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2278 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2279 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2280 loginDialog?.close(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2281 _sessionData = { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2282 kind: "user", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2283 username: data.username || username, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2284 role: data.role || "member", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2285 mustChangePassword: Boolean(data.mustChangePassword), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2286 csrfToken: data.csrfToken, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2287 quota: null, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2288 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2289 _csrfToken = data.csrfToken || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2290 if (data.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2291 window.location.href = "/account/password"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2292 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2293 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2294 /* Commit the returned principal before optional UI refresh work. */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2295 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2296 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2297 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2298 /* Clear conversation state — guest convs may transfer to user */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2299 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2300 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2301 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2302 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2303 _replaceUrlState(currentPanel, null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2304 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2305 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2306 await refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2307 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2308 archive?.setError(`Signed in, but archive refresh failed: ${error.message}`); |
|
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 requestAnimationFrame(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2311 accountSlot?.querySelector("button, a")?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2312 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2313 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2314 if (loginPasswordInput) loginPasswordInput.value = ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2315 _showLoginError("Sign-in failed. Please try again."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2316 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2317 if (loginSubmitBtn) loginSubmitBtn.disabled = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2318 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2319 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2320 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2321 /* Handle logout */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2322 _sessionRefreshCallback = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2323 /* Account changed or session lost — clear active state */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2324 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2325 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2326 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2327 activeController = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2328 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2329 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2330 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2331 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2332 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2333 _replaceUrlState(currentPanel, null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2334 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2335 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2336 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2337 archive?.setConversations([], null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2338 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2339 archive?.setError("Session changed. Loading conversations..."); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2340 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2341 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2342 window.addEventListener("mjj-logout", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2343 invalidatePrincipalRequests(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2344 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2345 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2346 activeController = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2347 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2348 archive?.setStreamActive(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2349 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2350 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2351 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2352 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2353 _replaceUrlState(currentPanel, null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2354 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2355 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2356 archiveCursor = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2357 archive?.setConversations([], null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2358 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2359 void (async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2360 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2361 renderAccountSlot(accountSlot, _sessionData); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2362 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2363 if (_sessionData?.kind === "user" && _sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2364 composer?.setDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2365 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2366 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2367 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2368 })(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2369 }); |
|
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 /* Session quota display */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2372 if (_sessionData?.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2373 updateQuotaDisplay(quotaEl, _sessionData.quota || null); |
|
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 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2376 /* Disable composer for forced-password-change */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2377 const isForced = _sessionData?.kind === "user" && _sessionData?.mustChangePassword; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2378 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2379 /* ---- URL state helpers ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2380 const _buildStateUrl = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2381 const url = new URL(window.location.href); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2382 if (panel && VALID_PANELS.includes(panel)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2383 url.searchParams.set("panel", panel); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2384 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2385 url.searchParams.delete("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2386 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2387 if (convId && UUID_REGEX.test(convId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2388 url.searchParams.set("conversation", convId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2389 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2390 url.searchParams.delete("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2391 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2392 return url; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2393 }; |
|
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 const _pushUrlState = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2396 history.pushState({ panel, conversation: convId || null }, "", _buildStateUrl(panel, convId)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2397 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2398 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2399 const _replaceUrlState = (panel, convId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2400 history.replaceState({ panel, conversation: convId || null }, "", _buildStateUrl(panel, convId)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2401 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2402 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2403 /* ---- Panel selection: shows archive or preview in utility ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2404 const utilityEl = shell?.querySelector(".jrpg-utility"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2405 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2406 const selectPanel = (panel, pushHistory = true) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2407 const validPanel = VALID_PANELS.includes(panel) ? panel : "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2408 currentPanel = validPanel; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2409 for (const btn of (shell?.querySelectorAll("button[data-preview]") || [])) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2410 btn.setAttribute("aria-pressed", String(btn.dataset.preview === validPanel)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2411 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2412 if (validPanel === "conversations") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2413 if (archive) archive.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2414 if (preview) preview.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2415 if (utilityEl) utilityEl.setAttribute("aria-label", "Conversations"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2416 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2417 if (archive) archive.hidden = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2418 if (preview) preview.hidden = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2419 preview?.show(validPanel); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2420 if (utilityEl) utilityEl.setAttribute("aria-label", "Selected destination"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2421 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2422 if (pushHistory) _pushUrlState(validPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2423 }; |
|
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 /* ---- Archive close button: navigate back to resume panel ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2426 shell?.addEventListener("mjj-archive-toggle", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2427 selectPanel("resume"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2428 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2429 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2430 shell?.addEventListener("mjj-archive-new", () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2431 if (activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2432 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2433 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2434 _pushUrlState(currentPanel, null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2435 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2436 archive?.setCurrentId(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2437 composer?.querySelector("textarea")?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2438 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2439 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2440 /* ---- Popstate: re-apply panel and conversation on back/forward ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2441 /* _applyPopState always increments openSeq so any pending archive-open |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2442 fetch (including a transition to no conversation) cannot overwrite the |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2443 state the user navigated to (Fix 2). */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2444 const _applyPopState = async (seq, popPanel, popConvId) => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2445 const localSeq = ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2446 const epoch = principalEpoch; |
|
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 selectPanel(popPanel, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2449 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2450 if (popConvId && popConvId !== currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2451 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2452 const conversation = await requestJson(`/api/conversations/${encodeURIComponent(popConvId)}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2453 if (seq !== popSeq || localSeq !== openSeq || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2454 epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2455 currentConversationId = popConvId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2456 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2457 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2458 archive?.setCurrentId(popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2459 } catch { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2460 if (seq !== popSeq || localSeq !== openSeq || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2461 epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2462 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2463 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2464 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2465 archive?.setCurrentId(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2466 history.replaceState( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2467 { panel: popPanel, conversation: null }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2468 "", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2469 _buildStateUrl(popPanel, null), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2470 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2471 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2472 } else if (!popConvId && currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2473 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2474 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2475 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2476 archive?.setCurrentId(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2477 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2478 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2479 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2480 /* 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
|
2481 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
|
2482 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
|
2483 window.addEventListener("popstate", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2484 const seq = ++popSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2485 ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2486 const state = event.state || {}; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2487 const popPanel = VALID_PANELS.includes(state.panel) ? state.panel : "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2488 const popConvId = (state.conversation && UUID_REGEX.test(state.conversation)) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2489 ? state.conversation : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2490 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2491 if (activeController) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2492 _pendingPopState = { seq, popPanel, popConvId }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2493 activeController.abort(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2494 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2495 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2496 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2497 await _applyPopState(seq, popPanel, popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2498 }); |
|
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 shell?.addEventListener("mjj-archive-rename", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2501 const { id, newTitle, trigger } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2502 if (activeController) { trigger?.focus(); return; } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2503 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2504 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2505 `/api/conversations/${encodeURIComponent(id)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2506 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2507 method: "PATCH", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2508 headers: { "Content-Type": "application/json" }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2509 body: JSON.stringify({ title: newTitle }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2510 }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2511 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2512 if (!response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2513 const body = await response.json().catch(() => ({})); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2514 throw new Error(body.error?.message || `Rename failed (${response.status})`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2515 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2516 archive?.updateConversation({ id, title: newTitle, turn_count: null }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2517 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2518 archive?.setError(`Rename failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2519 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2520 trigger?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2521 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2522 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2523 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2524 shell?.addEventListener("mjj-archive-open", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2525 const { id } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2526 if (id === currentConversationId) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2527 if (activeController) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2528 const seq = ++openSeq; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2529 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2530 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2531 const conversation = await requestJson(`/api/conversations/${encodeURIComponent(id)}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2532 if (seq !== openSeq || epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2533 currentConversationId = id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2534 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2535 _pushUrlState(currentPanel, id); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2536 chat?.replaceMessages(conversation.turns); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2537 archive?.setCurrentId(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2538 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2539 if (seq !== openSeq || epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2540 archive?.setError(`Unable to open: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2541 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2542 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2543 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2544 shell?.addEventListener("mjj-archive-delete", async event => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2545 const { id, trigger } = event.detail; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2546 if (activeController) { trigger?.focus(); return; } /* block while streaming */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2547 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2548 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2549 `/api/conversations/${encodeURIComponent(id)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2550 { method: "DELETE" }, |
|
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 if (response.ok || response.status === 404) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2553 /* 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
|
2554 const focusTarget = archive?.adjacentItemOrNewButton(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2555 _renderedIds.delete(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2556 archive?.removeConversation(id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2557 if (id === currentConversationId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2558 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2559 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2560 _replaceUrlState(currentPanel, null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2561 chat?.replaceMessages([]); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2562 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2563 /* Focus surviving element — never the detached trigger */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2564 requestAnimationFrame(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2565 (focusTarget || archive?.querySelector("[data-archive-new]"))?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2566 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2567 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2568 const body = await response.json().catch(() => ({})); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2569 archive?.setError(`Delete failed: ${body.error?.message || response.status}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2570 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2571 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2572 archive?.setError(`Delete failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2573 /* On error, the trigger is still attached — focus it */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2574 trigger?.focus(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2575 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2576 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2577 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2578 shell?.addEventListener("mjj-archive-load-more", async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2579 if (!archiveCursor || archiveLoadInFlight) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2580 archiveLoadInFlight = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2581 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2582 archive?.setLoadMoreDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2583 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2584 const data = await fetchConversationList(archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2585 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2586 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2587 const newConvs = (data.conversations || []).filter(c => !_renderedIds.has(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2588 newConvs.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2589 archive?.setConversations(newConvs, archiveCursor, true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2590 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2591 archive?.setError(`Load failed: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2592 } finally { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2593 archiveLoadInFlight = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2594 archive?.setLoadMoreDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2595 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2596 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2597 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2598 shell?.addEventListener("mjj-archive-claim", async () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2599 const legacyId = |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2600 legacyClaimId || sessionStorage.getItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2601 if (!legacyId || _sessionData?.kind !== "user" || _claimInFlight) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2602 _claimInFlight = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2603 archive?.showLegacyClaim("Claiming prior conversation...", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2604 let _claimOutcome = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2605 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2606 const response = await mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2607 "/api/conversations/claim", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2608 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2609 method: "POST", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2610 headers: { "Content-Type": "application/json" }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2611 body: JSON.stringify({ conversationId: legacyId }), |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2612 }, |
|
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 if (response.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2615 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2616 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2617 _claimOutcome = "success"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2618 } else if (response.status === 409) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2619 /* Definitive non-legacy conflict: clear key, no retry needed */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2620 sessionStorage.removeItem(CONVERSATION_LEGACY_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2621 legacyClaimId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2622 _claimOutcome = "conflict"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2623 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2624 /* Retryable (401, 403, 429, 5xx): retain key */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2625 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, legacyId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2626 legacyClaimId = legacyId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2627 _claimOutcome = "retry"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2628 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2629 } catch { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2630 /* Network error: retain key */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2631 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, legacyId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2632 legacyClaimId = legacyId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2633 _claimOutcome = "network-error"; |
|
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 /* 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
|
2636 _claimInFlight = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2637 if (_claimOutcome === "success") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2638 void refreshArchiveFromScratch(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2639 } else if (_claimOutcome === "conflict") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2640 archive?.showLegacyClaim("Prior conversation already claimed.", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2641 } else if (_claimOutcome === "retry") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2642 selectPanel("conversations", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2643 archive?.showLegacyClaim("Claim failed. Try again. "); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2644 } else if (_claimOutcome === "network-error") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2645 selectPanel("conversations", false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2646 archive?.showLegacyClaim("Claim failed. Check connection. "); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2647 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2648 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2649 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2650 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2651 async function refreshArchiveFromScratch() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2652 if (!archive) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2653 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2654 archive.setLoading(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2655 _renderedIds.clear(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2656 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2657 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2658 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2659 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2660 const convs = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2661 convs.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2662 archive.setConversations(convs, archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2663 archive.setCurrentId(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2664 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2665 archive.setError(`Archive error: ${error.message}`); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2666 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2667 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2668 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2669 async function refreshArchiveHead() { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2670 if (!archive) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2671 const epoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2672 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2673 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2674 if (epoch !== principalEpoch) return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2675 const fresh = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2676 for (const conv of fresh) archive.updateConversation(conv); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2677 if (fresh.length > 0) archive.setCurrentId(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2678 } catch { /* silent refresh failure */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2679 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2680 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2681 /* ---- Parse and validate URL params at startup ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2682 /* Track whether ?conversation was explicitly present (even if malformed) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2683 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
|
2684 let _convParamWasExplicit = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2685 { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2686 const initParams = new URLSearchParams(window.location.search); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2687 const rawPanel = initParams.get("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2688 const rawConvId = initParams.get("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2689 _convParamWasExplicit = rawConvId !== null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2690 const validPanel = VALID_PANELS.includes(rawPanel) ? rawPanel : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2691 const validConvId = (rawConvId && UUID_REGEX.test(rawConvId)) ? rawConvId : null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2692 /* Remove invalid params with replaceState */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2693 if ((rawPanel && !validPanel) || (rawConvId && !validConvId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2694 const normalUrl = new URL(window.location.href); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2695 if (!validPanel) normalUrl.searchParams.delete("panel"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2696 if (!validConvId) normalUrl.searchParams.delete("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2697 history.replaceState( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2698 { panel: validPanel, conversation: validConvId }, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2699 "", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2700 normalUrl, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2701 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2702 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2703 currentPanel = validPanel || "resume"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2704 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2705 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2706 /* ---- Load archive and restore conversation from URL or sessionStorage ---- */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2707 const startupEpoch = principalEpoch; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2708 const ensureStartupPrincipal = () => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2709 if (startupEpoch !== principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2710 const error = new Error("Principal changed during startup"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2711 error.name = "StalePrincipalError"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2712 throw error; |
|
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 }; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2715 archive?.setLoading(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2716 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2717 const data = await fetchConversationList(null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2718 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2719 archiveCursor = data.cursor || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2720 const conversations = data.conversations || []; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2721 conversations.forEach(c => _renderedIds.add(c.id)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2722 archive?.setConversations(conversations, archiveCursor); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2723 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2724 /* Determine target conversation: URL param is canonical; migrate sessionStorage once */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2725 const initParams2 = new URLSearchParams(window.location.search); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2726 const urlConvId = initParams2.get("conversation"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2727 const urlConvValid = urlConvId && UUID_REGEX.test(urlConvId) ? urlConvId : null; |
|
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 let resolvedConvId = urlConvValid; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2730 let migratedFromStorage = false; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2731 if (!resolvedConvId && !_convParamWasExplicit) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2732 const stored = sessionStorage.getItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2733 if (stored && UUID_REGEX.test(stored)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2734 resolvedConvId = stored; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2735 migratedFromStorage = true; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2736 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2737 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2738 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2739 if (resolvedConvId) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2740 const inArchive = conversations.find(c => c.id === resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2741 if (inArchive) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2742 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2743 const conversation = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2744 `/api/conversations/${encodeURIComponent(resolvedConvId)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2745 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2746 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2747 currentConversationId = resolvedConvId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2748 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2749 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2750 archive?.setCurrentId(resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2751 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2752 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2753 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2754 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2755 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2756 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2757 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2758 /* Not on first page — fetch directly to verify ownership */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2759 let ownedConversation = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2760 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2761 ownedConversation = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2762 `/api/conversations/${encodeURIComponent(resolvedConvId)}`, |
|
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 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2765 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2766 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2767 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2768 ownedConversation = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2769 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2770 if (ownedConversation) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2771 currentConversationId = resolvedConvId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2772 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2773 chat?.replaceMessages(ownedConversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2774 if (!_renderedIds.has(resolvedConvId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2775 _renderedIds.add(resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2776 archive?.addConversation({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2777 id: resolvedConvId, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2778 title: ownedConversation.title || "Conversation", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2779 turn_count: ownedConversation.turns?.length || 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2780 }); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2781 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2782 archive?.setCurrentId(resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2783 } else if (_sessionData?.kind === "user") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2784 /* Authenticated and 404 — offer legacy claim for sessionStorage migrations */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2785 if (migratedFromStorage) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2786 sessionStorage.setItem(CONVERSATION_LEGACY_KEY, resolvedConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2787 legacyClaimId = resolvedConvId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2788 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2789 archive?.showLegacyClaim(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2790 if (conversations.length > 0) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2791 currentConversationId = conversations[0].id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2792 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2793 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2794 const conv = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2795 `/api/conversations/${encodeURIComponent(currentConversationId)}`, |
|
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 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2798 chat?.replaceMessages(conv.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2799 archive?.setCurrentId(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2800 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2801 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2802 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2803 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2804 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2805 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2806 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2807 /* URL-specified conv returned 404 — clear it */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2808 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2809 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2810 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2811 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2812 /* Guest — not owned, clear stale ID */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2813 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2814 if (migratedFromStorage) sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
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 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2817 } else if (conversations.length > 0) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2818 /* No target conversation — open the most recent */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2819 const most_recent = conversations[0]; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2820 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2821 const conversation = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2822 `/api/conversations/${encodeURIComponent(most_recent.id)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2823 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2824 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2825 currentConversationId = most_recent.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2826 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2827 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2828 archive?.setCurrentId(most_recent.id); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2829 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2830 if (error.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2831 startupEpoch !== principalEpoch) throw error; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2832 currentConversationId = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2833 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2834 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2835 } catch (error) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2836 if (error.name !== "StalePrincipalError" && |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2837 startupEpoch === principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2838 archive?.setError(`Archive error: ${error.message}`); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2839 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2840 /* Fall back to direct fetch of URL/stored ID */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2841 const initParams3 = new URLSearchParams(window.location.search); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2842 const fallbackId = ( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2843 initParams3.get("conversation") || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2844 (!_convParamWasExplicit |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2845 ? sessionStorage.getItem(CONVERSATION_STORAGE_KEY) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2846 : null) |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2847 ) || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2848 if (startupEpoch === principalEpoch && |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2849 fallbackId && UUID_REGEX.test(fallbackId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2850 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2851 const conversation = await requestJson( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2852 `/api/conversations/${encodeURIComponent(fallbackId)}`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2853 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2854 ensureStartupPrincipal(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2855 currentConversationId = fallbackId; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2856 chat?.replaceMessages(conversation.turns); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2857 } catch (fallbackError) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2858 if (fallbackError.name === "StalePrincipalError" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2859 startupEpoch !== principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2860 // The new principal owns subsequent rendering. |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2861 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2862 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
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 } |
|
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 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2867 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2868 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2869 /* 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
|
2870 if (startupEpoch === principalEpoch) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2871 selectPanel(currentPanel, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2872 _replaceUrlState(currentPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2873 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2874 |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2875 shell?.addEventListener("mjj-jrpg-preview-change", event => { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2876 selectPanel(event.detail.selection); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2877 character?.setAttribute("state", "thinking"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2878 window.clearTimeout(characterTimer); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2879 characterTimer = window.setTimeout(() => { |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2880 character?.setAttribute("state", "default"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2881 }, 650); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2882 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2883 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2884 shell?.addEventListener("mjj-jrpg-cancel", () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2885 activeController?.abort(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2886 }); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2887 |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2888 shell?.addEventListener("mjj-jrpg-submit", async event => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2889 if (activeController) return; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2890 /* Block sends when password change is required */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2891 if (_sessionData?.kind === "user" && _sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2892 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2893 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2894 chat?.appendMessage("June", event.detail.message); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2895 composer?.setBusy(true); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2896 character?.setAttribute("state", "thinking"); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2897 window.clearTimeout(characterTimer); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2898 const assistantItem = chat?.startAssistantMessage(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2899 chat?.setMessageStreaming(assistantItem, true); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2900 const assistantText = new StreamingTextAnimator(text => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2901 chat?.setMessageText(assistantItem, text); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2902 }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2903 let turnFinished = false; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2904 activeController = new AbortController(); |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2905 activeController.signal.addEventListener("abort", () => { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2906 assistantText.cancel(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
2907 }, { once: true }); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2908 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2909 /* Disable archive/account mutations while stream is active (finding #7) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2910 archive?.setStreamActive(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2911 setAccountActionsDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2912 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2913 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2914 const createConversation = async () => { |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2915 const resp = await mutatingFetch("/api/conversations", { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2916 method: "POST", |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2917 headers: { "Content-Type": "application/json" }, |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2918 body: JSON.stringify({ title: "Shiba Quest" }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2919 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2920 if (!resp.ok) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2921 let message = `Create failed (${resp.status})`; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2922 try { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2923 const body = await resp.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2924 message = body.error?.message || message; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2925 } catch { /* keep */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2926 throw new Error(message); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2927 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2928 const conversation = await resp.json(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2929 currentConversationId = conversation.id; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2930 sessionStorage.setItem(CONVERSATION_STORAGE_KEY, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2931 _pushUrlState(currentPanel, currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2932 if (!_renderedIds.has(currentConversationId)) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2933 _renderedIds.add(currentConversationId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2934 archive?.addConversation({ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2935 id: currentConversationId, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2936 title: "Shiba Quest", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2937 turn_count: 0, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2938 last_message_preview: event.detail.message.slice(0, 32), |
|
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 archive?.setCurrentId(currentConversationId); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2942 }; |
|
264
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 if (!currentConversationId) { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2945 await createConversation(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2946 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2947 |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2948 const postTurn = () => mutatingFetch( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2949 `/api/conversations/${encodeURIComponent(currentConversationId)}/turns`, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2950 { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2951 method: "POST", |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2952 headers: { "Content-Type": "application/json" }, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2953 body: JSON.stringify({ prompt: event.detail.message }), |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2954 signal: activeController.signal, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2955 }, |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2956 ); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2957 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2958 let response = await postTurn(); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2959 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2960 /* 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
|
2961 if (response.status === 404) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2962 sessionStorage.removeItem(CONVERSATION_STORAGE_KEY); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2963 currentConversationId = null; |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2964 await createConversation(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2965 response = await postTurn(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
2966 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2967 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2968 /* Handle 429: distinguish permanent quota from temporary capacity */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2969 if (response.status === 429) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2970 let errorBody = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2971 try { errorBody = await response.json(); } catch { /* keep */ } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2972 const code = errorBody?.error?.code || ""; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2973 const isQuotaExhausted = |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2974 code === "guest_quota_turns_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2975 code === "guest_quota_tokens_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2976 code === "guest_turn_quota_exhausted" || |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2977 code === "guest_token_quota_exhausted"; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2978 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2979 if (isQuotaExhausted) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2980 const quotaState = errorBody?.error?.quota || null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2981 updateQuotaDisplay(quotaEl, quotaState); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2982 composer?.setDisabled(true); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2983 chat?.setMessageStreaming(assistantItem, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2984 chat?.setMessageText( |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2985 assistantItem, |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2986 quotaState |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2987 ? `Quota exhausted. ${formatQuotaText(quotaState)?.text || ""}` |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2988 : "Quota exhausted. Try again later.", |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2989 ); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2990 character?.setAttribute("state", "sad"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2991 setFrameStatus("QUOTA", "offline"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2992 /* Re-enable when quota resets */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2993 const resetsAt = quotaState?.resetsAt; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2994 if (resetsAt) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2995 const delay = Math.max(0, resetsAt * 1000 - Date.now()); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2996 window.setTimeout(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2997 if (_sessionData?.kind !== "user" || !_sessionData?.mustChangePassword) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2998 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
2999 setFrameStatus("ONLINE", "online"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3000 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3001 }, Math.min(delay + 1000, 24 * 3600 * 1000)); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3002 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3003 } else { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3004 /* Temporary capacity 429 — use Retry-After or default 5 s */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3005 const retryAfter = parseInt(response.headers.get("Retry-After") || "5", 10); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3006 chat?.setMessageStreaming(assistantItem, false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3007 chat?.setMessageText(assistantItem, "Server busy. Retrying shortly…"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3008 character?.setAttribute("state", "sad"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3009 setFrameStatus("BUSY", "offline"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3010 window.setTimeout(() => { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3011 if (!activeController?.signal.aborted) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3012 setFrameStatus("ONLINE", "online"); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3013 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3014 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3015 }, retryAfter * 1000); |
|
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 return; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3018 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3019 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3020 if (!response.ok) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3021 let message = `Turn failed (${response.status})`; |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3022 try { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3023 const body = await response.json(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3024 message = body.error?.message || message; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3025 } catch { /* keep */ } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3026 throw new Error(message); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3027 } |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3028 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3029 await consumeSse(response, (eventName, data) => { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3030 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
|
3031 bubbles: true, |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3032 detail: { data, type: eventName }, |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3033 })); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3034 if (eventName === "assistant.delta") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3035 assistantText.append(data.delta || ""); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3036 } else if (eventName === "assistant.completed") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3037 assistantText.complete(data.content); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3038 } else if (eventName === "turn.error") { |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3039 throw new Error( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3040 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
|
3041 ); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3042 } else if (eventName === "turn.done") { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3043 turnFinished = true; |
|
261
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3044 composer?.setCancellable(false); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3045 if (data.failed || data.aborted) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3046 throw new Error( |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3047 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
|
3048 ); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3049 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3050 } |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3051 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3052 |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3053 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
|
3054 await assistantText.waitForIdle(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3055 if (activeController.signal.aborted) { |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3056 throw new DOMException("Quest cancelled", "AbortError"); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3057 } |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3058 chat?.setMessageStreaming(assistantItem, false); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3059 character?.setAttribute("state", "default"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
3060 setFrameStatus("ONLINE", "online"); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3061 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3062 /* Refresh archive and quota after successful turn */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3063 void refreshArchiveHead(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3064 if (_sessionData?.kind === "guest") { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3065 _csrfToken = await _fetchSession(); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3066 updateQuotaDisplay(quotaEl, _sessionData?.quota || null); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3067 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3068 } catch (error) { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3069 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
|
3070 assistantText.cancel(); |
|
b401627fc49e
Add JRPG mock flows and interactive previews
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
260
diff
changeset
|
3071 chat?.setMessageStreaming(assistantItem, false); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3072 chat?.setMessageText( |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3073 assistantItem, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3074 aborted ? "Quest cancelled." : `System error: ${error.message}`, |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3075 ); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3076 character?.setAttribute("state", "sad"); |
|
263
ee04e4e69fed
Add functional JRPG frame and tools
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
262
diff
changeset
|
3077 if (!aborted) setFrameStatus("OFFLINE", "offline"); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3078 } finally { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3079 composer?.setBusy(false); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3080 composer?.querySelector("textarea")?.focus(); |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3081 activeController = null; |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3082 /* Re-enable archive/account mutations exactly once */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3083 archive?.setStreamActive(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3084 setAccountActionsDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3085 /* Apply popstate that was queued during the stream (Fix 1) */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3086 if (_pendingPopState) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3087 const pending = _pendingPopState; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3088 _pendingPopState = null; |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3089 if (pending.seq === popSeq) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3090 await _applyPopState(pending.seq, pending.popPanel, pending.popConvId); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3091 } |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3092 } |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3093 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3094 }); |
|
264
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3095 |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3096 /* Enable composer unless forced password change */ |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3097 if (!isForced) { |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3098 composer?.setDisabled(false); |
|
04fee26ecce0
add authenticated JRPG conversation platform
MrJuneJune <me@mrjunejune.com>
parents:
263
diff
changeset
|
3099 } |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3100 } |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3101 |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3102 if (document.readyState === "loading") { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3103 document.addEventListener("DOMContentLoaded", () => { |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3104 void initializeJrpgPage(); |
|
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3105 }, { |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3106 once: true, |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3107 }); |
|
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3108 } else { |
|
260
1f9877b637e9
Add Copilot-powered cyberpunk JRPG chat
MrJuneJune <mrjunejune@users.noreply.github.com>
parents:
259
diff
changeset
|
3109 void initializeJrpgPage(); |
|
259
667156fcd3e3
Add dev-only cyberpunk JRPG page
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3110 } |