import { Component, createSignal, Show, For, createEffect, onMount, onCleanup, createMemo } from "solid-js" import type { Instance } from "../types/instance" import { getParentSessions, createSession, setActiveParentSession } from "../stores/sessions" import InstanceInfo from "./instance-info" import KeyboardHint from "./keyboard-hint" import Kbd from "./kbd" import { keyboardRegistry, type KeyboardShortcut } from "../lib/keyboard-registry" import { isMac } from "../lib/keyboard-utils" interface InstanceWelcomeViewProps { instance: Instance } const InstanceWelcomeView: Component = (props) => { const [isCreating, setIsCreating] = createSignal(false) const [selectedIndex, setSelectedIndex] = createSignal(0) const [focusMode, setFocusMode] = createSignal<"sessions" | "new-session" | null>("sessions") const parentSessions = () => getParentSessions(props.instance.id) const newSessionShortcut = createMemo(() => { const registered = keyboardRegistry.get("session-new") if (registered) return registered return { id: "session-new-display", key: "n", modifiers: { shift: true, meta: isMac(), ctrl: !isMac(), }, handler: () => {}, description: "New Session", context: "global", } }) const newSessionShortcutString = createMemo(() => (isMac() ? "cmd+shift+n" : "ctrl+shift+n")) createEffect(() => { const sessions = parentSessions() if (sessions.length === 0) { setFocusMode("new-session") setSelectedIndex(0) } else { setFocusMode("sessions") setSelectedIndex(0) } }) function scrollToIndex(index: number) { const element = document.querySelector(`[data-session-index="${index}"]`) if (element) { element.scrollIntoView({ block: "nearest", behavior: "auto" }) } } function handleKeyDown(e: KeyboardEvent) { const sessions = parentSessions() if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "n") { e.preventDefault() handleNewSession() return } if (sessions.length === 0) return if (e.key === "ArrowDown") { e.preventDefault() const newIndex = Math.min(selectedIndex() + 1, sessions.length - 1) setSelectedIndex(newIndex) setFocusMode("sessions") scrollToIndex(newIndex) } else if (e.key === "ArrowUp") { e.preventDefault() const newIndex = Math.max(selectedIndex() - 1, 0) setSelectedIndex(newIndex) setFocusMode("sessions") scrollToIndex(newIndex) } else if (e.key === "PageDown") { e.preventDefault() const pageSize = 5 const newIndex = Math.min(selectedIndex() + pageSize, sessions.length - 1) setSelectedIndex(newIndex) setFocusMode("sessions") scrollToIndex(newIndex) } else if (e.key === "PageUp") { e.preventDefault() const pageSize = 5 const newIndex = Math.max(selectedIndex() - pageSize, 0) setSelectedIndex(newIndex) setFocusMode("sessions") scrollToIndex(newIndex) } else if (e.key === "Home") { e.preventDefault() setSelectedIndex(0) setFocusMode("sessions") scrollToIndex(0) } else if (e.key === "End") { e.preventDefault() const newIndex = sessions.length - 1 setSelectedIndex(newIndex) setFocusMode("sessions") scrollToIndex(newIndex) } else if (e.key === "Enter") { e.preventDefault() handleEnterKey() } } async function handleEnterKey() { const sessions = parentSessions() const index = selectedIndex() if (index < sessions.length) { await handleSessionSelect(sessions[index].id) } } onMount(() => { window.addEventListener("keydown", handleKeyDown) onCleanup(() => { window.removeEventListener("keydown", handleKeyDown) }) }) function formatRelativeTime(timestamp: number): string { const seconds = Math.floor((Date.now() - timestamp) / 1000) const minutes = Math.floor(seconds / 60) const hours = Math.floor(minutes / 60) const days = Math.floor(hours / 24) if (days > 0) return `${days}d ago` if (hours > 0) return `${hours}h ago` if (minutes > 0) return `${minutes}m ago` return "just now" } function formatTimestamp(timestamp: number): string { return new Date(timestamp).toLocaleString() } async function handleSessionSelect(sessionId: string) { setActiveParentSession(props.instance.id, sessionId) } async function handleNewSession() { if (isCreating()) return setIsCreating(true) try { const session = await createSession(props.instance.id) setActiveParentSession(props.instance.id, session.id) } catch (error) { console.error("Failed to create session:", error) } finally { setIsCreating(false) } } return (
0} fallback={

No Previous Sessions

Create a new session below to get started

} >

Resume Session

{parentSessions().length} {parentSessions().length === 1 ? "session" : "sessions"} available

{(session, index) => (
)}

Start New Session

We’ll reuse your last agent/model automatically

) } export default InstanceWelcomeView