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.
Consent Scope Redact
ns-ui/consent-scope-redactRemovedComponent
A consent surface that shows instead of tells: a live sentence templated from the user's own record, where each sharing scope is a real switch and toggling it sweeps a felt-pen redaction bar over exactly the tokens that scope withholds.
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/consent-scope-redact.jsonSource
1"use client";23import { useId, useState } from "react";45/** One sharing scope, rendered as a real switch. Toggling it redacts/reveals every token tagged with its `id`. */6export interface LampBlackScope {7 id: string;8 /** Visible + accessible label for the switch, e.g. "Location". */9 label: string;10 /** Optional one-line clarifier under the label. */11 hint?: string;12 /** Whether this scope is shared (visible) at mount. Defaults to true. */13 defaultShared?: boolean;14}1516/** A redactable span of the record — literal text tied to a scope. */17export interface LampBlackToken {18 /** Literal text of this token exactly as it appears in the record. */19 text: string;20 /** Which scope's switch controls this token. Must match a `LampBlackScope.id`. */21 scope: string;22 /** Short noun used in the a11y string "[withheld: kind]" — defaults to `scope`. */23 kind?: string;24}2526/** A part of the templated sentence: plain connective text, or a token tied to a scope. */27export type LampBlackPart = string | LampBlackToken;2829export interface LampBlackProps {30 /** The switches — one per sharing scope. */31 scopes: LampBlackScope[];32 /** The sentence, templated as an ordered list of literal text and scope-tagged tokens. */33 record: LampBlackPart[];34 className?: string;35}3637const SWEEP_MS = 260;38const STAGGER_MS = 40;3940function isToken(part: LampBlackPart): part is LampBlackToken {41 return typeof part !== "string";42}4344// Deterministic +/-0.3deg per bar so the strokes don't all sit dead-level — a45// hand-laid feel, not randomness that would differ mount to mount.46function rotationFor(text: string, groupIndex: number): number {47 const sign = (text.length + groupIndex) % 2 === 0 ? 1 : -1;48 return sign * 0.3;49}5051/**52 * A consent surface that shows instead of tells: a live sentence built from53 * the user's own record, where each sharing scope is a real switch and54 * toggling one sweeps a felt-pen redaction bar over exactly the tokens that55 * scope withholds. The bar is the consent document.56 */57export function LampBlack({ scopes, record, className = "" }: LampBlackProps) {58 const [shared, setShared] = useState<Record<string, boolean>>(() =>59 Object.fromEntries(scopes.map((s) => [s.id, s.defaultShared ?? true])),60 );61 const uid = useId();6263 const toggle = (id: string) => {64 setShared((prev) => ({ ...prev, [id]: !prev[id] }));65 };6667 // per-scope running counts, for the 40ms group stagger68 const groupCounts: Record<string, number> = {};6970// … truncated
