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.
- package/.hch/issues.json +156 -0
- package/.hch/issues.md +2 -0
- package/README.md +67 -0
- package/app/api/jsonl/route.ts +71 -0
- package/app/globals.css +125 -0
- package/app/layout.tsx +48 -0
- package/app/page.tsx +284 -0
- package/cli.ts +170 -0
- package/components/chart-detail.tsx +213 -0
- package/components/chart-list.tsx +184 -0
- package/components/data-stats.tsx +49 -0
- package/components/file-upload.tsx +73 -0
- package/components/narrative-beats.tsx +108 -0
- package/components/relation-graph.tsx +81 -0
- package/components/theme-provider.tsx +11 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/tabs.tsx +66 -0
- package/components.json +21 -0
- package/dist/cli.js +144 -0
- package/feat-2-webui-local-editing/IMPLEMENTATION.md +245 -0
- package/feat-2-webui-local-editing/INTEGRATION.md +302 -0
- package/feat-2-webui-local-editing/QUICKSTART.md +129 -0
- package/feat-2-webui-local-editing/README.md +254 -0
- package/feat-2-webui-local-editing/api-route-jsonl.ts +71 -0
- package/feat-2-webui-local-editing/cli.ts +170 -0
- package/feat-2-webui-local-editing/demo.sh +98 -0
- package/feat-2-webui-local-editing/package.json +82 -0
- package/feat-2-webui-local-editing/test-integration.sh +93 -0
- package/feat-2-webui-local-editing/updated-page.tsx +284 -0
- package/hooks/use-toast.ts +17 -0
- package/lib/jsonl-parser.ts +153 -0
- package/lib/types.ts +39 -0
- package/lib/utils.ts +6 -0
- package/next.config.mjs +12 -0
- package/package.json +82 -0
- package/postcss.config.mjs +8 -0
- package/public/apple-icon.png +0 -0
- package/public/icon-dark-32x32.png +0 -0
- package/public/icon-light-32x32.png +0 -0
- package/public/icon.svg +26 -0
- package/public/placeholder-logo.png +0 -0
- package/public/placeholder-logo.svg +1 -0
- package/public/placeholder-user.jpg +0 -0
- package/public/placeholder.jpg +0 -0
- package/public/placeholder.svg +1 -0
- package/styles/globals.css +125 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="card"
|
|
9
|
+
className={cn(
|
|
10
|
+
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
data-slot="card-header"
|
|
22
|
+
className={cn(
|
|
23
|
+
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
|
24
|
+
className,
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
data-slot="card-title"
|
|
35
|
+
className={cn('leading-none font-semibold', className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-slot="card-description"
|
|
45
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
data-slot="card-action"
|
|
55
|
+
className={cn(
|
|
56
|
+
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
|
57
|
+
className,
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
data-slot="card-content"
|
|
68
|
+
className={cn('px-6', className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
data-slot="card-footer"
|
|
78
|
+
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
Card,
|
|
86
|
+
CardHeader,
|
|
87
|
+
CardFooter,
|
|
88
|
+
CardTitle,
|
|
89
|
+
CardAction,
|
|
90
|
+
CardDescription,
|
|
91
|
+
CardContent,
|
|
92
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
function ScrollArea({
|
|
9
|
+
className,
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
|
13
|
+
return (
|
|
14
|
+
<ScrollAreaPrimitive.Root
|
|
15
|
+
data-slot="scroll-area"
|
|
16
|
+
className={cn('relative', className)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
<ScrollAreaPrimitive.Viewport
|
|
20
|
+
data-slot="scroll-area-viewport"
|
|
21
|
+
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</ScrollAreaPrimitive.Viewport>
|
|
25
|
+
<ScrollBar />
|
|
26
|
+
<ScrollAreaPrimitive.Corner />
|
|
27
|
+
</ScrollAreaPrimitive.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ScrollBar({
|
|
32
|
+
className,
|
|
33
|
+
orientation = 'vertical',
|
|
34
|
+
...props
|
|
35
|
+
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
|
36
|
+
return (
|
|
37
|
+
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
|
38
|
+
data-slot="scroll-area-scrollbar"
|
|
39
|
+
orientation={orientation}
|
|
40
|
+
className={cn(
|
|
41
|
+
'flex touch-none p-px transition-colors select-none',
|
|
42
|
+
orientation === 'vertical' &&
|
|
43
|
+
'h-full w-2.5 border-l border-l-transparent',
|
|
44
|
+
orientation === 'horizontal' &&
|
|
45
|
+
'h-2.5 flex-col border-t border-t-transparent',
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<ScrollAreaPrimitive.ScrollAreaThumb
|
|
51
|
+
data-slot="scroll-area-thumb"
|
|
52
|
+
className="bg-border relative flex-1 rounded-full"
|
|
53
|
+
/>
|
|
54
|
+
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { ScrollArea, ScrollBar }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as SeparatorPrimitive from '@radix-ui/react-separator'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
function Separator({
|
|
9
|
+
className,
|
|
10
|
+
orientation = 'horizontal',
|
|
11
|
+
decorative = true,
|
|
12
|
+
...props
|
|
13
|
+
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
|
14
|
+
return (
|
|
15
|
+
<SeparatorPrimitive.Root
|
|
16
|
+
data-slot="separator"
|
|
17
|
+
decorative={decorative}
|
|
18
|
+
orientation={orientation}
|
|
19
|
+
className={cn(
|
|
20
|
+
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { Separator }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
function Tabs({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<TabsPrimitive.Root
|
|
14
|
+
data-slot="tabs"
|
|
15
|
+
className={cn('flex flex-col gap-2', className)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function TabsList({
|
|
22
|
+
className,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
|
25
|
+
return (
|
|
26
|
+
<TabsPrimitive.List
|
|
27
|
+
data-slot="tabs-list"
|
|
28
|
+
className={cn(
|
|
29
|
+
'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function TabsTrigger({
|
|
38
|
+
className,
|
|
39
|
+
...props
|
|
40
|
+
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
|
41
|
+
return (
|
|
42
|
+
<TabsPrimitive.Trigger
|
|
43
|
+
data-slot="tabs-trigger"
|
|
44
|
+
className={cn(
|
|
45
|
+
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TabsContent({
|
|
54
|
+
className,
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
|
57
|
+
return (
|
|
58
|
+
<TabsPrimitive.Content
|
|
59
|
+
data-slot="tabs-content"
|
|
60
|
+
className={cn('flex-1 outline-none', className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
package/components.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"hooks": "@/hooks"
|
|
19
|
+
},
|
|
20
|
+
"iconLibrary": "lucide"
|
|
21
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* COAIA Visualizer CLI - Launch visualizer with local memory file
|
|
4
|
+
*
|
|
5
|
+
* Launches Next.js dev server with pre-loaded memory file
|
|
6
|
+
* Works analogously to coaia-narrative/cli.ts
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { promises as fs } from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import minimist from 'minimist';
|
|
12
|
+
import * as dotenv from 'dotenv';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
function loadConfig(args) {
|
|
17
|
+
let config = {
|
|
18
|
+
memoryPath: path.join(process.cwd(), 'memory.jsonl'),
|
|
19
|
+
port: 3000,
|
|
20
|
+
noOpen: false
|
|
21
|
+
};
|
|
22
|
+
// Load .env files
|
|
23
|
+
const localEnvPath = path.join(process.cwd(), '.env');
|
|
24
|
+
try {
|
|
25
|
+
dotenv.config({ path: localEnvPath });
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
// .env doesn't exist, that's okay
|
|
29
|
+
}
|
|
30
|
+
// Custom env file
|
|
31
|
+
if (args.env) {
|
|
32
|
+
dotenv.config({ path: args.env, override: true });
|
|
33
|
+
}
|
|
34
|
+
// Environment variables
|
|
35
|
+
if (process.env.COAIAN_MF) {
|
|
36
|
+
config.memoryPath = process.env.COAIAN_MF;
|
|
37
|
+
}
|
|
38
|
+
if (process.env.COAIAV_PORT) {
|
|
39
|
+
config.port = parseInt(process.env.COAIAV_PORT, 10);
|
|
40
|
+
}
|
|
41
|
+
// Command-line flags override everything
|
|
42
|
+
if (args['memory-path'] || args['M']) {
|
|
43
|
+
config.memoryPath = args['memory-path'] || args['M'];
|
|
44
|
+
}
|
|
45
|
+
if (args['port'] || args['p']) {
|
|
46
|
+
config.port = args['port'] || args['p'];
|
|
47
|
+
}
|
|
48
|
+
if (args['no-open']) {
|
|
49
|
+
config.noOpen = true;
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
async function main() {
|
|
54
|
+
const args = minimist(process.argv.slice(2));
|
|
55
|
+
if (args.help || args.h) {
|
|
56
|
+
console.log(`
|
|
57
|
+
🎨 COAIA Visualizer - Interactive Chart Viewer
|
|
58
|
+
|
|
59
|
+
DESCRIPTION:
|
|
60
|
+
Web-based visualizer for structural tension charts from coaia-narrative.
|
|
61
|
+
Launches a local web server to interactively explore your charts.
|
|
62
|
+
|
|
63
|
+
USAGE:
|
|
64
|
+
coaia-visualizer [OPTIONS]
|
|
65
|
+
npx coaia-visualizer [OPTIONS]
|
|
66
|
+
|
|
67
|
+
OPTIONS:
|
|
68
|
+
--memory-path PATH, -M PATH Path to memory.jsonl file (default: ./memory.jsonl)
|
|
69
|
+
--port PORT, -p PORT Server port (default: 3000)
|
|
70
|
+
--no-open Don't auto-open browser
|
|
71
|
+
--help, -h Show this help message
|
|
72
|
+
|
|
73
|
+
ENVIRONMENT VARIABLES:
|
|
74
|
+
COAIAN_MF Default memory file path
|
|
75
|
+
COAIAV_PORT Default server port
|
|
76
|
+
|
|
77
|
+
EXAMPLES:
|
|
78
|
+
# Launch with default memory.jsonl
|
|
79
|
+
coaia-visualizer
|
|
80
|
+
|
|
81
|
+
# Launch with specific memory file
|
|
82
|
+
coaia-visualizer --memory-path ./my-charts.jsonl
|
|
83
|
+
|
|
84
|
+
# Launch on different port
|
|
85
|
+
coaia-visualizer --port 3001
|
|
86
|
+
|
|
87
|
+
# Use environment variable
|
|
88
|
+
COAIAN_MF=./charts.jsonl coaia-visualizer
|
|
89
|
+
`);
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
const config = loadConfig(args);
|
|
93
|
+
// Verify memory file exists
|
|
94
|
+
try {
|
|
95
|
+
await fs.access(config.memoryPath);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(`❌ Memory file not found: ${config.memoryPath}`);
|
|
99
|
+
console.error(` Create a memory file or specify a different path with --memory-path`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
console.log(`🎨 COAIA Visualizer`);
|
|
103
|
+
console.log(`📁 Memory file: ${config.memoryPath}`);
|
|
104
|
+
console.log(`🌐 Port: ${config.port}`);
|
|
105
|
+
console.log();
|
|
106
|
+
// Set environment variables for Next.js
|
|
107
|
+
process.env.COAIAV_MEMORY_PATH = path.resolve(config.memoryPath);
|
|
108
|
+
process.env.PORT = config.port.toString();
|
|
109
|
+
// Navigate to visualizer root
|
|
110
|
+
const visualizerRoot = path.resolve(__dirname, '..');
|
|
111
|
+
// Launch Next.js dev server with explicit port flag
|
|
112
|
+
const nextProcess = spawn('npm', ['run', 'dev', '--', '-p', config.port.toString()], {
|
|
113
|
+
cwd: visualizerRoot,
|
|
114
|
+
stdio: 'inherit',
|
|
115
|
+
env: {
|
|
116
|
+
...process.env,
|
|
117
|
+
COAIAV_MEMORY_PATH: path.resolve(config.memoryPath),
|
|
118
|
+
PORT: config.port.toString()
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
// Open browser if not disabled
|
|
122
|
+
if (!config.noOpen) {
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
const url = `http://localhost:${config.port}`;
|
|
125
|
+
console.log(`\n🚀 Opening browser: ${url}\n`);
|
|
126
|
+
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
127
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
128
|
+
spawn(openCmd, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
129
|
+
}, 3000);
|
|
130
|
+
}
|
|
131
|
+
// Handle shutdown
|
|
132
|
+
process.on('SIGINT', () => {
|
|
133
|
+
console.log('\n👋 Shutting down visualizer...');
|
|
134
|
+
nextProcess.kill();
|
|
135
|
+
process.exit(0);
|
|
136
|
+
});
|
|
137
|
+
nextProcess.on('exit', (code) => {
|
|
138
|
+
process.exit(code || 0);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
main().catch((error) => {
|
|
142
|
+
console.error('❌ Fatal error:', error);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Implementation Summary - COAIA Visualizer Local Editing
|
|
2
|
+
|
|
3
|
+
## Changes Made
|
|
4
|
+
|
|
5
|
+
### New Files Created
|
|
6
|
+
|
|
7
|
+
1. **`cli.ts`** - CLI launcher
|
|
8
|
+
- Parses command-line arguments (--memory-path, --port, --no-open)
|
|
9
|
+
- Loads configuration from env vars and .env files
|
|
10
|
+
- Launches Next.js dev server with environment variables
|
|
11
|
+
- Auto-opens browser unless --no-open specified
|
|
12
|
+
|
|
13
|
+
2. **`app/api/jsonl/route.ts`** - API endpoints
|
|
14
|
+
- GET endpoint: reads memory file from filesystem
|
|
15
|
+
- POST endpoint: writes updated data with automatic backup
|
|
16
|
+
- Uses `COAIAV_MEMORY_PATH` environment variable
|
|
17
|
+
|
|
18
|
+
3. **`hooks/use-toast.ts`** - Toast notifications
|
|
19
|
+
- Wrapper around sonner for consistent toast API
|
|
20
|
+
- Used for success/error feedback
|
|
21
|
+
|
|
22
|
+
### Modified Files
|
|
23
|
+
|
|
24
|
+
1. **`app/page.tsx`** - Enhanced main UI
|
|
25
|
+
- Auto-loads file on mount if launched via CLI
|
|
26
|
+
- Adds "Save Changes" button for local persistence
|
|
27
|
+
- Adds "Reload" button to refresh from file
|
|
28
|
+
- Shows file path when auto-loaded
|
|
29
|
+
- Toast notifications for user feedback
|
|
30
|
+
|
|
31
|
+
2. **`app/layout.tsx`** - Added Toaster
|
|
32
|
+
- Imports and renders Sonner Toaster component
|
|
33
|
+
- Enables toast notifications throughout app
|
|
34
|
+
|
|
35
|
+
3. **`package.json`** - Updated configuration
|
|
36
|
+
- Changed name to `@coaia/visualizer`
|
|
37
|
+
- Added `bin` entry for CLI
|
|
38
|
+
- Added dependencies: minimist, dotenv, @types/minimist
|
|
39
|
+
- Added `build:cli` and `prepare` scripts
|
|
40
|
+
- Set `type: "module"` for ES modules
|
|
41
|
+
|
|
42
|
+
### Dependencies Added
|
|
43
|
+
|
|
44
|
+
- `minimist` - Command-line argument parsing
|
|
45
|
+
- `dotenv` - Environment variable loading
|
|
46
|
+
- `@types/minimist` - TypeScript types
|
|
47
|
+
|
|
48
|
+
### Build Process
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm run build:cli # Compiles cli.ts to dist/cli.js
|
|
52
|
+
npm run prepare # Runs automatically on npm install
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Architecture
|
|
56
|
+
|
|
57
|
+
### Data Flow
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
┌──────────────────┐
|
|
61
|
+
│ CLI Launcher │ node dist/cli.js --memory-path file.jsonl
|
|
62
|
+
│ (cli.ts) │ Sets COAIAV_MEMORY_PATH env var
|
|
63
|
+
└────────┬─────────┘
|
|
64
|
+
│
|
|
65
|
+
▼
|
|
66
|
+
┌──────────────────┐
|
|
67
|
+
│ Next.js Server │ Reads env var, starts on specified port
|
|
68
|
+
│ │
|
|
69
|
+
└────────┬─────────┘
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
┌──────────────────┐
|
|
73
|
+
│ API Route │ GET/POST /api/jsonl
|
|
74
|
+
│ (route.ts) │ Reads/writes filesystem using env var
|
|
75
|
+
└────────┬─────────┘
|
|
76
|
+
│
|
|
77
|
+
▼
|
|
78
|
+
┌──────────────────┐
|
|
79
|
+
│ React UI │ Auto-loads on mount via fetch()
|
|
80
|
+
│ (page.tsx) │ Save/Reload buttons call API
|
|
81
|
+
└──────────────────┘
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Configuration Priority
|
|
85
|
+
|
|
86
|
+
CLI follows same pattern as coaia-narrative/cli.ts:
|
|
87
|
+
|
|
88
|
+
1. Command-line flags (highest)
|
|
89
|
+
2. Custom env file (--env flag)
|
|
90
|
+
3. .env file in cwd
|
|
91
|
+
4. Environment variables
|
|
92
|
+
5. Defaults (lowest)
|
|
93
|
+
|
|
94
|
+
## Usage Examples
|
|
95
|
+
|
|
96
|
+
### Basic Launch
|
|
97
|
+
```bash
|
|
98
|
+
node dist/cli.js --memory-path ./charts.jsonl
|
|
99
|
+
# Opens http://localhost:3000 automatically
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Custom Port
|
|
103
|
+
```bash
|
|
104
|
+
node dist/cli.js --memory-path ./charts.jsonl --port 3001
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Environment Variables
|
|
108
|
+
```bash
|
|
109
|
+
export COAIAN_MF=./my-charts.jsonl
|
|
110
|
+
export COAIAV_PORT=3001
|
|
111
|
+
node dist/cli.js
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### No Auto-Open
|
|
115
|
+
```bash
|
|
116
|
+
node dist/cli.js --memory-path ./charts.jsonl --no-open
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Endpoints
|
|
120
|
+
|
|
121
|
+
### GET /api/jsonl
|
|
122
|
+
Returns file content from `COAIAV_MEMORY_PATH`:
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"content": "...",
|
|
126
|
+
"filename": "memory.jsonl",
|
|
127
|
+
"path": "/absolute/path/to/memory.jsonl"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### POST /api/jsonl
|
|
132
|
+
Saves content to file:
|
|
133
|
+
```json
|
|
134
|
+
Request: { "content": "..." }
|
|
135
|
+
Response: {
|
|
136
|
+
"success": true,
|
|
137
|
+
"backup": "/path/to/backup-123456.jsonl",
|
|
138
|
+
"message": "Memory file updated successfully"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Testing
|
|
143
|
+
|
|
144
|
+
### Integration Tests
|
|
145
|
+
```bash
|
|
146
|
+
cd coaia-visualizer
|
|
147
|
+
bash feat-2-webui-local-editing/test-integration.sh
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Tests:
|
|
151
|
+
- ✓ CLI help output
|
|
152
|
+
- ✓ CLI builds successfully
|
|
153
|
+
- ✓ CLI is executable
|
|
154
|
+
- ✓ Test file exists
|
|
155
|
+
- ✓ Server starts correctly
|
|
156
|
+
- ✓ API endpoint works
|
|
157
|
+
- ✓ Homepage loads
|
|
158
|
+
|
|
159
|
+
### Demo Script
|
|
160
|
+
```bash
|
|
161
|
+
bash feat-2-webui-local-editing/demo.sh
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Interactive demo that:
|
|
165
|
+
- Starts server with test file
|
|
166
|
+
- Tests all endpoints
|
|
167
|
+
- Displays data stats
|
|
168
|
+
- Leaves server running for manual testing
|
|
169
|
+
|
|
170
|
+
## Key Features
|
|
171
|
+
|
|
172
|
+
### 1. Auto-Loading
|
|
173
|
+
- If `COAIAV_MEMORY_PATH` is set, file auto-loads on page mount
|
|
174
|
+
- Shows filename in header
|
|
175
|
+
- Enables save/reload functionality
|
|
176
|
+
|
|
177
|
+
### 2. Backup System
|
|
178
|
+
- Every save creates timestamped backup
|
|
179
|
+
- Format: `{filename}.backup-{timestamp}`
|
|
180
|
+
- Prevents accidental data loss
|
|
181
|
+
|
|
182
|
+
### 3. User Feedback
|
|
183
|
+
- Toast notifications for all operations
|
|
184
|
+
- Loading states on buttons
|
|
185
|
+
- Error messages with details
|
|
186
|
+
|
|
187
|
+
### 4. Flexible Configuration
|
|
188
|
+
- Works like coaia-narrative/cli.ts
|
|
189
|
+
- Same flags and env vars
|
|
190
|
+
- Consistent user experience
|
|
191
|
+
|
|
192
|
+
## Files in feat-2-webui-local-editing/
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
feat-2-webui-local-editing/
|
|
196
|
+
├── cli.ts # Original CLI source
|
|
197
|
+
├── api-route-jsonl.ts # Original API route source
|
|
198
|
+
├── updated-page.tsx # Original page.tsx source
|
|
199
|
+
├── package.json # Reference package.json with changes
|
|
200
|
+
├── test-integration.sh # Automated test script
|
|
201
|
+
├── demo.sh # Interactive demo script
|
|
202
|
+
└── INTEGRATION.md # This documentation
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Integration with coaia-narrative
|
|
206
|
+
|
|
207
|
+
### Shared Components
|
|
208
|
+
- Both use `--memory-path` / `-M` flag
|
|
209
|
+
- Both use `COAIAN_MF` environment variable
|
|
210
|
+
- Both read/write same JSONL format
|
|
211
|
+
- Compatible data structures
|
|
212
|
+
|
|
213
|
+
### Workflow
|
|
214
|
+
1. LLM creates charts via coaia-narrative MCP
|
|
215
|
+
2. Human views with coaia-narrative CLI
|
|
216
|
+
3. Human visualizes with coaia-visualizer
|
|
217
|
+
4. Edit in web UI and save
|
|
218
|
+
5. Verify with coaia-narrative CLI
|
|
219
|
+
|
|
220
|
+
## Next Steps
|
|
221
|
+
|
|
222
|
+
To deploy as npm package:
|
|
223
|
+
```bash
|
|
224
|
+
npm login
|
|
225
|
+
npm publish --access public
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
To install globally:
|
|
229
|
+
```bash
|
|
230
|
+
npm install -g @coaia/visualizer
|
|
231
|
+
coaia-visualizer --memory-path ./charts.jsonl
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Complete Implementation ✓
|
|
235
|
+
|
|
236
|
+
All requirements met:
|
|
237
|
+
- ✓ CLI launcher with --memory-path flag
|
|
238
|
+
- ✓ Environment variable support
|
|
239
|
+
- ✓ API routes for file read/write
|
|
240
|
+
- ✓ Auto-loading in web UI
|
|
241
|
+
- ✓ Save/reload functionality
|
|
242
|
+
- ✓ Backup system
|
|
243
|
+
- ✓ Works with coaia-narrative charts
|
|
244
|
+
- ✓ Analogous to coaia-narrative/cli.ts
|
|
245
|
+
- ✓ Tested and working
|