- {info()?.summary?.files ?? 0}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Review
+
+
+ {info()?.summary?.files ?? 0}
+
+
-
-
+
+
+
+
+
+ tabs().close("context")} />
+
+ }
+ hideCloseButton
+ >
+
+
+
+
+ {(tab) => }
+
+
+
+ dialog.show(() => )}
+ />
+
-
+
+
+
+
+
+
+
+
-
- tabs().close("context")} />
-
- }
- hideCloseButton
- >
-
-
-
Context
+
+
+
-
+
-
- {(tab) => }
-
-
-
- dialog.show(() => )}
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {(tab) => {
- let scroll: HTMLDivElement | undefined
- let scrollFrame: number | undefined
- let pending: { x: number; y: number } | undefined
-
- const path = createMemo(() => file.pathFromTab(tab))
- const state = createMemo(() => {
- const p = path()
- if (!p) return
- return file.get(p)
- })
- const contents = createMemo(() => state()?.content?.content ?? "")
- const cacheKey = createMemo(() => checksum(contents()))
- const isImage = createMemo(() => {
- const c = state()?.content
- return (
- c?.encoding === "base64" && c?.mimeType?.startsWith("image/") && c?.mimeType !== "image/svg+xml"
- )
- })
- const isSvg = createMemo(() => {
- const c = state()?.content
- return c?.mimeType === "image/svg+xml"
- })
- const svgContent = createMemo(() => {
- if (!isSvg()) return
- const c = state()?.content
- if (!c) return
- if (c.encoding === "base64") return base64Decode(c.content)
- return c.content
- })
- const svgPreviewUrl = createMemo(() => {
- if (!isSvg()) return
- const c = state()?.content
- if (!c) return
- if (c.encoding === "base64") return `data:image/svg+xml;base64,${c.content}`
- return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(c.content)}`
- })
- const imageDataUrl = createMemo(() => {
- if (!isImage()) return
- const c = state()?.content
- return `data:${c?.mimeType};base64,${c?.content}`
- })
- const selectedLines = createMemo(() => {
- const p = path()
- if (!p) return null
- return file.selectedLines(p) ?? null
- })
- const selection = createMemo(() => {
- const range = selectedLines()
- if (!range) return
- return selectionFromLines(range)
- })
- const selectionLabel = createMemo(() => {
- const sel = selection()
- if (!sel) return
- if (sel.startLine === sel.endLine) return `L${sel.startLine}`
- return `L${sel.startLine}-${sel.endLine}`
- })
-
- const restoreScroll = (retries = 0) => {
- const el = scroll
- if (!el) return
-
- const s = view()?.scroll(tab)
- if (!s) return
-
- // Wait for content to be scrollable - content may not have rendered yet
- if (el.scrollHeight <= el.clientHeight && retries < 10) {
- requestAnimationFrame(() => restoreScroll(retries + 1))
- return
- }
-
- if (el.scrollTop !== s.y) el.scrollTop = s.y
- if (el.scrollLeft !== s.x) el.scrollLeft = s.x
- }
+
+ {(tab) => {
+ let scroll: HTMLDivElement | undefined
+ let scrollFrame: number | undefined
+ let pending: { x: number; y: number } | undefined
+
+ const path = createMemo(() => file.pathFromTab(tab))
+ const state = createMemo(() => {
+ const p = path()
+ if (!p) return
+ return file.get(p)
+ })
+ const contents = createMemo(() => state()?.content?.content ?? "")
+ const cacheKey = createMemo(() => checksum(contents()))
+ const isImage = createMemo(() => {
+ const c = state()?.content
+ return (
+ c?.encoding === "base64" &&
+ c?.mimeType?.startsWith("image/") &&
+ c?.mimeType !== "image/svg+xml"
+ )
+ })
+ const isSvg = createMemo(() => {
+ const c = state()?.content
+ return c?.mimeType === "image/svg+xml"
+ })
+ const svgContent = createMemo(() => {
+ if (!isSvg()) return
+ const c = state()?.content
+ if (!c) return
+ if (c.encoding === "base64") return base64Decode(c.content)
+ return c.content
+ })
+ const svgPreviewUrl = createMemo(() => {
+ if (!isSvg()) return
+ const c = state()?.content
+ if (!c) return
+ if (c.encoding === "base64") return `data:image/svg+xml;base64,${c.content}`
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(c.content)}`
+ })
+ const imageDataUrl = createMemo(() => {
+ if (!isImage()) return
+ const c = state()?.content
+ return `data:${c?.mimeType};base64,${c?.content}`
+ })
+ const selectedLines = createMemo(() => {
+ const p = path()
+ if (!p) return null
+ return file.selectedLines(p) ?? null
+ })
+ const selection = createMemo(() => {
+ const range = selectedLines()
+ if (!range) return
+ return selectionFromLines(range)
+ })
+ const selectionLabel = createMemo(() => {
+ const sel = selection()
+ if (!sel) return
+ if (sel.startLine === sel.endLine) return `L${sel.startLine}`
+ return `L${sel.startLine}-${sel.endLine}`
+ })
+
+ const restoreScroll = (retries = 0) => {
+ const el = scroll
+ if (!el) return
+
+ const s = view()?.scroll(tab)
+ if (!s) return
+
+ // Wait for content to be scrollable - content may not have rendered yet
+ if (el.scrollHeight <= el.clientHeight && retries < 10) {
+ requestAnimationFrame(() => restoreScroll(retries + 1))
+ 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 (scrollFrame !== undefined) return
+ const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => {
+ pending = {
+ x: event.currentTarget.scrollLeft,
+ y: event.currentTarget.scrollTop,
+ }
+ if (scrollFrame !== undefined) return
- scrollFrame = requestAnimationFrame(() => {
- scrollFrame = undefined
+ scrollFrame = requestAnimationFrame(() => {
+ scrollFrame = undefined
- const next = pending
- pending = undefined
- if (!next) return
+ const next = pending
+ pending = undefined
+ if (!next) return
- view().setScroll(tab, next)
- })
- }
+ view().setScroll(tab, next)
+ })
+ }
- createEffect(
- on(
- () => state()?.loaded,
- (loaded) => {
- if (!loaded) return
- requestAnimationFrame(restoreScroll)
- },
- { defer: true },
- ),
- )
-
- createEffect(
- on(
- () => file.ready(),
- (ready) => {
- if (!ready) return
- requestAnimationFrame(restoreScroll)
- },
- { defer: true },
- ),
- )
-
- createEffect(
- on(
- () => tabs().active() === tab,
- (active) => {
- if (!active) return
- if (!state()?.loaded) return
- requestAnimationFrame(restoreScroll)
- },
- ),
- )
-
- onCleanup(() => {
- if (scrollFrame === undefined) return
- cancelAnimationFrame(scrollFrame)
- })
-
- return (
- {
- scroll = el
- restoreScroll()
- }}
- onScroll={handleScroll}
- >
-
- {(sel) => (
-
-
-
- )}
-
-
-
-
-
})
-
-
-
-
-
{
- const p = path()
- if (!p) return
- file.setSelectedLines(p, range)
- }}
- overflow="scroll"
- class="select-text"
- />
-
-
-
})
+ createEffect(
+ on(
+ () => state()?.loaded,
+ (loaded) => {
+ if (!loaded) return
+ requestAnimationFrame(restoreScroll)
+ },
+ { defer: true },
+ ),
+ )
+
+ createEffect(
+ on(
+ () => file.ready(),
+ (ready) => {
+ if (!ready) return
+ requestAnimationFrame(restoreScroll)
+ },
+ { defer: true },
+ ),
+ )
+
+ createEffect(
+ on(
+ () => tabs().active() === tab,
+ (active) => {
+ if (!active) return
+ if (!state()?.loaded) return
+ requestAnimationFrame(restoreScroll)
+ },
+ ),
+ )
+
+ onCleanup(() => {
+ if (scrollFrame === undefined) return
+ cancelAnimationFrame(scrollFrame)
+ })
+
+ return (
+
{
+ scroll = el
+ restoreScroll()
+ }}
+ onScroll={handleScroll}
+ >
+
+ {(sel) => (
+
+
-
-
-
-
- {
- const p = path()
- if (!p) return
- file.setSelectedLines(p, range)
- }}
- overflow="scroll"
- class="select-text pb-40"
- />
-
-
- Loading...
-
-
- {(err) => {err()}
}
-
-
-
- )
- }}
-
-
-
-
- {(tab) => {
- const path = createMemo(() => file.pathFromTab(tab()))
- return (
-
- {(p) => }
-
- )
- }}
-
-
-
+ )}
+
+
+
+
+
})
+
+
+
+
+
{
+ const p = path()
+ if (!p) return
+ file.setSelectedLines(p, range)
+ }}
+ overflow="scroll"
+ class="select-text"
+ />
+
+
+
})
+
+
+
+
+
+ {
+ const p = path()
+ if (!p) return
+ file.setSelectedLines(p, range)
+ }}
+ overflow="scroll"
+ class="select-text pb-40"
+ />
+
+
+ Loading...
+
+
+ {(err) => {err()}
}
+
+
+
+ )
+ }}
+
+
+
+
+ {(tab) => {
+ const path = createMemo(() => file.pathFromTab(tab()))
+ return (
+
+ {(p) => }
+
+ )
+ }}
+
+
+
+
+
+
+
+
-