From b8872d9d20c76ef351a0ec356558b1484a74f20f Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 1 Jan 2026 10:52:26 -0600 Subject: wip(desktop): progress --- packages/app/src/pages/layout.tsx | 22 ++-- packages/app/src/pages/session.tsx | 199 +++++++++++++++++++++++++++++++------ 2 files changed, 174 insertions(+), 47 deletions(-) (limited to 'packages/app/src/pages') diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index 4629cd9b6..e237d2184 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -62,17 +62,9 @@ export default function Layout(props: ParentProps) { const [store, setStore] = createStore({ lastSession: {} as { [directory: string]: string }, activeDraggable: undefined as string | undefined, - mobileSidebarOpen: false, mobileProjectsExpanded: {} as Record, }) - const mobileSidebar = { - open: () => store.mobileSidebarOpen, - show: () => setStore("mobileSidebarOpen", true), - hide: () => setStore("mobileSidebarOpen", false), - toggle: () => setStore("mobileSidebarOpen", (x) => !x), - } - const mobileProjects = { expanded: (directory: string) => store.mobileProjectsExpanded[directory] ?? true, expand: (directory: string) => setStore("mobileProjectsExpanded", directory, true), @@ -468,13 +460,13 @@ export default function Layout(props: ParentProps) { if (!directory) return const lastSession = store.lastSession[directory] navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`) - mobileSidebar.hide() + layout.mobileSidebar.hide() } function navigateToSession(session: Session | undefined) { if (!session) return navigate(`/${params.dir}/session/${session?.id}`) - mobileSidebar.hide() + layout.mobileSidebar.hide() } function openProject(directory: string, navigate = true) { @@ -1064,18 +1056,18 @@ export default function Layout(props: ParentProps) {
{ - if (e.target === e.currentTarget) mobileSidebar.hide() + if (e.target === e.currentTarget) layout.mobileSidebar.hide() }} />
e.stopPropagation()} > diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index f0e6a6e1d..7f0203222 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -84,7 +84,7 @@ function same(a: readonly T[], b: readonly T[]) { return a.every((x, i) => x === b[i]) } -function Header(props: { onMobileMenuToggle?: () => void }) { +function Header() { const globalSDK = useGlobalSDK() const layout = useLayout() const params = useParams() @@ -113,7 +113,7 @@ function Header(props: { onMobileMenuToggle?: () => void }) { @@ -291,6 +291,7 @@ export default function Page() { const permission = usePermission() const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey())) + const view = createMemo(() => layout.view(sessionKey())) function normalizeTab(tab: string) { if (!tab.startsWith("file://")) return tab @@ -822,6 +823,8 @@ export default function Page() { .filter((tab) => tab !== "context"), ) + const reviewTab = createMemo(() => diffs().length > 0 || tabs().active() === "review") + const showTabs = createMemo( () => layout.review.opened() && (diffs().length > 0 || tabs().all().length > 0 || contextOpen()), ) @@ -829,8 +832,19 @@ export default function Page() { const activeTab = createMemo(() => { const active = tabs().active() if (active) return active - if (diffs().length > 0) return "review" - return tabs().all()[0] ?? "review" + if (reviewTab()) return "review" + + const first = openedTabs()[0] + if (first) return first + if (contextOpen()) return "context" + return "review" + }) + + createEffect(() => { + if (!layout.ready()) return + if (tabs().active()) return + if (diffs().length === 0 && openedTabs().length === 0 && !contextOpen()) return + tabs().setActive(activeTab()) }) const mobileWorking = createMemo(() => status().type !== "idle") @@ -1209,8 +1223,63 @@ export default function Page() { ) } + let scroll: HTMLDivElement | undefined + let frame: number | undefined + let pending: { x: number; y: number } | undefined + + const restoreScroll = () => { + const el = scroll + if (!el) return + + const s = view()?.scroll("context") + if (!s) return + + if (el.scrollTop !== s.y) el.scrollTop = s.y + if (el.scrollLeft !== s.x) el.scrollLeft = s.x + } + + const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => { + pending = { + x: event.currentTarget.scrollLeft, + y: event.currentTarget.scrollTop, + } + if (frame !== undefined) return + + frame = requestAnimationFrame(() => { + frame = undefined + + const next = pending + pending = undefined + if (!next) return + + view().setScroll("context", next) + }) + } + + createEffect( + on( + () => messages().length, + () => { + requestAnimationFrame(restoreScroll) + }, + { defer: true }, + ), + ) + + onCleanup(() => { + if (frame === undefined) return + cancelAnimationFrame(frame) + }) + return ( -
+
{ + scroll = el + restoreScroll() + }} + onScroll={handleScroll} + >
{(stat) => } @@ -1271,6 +1340,79 @@ export default function Page() { ) } + const ReviewTab = () => { + let scroll: HTMLDivElement | undefined + let frame: number | undefined + let pending: { x: number; y: number } | undefined + + const restoreScroll = () => { + const el = scroll + console.log("restoreScroll", el) + if (!el) return + + const s = view().scroll("review") + console.log("restoreScroll", s) + if (!s) return + + console.log("restoreScroll", el.scrollTop, s.y) + if (el.scrollTop !== s.y) el.scrollTop = s.y + if (el.scrollLeft !== s.x) el.scrollLeft = s.x + } + + const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => { + pending = { + x: event.currentTarget.scrollLeft, + y: event.currentTarget.scrollTop, + } + if (frame !== undefined) return + + frame = requestAnimationFrame(() => { + frame = undefined + + const next = pending + pending = undefined + if (!next) return + + view().setScroll("review", next) + }) + } + + createEffect( + on( + () => diffs().length, + () => { + requestAnimationFrame(restoreScroll) + }, + { defer: true }, + ), + ) + + onCleanup(() => { + if (frame === undefined) return + cancelAnimationFrame(frame) + }) + + return ( + { + scroll = el + restoreScroll() + }} + onScroll={handleScroll} + open={view().review.open()} + onOpenChange={view().review.setOpen} + classes={{ + root: "pb-40", + header: "px-6", + container: "px-6", + }} + diffs={diffs()} + diffStyle={layout.review.diffStyle()} + onDiffStyleChange={layout.review.setDiffStyle} + /> + ) + } + return (
@@ -1300,6 +1442,8 @@ export default function Page() { diffs={diffs()} diffStyle={layout.review.diffStyle()} onDiffStyleChange={layout.review.setDiffStyle} + open={view().review.open()} + onOpenChange={view().review.setOpen} classes={{ root: "pb-32", header: "px-4", @@ -1373,7 +1517,7 @@ export default function Page() {
- +
@@ -1425,19 +1569,10 @@ export default function Page() {
- +
- +
@@ -1452,7 +1587,7 @@ export default function Page() { {(tab) => { let scroll: HTMLDivElement | undefined let scrollFrame: number | undefined - let pendingTop: number | undefined + let pending: { x: number; y: number } | undefined const path = createMemo(() => file.pathFromTab(tab)) const state = createMemo(() => { @@ -1480,30 +1615,30 @@ export default function Page() { const restoreScroll = () => { const el = scroll - const p = path() - if (!el || !p) return + if (!el) return + + const s = view()?.scroll(tab) + if (!s) return - const top = file.scrollTop(p) - if (top === undefined) return - if (el.scrollTop === top) return - el.scrollTop = top + if (el.scrollTop !== s.y) el.scrollTop = s.y + if (el.scrollLeft !== s.x) el.scrollLeft = s.x } const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => { - const p = path() - if (!p) return - - pendingTop = event.currentTarget.scrollTop + pending = { + x: event.currentTarget.scrollLeft, + y: event.currentTarget.scrollTop, + } if (scrollFrame !== undefined) return scrollFrame = requestAnimationFrame(() => { scrollFrame = undefined - const top = pendingTop - pendingTop = undefined - if (top === undefined) return + const next = pending + pending = undefined + if (!next) return - file.setScrollTop(p, top) + view().setScroll(tab, next) }) } -- cgit v1.2.3