Files
supabase/apps/ui-library/components/block-item-code.tsx
Ivan Vasilov 640869da47 chore: Clean up remaining Tailwind code (#45925)
This PR finishes the Tailwind migration by doing some minor fixes:
- Remove `@radix-ui/colors` and inline the color values into the color
definitions. The default Tailwind colors are unset and replaced by our
own color set (both in light and dark variants).
- Remove the `colorA` colors because they were used with alpha values.
They were unused and Tailwind v4 supports alpha values natively.
- Replace the `hit-area` JS config with the original CSS config from
https://bazza.dev/craft/2026/hit-area. The original config was migrated
to JS config to work with Tailwind v3. Now that we're on v4, we can just
use the source format.
- Remove the `motion-safe-transition` plugin since it's now [supported
natively by
Tailwind](https://tailwindcss.com/docs/transition-duration#supporting-reduced-motion)
- Replace `tailwindcss-animate` with `tw-animate-css`. The old plugin
was unmaintained and using JS config. The new one should be a drop-in
replacement.
- Remove all scripts for generating colors, they're not needed anymore,
all values are hardcoded.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added hit-area debugging and sizing utilities for enhanced layout
control.

* **Refactor**
* Restructured color system to use hand-edited CSS variables with
inlined values for improved performance and maintainability.
  * Migrated animation utilities to a new framework.

* **Style**
* Enhanced motion-reduced animations with improved transition behavior.
  * Adjusted sidebar layout styling for better visual presentation.

* **Chores**
  * Updated animation dependencies.
  * Removed legacy color generation scripts.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45925)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:26:42 +02:00

122 lines
3.5 KiB
TypeScript

'use client'
import { File } from 'lucide-react'
import { useState } from 'react'
import { flattenTree, TreeView, TreeViewItem } from 'ui'
import { CodeBlock } from 'ui-patterns/CodeBlock'
import { RegistryNode } from '@/lib/process-registry'
interface BlockItemCodeProps {
files: RegistryNode[]
}
interface TreeNode {
name: string
children: TreeNode[]
metadata: { path: string }
}
const flattenChildren = (files: RegistryNode[]): TreeNode[] => {
return files.map(
(node): TreeNode => ({
name: node.name,
children: node.children ? flattenChildren(node.children) : [],
metadata: { path: node.path },
})
)
}
const findFirstFile = (nodes: RegistryNode[]): RegistryNode | null => {
for (const node of nodes) {
if (node.type === 'file') {
return node
}
if (node.children) {
const foundFile = findFirstFile(node.children)
if (foundFile) {
return foundFile
}
}
}
return null
}
export function BlockItemCode({ files }: BlockItemCodeProps) {
// Find the first file to select by default
const [selectedFile, setSelectedFile] = useState<RegistryNode | null>(findFirstFile(files))
const flattenedData = flattenTree({ name: '', children: flattenChildren(files) })
// Handle file selection from the TreeView
const handleNodeSelect = (element: any) => {
const findFileByPath = (nodes: RegistryNode[], path: string): RegistryNode | null => {
for (const node of nodes) {
if (node.path === path) {
return node
}
if (node.children) {
const found = findFileByPath(node.children, path)
if (found) {
return found
}
}
}
return null
}
const filePath = element.metadata.path
const foundFile = findFileByPath(files, filePath)
if (foundFile?.type === 'directory') return
setSelectedFile(foundFile || null)
}
return (
<div className="flex mt-4 border rounded-lg overflow-hidden h-[652px] not-prose">
{/* File browser sidebar */}
<div className="w-64 py-2 border-r bg-muted/30 overflow-y-auto">
<TreeView
data={flattenedData}
aria-label="file browser"
className="w-full"
defaultExpandedIds={flattenedData.filter((n) => n.children?.length).map((n) => n.id)}
defaultSelectedIds={flattenedData
.filter((n) => n.metadata?.path === selectedFile?.path)
.map((n) => n.id)}
onNodeSelect={({ element }) => handleNodeSelect(element)}
nodeRenderer={({ element, isBranch, isExpanded, getNodeProps, level, isSelected }) => (
<TreeViewItem
{...getNodeProps()}
isExpanded={isExpanded}
isBranch={isBranch}
isSelected={isSelected}
level={level}
icon={<File strokeWidth={1.5} size={16} className="shrink-0" />}
name={element.name}
className="gap-1.5"
/>
)}
/>
</div>
{/* Code display area */}
{selectedFile?.content ? (
<CodeBlock
wrapperClassName="w-full"
className="h-full max-w-none w-full! flex-1 font-mono text-xs rounded-none border-none"
language="ts"
>
{selectedFile?.content}
</CodeBlock>
) : (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="flex flex-col items-center gap-2">
<p>No file selected or file content unavailable</p>
</div>
</div>
)}
</div>
)
}