chore: cleanup

This commit is contained in:
adamelmore
2026-01-26 09:55:37 -06:00
parent 801eb5d2cb
commit b8e8d82323
3 changed files with 622 additions and 674 deletions

View File

@@ -22,6 +22,7 @@ export default function FileTree(props: {
nodeClass?: string
level?: number
allowed?: readonly string[]
modified?: readonly string[]
draggable?: boolean
tooltip?: boolean
onFileClick?: (file: FileNode) => void
@@ -50,6 +51,12 @@ export default function FileTree(props: {
return { files, dirs }
})
const marks = createMemo(() => {
const modified = props.modified
if (!modified || modified.length === 0) return
return new Set(modified)
})
createEffect(() => {
const current = filter()
if (!current) return
@@ -89,7 +96,7 @@ export default function FileTree(props: {
<Dynamic
component={local.as ?? "div"}
classList={{
"w-full flex items-center gap-x-2 rounded-md px-2 py-1 hover:bg-surface-raised-base-hover active:bg-surface-base-active transition-colors cursor-pointer": true,
"w-full min-w-0 flex items-center gap-x-2 rounded-md px-2 py-1 hover:bg-surface-raised-base-hover active:bg-surface-base-active transition-colors cursor-pointer": true,
...(local.classList ?? {}),
[local.class ?? ""]: !!local.class,
[props.nodeClass ?? ""]: !!props.nodeClass,
@@ -125,13 +132,16 @@ export default function FileTree(props: {
{local.children}
<span
classList={{
"text-12-regular whitespace-nowrap truncate": true,
"flex-1 min-w-0 text-12-regular whitespace-nowrap truncate": true,
"text-text-weaker": local.node.ignored,
"text-text-weak": !local.node.ignored,
}}
>
{local.node.name}
</span>
{local.node.type === "file" && marks()?.has(local.node.path) ? (
<div class="shrink-0 size-1.5 rounded-full bg-surface-warning-strong" />
) : null}
</Dynamic>
)
}
@@ -173,6 +183,7 @@ export default function FileTree(props: {
path={node.path}
level={level + 1}
allowed={props.allowed}
modified={props.modified}
draggable={props.draggable}
tooltip={props.tooltip}
onFileClick={props.onFileClick}

View File

@@ -425,6 +425,8 @@ export default function Page() {
}
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
const emptyDiffFiles: string[] = []
const diffFiles = createMemo(() => diffs().map((d) => d.file), emptyDiffFiles, { equals: same })
const diffsReady = createMemo(() => {
const id = params.id
if (!id) return true
@@ -1934,47 +1936,9 @@ export default function Page() {
class="relative flex-1 min-w-0 h-full border-l border-border-weak-base flex"
>
<div class="flex-1 min-w-0 h-full">
<Show when={layout.fileTree.opened() && fileTreeTab() === "changes"}>
<div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
<Switch>
<Match when={hasReview()}>
<Show
when={diffsReady()}
when={layout.fileTree.opened() && fileTreeTab() === "changes"}
fallback={
<div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>
}
>
<SessionReviewTab
diffs={diffs}
view={view}
diffStyle={layout.review.diffStyle()}
onDiffStyleChange={layout.review.setDiffStyle}
onScrollRef={setReviewScroll}
onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
comments={comments.all()}
focusedComment={comments.focus()}
onFocusedCommentChange={comments.setFocus}
onViewFile={(path) => {
const value = file.tab(path)
tabs().open(value)
file.load(path)
}}
/>
</Show>
</Match>
<Match when={true}>
<div class="h-full px-6 pb-30 flex flex-col items-center justify-center text-center gap-6">
<Mark class="w-14 opacity-10" />
<div class="text-13-regular text-text-weak max-w-56">No changes in this session yet</div>
</div>
</Match>
</Switch>
</div>
</div>
</Show>
<Show when={!layout.fileTree.opened() || fileTreeTab() === "all"}>
<DragDropProvider
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
@@ -2026,9 +1990,11 @@ export default function Page() {
</Tabs.Trigger>
</Show>
<SortableProvider ids={openedTabs()}>
<For each={openedTabs()}>{(tab) => <SortableTab tab={tab} onTabClose={tabs().close} />}</For>
<For each={openedTabs()}>
{(tab) => <SortableTab tab={tab} onTabClose={tabs().close} />}
</For>
</SortableProvider>
<div class="bg-background-base h-full flex items-center justify-center border-b border-border-weak-base px-3">
<div class="bg-background-base h-full shrink-0 sticky right-0 z-10 flex items-center justify-center border-b border-l border-border-weak-base px-3">
<TooltipKeybind
title={language.t("command.file.open")}
keybind={command.keybind("file.open")}
@@ -2114,13 +2080,13 @@ export default function Page() {
</Show>
</Tabs.Content>
</Show>
<For each={openedTabs()}>
{(tab) => {
let scroll: HTMLDivElement | undefined
let scrollFrame: number | undefined
let pending: { x: number; y: number } | undefined
let codeScroll: HTMLElement[] = []
let focusToken = 0
const path = createMemo(() => file.pathFromTab(tab))
const state = createMemo(() => {
@@ -2184,8 +2150,6 @@ export default function Page() {
const [positions, setPositions] = createSignal<Record<string, number>>({})
const [draftTop, setDraftTop] = createSignal<number | undefined>(undefined)
const empty = {} as Record<string, number>
const commentLabel = (range: SelectedLineRange) => {
const start = Math.min(range.start, range.end)
const end = Math.max(range.start, range.end)
@@ -2219,22 +2183,12 @@ export default function Page() {
return rect.top - wrapperRect.top + Math.max(0, (rect.height - 20) / 2)
}
const equal = (a: Record<string, number>, b: Record<string, number>) => {
const aKeys = Object.keys(a)
const bKeys = Object.keys(b)
if (aKeys.length !== bKeys.length) return false
for (const key of aKeys) {
if (a[key] !== b[key]) return false
}
return true
}
const updateComments = () => {
const el = wrap
const root = getRoot()
if (!el || !root) {
setPositions((prev) => (Object.keys(prev).length === 0 ? prev : empty))
setDraftTop((prev) => (prev === undefined ? prev : undefined))
setPositions({})
setDraftTop(undefined)
return
}
@@ -2245,7 +2199,7 @@ export default function Page() {
next[comment.id] = markerTop(el, marker)
}
setPositions((prev) => (equal(prev, next) ? prev : next))
setPositions(next)
const range = commenting()
if (!range) {
@@ -2259,18 +2213,11 @@ export default function Page() {
return
}
const nextTop = markerTop(el, marker)
setDraftTop((prev) => (prev === nextTop ? prev : nextTop))
setDraftTop(markerTop(el, marker))
}
let commentFrame: number | undefined
const scheduleComments = () => {
if (commentFrame !== undefined) return
commentFrame = requestAnimationFrame(() => {
commentFrame = undefined
updateComments()
})
requestAnimationFrame(updateComments)
}
createEffect(() => {
@@ -2278,13 +2225,9 @@ export default function Page() {
scheduleComments()
})
createEffect(() => {
commenting()
scheduleComments()
})
createEffect(() => {
const range = commenting()
scheduleComments()
if (!range) return
setDraft("")
})
@@ -2299,50 +2242,9 @@ export default function Page() {
const target = fileComments().find((comment) => comment.id === focus.id)
if (!target) return
focusToken++
const token = focusToken
setOpenedComment(target.id)
setCommenting(null)
file.setSelectedLines(p, target.selection)
const scrollTo = (attempt: number) => {
if (token !== focusToken) return
const root = scroll
if (!root) {
if (attempt >= 120) return
requestAnimationFrame(() => scrollTo(attempt + 1))
return
}
const anchor = root.querySelector(`[data-comment-id="${target.id}"]`)
const ready =
anchor instanceof HTMLElement &&
anchor.style.pointerEvents !== "none" &&
anchor.style.opacity !== "0"
const shadow = getRoot()
const marker = shadow ? findMarker(shadow, target.selection) : undefined
const node = (ready ? anchor : (marker ?? wrap)) as HTMLElement | undefined
if (!node) {
if (attempt >= 120) return
requestAnimationFrame(() => scrollTo(attempt + 1))
return
}
const rootRect = root.getBoundingClientRect()
const targetRect = node.getBoundingClientRect()
const offset = targetRect.top - rootRect.top
const next = root.scrollTop + offset - rootRect.height / 2 + targetRect.height / 2
root.scrollTop = Math.max(0, next)
if (ready || marker) return
if (attempt >= 120) return
requestAnimationFrame(() => scrollTo(attempt + 1))
}
requestAnimationFrame(() => scrollTo(0))
requestAnimationFrame(() => comments.clearFocus())
})
@@ -2392,6 +2294,8 @@ export default function Page() {
id={comment.id}
top={positions()[comment.id]}
open={openedComment() === comment.id}
comment={comment.comment}
selection={commentLabel(comment.selection)}
onMouseEnter={() => {
const p = path()
if (!p) return
@@ -2404,8 +2308,6 @@ export default function Page() {
setOpenedComment((current) => (current === comment.id ? null : comment.id))
file.setSelectedLines(p, comment.selection)
}}
comment={comment.comment}
selection={commentLabel(comment.selection)}
/>
)}
</For>
@@ -2416,28 +2318,26 @@ export default function Page() {
top={draftTop()}
value={draft()}
selection={commentLabel(range())}
onInput={setDraft}
onInput={(value) => setDraft(value)}
onCancel={() => setCommenting(null)}
onSubmit={(comment) => {
onSubmit={(value) => {
const p = path()
if (!p) return
addCommentToContext({
file: p,
selection: range(),
comment,
comment: value,
origin: "file",
})
setCommenting(null)
}}
onPopoverFocusOut={(e) => {
const target = e.relatedTarget as Node | null
if (target && e.currentTarget.contains(target)) return
// Delay to allow click handlers to fire first
onPopoverFocusOut={(e: FocusEvent) => {
const current = e.currentTarget as HTMLDivElement
const target = e.relatedTarget
if (target instanceof Node && current.contains(target)) return
setTimeout(() => {
if (
!document.activeElement ||
!e.currentTarget.contains(document.activeElement)
) {
if (!document.activeElement || !current.contains(document.activeElement)) {
setCommenting(null)
}
}, 0)
@@ -2572,7 +2472,6 @@ export default function Page() {
)
onCleanup(() => {
if (commentFrame !== undefined) cancelAnimationFrame(commentFrame)
for (const item of codeScroll) {
item.removeEventListener("scroll", handleCodeScroll)
}
@@ -2638,6 +2537,45 @@ export default function Page() {
</Show>
</DragOverlay>
</DragDropProvider>
}
>
<div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
<Switch>
<Match when={hasReview()}>
<Show
when={diffsReady()}
fallback={
<div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>
}
>
<SessionReviewTab
diffs={diffs}
view={view}
diffStyle={layout.review.diffStyle()}
onDiffStyleChange={layout.review.setDiffStyle}
onScrollRef={setReviewScroll}
onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
comments={comments.all()}
focusedComment={comments.focus()}
onFocusedCommentChange={comments.setFocus}
onViewFile={(path) => {
const value = file.tab(path)
tabs().open(value)
file.load(path)
}}
/>
</Show>
</Match>
<Match when={true}>
<div class="h-full px-6 pb-30 flex flex-col items-center justify-center text-center gap-6">
<Mark class="w-14 opacity-10" />
<div class="text-13-regular text-text-weak max-w-56">No changes in this session yet</div>
</div>
</Match>
</Switch>
</div>
</div>
</Show>
</div>
@@ -2647,7 +2585,7 @@ export default function Page() {
<Tabs variant="pill" value={fileTreeTab()} onChange={setFileTreeTabValue} class="h-full">
<Tabs.List>
<Tabs.Trigger value="changes" class="flex-1" classes={{ button: "w-full" }}>
Changes
{reviewCount()} {reviewCount() === 1 ? "Change" : "Changes"}
</Tabs.Trigger>
<Tabs.Trigger value="all" class="flex-1" classes={{ button: "w-full" }}>
All files
@@ -2662,7 +2600,7 @@ export default function Page() {
>
<FileTree
path=""
allowed={diffs().map((d) => d.file)}
allowed={diffFiles()}
draggable={false}
tooltip={false}
onFileClick={(node) => focusReviewDiff(node.path)}
@@ -2675,7 +2613,7 @@ export default function Page() {
</Switch>
</Tabs.Content>
<Tabs.Content value="all" class="bg-background-base p-2">
<FileTree path="" onFileClick={(node) => openTab(file.tab(node.path))} />
<FileTree path="" modified={diffFiles()} onFileClick={(node) => openTab(file.tab(node.path))} />
</Tabs.Content>
</Tabs>
</div>

View File

@@ -219,7 +219,6 @@
height: auto;
padding: 6px;
gap: 4px;
border-bottom: 1px solid var(--border-weak-base);
background-color: var(--background-base);
&::after {
@@ -230,7 +229,7 @@
[data-slot="tabs-trigger-wrapper"] {
height: 32px;
border: none;
border-radius: 999px;
border-radius: var(--radius-sm);
background-color: transparent;
gap: 0;