coaia-visualizer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.hch/issues.json +156 -0
  2. package/.hch/issues.md +2 -0
  3. package/README.md +67 -0
  4. package/app/api/jsonl/route.ts +71 -0
  5. package/app/globals.css +125 -0
  6. package/app/layout.tsx +48 -0
  7. package/app/page.tsx +284 -0
  8. package/cli.ts +170 -0
  9. package/components/chart-detail.tsx +213 -0
  10. package/components/chart-list.tsx +184 -0
  11. package/components/data-stats.tsx +49 -0
  12. package/components/file-upload.tsx +73 -0
  13. package/components/narrative-beats.tsx +108 -0
  14. package/components/relation-graph.tsx +81 -0
  15. package/components/theme-provider.tsx +11 -0
  16. package/components/ui/badge.tsx +46 -0
  17. package/components/ui/button.tsx +60 -0
  18. package/components/ui/card.tsx +92 -0
  19. package/components/ui/scroll-area.tsx +58 -0
  20. package/components/ui/separator.tsx +28 -0
  21. package/components/ui/tabs.tsx +66 -0
  22. package/components.json +21 -0
  23. package/dist/cli.js +144 -0
  24. package/feat-2-webui-local-editing/IMPLEMENTATION.md +245 -0
  25. package/feat-2-webui-local-editing/INTEGRATION.md +302 -0
  26. package/feat-2-webui-local-editing/QUICKSTART.md +129 -0
  27. package/feat-2-webui-local-editing/README.md +254 -0
  28. package/feat-2-webui-local-editing/api-route-jsonl.ts +71 -0
  29. package/feat-2-webui-local-editing/cli.ts +170 -0
  30. package/feat-2-webui-local-editing/demo.sh +98 -0
  31. package/feat-2-webui-local-editing/package.json +82 -0
  32. package/feat-2-webui-local-editing/test-integration.sh +93 -0
  33. package/feat-2-webui-local-editing/updated-page.tsx +284 -0
  34. package/hooks/use-toast.ts +17 -0
  35. package/lib/jsonl-parser.ts +153 -0
  36. package/lib/types.ts +39 -0
  37. package/lib/utils.ts +6 -0
  38. package/next.config.mjs +12 -0
  39. package/package.json +82 -0
  40. package/postcss.config.mjs +8 -0
  41. package/public/apple-icon.png +0 -0
  42. package/public/icon-dark-32x32.png +0 -0
  43. package/public/icon-light-32x32.png +0 -0
  44. package/public/icon.svg +26 -0
  45. package/public/placeholder-logo.png +0 -0
  46. package/public/placeholder-logo.svg +1 -0
  47. package/public/placeholder-user.jpg +0 -0
  48. package/public/placeholder.jpg +0 -0
  49. package/public/placeholder.svg +1 -0
  50. package/styles/globals.css +125 -0
  51. package/tsconfig.json +41 -0
@@ -0,0 +1,284 @@
1
+ // app/page.tsx - Updated to support auto-loading from API
2
+ "use client"
3
+
4
+ import { useState, useEffect } from "react"
5
+ import { FileUpload } from "@/components/file-upload"
6
+ import { ChartList } from "@/components/chart-list"
7
+ import { ChartDetail } from "@/components/chart-detail"
8
+ import { DataStats } from "@/components/data-stats"
9
+ import type { ParsedData, Chart } from "@/lib/types"
10
+ import { parseJSONL, organizeData } from "@/lib/jsonl-parser"
11
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
12
+ import { Download, Upload, RefreshCw, Save } from "lucide-react"
13
+ import { Button } from "@/components/ui/button"
14
+ import { useToast } from "@/hooks/use-toast"
15
+
16
+ export default function Page() {
17
+ const [data, setData] = useState<ParsedData | null>(null)
18
+ const [selectedChart, setSelectedChart] = useState<Chart | null>(null)
19
+ const [viewMode, setViewMode] = useState<"hierarchy" | "list">("hierarchy")
20
+ const [fileName, setFileName] = useState<string>("")
21
+ const [isAutoLoaded, setIsAutoLoaded] = useState(false)
22
+ const [isLoading, setIsLoading] = useState(false)
23
+ const { toast } = useToast()
24
+
25
+ // Auto-load from API if COAIAV_MEMORY_PATH is set
26
+ useEffect(() => {
27
+ async function autoLoad() {
28
+ try {
29
+ const response = await fetch('/api/jsonl')
30
+ if (response.ok) {
31
+ const { content, filename } = await response.json()
32
+ handleFileLoad(content, filename)
33
+ setIsAutoLoaded(true)
34
+ toast({
35
+ title: "File loaded",
36
+ description: `Loaded ${filename} from local filesystem`,
37
+ })
38
+ }
39
+ } catch (error) {
40
+ console.log('No auto-load available, waiting for manual upload')
41
+ }
42
+ }
43
+ autoLoad()
44
+ }, [])
45
+
46
+ const handleFileLoad = (content: string, name: string) => {
47
+ try {
48
+ const records = parseJSONL(content)
49
+ const organized = organizeData(records)
50
+ setData(organized)
51
+ setSelectedChart(null)
52
+ setFileName(name)
53
+ } catch (error) {
54
+ console.error("Failed to parse JSONL:", error)
55
+ toast({
56
+ variant: "destructive",
57
+ title: "Parse error",
58
+ description: "Failed to parse JSONL file. Please check the file format.",
59
+ })
60
+ }
61
+ }
62
+
63
+ const handleReload = async () => {
64
+ if (!isAutoLoaded) return
65
+
66
+ setIsLoading(true)
67
+ try {
68
+ const response = await fetch('/api/jsonl')
69
+ if (response.ok) {
70
+ const { content, filename } = await response.json()
71
+ handleFileLoad(content, filename)
72
+ toast({
73
+ title: "Reloaded",
74
+ description: `Reloaded ${filename} from filesystem`,
75
+ })
76
+ } else {
77
+ throw new Error('Failed to reload')
78
+ }
79
+ } catch (error) {
80
+ toast({
81
+ variant: "destructive",
82
+ title: "Reload failed",
83
+ description: "Failed to reload file from filesystem",
84
+ })
85
+ } finally {
86
+ setIsLoading(false)
87
+ }
88
+ }
89
+
90
+ const handleSave = async () => {
91
+ if (!data || !isAutoLoaded) return
92
+
93
+ setIsLoading(true)
94
+ try {
95
+ const jsonlLines: string[] = []
96
+
97
+ // Export all entities
98
+ for (const entity of data.entities.values()) {
99
+ jsonlLines.push(JSON.stringify(entity))
100
+ }
101
+
102
+ // Export all relations
103
+ for (const relation of data.relations) {
104
+ jsonlLines.push(JSON.stringify(relation))
105
+ }
106
+
107
+ const response = await fetch('/api/jsonl', {
108
+ method: 'POST',
109
+ headers: { 'Content-Type': 'application/json' },
110
+ body: JSON.stringify({ content: jsonlLines.join('\n') + '\n' })
111
+ })
112
+
113
+ if (response.ok) {
114
+ const { backup } = await response.json()
115
+ toast({
116
+ title: "Saved",
117
+ description: `File saved. Backup created at ${backup}`,
118
+ })
119
+ } else {
120
+ throw new Error('Save failed')
121
+ }
122
+ } catch (error) {
123
+ toast({
124
+ variant: "destructive",
125
+ title: "Save failed",
126
+ description: "Failed to save changes to filesystem",
127
+ })
128
+ } finally {
129
+ setIsLoading(false)
130
+ }
131
+ }
132
+
133
+ const handleExportData = () => {
134
+ if (!data) return
135
+
136
+ const jsonlLines: string[] = []
137
+
138
+ // Export all entities
139
+ for (const entity of data.entities.values()) {
140
+ jsonlLines.push(JSON.stringify(entity))
141
+ }
142
+
143
+ // Export all relations
144
+ for (const relation of data.relations) {
145
+ jsonlLines.push(JSON.stringify(relation))
146
+ }
147
+
148
+ const blob = new Blob([jsonlLines.join("\n") + "\n"], { type: "application/jsonl" })
149
+ const url = URL.createObjectURL(blob)
150
+ const a = document.createElement("a")
151
+ a.href = url
152
+ a.download = fileName || "coaia-narrative-export.jsonl"
153
+ document.body.appendChild(a)
154
+ a.click()
155
+ document.body.removeChild(a)
156
+ URL.revokeObjectURL(url)
157
+ }
158
+
159
+ return (
160
+ <div className="min-h-screen bg-background">
161
+ <header className="border-b border-border bg-card">
162
+ <div className="container mx-auto px-4 py-6">
163
+ <div className="flex items-center justify-between">
164
+ <div>
165
+ <h1 className="text-3xl font-bold text-balance">COAIA Narrative Visualizer</h1>
166
+ <p className="text-muted-foreground mt-2">
167
+ Visualize and explore structural tension charts from coaia-narrative JSONL files
168
+ </p>
169
+ {isAutoLoaded && (
170
+ <p className="text-xs text-muted-foreground mt-1">
171
+ 📁 {fileName}
172
+ </p>
173
+ )}
174
+ </div>
175
+ {data && (
176
+ <div className="flex items-center gap-2">
177
+ {isAutoLoaded && (
178
+ <>
179
+ <Button onClick={handleSave} variant="default" size="sm" disabled={isLoading}>
180
+ <Save className="w-4 h-4 mr-2" />
181
+ Save Changes
182
+ </Button>
183
+ <Button onClick={handleReload} variant="outline" size="sm" disabled={isLoading}>
184
+ <RefreshCw className={`w-4 h-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
185
+ Reload
186
+ </Button>
187
+ </>
188
+ )}
189
+ <Button onClick={handleExportData} variant="outline" size="sm">
190
+ <Download className="w-4 h-4 mr-2" />
191
+ Export JSONL
192
+ </Button>
193
+ <Button onClick={() => setData(null)} variant="outline" size="sm">
194
+ <Upload className="w-4 h-4 mr-2" />
195
+ Load New File
196
+ </Button>
197
+ </div>
198
+ )}
199
+ </div>
200
+ </div>
201
+ </header>
202
+
203
+ <main className="container mx-auto px-4 py-8">
204
+ {!data ? (
205
+ <div className="max-w-2xl mx-auto">
206
+ <FileUpload onFileLoad={handleFileLoad} />
207
+ <div className="mt-8 p-6 bg-muted/30 rounded-lg">
208
+ <h2 className="text-lg font-semibold mb-3">About This Tool</h2>
209
+ <p className="text-sm text-muted-foreground leading-relaxed mb-4">
210
+ This visualizer helps you explore and understand structural tension charts created by the
211
+ coaia-narrative MCP server. It displays the creative tension between current reality and desired
212
+ outcomes, action steps, narrative beats across multiple universes, and the relationships between
213
+ entities.
214
+ </p>
215
+ <div className="space-y-2">
216
+ <h3 className="text-sm font-semibold">Supported Features:</h3>
217
+ <ul className="text-sm text-muted-foreground space-y-1">
218
+ <li className="flex items-start gap-2">
219
+ <span className="text-chart-1 mt-1">•</span>
220
+ <span>Hierarchical chart visualization with expandable sub-charts</span>
221
+ </li>
222
+ <li className="flex items-start gap-2">
223
+ <span className="text-chart-2 mt-1">•</span>
224
+ <span>Action step tracking with completion status and due dates</span>
225
+ </li>
226
+ <li className="flex items-start gap-2">
227
+ <span className="text-chart-3 mt-1">•</span>
228
+ <span>Narrative beats with multi-universe perspectives</span>
229
+ </li>
230
+ <li className="flex items-start gap-2">
231
+ <span className="text-chart-4 mt-1">•</span>
232
+ <span>Entity relation graph visualization</span>
233
+ </li>
234
+ <li className="flex items-start gap-2">
235
+ <span className="text-chart-5 mt-1">•</span>
236
+ <span>Local file editing and auto-saving when launched via CLI</span>
237
+ </li>
238
+ </ul>
239
+ </div>
240
+ </div>
241
+ </div>
242
+ ) : (
243
+ <div className="space-y-6">
244
+ <DataStats data={data} />
245
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
246
+ <div className="lg:col-span-1">
247
+ <Tabs value={viewMode} onValueChange={(v) => setViewMode(v as any)}>
248
+ <TabsList className="w-full">
249
+ <TabsTrigger value="hierarchy" className="flex-1">
250
+ Hierarchy
251
+ </TabsTrigger>
252
+ <TabsTrigger value="list" className="flex-1">
253
+ All Charts
254
+ </TabsTrigger>
255
+ </TabsList>
256
+ <TabsContent value="hierarchy" className="mt-4">
257
+ <ChartList
258
+ data={data}
259
+ selectedChart={selectedChart}
260
+ onSelectChart={setSelectedChart}
261
+ mode="hierarchy"
262
+ />
263
+ </TabsContent>
264
+ <TabsContent value="list" className="mt-4">
265
+ <ChartList data={data} selectedChart={selectedChart} onSelectChart={setSelectedChart} mode="list" />
266
+ </TabsContent>
267
+ </Tabs>
268
+ </div>
269
+ <div className="lg:col-span-2">
270
+ {selectedChart ? (
271
+ <ChartDetail chart={selectedChart} data={data} />
272
+ ) : (
273
+ <div className="bg-card border border-border rounded-lg p-12 text-center">
274
+ <p className="text-muted-foreground">Select a chart from the list to view details</p>
275
+ </div>
276
+ )}
277
+ </div>
278
+ </div>
279
+ </div>
280
+ )}
281
+ </main>
282
+ </div>
283
+ )
284
+ }
@@ -0,0 +1,17 @@
1
+ import { toast as sonnerToast } from "sonner"
2
+
3
+ export function useToast() {
4
+ return {
5
+ toast: ({ title, description, variant }: {
6
+ title?: string;
7
+ description?: string;
8
+ variant?: "default" | "destructive"
9
+ }) => {
10
+ if (variant === "destructive") {
11
+ sonnerToast.error(title || "Error", { description })
12
+ } else {
13
+ sonnerToast.success(title || "Success", { description })
14
+ }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,153 @@
1
+ import type { EntityRecord, RelationRecord, JSONLRecord, Chart, ParsedData } from "./types"
2
+
3
+ export function parseJSONL(content: string): JSONLRecord[] {
4
+ const lines = content.trim().split("\n")
5
+ const records: JSONLRecord[] = []
6
+
7
+ for (const line of lines) {
8
+ if (!line.trim()) continue
9
+ try {
10
+ const record = JSON.parse(line)
11
+ records.push(record)
12
+ } catch (error) {
13
+ console.error("Failed to parse line:", line, error)
14
+ }
15
+ }
16
+
17
+ return records
18
+ }
19
+
20
+ export function organizeData(records: JSONLRecord[]): ParsedData {
21
+ const entities = new Map<string, EntityRecord>()
22
+ const relations: RelationRecord[] = []
23
+ const charts = new Map<string, Chart>()
24
+
25
+ console.log("[v0] Total records to process:", records.length)
26
+
27
+ // First pass: separate entities and relations
28
+ for (const record of records) {
29
+ if (record.type === "entity") {
30
+ entities.set(record.name, record)
31
+ } else if (record.type === "relation") {
32
+ relations.push(record)
33
+ }
34
+ }
35
+
36
+ console.log("[v0] Total entities:", entities.size)
37
+ console.log("[v0] Total relations:", relations.length)
38
+
39
+ // Second pass: create chart structures
40
+ for (const [name, entity] of entities.entries()) {
41
+ if (entity.entityType === "structural_tension_chart") {
42
+ const chartId = entity.metadata.chartId || entity.name.replace("_chart", "")
43
+ console.log("[v0] Found chart:", chartId, "Level:", entity.metadata.level, "Name:", entity.name)
44
+
45
+ if (!charts.has(chartId)) {
46
+ charts.set(chartId, {
47
+ id: chartId,
48
+ chartEntity: entity,
49
+ actions: [],
50
+ narrativeBeats: [],
51
+ subCharts: [],
52
+ level: entity.metadata.level || 0,
53
+ parentChart: entity.metadata.parentChart,
54
+ relations: [],
55
+ })
56
+ }
57
+ }
58
+ }
59
+
60
+ console.log("[v0] Total charts created:", charts.size)
61
+
62
+ // Third pass: populate charts with their components
63
+ for (const [name, entity] of entities.entries()) {
64
+ let chartId = entity.metadata.chartId
65
+
66
+ // If no chartId in metadata, extract from entity name
67
+ if (!chartId && name.includes("_")) {
68
+ const match = name.match(/^(chart_\d+)/)
69
+ if (match) {
70
+ chartId = match[1]
71
+ }
72
+ }
73
+
74
+ const chart = charts.get(chartId)
75
+
76
+ if (!chart) {
77
+ if (entity.entityType !== "structural_tension_chart") {
78
+ console.log("[v0] No chart found for entity:", name, "chartId:", chartId)
79
+ }
80
+ continue
81
+ }
82
+
83
+ switch (entity.entityType) {
84
+ case "desired_outcome":
85
+ chart.desiredOutcome = entity
86
+ break
87
+ case "current_reality":
88
+ chart.currentReality = entity
89
+ break
90
+ case "action_step":
91
+ chart.actions.push(entity)
92
+ break
93
+ case "narrative_beat":
94
+ chart.narrativeBeats.push(entity)
95
+ break
96
+ }
97
+ }
98
+
99
+ for (const [id, chart] of charts.entries()) {
100
+ console.log(
101
+ `[v0] Chart ${id}: ${chart.actions.length} actions, ${chart.narrativeBeats.length} beats, level ${chart.level}`,
102
+ )
103
+ }
104
+
105
+ // Fourth pass: organize chart hierarchy and relations
106
+ for (const chart of charts.values()) {
107
+ chart.relations = relations.filter(
108
+ (r) =>
109
+ r.from.startsWith(chart.id) ||
110
+ r.to.startsWith(chart.id) ||
111
+ r.from.startsWith(`chart_${chart.id}`) ||
112
+ r.to.startsWith(`chart_${chart.id}`),
113
+ )
114
+
115
+ if (chart.parentChart) {
116
+ const parentId = chart.parentChart.replace("chart_", "")
117
+ const parentChart = charts.get(parentId) || charts.get(chart.parentChart)
118
+ if (parentChart) {
119
+ parentChart.subCharts.push(chart)
120
+ console.log(`[v0] Added ${chart.id} as subChart of ${parentChart.id}`)
121
+ } else {
122
+ console.log(`[v0] Parent chart not found: ${chart.parentChart} for chart ${chart.id}`)
123
+ }
124
+ }
125
+ }
126
+
127
+ // Identify root charts (level 0 or no parent)
128
+ const rootCharts = Array.from(charts.values()).filter((chart) => chart.level === 0 || !chart.parentChart)
129
+
130
+ console.log("[v0] Root charts found:", rootCharts.length)
131
+ console.log(
132
+ "[v0] Root chart IDs:",
133
+ rootCharts.map((c) => c.id),
134
+ )
135
+
136
+ return {
137
+ charts: Array.from(charts.values()),
138
+ entities,
139
+ relations,
140
+ rootCharts,
141
+ }
142
+ }
143
+
144
+ export function getChartSummary(chart: Chart): string {
145
+ const outcome = chart.desiredOutcome?.observations[0] || "No desired outcome"
146
+ return outcome.length > 100 ? outcome.substring(0, 100) + "..." : outcome
147
+ }
148
+
149
+ export function getChartProgress(chart: Chart): number {
150
+ if (chart.actions.length === 0) return 0
151
+ const completed = chart.actions.filter((a) => a.metadata.completionStatus).length
152
+ return Math.round((completed / chart.actions.length) * 100)
153
+ }
package/lib/types.ts ADDED
@@ -0,0 +1,39 @@
1
+ // Type definitions for coaia-narrative JSONL data structures
2
+
3
+ export interface EntityRecord {
4
+ type: "entity"
5
+ name: string
6
+ entityType: "structural_tension_chart" | "desired_outcome" | "current_reality" | "action_step" | "narrative_beat"
7
+ observations: string[]
8
+ metadata: Record<string, any>
9
+ }
10
+
11
+ export interface RelationRecord {
12
+ type: "relation"
13
+ from: string
14
+ to: string
15
+ relationType: "contains" | "creates_tension_with" | "advances_toward" | "documents"
16
+ metadata: Record<string, any>
17
+ }
18
+
19
+ export type JSONLRecord = EntityRecord | RelationRecord
20
+
21
+ export interface Chart {
22
+ id: string
23
+ chartEntity: EntityRecord
24
+ desiredOutcome?: EntityRecord
25
+ currentReality?: EntityRecord
26
+ actions: EntityRecord[]
27
+ narrativeBeats: EntityRecord[]
28
+ subCharts: Chart[]
29
+ level: number
30
+ parentChart?: string
31
+ relations: RelationRecord[]
32
+ }
33
+
34
+ export interface ParsedData {
35
+ charts: Chart[]
36
+ entities: Map<string, EntityRecord>
37
+ relations: RelationRecord[]
38
+ rootCharts: Chart[]
39
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,12 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ typescript: {
4
+ ignoreBuildErrors: true,
5
+ },
6
+ images: {
7
+ unoptimized: true,
8
+ },
9
+
10
+ }
11
+
12
+ export default nextConfig
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "coaia-visualizer",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "bin": {
7
+ "coaia-visualizer": "./dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "next build",
11
+ "dev": "next dev",
12
+ "lint": "eslint .",
13
+ "start": "next start",
14
+ "build:cli": "tsc cli.ts --outDir dist --module esnext --target es2022 --moduleResolution bundler --esModuleInterop --skipLibCheck",
15
+ "prepare": "npm run build:cli"
16
+ },
17
+ "dependencies": {
18
+ "@hookform/resolvers": "^3.10.0",
19
+ "@radix-ui/react-accordion": "1.2.2",
20
+ "@radix-ui/react-alert-dialog": "1.1.4",
21
+ "@radix-ui/react-aspect-ratio": "1.1.1",
22
+ "@radix-ui/react-avatar": "1.1.2",
23
+ "@radix-ui/react-checkbox": "1.1.3",
24
+ "@radix-ui/react-collapsible": "1.1.2",
25
+ "@radix-ui/react-context-menu": "2.2.4",
26
+ "@radix-ui/react-dialog": "1.1.4",
27
+ "@radix-ui/react-dropdown-menu": "2.1.4",
28
+ "@radix-ui/react-hover-card": "1.1.4",
29
+ "@radix-ui/react-label": "2.1.1",
30
+ "@radix-ui/react-menubar": "1.1.4",
31
+ "@radix-ui/react-navigation-menu": "1.2.3",
32
+ "@radix-ui/react-popover": "1.1.4",
33
+ "@radix-ui/react-progress": "1.1.1",
34
+ "@radix-ui/react-radio-group": "1.2.2",
35
+ "@radix-ui/react-scroll-area": "1.2.2",
36
+ "@radix-ui/react-select": "2.1.4",
37
+ "@radix-ui/react-separator": "1.1.1",
38
+ "@radix-ui/react-slider": "1.2.2",
39
+ "@radix-ui/react-slot": "1.1.1",
40
+ "@radix-ui/react-switch": "1.1.2",
41
+ "@radix-ui/react-tabs": "1.1.2",
42
+ "@radix-ui/react-toast": "1.2.4",
43
+ "@radix-ui/react-toggle": "1.1.1",
44
+ "@radix-ui/react-toggle-group": "1.1.1",
45
+ "@radix-ui/react-tooltip": "1.1.6",
46
+ "@types/minimist": "^1.2.5",
47
+ "@vercel/analytics": "1.3.1",
48
+ "autoprefixer": "^10.4.20",
49
+ "class-variance-authority": "^0.7.1",
50
+ "clsx": "^2.1.1",
51
+ "cmdk": "1.0.4",
52
+ "date-fns": "4.1.0",
53
+ "dotenv": "^17.2.3",
54
+ "embla-carousel-react": "8.5.1",
55
+ "input-otp": "1.4.1",
56
+ "lucide-react": "^0.454.0",
57
+ "minimist": "^1.2.8",
58
+ "next": "16.0.10",
59
+ "next-themes": "^0.4.6",
60
+ "react": "19.2.0",
61
+ "react-day-picker": "9.8.0",
62
+ "react-dom": "19.2.0",
63
+ "react-hook-form": "^7.60.0",
64
+ "react-resizable-panels": "^2.1.7",
65
+ "recharts": "2.15.4",
66
+ "sonner": "^1.7.4",
67
+ "tailwind-merge": "^3.3.1",
68
+ "tailwindcss-animate": "^1.0.7",
69
+ "vaul": "^1.1.2",
70
+ "zod": "3.25.76"
71
+ },
72
+ "devDependencies": {
73
+ "@tailwindcss/postcss": "^4.1.9",
74
+ "@types/node": "^22",
75
+ "@types/react": "^19",
76
+ "@types/react-dom": "^19",
77
+ "postcss": "^8.5",
78
+ "tailwindcss": "^4.1.9",
79
+ "tw-animate-css": "1.3.3",
80
+ "typescript": "^5"
81
+ }
82
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ '@tailwindcss/postcss': {},
5
+ },
6
+ }
7
+
8
+ export default config
Binary file
Binary file
Binary file
@@ -0,0 +1,26 @@
1
+ <svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <style>
3
+ @media (prefers-color-scheme: light) {
4
+ .background { fill: black; }
5
+ .foreground { fill: white; }
6
+ }
7
+ @media (prefers-color-scheme: dark) {
8
+ .background { fill: white; }
9
+ .foreground { fill: black; }
10
+ }
11
+ </style>
12
+ <g clip-path="url(#clip0_7960_43945)">
13
+ <rect class="background" width="180" height="180" rx="37" />
14
+ <g style="transform: scale(95%); transform-origin: center">
15
+ <path class="foreground"
16
+ d="M101.141 53H136.632C151.023 53 162.689 64.6662 162.689 79.0573V112.904H148.112V79.0573C148.112 78.7105 148.098 78.3662 148.072 78.0251L112.581 112.898C112.701 112.902 112.821 112.904 112.941 112.904H148.112V126.672H112.941C98.5504 126.672 86.5638 114.891 86.5638 100.5V66.7434H101.141V100.5C101.141 101.15 101.191 101.792 101.289 102.422L137.56 66.7816C137.255 66.7563 136.945 66.7434 136.632 66.7434H101.141V53Z" />
17
+ <path class="foreground"
18
+ d="M65.2926 124.136L14 66.7372H34.6355L64.7495 100.436V66.7372H80.1365V118.47C80.1365 126.278 70.4953 129.958 65.2926 124.136Z" />
19
+ </g>
20
+ </g>
21
+ <defs>
22
+ <clipPath id="clip0_7960_43945">
23
+ <rect width="180" height="180" fill="white" />
24
+ </clipPath>
25
+ </defs>
26
+ </svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="215" height="48" fill="none"><path fill="#000" d="M57.588 9.6h6L73.828 38h-5.2l-2.36-6.88h-11.36L52.548 38h-5.2l10.24-28.4Zm7.16 17.16-4.16-12.16-4.16 12.16h8.32Zm23.694-2.24c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.486-7.72.12 3.4c.534-1.227 1.307-2.173 2.32-2.84 1.04-.693 2.267-1.04 3.68-1.04 1.494 0 2.76.387 3.8 1.16 1.067.747 1.827 1.813 2.28 3.2.507-1.44 1.294-2.52 2.36-3.24 1.094-.747 2.414-1.12 3.96-1.12 1.414 0 2.64.307 3.68.92s1.84 1.52 2.4 2.72c.56 1.2.84 2.667.84 4.4V38h-4.96V25.92c0-1.813-.293-3.187-.88-4.12-.56-.96-1.413-1.44-2.56-1.44-.906 0-1.68.213-2.32.64-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.84-.48 3.04V38h-4.56V25.92c0-1.2-.133-2.213-.4-3.04-.24-.827-.626-1.453-1.16-1.88-.506-.427-1.133-.64-1.88-.64-.906 0-1.68.227-2.32.68-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.827-.48 3V38h-4.96V16.8h4.48Zm26.723 10.6c0-2.24.427-4.187 1.28-5.84.854-1.68 2.067-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.84 0 3.494.413 4.96 1.24 1.467.827 2.64 2.08 3.52 3.76.88 1.653 1.347 3.693 1.4 6.12v1.32h-15.08c.107 1.813.614 3.227 1.52 4.24.907.987 2.134 1.48 3.68 1.48.987 0 1.88-.253 2.68-.76a4.803 4.803 0 0 0 1.84-2.2l5.08.36c-.64 2.027-1.84 3.64-3.6 4.84-1.733 1.173-3.733 1.76-6 1.76-2.08 0-3.906-.453-5.48-1.36-1.573-.907-2.786-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84Zm15.16-2.04c-.213-1.733-.76-3.013-1.64-3.84-.853-.827-1.893-1.24-3.12-1.24-1.44 0-2.6.453-3.48 1.36-.88.88-1.44 2.12-1.68 3.72h9.92ZM163.139 9.6V38h-5.04V9.6h5.04Zm8.322 7.2.24 5.88-.64-.36c.32-2.053 1.094-3.56 2.32-4.52 1.254-.987 2.787-1.48 4.6-1.48 2.32 0 4.107.733 5.36 2.2 1.254 1.44 1.88 3.387 1.88 5.84V38h-4.96V25.92c0-1.253-.12-2.28-.36-3.08-.24-.8-.64-1.413-1.2-1.84-.533-.427-1.253-.64-2.16-.64-1.44 0-2.573.48-3.4 1.44-.8.933-1.2 2.307-1.2 4.12V38h-4.96V16.8h4.48Zm30.003 7.72c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.443 8.16V38h-5.6v-5.32h5.6Z"/><path fill="#171717" fill-rule="evenodd" d="m7.839 40.783 16.03-28.054L20 6 0 40.783h7.839Zm8.214 0H40L27.99 19.894l-4.02 7.032 3.976 6.914H20.02l-3.967 6.943Z" clip-rule="evenodd"/></svg>
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>