feat: publish initial manifest on startup and improve UI focus
- Publish the existing session manifest via the broker on startup - Automatically focus the new session input field when it is shown - Prevent event propagation when clicking to create a new session
This commit is contained in:
parent
3eba8b3279
commit
57ffb35f2b
|
|
@ -150,6 +150,20 @@ export async function createJournalServer(
|
||||||
() => {},
|
() => {},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Publish the initial manifest on startup so clients see existing sessions
|
||||||
|
const initialManifest = loadManifest(root);
|
||||||
|
broker.publish(
|
||||||
|
{
|
||||||
|
topic: $SESSIONS,
|
||||||
|
payload: Buffer.from(JSON.stringify(initialManifest)),
|
||||||
|
qos: 1,
|
||||||
|
retain: true,
|
||||||
|
cmd: "publish" as const,
|
||||||
|
dup: false,
|
||||||
|
},
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
|
||||||
console.log("[journal] persistence listener active");
|
console.log("[journal] persistence listener active");
|
||||||
console.log("[journal] broker available at ws://<host>:<port>");
|
console.log("[journal] broker available at ws://<host>:<port>");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,14 @@
|
||||||
* Auto-connects to ws://{current host}:{current port} — no URL input needed.
|
* Auto-connects to ws://{current host}:{current port} — no URL input needed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, Show, createSignal, onMount, For } from "solid-js";
|
import {
|
||||||
|
Component,
|
||||||
|
Show,
|
||||||
|
createSignal,
|
||||||
|
onMount,
|
||||||
|
For,
|
||||||
|
createEffect,
|
||||||
|
} from "solid-js";
|
||||||
import {
|
import {
|
||||||
connectStream,
|
connectStream,
|
||||||
hydrateFromServer,
|
hydrateFromServer,
|
||||||
|
|
@ -113,6 +120,7 @@ const JournalHeader: Component<JournalHeaderProps> = (props) => {
|
||||||
const [newSessionName, setNewSessionName] = createSignal("");
|
const [newSessionName, setNewSessionName] = createSignal("");
|
||||||
const [showNewInput, setShowNewInput] = createSignal(false);
|
const [showNewInput, setShowNewInput] = createSignal(false);
|
||||||
let dropdownRef!: HTMLDivElement;
|
let dropdownRef!: HTMLDivElement;
|
||||||
|
let newSessionInputRef!: HTMLInputElement;
|
||||||
|
|
||||||
const handleSessionSelect = async (id: string) => {
|
const handleSessionSelect = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -132,6 +140,13 @@ const JournalHeader: Component<JournalHeaderProps> = (props) => {
|
||||||
setShowNewInput(false);
|
setShowNewInput(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Focus the input when it appears
|
||||||
|
createEffect(() => {
|
||||||
|
if (showNewInput() && newSessionInputRef) {
|
||||||
|
newSessionInputRef.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const handleDisconnect = () => disconnectStream();
|
const handleDisconnect = () => disconnectStream();
|
||||||
|
|
||||||
// Close dropdown on outside click
|
// Close dropdown on outside click
|
||||||
|
|
@ -214,6 +229,7 @@ const JournalHeader: Component<JournalHeaderProps> = (props) => {
|
||||||
fallback={
|
fallback={
|
||||||
<div class="px-3 py-1 flex gap-1">
|
<div class="px-3 py-1 flex gap-1">
|
||||||
<input
|
<input
|
||||||
|
ref={newSessionInputRef}
|
||||||
type="text"
|
type="text"
|
||||||
value={newSessionName()}
|
value={newSessionName()}
|
||||||
onInput={(e) =>
|
onInput={(e) =>
|
||||||
|
|
@ -221,7 +237,6 @@ const JournalHeader: Component<JournalHeaderProps> = (props) => {
|
||||||
}
|
}
|
||||||
placeholder="会话名称"
|
placeholder="会话名称"
|
||||||
class="flex-1 border border-gray-300 rounded px-1.5 py-0.5 text-xs"
|
class="flex-1 border border-gray-300 rounded px-1.5 py-0.5 text-xs"
|
||||||
autofocus
|
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter") handleCreate();
|
if (e.key === "Enter") handleCreate();
|
||||||
if (e.key === "Escape") setShowNewInput(false);
|
if (e.key === "Escape") setShowNewInput(false);
|
||||||
|
|
@ -237,7 +252,10 @@ const JournalHeader: Component<JournalHeaderProps> = (props) => {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowNewInput(true)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setShowNewInput(true);
|
||||||
|
}}
|
||||||
class="w-full text-left px-3 py-1 text-xs text-blue-600 hover:bg-gray-100"
|
class="w-full text-left px-3 py-1 text-xs text-blue-600 hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
+ 新建会话
|
+ 新建会话
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue