Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/ui/alert-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function AlertDialogOverlay({
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-black/80 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
"fixed inset-0 isolate z-50 bg-black/80 duration-100 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function DialogOverlay({
<DialogPrimitive.Backdrop
data-slot="dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-background/80 duration-150 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
"fixed inset-0 isolate z-50 bg-background/80 duration-150 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
)}
{...props}
Expand Down
4 changes: 4 additions & 0 deletions src/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@
animation: var(--ew-vt-enter) ease-out both ew-vt-slide-in;
}

::view-transition-group(ew-main) {
animation: none;
}

::view-transition-group(site-header) {
animation: none;
z-index: 100;
Expand Down
2 changes: 0 additions & 2 deletions src/routes/-hunt/hunt-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,6 @@
<ProcessingReplayPanel
key={replayPanelEpoch}
frames={run.replay.frames}
chunks={run.replay.chunks}
durationMs={run.replay.durationMs}
dag={replayEngine?.dag ?? resolvedEngine.dag}
/>
Expand Down Expand Up @@ -383,7 +382,6 @@
<ProcessingStep
progress={run.progress}
dag={run.activeRunDag ?? resolvedEngine.dag}
bus={run.throughputBus}
/>
<div className="pointer-events-auto absolute bottom-4 left-4 z-10 flex items-center gap-2">
<Button
Expand Down Expand Up @@ -776,7 +774,7 @@
}}
placeholder={STACK_TRACE_PLACEHOLDER}
className="no-scrollbar min-h-0 flex-1 overflow-y-auto max-sm:text-base"
autoFocus

Check warning on line 777 in src/routes/-hunt/hunt-client.tsx

View workflow job for this annotation

GitHub Actions / build

eslint-plugin-jsx-a11y(no-autofocus)

The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.
/>
</div>

Expand Down
93 changes: 47 additions & 46 deletions src/routes/-hunt/processing-dag.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useMemo } from "react"
import { memo, useMemo } from "react"

import { formatDuration } from "@/lib/recent-results"
import type { SimplifyPipelineNode } from "@/lib/simplify/pipeline-dag"
import type { ThroughputBus } from "@/lib/simplify/throughput-bus"
import type {
SimplifyPipelineStepId,
SimplifyPipelineStepStatus,
Expand All @@ -27,9 +26,6 @@ type ProcessingDagProps = {
tier?: DagTier
/** Kept for API compatibility with the previous DAG zoom-to-fit. No-op. */
disableZoom?: boolean
/** Unused by the simplified renderer; retained so the throughput bus can
* continue to be plumbed without churning call sites. */
bus?: ThroughputBus | null
}

type Lane = {
Expand Down Expand Up @@ -97,49 +93,54 @@ export function ProcessingDag({
)
}

function LoadingLane({ lane, nowMs }: { lane: Lane; nowMs: number }) {
const elapsedMs =
lane.endedAtMs !== null && lane.startedAtMs !== null
? Math.max(0, lane.endedAtMs - lane.startedAtMs)
: lane.startedAtMs !== null
? Math.max(0, nowMs - lane.startedAtMs)
: null
const LoadingLane = memo(
function LoadingLane({ lane, nowMs }: { lane: Lane; nowMs: number }) {
const elapsedMs =
lane.endedAtMs !== null && lane.startedAtMs !== null
? Math.max(0, lane.endedAtMs - lane.startedAtMs)
: lane.startedAtMs !== null
? Math.max(0, nowMs - lane.startedAtMs)
: null

return (
<div className="grid grid-cols-[3rem_minmax(0,10rem)_1fr_auto] items-center gap-2 font-mono text-[0.625rem] tracking-wider uppercase">
<span className={cn("shrink-0 tabular-nums", statusTone(lane.status))}>
{statusLabel(lane.status)}
</span>
<span
className={cn(
"min-w-0 truncate",
lane.status === "pending"
? "text-muted-foreground/70"
: "text-foreground/85"
)}
>
{lane.label}
{lane.retries > 0 && (
<span className="ml-1 text-muted-foreground">
×{lane.retries + 1}
</span>
)}
</span>
<div
className="relative h-2 w-full overflow-hidden rounded-full border border-foreground/10 bg-foreground/[0.04]"
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={laneProgress(lane.status)}
>
<LoadingFill status={lane.status} />
return (
<div className="grid grid-cols-[3rem_minmax(0,10rem)_1fr_auto] items-center gap-2 font-mono text-[0.625rem] tracking-wider uppercase">
<span className={cn("shrink-0 tabular-nums", statusTone(lane.status))}>
{statusLabel(lane.status)}
</span>
<span
className={cn(
"min-w-0 truncate",
lane.status === "pending"
? "text-muted-foreground/70"
: "text-foreground/85"
)}
>
{lane.label}
{lane.retries > 0 && (
<span className="ml-1 text-muted-foreground">
×{lane.retries + 1}
</span>
)}
</span>
<div
className="relative h-2 w-full overflow-hidden rounded-full border border-foreground/10 bg-foreground/[0.04]"
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={laneProgress(lane.status)}
>
<LoadingFill status={lane.status} />
</div>
<span className="shrink-0 text-muted-foreground tabular-nums">
{elapsedMs === null ? "—" : formatDuration(elapsedMs)}
</span>
</div>
<span className="shrink-0 text-muted-foreground tabular-nums">
{elapsedMs === null ? "—" : formatDuration(elapsedMs)}
</span>
</div>
)
}
)
},
(previous, next) =>
previous.lane === next.lane &&
(previous.lane.status !== "running" || previous.nowMs === next.nowMs)
)

function LoadingFill({ status }: { status: SimplifyPipelineStepStatus }) {
if (status === "running") {
Expand Down
71 changes: 29 additions & 42 deletions src/routes/-hunt/processing-replay-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,51 @@
import { useEffect, useMemo, useRef, useState } from "react"

import type { SimplifyPipelineNode } from "@/lib/simplify/pipeline-dag"
import {
createThroughputBus,
type ThroughputBus,
} from "@/lib/simplify/throughput-bus"
import type { SimplifyProgressSnapshot } from "@/lib/simplify/types"

import { ProcessingStep } from "./processing-step"
import type { SimplifyReplayChunk, SimplifyReplayFrame } from "./use-hunt-run"
import type { SimplifyReplayFrame } from "./use-hunt-run"

export type ProcessingReplayPanelProps = {
frames: SimplifyReplayFrame[]
chunks: SimplifyReplayChunk[]
durationMs: number
dag: SimplifyPipelineNode[]
}

const REPLAY_RENDER_INTERVAL_MS = 33

/**
* Replays a finished run through the same {@link ProcessingStep} + DAG as a
* live compress: one real-time playback at 1×, chunk-fed bus, no zoom/speed UI.
* live compress: one real-time playback at 1×, no zoom/speed UI.
*/
export function ProcessingReplayPanel({
frames,
chunks,
durationMs,
dag,
}: ProcessingReplayPanelProps) {
const [currentMs, setCurrentMs] = useState(0)
const [playing, setPlaying] = useState(true)

const [bus] = useState<ThroughputBus>(() => createThroughputBus())
const fedIndexRef = useRef(0)
const lastFedMsRef = useRef(0)

useEffect(() => {
if (currentMs < lastFedMsRef.current) {
bus.reset()
fedIndexRef.current = 0
lastFedMsRef.current = 0
}
const now = performance.now()
while (
fedIndexRef.current < chunks.length &&
chunks[fedIndexRef.current].timeMs <= currentMs
) {
const evt = chunks[fedIndexRef.current]
bus.report(evt.stepId, evt.chars, now)
fedIndexRef.current += 1
}
lastFedMsRef.current = currentMs
}, [currentMs, chunks, bus])
const currentMsRef = useRef(0)

useEffect(() => {
if (!playing) return
let rafId = 0
let last = performance.now()
let lastRenderedMs = currentMsRef.current
const tick = (now: number) => {
const dt = now - last
last = now
setCurrentMs((prev) => {
const next = prev + dt
if (next >= durationMs) {
setPlaying(false)
return durationMs
}
return next
})
const next = currentMsRef.current + dt
currentMsRef.current = next
const finished = next >= durationMs
if (finished || next - lastRenderedMs >= REPLAY_RENDER_INTERVAL_MS) {
lastRenderedMs = next
setCurrentMs(Math.min(next, durationMs))
}
if (finished) {
setPlaying(false)
return
}
rafId = requestAnimationFrame(tick)
}
rafId = requestAnimationFrame(tick)
Expand All @@ -75,10 +54,19 @@ export function ProcessingReplayPanel({

const currentSnapshot = useMemo<SimplifyProgressSnapshot | null>(() => {
if (frames.length === 0) return null

let low = 0
let high = frames.length - 1
let chosen = frames[0].snapshot
for (const frame of frames) {
if (frame.timeMs <= currentMs) chosen = frame.snapshot
else break
while (low <= high) {
const middle = Math.floor((low + high) / 2)
const frame = frames[middle]
if (frame.timeMs <= currentMs) {
chosen = frame.snapshot
low = middle + 1
} else {
high = middle - 1
}
}
return chosen
}, [frames, currentMs])
Expand All @@ -90,7 +78,6 @@ export function ProcessingReplayPanel({
<ProcessingStep
progress={currentSnapshot}
dag={dag}
bus={bus}
controlledNowMs={nowMsForDag}
/>
)
Expand Down
14 changes: 2 additions & 12 deletions src/routes/-hunt/processing-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,18 @@ import { useEffect, useState } from "react"
import { formatDuration } from "@/lib/recent-results"
import { getProgressElapsedMs } from "@/lib/simplify/progress"
import type { SimplifyEngineDefinition } from "@/lib/simplify/engines/types"
import {
type SimplifyProgressSnapshot,
type ThroughputBus,
} from "@/lib/simplify/stub"
import type { SimplifyProgressSnapshot } from "@/lib/simplify/stub"

import { ProcessingDag } from "./processing-dag"

export function ProcessingStep({
progress,
dag,
bus,
/** When set, drives the DAG and elapsed readout (replay); no live clock. */
controlledNowMs,
}: {
progress: SimplifyProgressSnapshot | null
dag: SimplifyEngineDefinition["dag"]
bus?: ThroughputBus | null
controlledNowMs?: number
}) {
const [now, setNow] = useState(() =>
Expand Down Expand Up @@ -59,12 +54,7 @@ export function ProcessingStep({
</p>
</div>

<ProcessingDag
progress={progress}
dag={dag}
nowMs={effectiveNowMs}
bus={bus}
/>
<ProcessingDag progress={progress} dag={dag} nowMs={effectiveNowMs} />
</div>
)
}
Loading
Loading