From 4163c1dbea0a8f5d96229f8a8ff622fb69cb889e Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 6 Jul 2026 12:32:29 +0400 Subject: [PATCH] feat(create): pick shuffle presets from a curated list (#11092) * feat(create): pick shuffle presets from a curated list Co-Authored-By: Claude Fable 5 * chore: format Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- apps/v4/app/(app)/create/hooks/use-random.tsx | 72 ++++++++++++++++++- .../app/(app)/create/lib/shuffle-presets.ts | 28 ++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 apps/v4/app/(app)/create/lib/shuffle-presets.ts diff --git a/apps/v4/app/(app)/create/hooks/use-random.tsx b/apps/v4/app/(app)/create/hooks/use-random.tsx index 45867c8a0b..0fa58d8ac5 100644 --- a/apps/v4/app/(app)/create/hooks/use-random.tsx +++ b/apps/v4/app/(app)/create/hooks/use-random.tsx @@ -1,6 +1,7 @@ "use client" import * as React from "react" +import { decodePreset } from "shadcn/preset" import { BASE_COLORS, @@ -10,10 +11,20 @@ import { MENU_COLORS, RADII, STYLES, + type BaseColorName, + type ChartColorName, type FontHeadingValue, + type FontValue, + type IconLibraryName, + type MenuAccentValue, + type MenuColorValue, + type RadiusValue, + type StyleName, + type ThemeName, } from "@/registry/config" import { useLocks } from "@/app/(app)/create/hooks/use-locks" import { FONTS } from "@/app/(app)/create/lib/fonts" +import { getPresetCode } from "@/app/(app)/create/lib/preset-code" import { applyBias, RANDOMIZE_BIASES, @@ -23,6 +34,7 @@ import { isTranslucentMenuColor, useDesignSystemSearchParams, } from "@/app/(app)/create/lib/search-params" +import { SHUFFLE_PRESETS } from "@/app/(app)/create/lib/shuffle-presets" function randomItem(array: readonly T[]): T { return array[Math.floor(Math.random() * array.length)] @@ -37,7 +49,9 @@ export function useRandom() { paramsRef.current = params }, [params]) - const randomize = React.useCallback(() => { + // True-random implementation. Kept, but shuffle is now wired to the + // curated preset list below. + const randomizeTrueRandom = React.useCallback(() => { const selectedStyle = locks.has("style") ? paramsRef.current.style : randomItem(STYLES).name @@ -149,6 +163,60 @@ export function useRandom() { setParams(nextParams) }, [setParams, locks]) + // Picks a random preset from the curated list instead of true random. + // Locked params are preserved over the decoded preset values. + const randomize = React.useCallback(() => { + // Avoid re-picking the preset that is currently applied. + const currentCode = getPresetCode(paramsRef.current) + const availableCodes = SHUFFLE_PRESETS.filter( + (code) => code !== currentCode + ) + const decoded = decodePreset( + randomItem(availableCodes.length > 0 ? availableCodes : SHUFFLE_PRESETS) + ) + + if (!decoded) { + return + } + + const current = paramsRef.current + const nextParams = { + style: locks.has("style") ? current.style : (decoded.style as StyleName), + baseColor: locks.has("baseColor") + ? current.baseColor + : (decoded.baseColor as BaseColorName), + theme: locks.has("theme") ? current.theme : (decoded.theme as ThemeName), + chartColor: locks.has("chartColor") + ? current.chartColor + : ((decoded.chartColor ?? decoded.theme) as ChartColorName), + iconLibrary: locks.has("iconLibrary") + ? current.iconLibrary + : (decoded.iconLibrary as IconLibraryName), + font: locks.has("font") ? current.font : (decoded.font as FontValue), + fontHeading: locks.has("fontHeading") + ? current.fontHeading + : (decoded.fontHeading as FontHeadingValue), + menuAccent: locks.has("menuAccent") + ? current.menuAccent + : (decoded.menuAccent as MenuAccentValue), + menuColor: locks.has("menuColor") + ? current.menuColor + : (decoded.menuColor as MenuColorValue), + radius: locks.has("radius") + ? current.radius + : (decoded.radius as RadiusValue), + } + + // Keep the ref in sync so rapid repeats use the latest randomized state + // even before the URL state finishes committing. + paramsRef.current = { + ...paramsRef.current, + ...nextParams, + } + + setParams(nextParams) + }, [setParams, locks]) + const randomizeRef = React.useRef(randomize) React.useEffect(() => { randomizeRef.current = randomize @@ -177,5 +245,5 @@ export function useRandom() { } }, []) - return { randomize } + return { randomize, randomizeTrueRandom } } diff --git a/apps/v4/app/(app)/create/lib/shuffle-presets.ts b/apps/v4/app/(app)/create/lib/shuffle-presets.ts new file mode 100644 index 0000000000..0f0e4b492e --- /dev/null +++ b/apps/v4/app/(app)/create/lib/shuffle-presets.ts @@ -0,0 +1,28 @@ +// Curated preset codes from https://dialectcn.xyz. +export const SHUFFLE_PRESETS = [ + "b6sUj34d9", + "b2tqYzpa88", + "b1W4tDrk", + "b1aIuQ2XC", + "b7jsW1RxJ5", + "b870VEw0in", + "b3Zheoix4U", + "b1x9M2c4aI", + "b1W7jDEW", + "b51GFh7y6", + "b2fms620zo", + "b1Q5GC", + "buKEvLs", + "b5rR41Mtnc", + "b6tOz2I0x", + "b2hNTREGRN", + "bdIJ7Sq", + "b6TqMNb5Wb", + "bJIirQ", + "b4aRK5K0fb", + "b5HCiD38LI", + "bdHjvCi", + "b7QDHijUjj", + "b4ZVZIPi9h", + "b1W4bcno", +]