diff --git a/src/components/journal/JournalPanel.tsx b/src/components/journal/JournalPanel.tsx index e849917..70863f0 100644 --- a/src/components/journal/JournalPanel.tsx +++ b/src/components/journal/JournalPanel.tsx @@ -5,15 +5,17 @@ * Auto-connects to ws://{current host}:{current port} — no URL input needed. */ -import { Component, Show, createSignal, onMount } from "solid-js"; +import { Component, Show, createSignal, onMount, For } from "solid-js"; import { connectStream, hydrateFromServer, useJournalStream, setMyName, setSessionId, + sessions, + createSession, + disconnectStream, } from "../stores/journalStream"; -import { ConnectionBar } from "./ConnectionBar"; import { StreamView } from "./StreamView"; import { ComposePanel } from "./ComposePanel"; @@ -43,45 +45,7 @@ export const JournalPanel: Component = (props) => { aria-hidden={!props.open} > {/* Header */} -
-
- - - Disconnected - - } - > -
-

- {stream.myName} -

-

- {stream.sessionName || stream.sessionId} -

-
-
-
- -
+ = (props) => { } > -
@@ -102,6 +65,169 @@ export const JournalPanel: Component = (props) => { ); }; +// --------------------------------------------------------------------------- +// Journal Header — status dot, player name, session dropdown, disconnect & close +// --------------------------------------------------------------------------- + +interface JournalHeaderProps { + onClose: () => void; +} + +const JournalHeader: Component = (props) => { + const stream = useJournalStream(); + const manifest = sessions; + const [dropdownOpen, setDropdownOpen] = createSignal(false); + const [newSessionName, setNewSessionName] = createSignal(""); + const [showNewInput, setShowNewInput] = createSignal(false); + let dropdownRef!: HTMLDivElement; + + const handleSessionSelect = async (id: string) => { + try { + await hydrateFromServer(id); + setSessionId(id); + } catch { + /* */ + } + setDropdownOpen(false); + }; + + const handleCreate = () => { + const name = newSessionName().trim(); + if (!name) return; + createSession(name); + setNewSessionName(""); + setShowNewInput(false); + }; + + const handleDisconnect = () => disconnectStream(); + + // Close dropdown on outside click + if (typeof document !== "undefined") { + document.addEventListener("click", (e) => { + if (dropdownRef && !dropdownRef.contains(e.target as Node)) { + setDropdownOpen(false); + setShowNewInput(false); + } + }); + } + + return ( +
+
+ + + Disconnected + + } + > +
+

+ {stream.myName} +

+ {/* Clickable subtitle — session dropdown */} +
+ + +
+ + {([id, meta]) => ( + + )} + +
+ + + setNewSessionName(e.currentTarget.value) + } + placeholder="Session name" + class="flex-1 border border-gray-300 rounded px-1.5 py-0.5 text-xs" + autofocus + onKeyDown={(e) => { + if (e.key === "Enter") handleCreate(); + if (e.key === "Escape") setShowNewInput(false); + }} + /> + +
+ } + > + + +
+
+
+
+
+
+
+ + + + +
+
+ ); +}; + // --------------------------------------------------------------------------- // Connect dialog — one input: player name. Broker URL is always current host. // ---------------------------------------------------------------------------