diff options
| author | Adam <[email protected]> | 2026-03-02 10:50:50 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-02 10:50:50 -0600 |
| commit | 8176bafc555e562ade48a675dffa3f38751ed8c9 (patch) | |
| tree | 7d4b0f6e98f431999b89c1f24687f6f53bd0bc6b /packages/app/src/pages | |
| parent | 0a3a3216db5974efd3edc9a213054fd97d8dbd34 (diff) | |
| download | opencode-8176bafc555e562ade48a675dffa3f38751ed8c9.tar.gz opencode-8176bafc555e562ade48a675dffa3f38751ed8c9.zip | |
chore(app): solidjs refactoring (#13399)
Diffstat (limited to 'packages/app/src/pages')
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 307 | ||||
| -rw-r--r-- | packages/app/src/pages/layout/helpers.ts | 30 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 18 | ||||
| -rw-r--r-- | packages/app/src/pages/session/file-tabs.tsx | 63 | ||||
| -rw-r--r-- | packages/app/src/pages/session/review-tab.tsx | 34 | ||||
| -rw-r--r-- | packages/app/src/pages/session/terminal-panel.tsx | 6 | ||||
| -rw-r--r-- | packages/app/src/pages/session/use-session-hash-scroll.ts | 36 |
7 files changed, 263 insertions, 231 deletions
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index cb194052d..f93d1f069 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -59,11 +59,11 @@ import { useLanguage, type Locale } from "@/context/language" import { childMapByParent, displayName, + effectiveWorkspaceOrder, errorMessage, getDraggableId, latestRootSession, sortedRootSessions, - syncWorkspaceOrder, workspaceKey, } from "./layout/helpers" import { collectOpenProjectDeepLinks, deepLinkEvent, drainPendingDeepLinks } from "./layout/deep-links" @@ -483,21 +483,6 @@ export default function Layout(props: ParentProps) { createEffect( on( - () => ({ ready: pageReady(), project: currentProject() }), - (value) => { - if (!value.ready) return - const project = value.project - if (!project) return - const last = server.projects.last() - if (last === project.worktree) return - server.projects.touch(project.worktree) - }, - { defer: true }, - ), - ) - - createEffect( - on( () => ({ ready: pageReady(), layoutReady: layoutReady(), dir: params.dir, list: layout.projects.list() }), (value) => { if (!value.ready) return @@ -554,29 +539,17 @@ export default function Layout(props: ParentProps) { return layout.sidebar.workspaces(project.worktree)() }) - createEffect(() => { - if (!pageReady()) return - if (!layoutReady()) return + const visibleSessionDirs = createMemo(() => { const project = currentProject() - if (!project) return - - const local = project.worktree - const dirs = [project.worktree, ...(project.sandboxes ?? [])] - const existing = store.workspaceOrder[project.worktree] - const merged = syncWorkspaceOrder(local, dirs, existing) - if (!existing) { - setStore("workspaceOrder", project.worktree, merged) - return - } - - if (merged.length !== existing.length) { - setStore("workspaceOrder", project.worktree, merged) - return - } - - if (merged.some((d, i) => d !== existing[i])) { - setStore("workspaceOrder", project.worktree, merged) - } + if (!project) return [] as string[] + if (!workspaceSetting()) return [project.worktree] + + const activeDir = currentDir() + return workspaceIds(project).filter((directory) => { + const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree + const active = directory === activeDir + return expanded || active + }) }) createEffect(() => { @@ -593,25 +566,17 @@ export default function Layout(props: ParentProps) { }) const currentSessions = createMemo(() => { - const project = currentProject() - if (!project) return [] as Session[] const now = Date.now() - if (workspaceSetting()) { - const dirs = workspaceIds(project) - const activeDir = currentDir() - const result: Session[] = [] - for (const dir of dirs) { - const expanded = store.workspaceExpanded[dir] ?? dir === project.worktree - const active = dir === activeDir - if (!expanded && !active) continue - const [dirStore] = globalSync.child(dir, { bootstrap: true }) - const dirSessions = sortedRootSessions(dirStore, now) - result.push(...dirSessions) - } - return result + const dirs = visibleSessionDirs() + if (dirs.length === 0) return [] as Session[] + + const result: Session[] = [] + for (const dir of dirs) { + const [dirStore] = globalSync.child(dir, { bootstrap: true }) + const dirSessions = sortedRootSessions(dirStore, now) + result.push(...dirSessions) } - const [projectStore] = globalSync.child(project.worktree) - return sortedRootSessions(projectStore, now) + return result }) type PrefetchQueue = { @@ -826,7 +791,6 @@ export default function Layout(props: ParentProps) { } navigateToSession(session) - queueMicrotask(() => scrollToSession(session.id, `${session.directory}:${session.id}`)) } function navigateSessionByUnseen(offset: number) { @@ -861,7 +825,6 @@ export default function Layout(props: ParentProps) { } navigateToSession(session) - queueMicrotask(() => scrollToSession(session.id, `${session.directory}:${session.id}`)) return } } @@ -1094,34 +1057,90 @@ export default function Layout(props: ParentProps) { return meta?.worktree ?? directory } + function activeProjectRoot(directory: string) { + return currentProject()?.worktree ?? projectRoot(directory) + } + + function touchProjectRoute() { + const root = currentProject()?.worktree + if (!root) return + if (server.projects.last() !== root) server.projects.touch(root) + return root + } + + function rememberSessionRoute(directory: string, id: string, root = activeProjectRoot(directory)) { + setStore("lastProjectSession", root, { directory, id, at: Date.now() }) + return root + } + + function clearLastProjectSession(root: string) { + if (!store.lastProjectSession[root]) return + setStore( + "lastProjectSession", + produce((draft) => { + delete draft[root] + }), + ) + } + + function syncSessionRoute(directory: string, id: string, root = activeProjectRoot(directory)) { + rememberSessionRoute(directory, id, root) + notification.session.markViewed(id) + const expanded = untrack(() => store.workspaceExpanded[directory]) + if (expanded === false) { + setStore("workspaceExpanded", directory, true) + } + requestAnimationFrame(() => scrollToSession(id, `${directory}:${id}`)) + return root + } + async function navigateToProject(directory: string | undefined) { if (!directory) return const root = projectRoot(directory) server.projects.touch(root) const project = layout.projects.list().find((item) => item.worktree === root) - const dirs = Array.from(new Set([root, ...(store.workspaceOrder[root] ?? []), ...(project?.sandboxes ?? [])])) + let dirs = project + ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root]) + : [root] + const canOpen = (value: string | undefined) => { + if (!value) return false + return dirs.some((item) => workspaceKey(item) === workspaceKey(value)) + } + const refreshDirs = async (target?: string) => { + if (!target || target === root || canOpen(target)) return canOpen(target) + const listed = await globalSDK.client.worktree + .list({ directory: root }) + .then((x) => x.data ?? []) + .catch(() => [] as string[]) + dirs = effectiveWorkspaceOrder(root, [root, ...listed], store.workspaceOrder[root]) + return canOpen(target) + } const openSession = async (target: { directory: string; id: string }) => { + if (!canOpen(target.directory)) return false const resolved = await globalSDK.client.session .get({ sessionID: target.id }) .then((x) => x.data) .catch(() => undefined) - const next = resolved?.directory ? resolved : target - setStore("lastProjectSession", root, { directory: next.directory, id: next.id, at: Date.now() }) - navigateWithSidebarReset(`/${base64Encode(next.directory)}/session/${next.id}`) + if (!resolved?.directory) return false + if (!canOpen(resolved.directory)) return false + setStore("lastProjectSession", root, { directory: resolved.directory, id: resolved.id, at: Date.now() }) + navigateWithSidebarReset(`/${base64Encode(resolved.directory)}/session/${resolved.id}`) + return true } const projectSession = store.lastProjectSession[root] if (projectSession?.id) { - await openSession(projectSession) - return + await refreshDirs(projectSession.directory) + const opened = await openSession(projectSession) + if (opened) return + clearLastProjectSession(root) } const latest = latestRootSession( dirs.map((item) => globalSync.child(item, { bootstrap: false })[0]), Date.now(), ) - if (latest) { - await openSession(latest) + if (latest && (await openSession(latest))) { return } @@ -1137,8 +1156,7 @@ export default function Layout(props: ParentProps) { ), Date.now(), ) - if (fetched) { - await openSession(fetched) + if (fetched && (await openSession(fetched))) { return } @@ -1240,9 +1258,17 @@ export default function Layout(props: ParentProps) { } } - const deleteWorkspace = async (root: string, directory: string) => { + const deleteWorkspace = async (root: string, directory: string, leaveDeletedWorkspace = false) => { if (directory === root) return + const current = currentDir() + const currentKey = workspaceKey(current) + const deletedKey = workspaceKey(directory) + const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey) + if (!leaveDeletedWorkspace && shouldLeave) { + navigateWithSidebarReset(`/${base64Encode(root)}/session`) + } + setBusy(directory, true) const result = await globalSDK.client.worktree @@ -1260,6 +1286,10 @@ export default function Layout(props: ParentProps) { if (!result) return + if (workspaceKey(store.lastProjectSession[root]?.directory ?? "") === workspaceKey(directory)) { + clearLastProjectSession(root) + } + globalSync.set( "project", produce((draft) => { @@ -1273,8 +1303,18 @@ export default function Layout(props: ParentProps) { layout.projects.close(directory) layout.projects.open(root) - if (params.dir && currentDir() === directory) { - navigateToProject(root) + if (shouldLeave) return + + const nextCurrent = currentDir() + const nextKey = workspaceKey(nextCurrent) + const project = layout.projects.list().find((item) => item.worktree === root) + const dirs = project + ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root]) + : [root] + const valid = dirs.some((item) => workspaceKey(item) === nextKey) + + if (params.dir && projectRoot(nextCurrent) === root && !valid) { + navigateWithSidebarReset(`/${base64Encode(root)}/session`) } } @@ -1377,8 +1417,12 @@ export default function Layout(props: ParentProps) { }) const handleDelete = () => { + const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory) + if (leaveDeletedWorkspace) { + navigateWithSidebarReset(`/${base64Encode(props.root)}/session`) + } dialog.close() - void deleteWorkspace(props.root, props.directory) + void deleteWorkspace(props.root, props.directory, leaveDeletedWorkspace) } const description = () => { @@ -1486,26 +1530,42 @@ export default function Layout(props: ParentProps) { ) } + const activeRoute = { + session: "", + sessionProject: "", + } + createEffect( on( - () => ({ ready: pageReady(), dir: params.dir, id: params.id }), - (value) => { - if (!value.ready) return - const dir = value.dir - const id = value.id - if (!dir || !id) return + () => [pageReady(), params.dir, params.id, currentProject()?.worktree] as const, + ([ready, dir, id]) => { + if (!ready || !dir) { + activeRoute.session = "" + activeRoute.sessionProject = "" + return + } + const directory = decode64(dir) if (!directory) return - const at = Date.now() - setStore("lastProjectSession", projectRoot(directory), { directory, id, at }) - notification.session.markViewed(id) - const expanded = untrack(() => store.workspaceExpanded[directory]) - if (expanded === false) { - setStore("workspaceExpanded", directory, true) + + const root = touchProjectRoute() ?? activeProjectRoot(directory) + + if (!id) { + activeRoute.session = "" + activeRoute.sessionProject = "" + return + } + + const session = `${dir}/${id}` + if (session !== activeRoute.session) { + activeRoute.session = session + activeRoute.sessionProject = syncSessionRoute(directory, id, root) + return } - requestAnimationFrame(() => scrollToSession(id, `${directory}:${id}`)) + + if (root === activeRoute.sessionProject) return + activeRoute.sessionProject = rememberSessionRoute(directory, id, root) }, - { defer: true }, ), ) @@ -1516,40 +1576,29 @@ export default function Layout(props: ParentProps) { const loadedSessionDirs = new Set<string>() - createEffect(() => { - const project = currentProject() - const workspaces = workspaceSetting() - const next = new Set<string>() - if (!project) { - loadedSessionDirs.clear() - return - } - - if (workspaces) { - const activeDir = currentDir() - const dirs = [project.worktree, ...(project.sandboxes ?? [])] - for (const directory of dirs) { - const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree - const active = directory === activeDir - if (!expanded && !active) continue - next.add(directory) - } - } - - if (!workspaces) { - next.add(project.worktree) - } + createEffect( + on( + visibleSessionDirs, + (dirs) => { + if (dirs.length === 0) { + loadedSessionDirs.clear() + return + } - for (const directory of next) { - if (loadedSessionDirs.has(directory)) continue - globalSync.project.loadSessions(directory) - } + const next = new Set(dirs) + for (const directory of next) { + if (loadedSessionDirs.has(directory)) continue + globalSync.project.loadSessions(directory) + } - loadedSessionDirs.clear() - for (const directory of next) { - loadedSessionDirs.add(directory) - } - }) + loadedSessionDirs.clear() + for (const directory of next) { + loadedSessionDirs.add(directory) + } + }, + { defer: true }, + ), + ) function handleDragStart(event: unknown) { const id = getDraggableId(event) @@ -1583,14 +1632,11 @@ export default function Layout(props: ParentProps) { const extra = directory && directory !== local && !dirs.includes(directory) ? directory : undefined const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false - const existing = store.workspaceOrder[project.worktree] - if (!existing) return extra ? [...dirs, extra] : dirs - - const merged = syncWorkspaceOrder(local, dirs, existing) - if (pending && extra) return [local, extra, ...merged.filter((directory) => directory !== local)] - if (!extra) return merged - if (pending) return merged - return [...merged, extra] + const ordered = effectiveWorkspaceOrder(local, dirs, store.workspaceOrder[project.worktree]) + if (pending && extra) return [local, extra, ...ordered.filter((item) => item !== local)] + if (!extra) return ordered + if (pending) return ordered + return [...ordered, extra] } const sidebarProject = createMemo(() => { @@ -1623,7 +1669,11 @@ export default function Layout(props: ParentProps) { const [item] = result.splice(fromIndex, 1) if (!item) return result.splice(toIndex, 0, item) - setStore("workspaceOrder", project.worktree, result) + setStore( + "workspaceOrder", + project.worktree, + result.filter((directory) => workspaceKey(directory) !== workspaceKey(project.worktree)), + ) } function handleWorkspaceDragEnd() { @@ -1661,10 +1711,9 @@ export default function Layout(props: ParentProps) { const existing = prev ?? [] const next = existing.filter((item) => { const id = workspaceKey(item) - if (id === root) return false - return id !== key + return id !== root && id !== key }) - return [local, created.directory, ...next] + return [created.directory, ...next] }) globalSync.child(created.directory) diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts index 2c4b834be..42315e589 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -74,9 +74,29 @@ export const errorMessage = (err: unknown, fallback: string) => { return fallback } -export const syncWorkspaceOrder = (local: string, dirs: string[], existing?: string[]) => { - if (!existing) return dirs - const keep = existing.filter((d) => d !== local && dirs.includes(d)) - const missing = dirs.filter((d) => d !== local && !existing.includes(d)) - return [local, ...missing, ...keep] +export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => { + const root = workspaceKey(local) + const live = new Map<string, string>() + + for (const dir of dirs) { + const key = workspaceKey(dir) + if (key === root) continue + if (!live.has(key)) live.set(key, dir) + } + + if (!persisted?.length) return [local, ...live.values()] + + const result = [local] + for (const dir of persisted) { + const key = workspaceKey(dir) + if (key === root) continue + const match = live.get(key) + if (!match) continue + result.push(match) + live.delete(key) + } + + return [...result, ...live.values()] } + +export const syncWorkspaceOrder = effectiveWorkspaceOrder diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 16b38cbd3..4f01badf4 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -347,24 +347,6 @@ export default function Page() { if (path) file.load(path) }) - createEffect(() => { - const current = tabs().all() - if (current.length === 0) return - - const next = normalizeTabs(current) - if (same(current, next)) return - - tabs().setAll(next) - - const active = tabs().active() - if (!active) return - if (!active.startsWith("file://")) return - - const normalized = normalizeTab(active) - if (active === normalized) return - tabs().setActive(normalized) - }) - const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined)) const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : [])) const reviewCount = createMemo(() => Math.max(info()?.summary?.files ?? 0, diffs().length)) diff --git a/packages/app/src/pages/session/file-tabs.tsx b/packages/app/src/pages/session/file-tabs.tsx index e92eee670..93264353b 100644 --- a/packages/app/src/pages/session/file-tabs.tsx +++ b/packages/app/src/pages/session/file-tabs.tsx @@ -67,6 +67,7 @@ export function FileTabContent(props: { tab: string }) { let scroll: HTMLDivElement | undefined let scrollFrame: number | undefined + let restoreFrame: number | undefined let pending: { x: number; y: number } | undefined let codeScroll: HTMLElement[] = [] let find: FileSearchHandle | null = null @@ -349,6 +350,15 @@ export function FileTabContent(props: { tab: string }) { if (el.scrollLeft !== s.x) el.scrollLeft = s.x } + const queueRestore = () => { + if (restoreFrame !== undefined) return + + restoreFrame = requestAnimationFrame(() => { + restoreFrame = undefined + restoreScroll() + }) + } + const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => { if (codeScroll.length === 0) syncCodeScroll() @@ -364,46 +374,29 @@ export function FileTabContent(props: { tab: string }) { setNote("commenting", null) } - createEffect( - on( - () => state()?.loaded, - (loaded) => { - if (!loaded) return - requestAnimationFrame(restoreScroll) - }, - { defer: true }, - ), - ) - - createEffect( - on( - () => file.ready(), - (ready) => { - if (!ready) return - requestAnimationFrame(restoreScroll) - }, - { defer: true }, - ), - ) + let prev = { + loaded: false, + ready: false, + active: false, + } - createEffect( - on( - () => tabs().active() === props.tab, - (active) => { - if (!active) return - if (!state()?.loaded) return - requestAnimationFrame(restoreScroll) - }, - ), - ) + createEffect(() => { + const loaded = !!state()?.loaded + const ready = file.ready() + const active = tabs().active() === props.tab + const restore = (loaded && !prev.loaded) || (ready && !prev.ready) || (active && loaded && !prev.active) + prev = { loaded, ready, active } + if (!restore) return + queueRestore() + }) onCleanup(() => { for (const item of codeScroll) { item.removeEventListener("scroll", handleCodeScroll) } - if (scrollFrame === undefined) return - cancelAnimationFrame(scrollFrame) + if (scrollFrame !== undefined) cancelAnimationFrame(scrollFrame) + if (restoreFrame !== undefined) cancelAnimationFrame(restoreFrame) }) const renderFile = (source: string) => ( @@ -421,7 +414,7 @@ export function FileTabContent(props: { tab: string }) { selectedLines={activeSelection()} commentedLines={commentedLines()} onRendered={() => { - requestAnimationFrame(restoreScroll) + queueRestore() }} annotations={commentsUi.annotations()} renderAnnotation={commentsUi.renderAnnotation} @@ -440,7 +433,7 @@ export function FileTabContent(props: { tab: string }) { mode: "auto", path: path(), current: state()?.content, - onLoad: () => requestAnimationFrame(restoreScroll), + onLoad: queueRestore, onError: (args: { kind: "image" | "audio" | "svg" }) => { if (args.kind !== "svg") return showToast({ diff --git a/packages/app/src/pages/session/review-tab.tsx b/packages/app/src/pages/session/review-tab.tsx index 2b7eba324..142ee7ad9 100644 --- a/packages/app/src/pages/session/review-tab.tsx +++ b/packages/app/src/pages/session/review-tab.tsx @@ -1,4 +1,4 @@ -import { createEffect, on, onCleanup, type JSX } from "solid-js" +import { createEffect, onCleanup, type JSX } from "solid-js" import type { FileDiff } from "@opencode-ai/sdk/v2" import { SessionReview } from "@opencode-ai/ui/session-review" import type { @@ -119,32 +119,12 @@ export function SessionReviewTab(props: SessionReviewTabProps) { }) } - createEffect( - on( - () => props.diffs().length, - () => queueRestore(), - { defer: true }, - ), - ) - - createEffect( - on( - () => props.diffStyle, - () => queueRestore(), - { defer: true }, - ), - ) - - createEffect( - on( - () => layout.ready(), - (ready) => { - if (!ready) return - queueRestore() - }, - { defer: true }, - ), - ) + createEffect(() => { + props.diffs().length + props.diffStyle + if (!layout.ready()) return + queueRestore() + }) onCleanup(() => { if (restoreFrame !== undefined) cancelAnimationFrame(restoreFrame) diff --git a/packages/app/src/pages/session/terminal-panel.tsx b/packages/app/src/pages/session/terminal-panel.tsx index 27ea4e6f3..49bed9490 100644 --- a/packages/app/src/pages/session/terminal-panel.tsx +++ b/packages/app/src/pages/session/terminal-panel.tsx @@ -56,9 +56,9 @@ export function TerminalPanel() { on( () => terminal.all().length, (count, prevCount) => { - if (prevCount !== undefined && prevCount > 0 && count === 0) { - if (opened()) view().terminal.toggle() - } + if (prevCount === undefined || prevCount <= 0 || count !== 0) return + if (!opened()) return + close() }, ), ) diff --git a/packages/app/src/pages/session/use-session-hash-scroll.ts b/packages/app/src/pages/session/use-session-hash-scroll.ts index 235714588..c5a7dde9e 100644 --- a/packages/app/src/pages/session/use-session-hash-scroll.ts +++ b/packages/app/src/pages/session/use-session-hash-scroll.ts @@ -1,4 +1,4 @@ -import { createEffect, createMemo, on, onCleanup } from "solid-js" +import { createEffect, createMemo, onCleanup, onMount } from "solid-js" import { UserMessage } from "@opencode-ai/sdk/v2" export const messageIdFromHash = (hash: string) => { @@ -28,6 +28,7 @@ export const useSessionHashScroll = (input: { const visibleUserMessages = createMemo(() => input.visibleUserMessages()) const messageById = createMemo(() => new Map(visibleUserMessages().map((m) => [m.id, m]))) const messageIndex = createMemo(() => new Map(visibleUserMessages().map((m, i) => [m.id, i]))) + let pendingKey = "" const clearMessageHash = () => { if (!window.location.hash) return @@ -130,15 +131,6 @@ export const useSessionHashScroll = (input: { if (el) input.scheduleScrollState(el) } - createEffect( - on(input.sessionKey, (key) => { - if (!input.sessionID()) return - const messageID = input.consumePendingMessage(key) - if (!messageID) return - input.setPendingMessage(messageID) - }), - ) - createEffect(() => { if (!input.sessionID() || !input.messagesReady()) return requestAnimationFrame(() => applyHash("auto")) @@ -150,7 +142,20 @@ export const useSessionHashScroll = (input: { visibleUserMessages() input.turnStart() - const targetId = input.pendingMessage() ?? messageIdFromHash(window.location.hash) + let targetId = input.pendingMessage() + if (!targetId) { + const key = input.sessionKey() + if (pendingKey !== key) { + pendingKey = key + const next = input.consumePendingMessage(key) + if (next) { + input.setPendingMessage(next) + targetId = next + } + } + } + + if (!targetId) targetId = messageIdFromHash(window.location.hash) if (!targetId) return if (input.currentMessageId() === targetId) return @@ -162,9 +167,12 @@ export const useSessionHashScroll = (input: { requestAnimationFrame(() => scrollToMessage(msg, "auto")) }) - createEffect(() => { - if (!input.sessionID() || !input.messagesReady()) return - const handler = () => requestAnimationFrame(() => applyHash("auto")) + onMount(() => { + const handler = () => { + if (!input.sessionID() || !input.messagesReady()) return + requestAnimationFrame(() => applyHash("auto")) + } + window.addEventListener("hashchange", handler) onCleanup(() => window.removeEventListener("hashchange", handler)) }) |
