summaryrefslogtreecommitdiff
path: root/apps/web/src/actions/components
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-28 19:41:14 +0200
committercashmere <cashmere@autistici.org>2026-07-28 19:41:14 +0200
commite837e7504379e100ff3b56282577ec67bb88bec9 (patch)
treeeec7e6f964725a783a9e907e931862591bb46b55 /apps/web/src/actions/components
parentda99dec8a2de1f3849aebbf5695c1e812b2dbdf9 (diff)
downloadopencut-e837e7504379e100ff3b56282577ec67bb88bec9.tar.gz
feat: ripple-extend resize, timeline filmstrip, export hardening
Yesterday's verified web session (gates green: 316 tests, tsc, build): - ripple-extend pushes right-hand chain on static-element resize - per-position video filmstrip thumbnails with zoom-adaptive density - fps-aware export bitrate, 2s keyframes, even-dim export canvas - freeze frame uses fixed 3s duration, prompt removed
Diffstat (limited to 'apps/web/src/actions/components')
-rw-r--r--apps/web/src/actions/components/freeze-frame-dialog.tsx109
1 files changed, 0 insertions, 109 deletions
diff --git a/apps/web/src/actions/components/freeze-frame-dialog.tsx b/apps/web/src/actions/components/freeze-frame-dialog.tsx
deleted file mode 100644
index a6a7e0ae..00000000
--- a/apps/web/src/actions/components/freeze-frame-dialog.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-"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 (
- <Dialog
- open={isOpen}
- onOpenChange={(open) => {
- if (!open) {
- cancelDuration();
- }
- }}
- >
- <DialogContent className="sm:max-w-md">
- <DialogHeader>
- <DialogTitle>Freeze frame</DialogTitle>
- <DialogDescription>
- How long should the frozen frame stay on screen?
- </DialogDescription>
- </DialogHeader>
- {isOpen && <FreezeFrameForm />}
- </DialogContent>
- </Dialog>
- );
-}
-
-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 (
- <>
- <DialogBody className="gap-3">
- <Label htmlFor="freeze-frame-duration">Duration (seconds)</Label>
- <Input
- id="freeze-frame-duration"
- value={value}
- inputMode="decimal"
- aria-invalid={showError}
- onChange={(e) => setValue(e.target.value)}
- onKeyDown={(e) => {
- if (e.key === "Enter") {
- e.preventDefault();
- handleConfirm();
- }
- }}
- placeholder="e.g. 2.5"
- />
- {showError && (
- <p className="text-destructive text-xs">
- Enter a duration greater than 0 and at most{" "}
- {MAX_FREEZE_DURATION_SECONDS} seconds.
- </p>
- )}
- </DialogBody>
-
- <DialogFooter>
- <Button
- variant="outline"
- onClick={(e) => {
- e.preventDefault();
- e.stopPropagation();
- cancelDuration();
- }}
- >
- Cancel
- </Button>
- <Button onClick={handleConfirm} disabled={!canConfirm}>
- Freeze
- </Button>
- </DialogFooter>
- </>
- );
-}