feat: migrate DocumentationView to JSON

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-21 05:04:02 +00:00
parent 9fb6c4a6ab
commit 6df4f162bb
53 changed files with 697 additions and 1414 deletions

View File

@@ -1,73 +0,0 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Input } from '@/components/ui/input'
import { BookOpen, GitBranch, MagnifyingGlass, MapPin, Sparkle, PaintBrush, Rocket } from '@phosphor-icons/react'
import { AgentsTab } from './DocumentationView/AgentsTab'
import { CicdTab } from './DocumentationView/CicdTab'
import { PwaTab } from './DocumentationView/PwaTab'
import { ReadmeTab } from './DocumentationView/ReadmeTab'
import { RoadmapTab } from './DocumentationView/RoadmapTab'
import { SassTab } from './DocumentationView/SassTab'
import { useDocumentationViewState } from './DocumentationView/useDocumentationViewState'
const tabs = [
{ value: 'readme', label: 'README', icon: <BookOpen size={16} /> },
{ value: 'roadmap', label: 'Roadmap', icon: <MapPin size={16} /> },
{ value: 'agents', label: 'Agents', icon: <Sparkle size={16} /> },
{ value: 'pwa', label: 'PWA', icon: <Rocket size={16} /> },
{ value: 'sass', label: 'Sass', icon: <PaintBrush size={16} /> },
{ value: 'cicd', label: 'CI/CD', icon: <GitBranch size={16} /> }
]
export function DocumentationView() {
const { activeTab, setActiveTab, searchQuery, handleSearchChange } = useDocumentationViewState()
return (
<div className="h-full flex flex-col bg-background">
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col">
<div className="border-b border-border bg-card px-6 py-3 space-y-3">
<TabsList className="bg-muted/50">
{tabs.map((tab) => (
<TabsTrigger key={tab.value} value={tab.value} className="gap-2">
{tab.icon}
{tab.label}
</TabsTrigger>
))}
</TabsList>
<div className="relative">
<MagnifyingGlass size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
<Input
placeholder="Search documentation..."
value={searchQuery}
onChange={handleSearchChange}
className="pl-10"
/>
</div>
</div>
<ScrollArea className="flex-1">
<div className="max-w-5xl mx-auto p-8">
<TabsContent value="readme" className="m-0 space-y-6">
<ReadmeTab />
</TabsContent>
<TabsContent value="roadmap" className="m-0 space-y-6">
<RoadmapTab />
</TabsContent>
<TabsContent value="agents" className="m-0 space-y-6">
<AgentsTab />
</TabsContent>
<TabsContent value="pwa" className="m-0 space-y-6">
<PwaTab />
</TabsContent>
<TabsContent value="sass" className="m-0 space-y-6">
<SassTab />
</TabsContent>
<TabsContent value="cicd" className="m-0 space-y-6">
<CicdTab />
</TabsContent>
</div>
</ScrollArea>
</Tabs>
</div>
)
}

View File

@@ -1,18 +0,0 @@
import { Card, CardContent } from '@/components/ui/card'
import { Sparkle } from '@phosphor-icons/react'
export function AIFeatureCard({ title, description }: { title: string; description: string }) {
return (
<Card className="bg-primary/5 border-primary/20">
<CardContent className="pt-4 pb-4">
<div className="flex gap-3">
<Sparkle size={20} weight="duotone" className="text-accent flex-shrink-0 mt-0.5" />
<div className="space-y-1">
<h4 className="font-semibold text-sm">{title}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,52 +0,0 @@
import { FileCode, CheckCircle, Sparkle } from '@phosphor-icons/react'
export function AgentFileItem({ filename, path, description, features, featureLabel }: {
filename: string
path: string
description: string
features: string[]
featureLabel: string
}) {
return (
<div className="space-y-3 border-l-2 border-accent pl-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<FileCode size={18} className="text-accent" />
<code className="text-sm font-semibold text-accent">{filename}</code>
</div>
<p className="text-xs text-muted-foreground font-mono">{path}</p>
<p className="text-sm text-foreground/90">{description}</p>
</div>
<div className="space-y-1">
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">{featureLabel}</p>
<ul className="space-y-1">
{features.map((feature) => (
<li key={feature} className="text-sm text-foreground/80 flex items-start gap-2">
<CheckCircle size={14} weight="fill" className="text-accent mt-1 flex-shrink-0" />
<span>{feature}</span>
</li>
))}
</ul>
</div>
</div>
)
}
export function IntegrationPoint({ component, capabilities }: { component: string; capabilities: string[] }) {
return (
<div className="space-y-2 border rounded-lg p-4 bg-card">
<h4 className="font-semibold text-sm flex items-center gap-2">
<Sparkle size={16} weight="duotone" className="text-accent" />
{component}
</h4>
<ul className="space-y-1">
{capabilities.map((capability) => (
<li key={capability} className="text-sm text-muted-foreground flex items-start gap-2">
<span className="text-accent"></span>
<span>{capability}</span>
</li>
))}
</ul>
</div>
)
}

View File

@@ -1,26 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import agentsData from '@/data/documentation/agents-data.json'
import { AgentFileItem } from './AgentItems'
export function AgentsCoreServices() {
return (
<Card>
<CardHeader>
<CardTitle>{agentsData.coreServicesTitle}</CardTitle>
<CardDescription>{agentsData.coreServicesDescription}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{agentsData.coreServices.map((service) => (
<AgentFileItem
key={service.filename}
featureLabel={agentsData.coreServicesFeaturesLabel}
filename={service.filename}
path={service.path}
description={service.description}
features={service.features}
/>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,28 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Target, Package } from '@phosphor-icons/react'
import agentsData from '@/data/documentation/agents-data.json'
export function AgentsFutureEnhancements() {
return (
<Card className="bg-muted/50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Package size={20} weight="duotone" />
{agentsData.futureEnhancementsTitle}
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm">
{agentsData.futureEnhancements.map((item) => (
<li key={item.title} className="flex items-start gap-2">
<Target size={16} className="text-accent mt-1 flex-shrink-0" />
<span>
<strong>{item.title}:</strong> {item.description}
</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

View File

@@ -1,21 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import agentsData from '@/data/documentation/agents-data.json'
import { IntegrationPoint } from './AgentItems'
export function AgentsIntegrationPoints() {
return (
<Card>
<CardHeader>
<CardTitle>{agentsData.integrationPointsTitle}</CardTitle>
<CardDescription>{agentsData.integrationPointsDescription}</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4">
{agentsData.integrationPoints.map((point) => (
<IntegrationPoint key={point.component} component={point.component} capabilities={point.capabilities} />
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,22 +0,0 @@
import { Separator } from '@/components/ui/separator'
import { FileCode } from '@phosphor-icons/react'
import agentsData from '@/data/documentation/agents-data.json'
export function AgentsOverviewSection() {
return (
<div className="space-y-4">
<h1 className="text-4xl font-bold flex items-center gap-3">
<FileCode size={36} weight="duotone" className="text-accent" />
{agentsData.title}
</h1>
<p className="text-lg text-muted-foreground">{agentsData.subtitle}</p>
<Separator />
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{agentsData.overviewTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{agentsData.overview}</p>
</div>
</div>
)
}

View File

@@ -1,21 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import agentsData from '@/data/documentation/agents-data.json'
export function AgentsPromptEngineering() {
return (
<Card>
<CardHeader>
<CardTitle>{agentsData.promptEngineeringTitle}</CardTitle>
<CardDescription>{agentsData.promptEngineeringDescription}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{agentsData.promptEngineering.map((item) => (
<div key={item.title} className="space-y-2">
<h3 className="font-semibold">{item.title}</h3>
<p className="text-sm text-muted-foreground">{item.description}</p>
</div>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,19 +0,0 @@
import { AgentsCoreServices } from './AgentsCoreServices'
import { AgentsFutureEnhancements } from './AgentsFutureEnhancements'
import { AgentsIntegrationPoints } from './AgentsIntegrationPoints'
import { AgentsOverviewSection } from './AgentsOverviewSection'
import { AgentsPromptEngineering } from './AgentsPromptEngineering'
export function AgentsTab() {
return (
<div className="space-y-6">
<AgentsOverviewSection />
<div className="space-y-4">
<AgentsCoreServices />
<AgentsIntegrationPoints />
<AgentsPromptEngineering />
<AgentsFutureEnhancements />
</div>
</div>
)
}

View File

@@ -1,26 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { CheckCircle, Rocket } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdBestPracticesCard() {
return (
<Card className="bg-muted/50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Rocket size={20} weight="duotone" />
{cicdData.bestPracticesTitle}
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm">
{cicdData.bestPractices.map((practice) => (
<li key={practice} className="flex items-start gap-2">
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
<span>{practice}</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

View File

@@ -1,48 +0,0 @@
import { Card, CardContent } from '@/components/ui/card'
import { GitBranch } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
const toneStyles = {
green: {
card: 'bg-green-500/5 border-green-500/20',
icon: 'text-green-500'
},
blue: {
card: 'bg-blue-500/5 border-blue-500/20',
icon: 'text-blue-500'
},
purple: {
card: 'bg-purple-500/5 border-purple-500/20',
icon: 'text-purple-500'
},
orange: {
card: 'bg-orange-500/5 border-orange-500/20',
icon: 'text-orange-500'
}
} as const
export function CicdBranchStrategySection() {
return (
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{cicdData.branchStrategyTitle}</h2>
<div className="grid gap-4">
{cicdData.branches.map((branch) => {
const styles = toneStyles[branch.tone]
return (
<Card key={branch.name} className={styles.card}>
<CardContent className="pt-4 pb-4">
<div className="flex items-start gap-3">
<GitBranch size={20} weight="duotone" className={`${styles.icon} mt-0.5`} />
<div className="space-y-1">
<h4 className="font-semibold">{branch.name}</h4>
<p className="text-sm text-muted-foreground">{branch.description}</p>
</div>
</div>
</CardContent>
</Card>
)
})}
</div>
</div>
)
}

View File

@@ -1,49 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { CheckCircle } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdDockerCard() {
return (
<Card>
<CardHeader>
<CardTitle>{cicdData.dockerTitle}</CardTitle>
<CardDescription>{cicdData.dockerDescription}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<h3 className="font-semibold">{cicdData.dockerFilesTitle}</h3>
<div className="space-y-2 ml-4">
{cicdData.docker.files.map((file) => (
<div key={file.name} className="space-y-1">
<code className="text-sm font-mono text-accent">{file.name}</code>
<p className="text-sm text-muted-foreground">{file.description}</p>
</div>
))}
</div>
</div>
<Separator />
<div className="space-y-2">
<h3 className="font-semibold">{cicdData.dockerCommandsTitle}</h3>
<pre className="custom-mui-code-block">{cicdData.docker.commands}</pre>
</div>
<Separator />
<div className="space-y-2">
<h3 className="font-semibold">{cicdData.dockerFeaturesTitle}</h3>
<ul className="space-y-2 text-sm">
{cicdData.docker.features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
<span>{feature}</span>
</li>
))}
</ul>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,39 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdEnvVarsCard() {
return (
<Card>
<CardHeader>
<CardTitle>{cicdData.envVarsTitle}</CardTitle>
<CardDescription>{cicdData.envVarsDescription}</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b">
{cicdData.envVarsColumns.map((column) => (
<th key={column} className="text-left py-2 pr-4 font-semibold">{column}</th>
))}
</tr>
</thead>
<tbody className="text-muted-foreground">
{cicdData.environmentVariables.map((variable) => (
<tr key={variable.variable} className="border-b">
<td className="py-2 pr-4">
<code className="text-accent">{variable.variable}</code>
</td>
<td className="py-2 pr-4">{variable.description}</td>
<td className="py-2">{variable.required}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,57 +0,0 @@
import { Badge } from '@/components/ui/badge'
import { Card, CardContent } from '@/components/ui/card'
import { CheckCircle, GitBranch } from '@phosphor-icons/react'
export function CICDPlatformItem({ name, file, description, features, featureLabel }: {
name: string
file: string
description: string
features: string[]
featureLabel: string
}) {
return (
<div className="space-y-3 border-l-2 border-accent pl-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<GitBranch size={18} className="text-accent" />
<h3 className="text-base font-semibold">{name}</h3>
</div>
<code className="text-xs text-muted-foreground font-mono">{file}</code>
<p className="text-sm text-foreground/90">{description}</p>
</div>
<div className="space-y-1">
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">{featureLabel}</p>
<ul className="space-y-1">
{features.map((feature) => (
<li key={feature} className="text-sm text-foreground/80 flex items-start gap-2">
<CheckCircle size={14} weight="fill" className="text-accent mt-1 flex-shrink-0" />
<span>{feature}</span>
</li>
))}
</ul>
</div>
</div>
)
}
export function PipelineStageCard({ stage, description, duration }: {
stage: string
description: string
duration: string
}) {
return (
<Card className="bg-primary/5 border-primary/20">
<CardContent className="pt-4 pb-4">
<div className="flex items-start justify-between gap-4">
<div className="space-y-1 flex-1">
<h4 className="font-semibold text-sm">{stage}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
<Badge variant="secondary" className="text-xs whitespace-nowrap">
{duration}
</Badge>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,26 +0,0 @@
import { Separator } from '@/components/ui/separator'
import { GitBranch } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdOverviewSection() {
return (
<div className="space-y-4">
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<GitBranch size={32} weight="duotone" className="text-white" />
</div>
<div>
<h1 className="text-4xl font-bold">{cicdData.title}</h1>
<p className="text-lg text-muted-foreground">{cicdData.subtitle}</p>
</div>
</div>
<Separator />
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{cicdData.overviewTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{cicdData.overview}</p>
</div>
</div>
)
}

View File

@@ -1,21 +0,0 @@
import cicdData from '@/data/documentation/cicd-data.json'
import { PipelineStageCard } from './CicdItems'
export function CicdPipelineSection() {
return (
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{cicdData.pipelineTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{cicdData.pipeline.intro}</p>
<div className="grid gap-3">
{cicdData.pipeline.stages.map((stage) => (
<PipelineStageCard
key={stage.stage}
stage={stage.stage}
description={stage.description}
duration={stage.duration}
/>
))}
</div>
</div>
)
}

View File

@@ -1,25 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import cicdData from '@/data/documentation/cicd-data.json'
import { CICDPlatformItem } from './CicdItems'
export function CicdPlatformsCard() {
return (
<Card>
<CardHeader>
<CardTitle>{cicdData.platformsTitle}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{cicdData.platforms.map((platform) => (
<CICDPlatformItem
key={platform.name}
featureLabel={cicdData.platformsFeaturesLabel}
name={platform.name}
file={platform.file}
description={platform.description}
features={platform.features}
/>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,31 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Lightbulb } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdQuickStartCard() {
return (
<Card className="bg-accent/10 border-accent/20">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb size={20} weight="duotone" className="text-accent" />
{cicdData.quickStartTitle}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
{cicdData.quickStart.map((step) => (
<div key={step.step} className="space-y-2">
<h3 className="font-semibold flex items-center gap-2">
<span className="w-6 h-6 rounded-full bg-accent text-accent-foreground flex items-center justify-center text-sm">
{step.step}
</span>
{step.title}
</h3>
<p className="text-sm text-foreground/80 ml-8">{step.description}</p>
</div>
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,28 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { FileCode, Package } from '@phosphor-icons/react'
import cicdData from '@/data/documentation/cicd-data.json'
export function CicdResourcesCard() {
return (
<Card className="border-primary/30">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Package size={20} weight="duotone" />
{cicdData.resourcesTitle}
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm">
{cicdData.resources.map((resource) => (
<li key={resource.label} className="flex items-start gap-2">
<FileCode size={16} className="text-accent mt-1 flex-shrink-0" />
<span>
<code className="text-accent">{resource.label}</code> - {resource.description}
</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

View File

@@ -1,25 +0,0 @@
import { CicdBestPracticesCard } from './CicdBestPracticesCard'
import { CicdBranchStrategySection } from './CicdBranchStrategySection'
import { CicdDockerCard } from './CicdDockerCard'
import { CicdEnvVarsCard } from './CicdEnvVarsCard'
import { CicdOverviewSection } from './CicdOverviewSection'
import { CicdPipelineSection } from './CicdPipelineSection'
import { CicdPlatformsCard } from './CicdPlatformsCard'
import { CicdQuickStartCard } from './CicdQuickStartCard'
import { CicdResourcesCard } from './CicdResourcesCard'
export function CicdTab() {
return (
<div className="space-y-6">
<CicdOverviewSection />
<CicdPlatformsCard />
<CicdPipelineSection />
<CicdDockerCard />
<CicdEnvVarsCard />
<CicdBranchStrategySection />
<CicdQuickStartCard />
<CicdBestPracticesCard />
<CicdResourcesCard />
</div>
)
}

View File

@@ -1,13 +0,0 @@
import type { ReactNode } from 'react'
export function FeatureItem({ icon, title, description }: { icon: ReactNode; title: string; description: string }) {
return (
<div className="flex gap-3">
<div className="text-accent mt-0.5">{icon}</div>
<div className="space-y-1">
<h4 className="font-semibold text-sm">{title}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</div>
)
}

View File

@@ -1,30 +0,0 @@
import { Card, CardContent } from '@/components/ui/card'
import { Sparkle } from '@phosphor-icons/react'
export function FeatureItem({ icon, title, description }: { icon: React.ReactNode; title: string; description: string }) {
return (
<div className="flex gap-3">
<div className="text-accent mt-0.5">{icon}</div>
<div className="space-y-1">
<h4 className="font-semibold text-sm">{title}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</div>
)
}
export function AIFeatureCard({ title, description }: { title: string; description: string }) {
return (
<Card className="bg-primary/5 border-primary/20">
<CardContent className="pt-4 pb-4">
<div className="flex gap-3">
<Sparkle size={20} weight="duotone" className="text-accent flex-shrink-0 mt-0.5" />
<div className="space-y-1">
<h4 className="font-semibold text-sm">{title}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,25 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { CheckCircle } from '@phosphor-icons/react'
import pwaData from '@/data/documentation/pwa-data.json'
export function PwaFeaturesCard() {
return (
<Card>
<CardHeader>
<CardTitle>{pwaData.featuresTitle}</CardTitle>
<CardDescription>{pwaData.featuresDescription}</CardDescription>
</CardHeader>
<CardContent className="grid md:grid-cols-2 gap-4">
{pwaData.features.map((feature) => (
<div key={feature.title} className="space-y-2">
<div className="flex items-center gap-2">
<CheckCircle size={16} weight="fill" className="text-accent" />
<span className="font-semibold text-sm">{feature.title}</span>
</div>
<p className="text-xs text-muted-foreground ml-6">{feature.description}</p>
</div>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,36 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import pwaData from '@/data/documentation/pwa-data.json'
function InstallationCard({ title, items }: { title: string; items: { title: string; steps: string[] }[] }) {
return (
<Card>
<CardHeader>
<CardTitle className="text-base">{title}</CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-sm">
{items.map((item) => (
<div key={item.title}>
<div className="font-semibold mb-1">{item.title}</div>
<ol className="list-decimal list-inside space-y-1 text-muted-foreground ml-2">
{item.steps.map((step) => (
<li key={step}>{step}</li>
))}
</ol>
</div>
))}
</CardContent>
</Card>
)
}
export function PwaInstallationSection() {
return (
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{pwaData.installationTitle}</h2>
<div className="grid md:grid-cols-2 gap-4">
<InstallationCard title={pwaData.installationDesktopTitle} items={pwaData.installation.desktop} />
<InstallationCard title={pwaData.installationMobileTitle} items={pwaData.installation.mobile} />
</div>
</div>
)
}

View File

@@ -1,50 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { CheckCircle, Wrench } from '@phosphor-icons/react'
import pwaData from '@/data/documentation/pwa-data.json'
function OfflineList({ items, accent }: { items: string[]; accent: boolean }) {
return (
<ul className={`space-y-2 text-sm ${accent ? 'text-foreground/80' : 'text-muted-foreground'}`}>
{items.map((item) => (
<li key={item} className="flex items-start gap-2">
<span className={accent ? 'text-accent mt-0.5' : 'mt-0.5'}></span>
<span>{item}</span>
</li>
))}
</ul>
)
}
export function PwaOfflineSection() {
return (
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{pwaData.offlineTitle}</h2>
<div className="grid md:grid-cols-2 gap-4">
<Card className="border-accent/50">
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<CheckCircle size={20} weight="fill" className="text-accent" />
{pwaData.offlineWorksTitle}
</CardTitle>
</CardHeader>
<CardContent>
<OfflineList items={pwaData.offline.worksOffline} accent />
</CardContent>
</Card>
<Card className="border-muted">
<CardHeader>
<CardTitle className="text-base flex items-center gap-2">
<Wrench size={20} weight="duotone" className="text-muted-foreground" />
{pwaData.offlineRequiresTitle}
</CardTitle>
</CardHeader>
<CardContent>
<OfflineList items={pwaData.offline.requiresInternet} accent={false} />
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -1,26 +0,0 @@
import { Separator } from '@/components/ui/separator'
import { Rocket } from '@phosphor-icons/react'
import pwaData from '@/data/documentation/pwa-data.json'
export function PwaOverviewSection() {
return (
<div className="space-y-4">
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<Rocket size={32} weight="duotone" className="text-white" />
</div>
<div>
<h1 className="text-4xl font-bold">{pwaData.title}</h1>
<p className="text-lg text-muted-foreground">{pwaData.subtitle}</p>
</div>
</div>
<Separator />
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{pwaData.overviewTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{pwaData.overview}</p>
</div>
</div>
)
}

View File

@@ -1,28 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Lightbulb } from '@phosphor-icons/react'
import pwaData from '@/data/documentation/pwa-data.json'
export function PwaProTipsCard() {
return (
<Card className="bg-accent/10 border-accent/20">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb size={20} weight="duotone" className="text-accent" />
{pwaData.proTipsTitle}
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm">
{pwaData.proTips.map((tip) => (
<li key={tip.title} className="flex items-start gap-2">
<span className="text-accent mt-1"></span>
<span>
<strong>{tip.title}:</strong> {tip.description}
</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

View File

@@ -1,27 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import pwaData from '@/data/documentation/pwa-data.json'
export function PwaSettingsCard() {
return (
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{pwaData.settingsTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{pwaData.settingsDescription}</p>
<Card>
<CardHeader>
<CardTitle>{pwaData.settingsCardTitle}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{pwaData.settings.map((setting, index) => (
<div key={setting.title} className="space-y-2">
<div className="font-semibold">{setting.title}</div>
<p className="text-sm text-muted-foreground">{setting.description}</p>
{index < pwaData.settings.length - 1 && <Separator />}
</div>
))}
</CardContent>
</Card>
</div>
)
}

View File

@@ -1,19 +0,0 @@
import { PwaFeaturesCard } from './PwaFeaturesCard'
import { PwaInstallationSection } from './PwaInstallationSection'
import { PwaOfflineSection } from './PwaOfflineSection'
import { PwaOverviewSection } from './PwaOverviewSection'
import { PwaProTipsCard } from './PwaProTipsCard'
import { PwaSettingsCard } from './PwaSettingsCard'
export function PwaTab() {
return (
<div className="space-y-6">
<PwaOverviewSection />
<PwaFeaturesCard />
<PwaInstallationSection />
<PwaSettingsCard />
<PwaOfflineSection />
<PwaProTipsCard />
</div>
)
}

View File

@@ -1,140 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Code, Database, Tree, PaintBrush, Flask, Play, Cube, Wrench, Gear, Rocket, Lightbulb, CheckCircle } from '@phosphor-icons/react'
import { AIFeatureCard } from './AIFeatureCard'
import { FeatureItem } from './FeatureItem'
import readmeData from '@/data/documentation/readme-data.json'
const Sparkle = ({ size }: { size: number }) => <span style={{ fontSize: size }}></span>
const iconMap: Record<string, any> = {
Code, Database, Tree, PaintBrush, Flask, Play, Cube, Wrench, Gear, Sparkle
}
export function ReadmeTab() {
return (
<div className="space-y-6">
<div className="space-y-4">
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<Code size={32} weight="duotone" className="text-white" />
</div>
<div>
<h1 className="text-4xl font-bold">{readmeData.title}</h1>
<p className="text-lg text-muted-foreground">{readmeData.subtitle}</p>
</div>
</div>
<Separator />
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{readmeData.sections.overviewTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{readmeData.overview}</p>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Rocket size={20} weight="duotone" />
{readmeData.sections.featuresTitle}
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{readmeData.features.map((feature, idx) => {
const Icon = iconMap[feature.icon] || Code
return (
<FeatureItem
key={idx}
icon={<Icon size={18} />}
title={feature.title}
description={feature.description}
/>
)
})}
</CardContent>
</Card>
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{readmeData.sections.gettingStartedTitle}</h2>
<Card>
<CardContent className="pt-6 space-y-4">
{readmeData.gettingStarted.map((step) => (
<div key={step.step} className="space-y-2">
<h3 className="text-lg font-semibold flex items-center gap-2">
<span className="w-6 h-6 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm">
{step.step}
</span>
{step.title}
</h3>
<p className="text-muted-foreground ml-8">{step.description}</p>
</div>
))}
</CardContent>
</Card>
</div>
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{readmeData.sections.aiFeaturesTitle}</h2>
<p className="text-foreground/90 leading-relaxed">
{readmeData.sections.aiFeaturesDescription}
</p>
<div className="grid gap-3">
{readmeData.aiFeatures.map((feature, idx) => (
<AIFeatureCard key={idx} title={feature.title} description={feature.description} />
))}
</div>
</div>
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{readmeData.sections.techStackTitle}</h2>
<div className="grid md:grid-cols-2 gap-4">
<Card>
<CardHeader>
<CardTitle className="text-base">{readmeData.sections.techStackFrontendTitle}</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-foreground/80">
{readmeData.techStack.frontend.map((tech, idx) => (
<li key={idx} className="flex items-center gap-2">
<CheckCircle size={16} weight="fill" className="text-accent" />
{tech}
</li>
))}
</ul>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base">{readmeData.sections.techStackBackendTitle}</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-foreground/80">
{readmeData.techStack.backend.map((tech, idx) => (
<li key={idx} className="flex items-center gap-2">
<CheckCircle size={16} weight="fill" className="text-accent" />
{tech}
</li>
))}
</ul>
</CardContent>
</Card>
</div>
</div>
<Card className="bg-accent/10 border-accent/20">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb size={20} weight="duotone" className="text-accent" />
{readmeData.sections.proTipsTitle}
</CardTitle>
</CardHeader>
<CardContent className="space-y-2 text-sm">
{readmeData.proTips.map((tip, idx) => (
<p key={idx}>{tip}</p>
))}
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -1,27 +0,0 @@
import { Card, CardContent } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
export function RoadmapItem({ status, title, description, version }: {
status: 'completed' | 'planned'
title: string
description: string
version: string
}) {
return (
<Card className={status === 'completed' ? 'bg-green-500/5 border-green-500/20' : 'bg-muted/50'}>
<CardContent className="pt-4 pb-4">
<div className="flex items-start justify-between gap-4">
<div className="space-y-1 flex-1">
<div className="flex items-center gap-2">
<h4 className="font-semibold">{title}</h4>
<Badge variant={status === 'completed' ? 'default' : 'secondary'} className="text-xs">
{version}
</Badge>
</div>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,57 +0,0 @@
import { Separator } from '@/components/ui/separator'
import { CheckCircle, Clock, MapPin } from '@phosphor-icons/react'
import roadmapData from '@/data/documentation/roadmap-data.json'
import { RoadmapItem } from './RoadmapItem'
const sections = [
{
key: 'completed',
title: roadmapData.sections.completedTitle,
icon: <CheckCircle size={24} weight="fill" className="text-green-500" />,
items: roadmapData.completed
},
{
key: 'planned',
title: roadmapData.sections.plannedTitle,
icon: <Clock size={24} weight="duotone" className="text-accent" />,
items: roadmapData.planned
}
]
export function RoadmapTab() {
return (
<div className="space-y-6">
<div className="space-y-4">
<h1 className="text-4xl font-bold flex items-center gap-3">
<MapPin size={36} weight="duotone" className="text-accent" />
{roadmapData.title}
</h1>
<p className="text-lg text-muted-foreground">{roadmapData.subtitle}</p>
<Separator />
<div className="space-y-6">
{sections.map((section) => (
<div key={section.key}>
<div className="flex items-center gap-3 mb-4">
{section.icon}
<h2 className="text-2xl font-semibold">{section.title}</h2>
</div>
<div className="space-y-3 ml-9">
{section.items.map((item) => (
<RoadmapItem
key={`${section.key}-${item.title}`}
status={section.key === 'completed' ? 'completed' : 'planned'}
title={item.title}
description={item.description}
version={item.version}
/>
))}
</div>
</div>
))}
</div>
</div>
</div>
)
}

View File

@@ -1,21 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import sassData from '@/data/documentation/sass-data.json'
import { AnimationItem } from './SassItems'
export function SassAnimationsCard() {
return (
<Card>
<CardHeader>
<CardTitle>{sassData.animationsTitle}</CardTitle>
<CardDescription>{sassData.animationsDescription}</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
{sassData.animations.map((animation) => (
<AnimationItem key={animation.name} name={animation.name} description={animation.description} />
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,26 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { CheckCircle, Target } from '@phosphor-icons/react'
import sassData from '@/data/documentation/sass-data.json'
export function SassBestPracticesCard() {
return (
<Card className="bg-muted/50">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Target size={20} weight="duotone" />
{sassData.bestPracticesTitle}
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm">
{sassData.bestPractices.map((practice) => (
<li key={practice} className="flex items-start gap-2">
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
<span>{practice}</span>
</li>
))}
</ul>
</CardContent>
</Card>
)
}

View File

@@ -1,26 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import sassData from '@/data/documentation/sass-data.json'
import { SassComponentItem } from './SassItems'
export function SassComponentsCard() {
return (
<Card>
<CardHeader>
<CardTitle>{sassData.componentsTitle}</CardTitle>
<CardDescription>{sassData.componentsDescription}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid gap-4 md:grid-cols-2">
{sassData.components.map((component) => (
<SassComponentItem
key={component.name}
name={component.name}
classes={component.classes}
description={component.description}
/>
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,20 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import sassData from '@/data/documentation/sass-data.json'
export function SassFileStructureCard() {
return (
<Card>
<CardHeader>
<CardTitle>{sassData.fileStructureTitle}</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{sassData.fileStructure.map((item) => (
<div key={item.file} className="space-y-2">
<code className="text-sm font-mono text-accent">{item.file}</code>
<p className="text-sm text-muted-foreground ml-4">{item.description}</p>
</div>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,22 +0,0 @@
export function SassComponentItem({ name, classes, description }: { name: string; classes: string[]; description: string }) {
return (
<div className="space-y-2 p-4 border rounded-lg bg-card">
<h4 className="font-semibold">{name}</h4>
<p className="text-sm text-muted-foreground">{description}</p>
<div className="space-y-1">
{classes.map((cls) => (
<code key={cls} className="text-xs font-mono text-accent block">{cls}</code>
))}
</div>
</div>
)
}
export function AnimationItem({ name, description }: { name: string; description: string }) {
return (
<div className="space-y-1 p-3 border rounded-lg bg-card">
<code className="text-xs font-mono text-accent">{name}</code>
<p className="text-xs text-muted-foreground">{description}</p>
</div>
)
}

View File

@@ -1,25 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Code } from '@phosphor-icons/react'
import sassData from '@/data/documentation/sass-data.json'
import { FeatureItem } from './FeatureItems'
export function SassLayoutCard() {
return (
<Card>
<CardHeader>
<CardTitle>{sassData.layoutTitle}</CardTitle>
<CardDescription>{sassData.layoutDescription}</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{sassData.layoutComponents.map((item) => (
<FeatureItem
key={item.title}
icon={<Code size={18} />}
title={item.title}
description={item.description}
/>
))}
</CardContent>
</Card>
)
}

View File

@@ -1,26 +0,0 @@
import { Separator } from '@/components/ui/separator'
import { PaintBrush } from '@phosphor-icons/react'
import sassData from '@/data/documentation/sass-data.json'
export function SassOverviewSection() {
return (
<div className="space-y-4">
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<PaintBrush size={32} weight="duotone" className="text-white" />
</div>
<div>
<h1 className="text-4xl font-bold">{sassData.title}</h1>
<p className="text-lg text-muted-foreground">{sassData.subtitle}</p>
</div>
</div>
<Separator />
<div className="space-y-4">
<h2 className="text-2xl font-semibold">{sassData.overviewTitle}</h2>
<p className="text-foreground/90 leading-relaxed">{sassData.overview}</p>
</div>
</div>
)
}

View File

@@ -1,30 +0,0 @@
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Rocket } from '@phosphor-icons/react'
import sassData from '@/data/documentation/sass-data.json'
export function SassQuickStartCard() {
return (
<Card className="bg-accent/5 border-accent/20">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Rocket size={20} weight="duotone" />
{sassData.quickStartTitle}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<h3 className="font-semibold">{sassData.quickStart.components.title}</h3>
<pre className="custom-mui-code-block">{sassData.quickStart.components.code}</pre>
</div>
<Separator />
<div className="space-y-2">
<h3 className="font-semibold">{sassData.quickStart.mixins.title}</h3>
<pre className="custom-mui-code-block">{sassData.quickStart.mixins.code}</pre>
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,23 +0,0 @@
import { SassAnimationsCard } from './SassAnimationsCard'
import { SassBestPracticesCard } from './SassBestPracticesCard'
import { SassComponentsCard } from './SassComponentsCard'
import { SassFileStructureCard } from './SassFileStructureCard'
import { SassLayoutCard } from './SassLayoutCard'
import { SassOverviewSection } from './SassOverviewSection'
import { SassQuickStartCard } from './SassQuickStartCard'
import { SassUtilitiesCard } from './SassUtilitiesCard'
export function SassTab() {
return (
<div className="space-y-6">
<SassOverviewSection />
<SassFileStructureCard />
<SassComponentsCard />
<SassLayoutCard />
<SassUtilitiesCard />
<SassAnimationsCard />
<SassQuickStartCard />
<SassBestPracticesCard />
</div>
)
}

View File

@@ -1,33 +0,0 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Lightbulb } from '@phosphor-icons/react'
import sassData from '@/data/documentation/sass-data.json'
export function SassUtilitiesCard() {
return (
<Card>
<CardHeader>
<CardTitle>{sassData.utilitiesTitle}</CardTitle>
<CardDescription>{sassData.utilitiesDescription}</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{sassData.utilities.map((utility, index) => (
<div key={utility.title} className="space-y-2">
<h3 className="font-semibold flex items-center gap-2">
<Lightbulb size={18} weight="duotone" className="text-accent" />
{utility.title}
</h3>
<div className="ml-6 space-y-2 text-sm">
<p className="font-mono text-accent">{utility.mixin}</p>
<p className="text-muted-foreground">{utility.description}</p>
<pre className="custom-mui-code-block text-xs mt-2">{utility.snippet}</pre>
</div>
{index < sassData.utilities.length - 1 && <Separator />}
</div>
))}
</div>
</CardContent>
</Card>
)
}

View File

@@ -1,18 +0,0 @@
import { useCallback, useState } from 'react'
import type { ChangeEvent } from 'react'
export function useDocumentationViewState() {
const [activeTab, setActiveTab] = useState('readme')
const [searchQuery, setSearchQuery] = useState('')
const handleSearchChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setSearchQuery(event.target.value)
}, [])
return {
activeTab,
setActiveTab,
searchQuery,
handleSearchChange
}
}

View File

@@ -1,8 +1,545 @@
{
"id": "conflict-card",
"type": "div",
"id": "conflict-card-motion",
"type": "motion.div",
"props": {
"className": "conflict-card"
"layout": true,
"initial": { "opacity": 0, "y": 20 },
"animate": { "opacity": 1, "y": 0 },
"exit": { "opacity": 0, "x": -100 },
"transition": { "duration": 0.2 }
},
"children": []
"children": [
{
"id": "conflict-card-container",
"type": "Card",
"props": {
"className": "border-destructive/30 hover:border-destructive/50 transition-colors"
},
"children": [
{
"id": "conflict-card-header",
"type": "CardHeader",
"props": {
"className": "pb-3"
},
"children": [
{
"id": "conflict-card-header-flex",
"type": "div",
"props": {
"className": "flex items-start justify-between gap-4"
},
"children": [
{
"id": "conflict-card-header-content",
"type": "div",
"props": {
"className": "flex items-start gap-3 flex-1 min-w-0"
},
"children": [
{
"id": "conflict-card-entity-icon",
"type": "div",
"props": {
"className": "mt-0.5"
},
"children": [
{
"id": "conflict-icon-code",
"type": "Code",
"conditional": {
"if": "conflict.entityType === 'files'"
},
"props": {
"size": 20,
"weight": "duotone",
"className": "text-primary"
}
},
{
"id": "conflict-icon-database",
"type": "Database",
"conditional": {
"if": "conflict.entityType !== 'files'"
},
"props": {
"size": 20,
"weight": "duotone",
"className": "text-accent"
}
}
]
},
{
"id": "conflict-card-info",
"type": "div",
"props": {
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "conflict-card-title",
"type": "CardTitle",
"bindings": {
"children": "conflict.id"
},
"props": {
"className": "text-base font-mono truncate"
}
},
{
"id": "conflict-card-description",
"type": "CardDescription",
"props": {
"className": "flex items-center gap-2 mt-1"
},
"children": [
{
"id": "conflict-entity-badge",
"type": "Badge",
"bindings": {
"children": "conflict.entityType"
},
"props": {
"variant": "outline",
"className": "text-xs"
}
},
{
"id": "conflict-time-diff",
"type": "span",
"bindings": {
"children": {
"source": "cardState.timeDiffMinutes",
"transform": "`${data} minutes ago`"
}
},
"props": {
"className": "text-xs text-muted-foreground"
}
}
]
}
]
}
]
},
{
"id": "conflict-card-toggle",
"type": "Button",
"bindings": {
"onClick": {
"source": "cardState.setExpanded",
"transform": "(prev) => data(!prev)"
}
},
"props": {
"size": "sm",
"variant": "ghost"
},
"children": [
{
"id": "conflict-card-toggle-icon",
"type": "CaretDown",
"conditional": {
"if": "cardState.expanded"
},
"props": {
"size": 16
}
},
{
"id": "conflict-card-toggle-icon-collapsed",
"type": "CaretRight",
"conditional": {
"if": "!cardState.expanded"
},
"props": {
"size": 16
}
}
]
}
]
}
]
},
{
"id": "conflict-card-content-animation",
"type": "AnimatePresence",
"children": [
{
"id": "conflict-card-content-motion",
"type": "motion.div",
"conditional": {
"if": "cardState.expanded"
},
"props": {
"initial": { "height": 0, "opacity": 0 },
"animate": { "height": "auto", "opacity": 1 },
"exit": { "height": 0, "opacity": 0 },
"transition": { "duration": 0.2 }
},
"children": [
{
"id": "conflict-card-content",
"type": "CardContent",
"props": {
"className": "space-y-4"
},
"children": [
{
"id": "conflict-separator-1",
"type": "Separator"
},
{
"id": "conflict-versions-grid",
"type": "div",
"props": {
"className": "grid grid-cols-2 gap-4"
},
"children": [
{
"id": "conflict-local-version",
"type": "div",
"props": {
"className": "space-y-2"
},
"children": [
{
"id": "conflict-local-header",
"type": "div",
"props": {
"className": "flex items-center gap-2"
},
"children": [
{
"id": "conflict-local-icon",
"type": "Database",
"props": {
"size": 16,
"className": "text-primary"
}
},
{
"id": "conflict-local-label",
"type": "h4",
"bindings": {
"children": "conflict.localVersion ? 'Local Version' : 'Local'"
},
"props": {
"className": "text-sm font-medium"
}
},
{
"id": "conflict-local-newer-badge",
"type": "Badge",
"conditional": {
"if": "cardState.isLocalNewer"
},
"props": {
"variant": "secondary",
"className": "text-xs",
"children": "Newer"
}
}
]
},
{
"id": "conflict-local-content",
"type": "div",
"props": {
"className": "bg-muted/50 rounded-md p-3 space-y-1"
},
"children": [
{
"id": "conflict-local-time",
"type": "div",
"props": {
"className": "flex items-center gap-1.5 text-xs text-muted-foreground"
},
"children": [
{
"id": "conflict-local-clock",
"type": "Clock",
"props": {
"size": 12
}
},
{
"id": "conflict-local-timestamp",
"type": "span",
"bindings": {
"children": {
"source": "conflict.localTimestamp",
"transform": "new Date(data).toLocaleString()"
}
}
}
]
},
{
"id": "conflict-local-json",
"type": "pre",
"bindings": {
"children": {
"source": "conflict.localVersion",
"transform": "JSON.stringify(data, null, 2).slice(0, 200) + '...'"
}
},
"props": {
"className": "text-xs overflow-hidden text-ellipsis text-primary"
}
}
]
}
]
},
{
"id": "conflict-remote-version",
"type": "div",
"props": {
"className": "space-y-2"
},
"children": [
{
"id": "conflict-remote-header",
"type": "div",
"props": {
"className": "flex items-center gap-2"
},
"children": [
{
"id": "conflict-remote-icon",
"type": "Cloud",
"props": {
"size": 16,
"className": "text-accent"
}
},
{
"id": "conflict-remote-label",
"type": "h4",
"bindings": {
"children": "conflict.remoteVersion ? 'Remote Version' : 'Remote'"
},
"props": {
"className": "text-sm font-medium"
}
},
{
"id": "conflict-remote-newer-badge",
"type": "Badge",
"conditional": {
"if": "!cardState.isLocalNewer"
},
"props": {
"variant": "secondary",
"className": "text-xs",
"children": "Newer"
}
}
]
},
{
"id": "conflict-remote-content",
"type": "div",
"props": {
"className": "bg-muted/50 rounded-md p-3 space-y-1"
},
"children": [
{
"id": "conflict-remote-time",
"type": "div",
"props": {
"className": "flex items-center gap-1.5 text-xs text-muted-foreground"
},
"children": [
{
"id": "conflict-remote-clock",
"type": "Clock",
"props": {
"size": 12
}
},
{
"id": "conflict-remote-timestamp",
"type": "span",
"bindings": {
"children": {
"source": "conflict.remoteTimestamp",
"transform": "new Date(data).toLocaleString()"
}
}
}
]
},
{
"id": "conflict-remote-json",
"type": "pre",
"bindings": {
"children": {
"source": "conflict.remoteVersion",
"transform": "JSON.stringify(data, null, 2).slice(0, 200) + '...'"
}
},
"props": {
"className": "text-xs overflow-hidden text-ellipsis text-accent"
}
}
]
}
]
}
]
},
{
"id": "conflict-separator-2",
"type": "Separator"
},
{
"id": "conflict-actions",
"type": "div",
"props": {
"className": "flex flex-wrap gap-2"
},
"children": [
{
"id": "conflict-resolve-local",
"type": "Button",
"bindings": {
"onClick": {
"source": "onResolve",
"transform": "() => data(conflict.id, 'local')"
},
"disabled": "isResolving"
},
"props": {
"size": "sm",
"variant": "outline",
"className": "flex-1 min-w-[120px]"
},
"children": [
{
"id": "conflict-resolve-local-icon",
"type": "Database",
"props": {
"size": 16
}
},
{
"id": "conflict-resolve-local-text",
"type": "span",
"props": {
"children": "Keep Local"
}
}
]
},
{
"id": "conflict-resolve-remote",
"type": "Button",
"bindings": {
"onClick": {
"source": "onResolve",
"transform": "() => data(conflict.id, 'remote')"
},
"disabled": "isResolving"
},
"props": {
"size": "sm",
"variant": "outline",
"className": "flex-1 min-w-[120px]"
},
"children": [
{
"id": "conflict-resolve-remote-icon",
"type": "Cloud",
"props": {
"size": 16
}
},
{
"id": "conflict-resolve-remote-text",
"type": "span",
"props": {
"children": "Keep Remote"
}
}
]
},
{
"id": "conflict-resolve-merge",
"type": "Button",
"bindings": {
"onClick": {
"source": "onResolve",
"transform": "() => data(conflict.id, 'merge')"
},
"disabled": "isResolving"
},
"props": {
"size": "sm",
"variant": "outline",
"className": "flex-1 min-w-[120px]"
},
"children": [
{
"id": "conflict-resolve-merge-icon",
"type": "ArrowsLeftRight",
"props": {
"size": 16
}
},
{
"id": "conflict-resolve-merge-text",
"type": "span",
"props": {
"children": "Merge Both"
}
}
]
},
{
"id": "conflict-resolve-details",
"type": "Button",
"bindings": {
"onClick": {
"source": "onViewDetails",
"transform": "() => data(conflict)"
},
"disabled": "isResolving"
},
"props": {
"size": "sm",
"variant": "secondary"
},
"children": [
{
"id": "conflict-resolve-details-icon",
"type": "MagnifyingGlass",
"props": {
"size": 16
}
},
{
"id": "conflict-resolve-details-text",
"type": "span",
"props": {
"children": "Details"
}
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}

View File

@@ -0,0 +1,89 @@
{
"id": "documentation-view",
"type": "div",
"className": "h-full flex flex-col bg-background",
"children": [
{
"id": "documentation-tabs",
"type": "Tabs",
"className": "flex-1 flex flex-col",
"bindings": {
"value": {
"source": "activeTab",
"transform": "data"
},
"onValueChange": {
"source": "setActiveTab",
"transform": "data"
}
},
"children": [
{
"id": "documentation-header",
"type": "div",
"className": "border-b border-border bg-card px-6 py-3 space-y-3",
"children": [
{
"id": "tabs-list",
"type": "TabsList",
"className": "bg-muted/50",
"bindings": {
"children": {
"source": "tabsData",
"transform": "data"
}
}
},
{
"id": "search-input-wrapper",
"type": "div",
"className": "relative",
"children": [
{
"id": "search-icon",
"type": "MagnifyingGlass",
"className": "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
"size": 18
},
{
"id": "search-input",
"type": "Input",
"placeholder": "Search documentation...",
"className": "pl-10",
"bindings": {
"value": {
"source": "searchQuery",
"transform": "data"
},
"onChange": {
"source": "handleSearchChange",
"transform": "data"
}
}
}
]
}
]
},
{
"id": "tabs-content",
"type": "ScrollArea",
"className": "flex-1",
"children": [
{
"id": "content-wrapper",
"type": "div",
"className": "max-w-5xl mx-auto p-8",
"bindings": {
"children": {
"source": "activeTab",
"transform": "data"
}
}
}
]
}
]
}
]
}

View File

@@ -43,3 +43,6 @@ export { useAppLayout } from './use-app-layout'
export { useAppRouterLayout } from './use-app-router-layout'
export { useNavigationMenu } from './use-navigation-menu'
export { useDataSourceManagerState } from './use-data-source-manager-state'
export { useConflictResolution } from './use-conflict-resolution'
export { useConflictCard } from './use-conflict-card'
export { useDocumentationView } from './use-documentation-view'

View File

@@ -0,0 +1,28 @@
import { useCallback, useState } from 'react'
import type { ChangeEvent } from 'react'
const tabs = [
{ value: 'readme', label: 'README', icon: 'BookOpen' },
{ value: 'roadmap', label: 'Roadmap', icon: 'MapPin' },
{ value: 'agents', label: 'Agents', icon: 'Sparkle' },
{ value: 'pwa', label: 'PWA', icon: 'Rocket' },
{ value: 'sass', label: 'Sass', icon: 'PaintBrush' },
{ value: 'cicd', label: 'CI/CD', icon: 'GitBranch' }
]
export function useDocumentationView() {
const [activeTab, setActiveTab] = useState('readme')
const [searchQuery, setSearchQuery] = useState('')
const handleSearchChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
setSearchQuery(event.target.value)
}, [])
return {
activeTab,
setActiveTab,
searchQuery,
handleSearchChange,
tabsData: tabs,
}
}

View File

@@ -23,6 +23,7 @@ import { useDataSourceManagerState } from '@/hooks/use-data-source-manager-state
import { useFormatValue } from '@/hooks/use-format-value'
import { useConflictResolution } from '@/hooks/use-conflict-resolution'
import { useConflictCard } from '@/hooks/use-conflict-card'
import { useDocumentationView } from '@/hooks/use-documentation-view'
export interface HookRegistry {
[key: string]: (...args: any[]) => any
@@ -53,6 +54,7 @@ export const hooksRegistry: HookRegistry = {
useFormatValue,
useConflictResolution,
useConflictCard,
useDocumentationView,
// Add more hooks here as needed
}

View File

@@ -0,0 +1,4 @@
export interface DocumentationViewProps {
// This component is stateful and uses useDocumentationViewState hook
// No props required - all state is managed internally
}

View File

@@ -231,3 +231,4 @@ export * from './template-explorer'
export * from './project-manager'
export * from './storage-settings-panel'
export * from './feature-toggle-settings'
export * from './documentation-view'

View File

@@ -250,6 +250,7 @@ import type {
ProjectManagerProps,
StorageSettingsPanelProps,
FeatureToggleSettingsProps,
DocumentationViewProps,
} from './interfaces'
// Import JSON definitions
@@ -497,6 +498,7 @@ import templateExplorerDef from '@/components/json-definitions/template-explorer
import projectManagerDef from '@/components/json-definitions/project-manager.json'
import storageSettingsPanelDef from '@/components/json-definitions/storage-settings-panel.json'
import featureToggleSettingsDef from '@/components/json-definitions/feature-toggle-settings.json'
import documentationViewDef from '@/components/json-definitions/documentation-view.json'
// Create pure JSON components (no hooks)
export const BindingIndicator = createJsonComponent<BindingIndicatorProps>(bindingIndicatorDef)
@@ -884,7 +886,17 @@ export const PreloadIndicator = createJsonComponent<PreloadIndicatorProps>(prelo
export const PWAStatusBar = createJsonComponent<PWAStatusBarProps>(pwaStatusBarDef)
export const PWAUpdatePrompt = createJsonComponent<PWAUpdatePromptProps>(pwaUpdatePromptDef)
export const PWAInstallPrompt = createJsonComponent<PWAInstallPromptProps>(pwaInstallPromptDef)
export const ConflictCard = createJsonComponent<ConflictCardProps>(conflictCardDef)
export const ConflictCard = createJsonComponentWithHooks<ConflictCardProps>(
conflictCardDef,
{
hooks: {
cardState: {
hookName: 'useConflictCard',
args: (props) => [props.conflict]
}
}
}
)
export const ConflictIndicator = createJsonComponentWithHooks<ConflictIndicatorProps>(
conflictIndicatorDef,
{
@@ -926,4 +938,13 @@ export const ProjectManager = createJsonComponent<ProjectManagerProps>(projectMa
export const StorageSettingsPanel = createJsonComponent<StorageSettingsPanelProps>(storageSettingsPanelDef)
export const FeatureToggleSettings = createJsonComponent<FeatureToggleSettingsProps>(featureToggleSettingsDef)
export const DocumentationView = createJsonComponentWithHooks<DocumentationViewProps>(documentationViewDef, {
hooks: {
viewState: {
hookName: 'useDocumentationView',
args: () => []
}
}
})
// All components converted to pure JSON! 🎉