From 2bbada7d2c5c00c5f844b6916c08bc4a9ea8cb25 Mon Sep 17 00:00:00 2001 From: cashmere Date: Mon, 27 Jul 2026 01:21:02 +0200 Subject: feat: freeze-duration prompt Freeze frame now asks for the duration (decimal seconds, 0 < d <= 600) before capture, via a promise-based zustand store mounted as an editor-global dialog. Last-used duration persists and prefills. Cancel aborts before capture with no commands. --- .../src/actions/components/freeze-frame-dialog.tsx | 109 +++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 apps/web/src/actions/components/freeze-frame-dialog.tsx (limited to 'apps/web/src/actions/components') diff --git a/apps/web/src/actions/components/freeze-frame-dialog.tsx b/apps/web/src/actions/components/freeze-frame-dialog.tsx new file mode 100644 index 00000000..a6a7e0ae --- /dev/null +++ b/apps/web/src/actions/components/freeze-frame-dialog.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogBody, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + isValidFreezeDuration, + MAX_FREEZE_DURATION_SECONDS, + useFreezeFrameStore, +} from "@/actions/freeze-frame-store"; + +export function FreezeFrameDialog() { + const isOpen = useFreezeFrameStore((state) => state.isOpen); + const cancelDuration = useFreezeFrameStore((state) => state.cancelDuration); + + return ( + { + if (!open) { + cancelDuration(); + } + }} + > + + + Freeze frame + + How long should the frozen frame stay on screen? + + + {isOpen && } + + + ); +} + +function FreezeFrameForm() { + const lastDurationSeconds = useFreezeFrameStore( + (state) => state.lastDurationSeconds, + ); + const confirmDuration = useFreezeFrameStore((state) => state.confirmDuration); + const cancelDuration = useFreezeFrameStore((state) => state.cancelDuration); + const [value, setValue] = useState(String(lastDurationSeconds)); + + const seconds = Number(value); + const showError = value.trim() !== "" && !isValidFreezeDuration({ seconds }); + const canConfirm = isValidFreezeDuration({ seconds }); + + const handleConfirm = () => { + if (canConfirm) { + confirmDuration({ seconds }); + } + }; + + return ( + <> + + + setValue(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleConfirm(); + } + }} + placeholder="e.g. 2.5" + /> + {showError && ( +

+ Enter a duration greater than 0 and at most{" "} + {MAX_FREEZE_DURATION_SECONDS} seconds. +

+ )} +
+ + + + + + + ); +} -- cgit v1.3.1