This component no longer exists. It was in ns-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-07 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Confirm Hold Ink
ns-ui/confirm-hold-inkRemovedComponent
Press-and-hold destructive action — monochrome ink pours up from the press point, release early and it recoils.
Live preview
live · rendered by the registry
Rendered by ns-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://design.helpmarq.com/r/confirm-hold-ink.jsonSource
1"use client";23import { useEffect, useRef, useState, type ReactNode } from "react";45// Press-and-hold destructive action, redesigned as an ink fill: holding pours6// monochrome ink up from the press point with a live meniscus edge and subtle7// grain; pressure microshake builds near the top; releasing early recoils the8// ink elastically; completion pops with one spring overshoot and swaps the9// label. Canvas colors derive from computed CSS tokens (re-derived on theme10// change), rAF sleeps when settled and pauses offscreen, reduced-motion gets a11// plain clean fill. Direct-DOM on the hot path — React state only at confirm.1213type Mode = "idle" | "hold" | "recoil" | "pop";1415export function HoldToConfirm({16 children,17 confirmedLabel = "Done",18 holdMs = 1200,19 onConfirm,20 className = "",21}: {22 children: ReactNode;23 confirmedLabel?: ReactNode;24 holdMs?: number;25 onConfirm?: () => void;26 className?: string;27}) {28 const btnRef = useRef<HTMLButtonElement>(null);29 const canvasRef = useRef<HTMLCanvasElement>(null);30 const [confirmed, setConfirmed] = useState(false);31 const onConfirmRef = useRef(onConfirm);32 onConfirmRef.current = onConfirm;33 const holdMsRef = useRef(holdMs);34 holdMsRef.current = holdMs;3536 const stateRef = useRef({37 mode: "idle" as Mode,38 p: 0, // fill progress 0..139 v: 0, // recoil spring velocity (progress units / s)40 holding: false,41 done: false,42 pressX: 0.5, // normalized press point43 scale: 1,44 sv: 0, // pop spring velocity45 t: 0, // wave clock (ms)46 raf: 0,47 last: 0,48 w: 0,49 h: 0,50 dpr: 1,51 ink: "#ededed",52 grainColor: "#171717",53 reduced: false,54 visible: true,55 noise: null as HTMLCanvasElement | null,56 pattern: null as CanvasPattern | null,57 });5859 useEffect(() => {60 const s = stateRef.current;61 const btn = btnRef.current;62 const canvas = canvasRef.current;63 if (!btn || !canvas) return;64 const ctx = canvas.getContext("2d");65 if (!ctx) return;6667 const makeNoise = () => {68 const n = document.createElement("canvas");69 n.width = 128;70 n.height = 128;71 const nctx = n.getContext("2d");72 if (!nctx) return;73 nctx.fillStyle = s.grainColor;74 for (let i = 0; i < 2400; i++) {75 nctx.globalAlpha = Math.random() * 0.5;76 nctx.fillRect(Math.floor(Math.random() * 128), Math.floor(Math.random() * 128), 1, 1);77 }78 s.noise = n;79 s.pattern = null; // rebuilt lazily against the drawing ctx80 };8182// … truncated
