diff --git a/apps/platform/lib/registry-catalog.ts b/apps/platform/lib/registry-catalog.ts index 40f50b7..8eb4146 100644 --- a/apps/platform/lib/registry-catalog.ts +++ b/apps/platform/lib/registry-catalog.ts @@ -1,6 +1,5 @@ import { getComponentLinks } from "@/lib/docs-navigation"; import registryData from "./registry-data.json"; -import { customShowcaseItems } from "./custom-showcase"; export type ShowcaseComponent = { slug: string; @@ -29,7 +28,7 @@ export async function getRegistryCatalog(): Promise { getComponentLinks().map((item) => [item.slug, item] as const), ); - const items = [...registryData, ...customShowcaseItems].map((component) => { + const items = registryData.map((component) => { const slug = component.slug; const preset = docsMeta.get(slug); diff --git a/apps/platform/lib/registry-data.json b/apps/platform/lib/registry-data.json index 3f2fae1..73babea 100644 --- a/apps/platform/lib/registry-data.json +++ b/apps/platform/lib/registry-data.json @@ -92,5 +92,17 @@ "registryDependencies": [], "sourcePath": "components/ui/textarea.tsx", "source": "import * as React from 'react';\nimport { StyleSheet, TextInput, type TextInputProps } from 'react-native';\n\ntype TextareaProps = TextInputProps & {\n className?: string;\n variant?: 'default' | 'ghost';\n};\n\nfunction Textarea({ style, variant = 'default', editable = true, ...props }: TextareaProps) {\n return (\n \n );\n}\n\nconst styles = StyleSheet.create({\n base: {\n borderRadius: 8,\n color: '#09090b',\n fontSize: 14,\n minHeight: 128,\n paddingHorizontal: 12,\n paddingVertical: 12,\n width: '100%',\n },\n default: {\n backgroundColor: '#ffffff',\n borderColor: '#e4e4e7',\n borderWidth: 1,\n },\n ghost: {\n backgroundColor: '#f4f4f5',\n borderColor: 'transparent',\n borderWidth: 1,\n },\n disabled: {\n opacity: 0.5,\n },\n});\n\nexport { Textarea };\nexport type { TextareaProps };\n" + }, + { + "slug": "spotlight-button", + "name": "spotlight-button", + "dependencies": [ + "class-variance-authority", + "lucide-react", + "motion" + ], + "registryDependencies": [], + "sourcePath": "components/animated/spotlight-button.tsx", + "source": "import { useEffect, useMemo, useRef } from \"react\";\nimport {\n Animated,\n Easing,\n Pressable,\n StyleSheet,\n View,\n type PressableProps,\n type StyleProp,\n type ViewStyle,\n} from \"react-native\";\n\nimport { Colors } from \"@/constants/theme\";\nimport { useColorScheme } from \"@/hooks/use-color-scheme\";\nimport { Text } from \"@/registry/components/ui/text\";\n\ntype SpotlightButtonVariant = \"default\" | \"neutral\";\ntype SpotlightButtonSize = \"default\" | \"lg\";\n\ntype SpotlightButtonProps = Omit & {\n badge?: string;\n children: React.ReactNode;\n variant?: SpotlightButtonVariant;\n size?: SpotlightButtonSize;\n};\n\nfunction getPalette(theme: \"light\" | \"dark\", variant: SpotlightButtonVariant) {\n if (variant === \"neutral\") {\n return theme === \"dark\"\n ? {\n background: \"#18181b\",\n border: \"rgba(255,255,255,0.10)\",\n text: \"#f4f4f5\",\n badgeBackground: \"rgba(255,255,255,0.08)\",\n badgeBorder: \"rgba(255,255,255,0.14)\",\n badgeText: \"#d4d4d8\",\n glow: \"rgba(255,255,255,0.12)\",\n shadow: \"rgba(0,0,0,0.45)\",\n }\n : {\n background: \"#ffffff\",\n border: \"rgba(15,23,42,0.10)\",\n text: \"#111827\",\n badgeBackground: \"rgba(15,23,42,0.05)\",\n badgeBorder: \"rgba(15,23,42,0.08)\",\n badgeText: \"#475569\",\n glow: \"rgba(59,130,246,0.14)\",\n shadow: \"rgba(15,23,42,0.14)\",\n };\n }\n\n return theme === \"dark\"\n ? {\n background: \"#60a5fa\",\n border: \"rgba(191,219,254,0.45)\",\n text: \"#08111f\",\n badgeBackground: \"rgba(255,255,255,0.30)\",\n badgeBorder: \"rgba(255,255,255,0.35)\",\n badgeText: \"#0f172a\",\n glow: \"rgba(96,165,250,0.34)\",\n shadow: \"rgba(37,99,235,0.34)\",\n }\n : {\n background: Colors.light.tint,\n border: \"rgba(125,211,252,0.45)\",\n text: \"#eff6ff\",\n badgeBackground: \"rgba(255,255,255,0.18)\",\n badgeBorder: \"rgba(255,255,255,0.22)\",\n badgeText: \"#e0f2fe\",\n glow: \"rgba(14,165,233,0.24)\",\n shadow: \"rgba(14,165,233,0.28)\",\n };\n}\n\nfunction getContainerStyle(size: SpotlightButtonSize): StyleProp {\n if (size === \"lg\") return styles.containerLg;\n return styles.containerDefault;\n}\n\nexport function SpotlightButton({\n badge = \"New\",\n children,\n variant = \"default\",\n size = \"default\",\n style,\n ...props\n}: SpotlightButtonProps) {\n const theme = useColorScheme() ?? \"light\";\n const shimmerX = useRef(new Animated.Value(-220)).current;\n const glowPulse = useRef(new Animated.Value(0.88)).current;\n const palette = useMemo(() => getPalette(theme, variant), [theme, variant]);\n\n useEffect(() => {\n const shimmerLoop = Animated.loop(\n Animated.timing(shimmerX, {\n toValue: 220,\n duration: 2600,\n easing: Easing.linear,\n useNativeDriver: true,\n }),\n );\n\n const glowLoop = Animated.loop(\n Animated.sequence([\n Animated.timing(glowPulse, {\n toValue: 1.06,\n duration: 1400,\n easing: Easing.inOut(Easing.ease),\n useNativeDriver: true,\n }),\n Animated.timing(glowPulse, {\n toValue: 0.88,\n duration: 1400,\n easing: Easing.inOut(Easing.ease),\n useNativeDriver: true,\n }),\n ]),\n );\n\n shimmerLoop.start();\n glowLoop.start();\n\n return () => {\n shimmerLoop.stop();\n glowLoop.stop();\n };\n }, [glowPulse, shimmerX]);\n\n return (\n {\n const { pressed } = state;\n const resolvedStyle =\n typeof style === \"function\" ? style(state) : style;\n\n return [\n styles.wrapper,\n {\n transform: [{ scale: pressed ? 0.985 : 1 }],\n opacity: props.disabled ? 0.55 : 1,\n },\n resolvedStyle,\n ];\n }}\n >\n {({ pressed }) => (\n \n \n \n \n \n {badge}\n \n \n \n \n {children}\n \n \n \n \n )}\n \n );\n}\n\nconst styles = StyleSheet.create({\n wrapper: {\n borderRadius: 18,\n },\n containerBase: {\n position: \"relative\",\n overflow: \"hidden\",\n borderRadius: 18,\n borderWidth: 1,\n flexDirection: \"row\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n gap: 12,\n shadowOffset: { width: 0, height: 18 },\n shadowOpacity: 0.24,\n shadowRadius: 26,\n elevation: 6,\n },\n containerDefault: {\n minHeight: 52,\n paddingHorizontal: 18,\n paddingVertical: 14,\n },\n containerLg: {\n minHeight: 58,\n paddingHorizontal: 22,\n paddingVertical: 16,\n },\n containerPressed: {\n opacity: 0.96,\n },\n glow: {\n position: \"absolute\",\n left: -24,\n right: -24,\n bottom: -14,\n height: 44,\n borderRadius: 999,\n },\n shimmer: {\n position: \"absolute\",\n top: -10,\n bottom: -10,\n width: 84,\n backgroundColor: \"rgba(255,255,255,0.20)\",\n },\n badge: {\n borderRadius: 999,\n borderWidth: 1,\n paddingHorizontal: 10,\n paddingVertical: 5,\n },\n badgeText: {\n fontSize: 10,\n fontWeight: \"700\",\n letterSpacing: 1.3,\n textTransform: \"uppercase\",\n },\n labelRow: {\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 10,\n },\n labelText: {\n fontSize: 15,\n fontWeight: \"700\",\n },\n arrow: {\n fontSize: 16,\n fontWeight: \"700\",\n },\n});\n" } ] \ No newline at end of file diff --git a/apps/platform/scripts/generate-registry.mjs b/apps/platform/scripts/generate-registry.mjs index 26805bb..043f4f3 100644 --- a/apps/platform/scripts/generate-registry.mjs +++ b/apps/platform/scripts/generate-registry.mjs @@ -5,12 +5,13 @@ async function generateRegistryData() { const root = process.cwd(); const registryRoot = path.join(root, "..", "..", "packages", "registry"); const registryPath = path.join(registryRoot, "registry.json"); + const showcaseRoot = path.join(root, "..", "showcase"); console.log("Reading registry from:", registryPath); const registryRaw = await readFile(registryPath, "utf8"); const registry = JSON.parse(registryRaw); - const items = await Promise.all( + const registryItems = await Promise.all( Object.entries(registry.components).map(async ([slug, component]) => { const primaryFile = component.files[0]; const sourcePath = path.join(registryRoot, "src", primaryFile.path); @@ -27,6 +28,26 @@ async function generateRegistryData() { }) ); + const spotlightSourcePath = path.join( + showcaseRoot, + "components", + "animated", + "spotlight-button.tsx", + ); + const spotlightSource = await readFile(spotlightSourcePath, "utf8"); + + const items = [ + ...registryItems, + { + slug: "spotlight-button", + name: "spotlight-button", + dependencies: ["class-variance-authority", "lucide-react", "motion"], + registryDependencies: [], + sourcePath: "components/animated/spotlight-button.tsx", + source: spotlightSource, + }, + ]; + const outputPath = path.join(root, "lib", "registry-data.json"); await writeFile(outputPath, JSON.stringify(items, null, 2)); console.log("Generated registry data to:", outputPath); diff --git a/apps/platform/wrangler.toml b/apps/platform/wrangler.toml index 3e9763a..9d8c102 100644 --- a/apps/platform/wrangler.toml +++ b/apps/platform/wrangler.toml @@ -1,6 +1,7 @@ name = "watermelon-platform" main = ".open-next/worker.js" -compatibility_date = "2024-11-01" +compatibility_date = "2026-03-19" +compatibility_flags = ["nodejs_compat"] [vars] NEXT_TELEMETRY_DISABLED = "1" @@ -9,5 +10,8 @@ NEXT_TELEMETRY_DISABLED = "1" directory = ".open-next/assets" binding = "ASSETS" +[observability] +enabled = true + [[package_manager_names]] package_manager = "pnpm"