coaia-visualizer 1.4.2 → 1.5.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/.dockerignore +9 -0
- package/Dockerfile.app +50 -0
- package/Dockerfile.test +24 -0
- package/LIVE_MODE_DESIGN.md +435 -0
- package/MCP_TESTING_COMPLETE.md +302 -0
- package/MCP_TESTING_IMPLEMENTATION_SUMMARY.md +317 -0
- package/MCP_TESTING_SETUP.md +268 -0
- package/NAMING.md +218 -0
- package/QUICK_START_MCP_TESTING.md +236 -0
- package/WS__issue_8__coaia-visualizer__260207.code-workspace +45 -0
- package/app/api/audio/[filename]/route.ts +37 -0
- package/app/api/charts/[id]/route.ts +48 -35
- package/app/api/watch/route.ts +42 -0
- package/app/page.tsx +103 -53
- package/cli.ts +56 -3
- package/components/add-master-chart.tsx +230 -0
- package/components/chart-detail-editable.tsx +27 -16
- package/components/chart-list.tsx +13 -1
- package/components/create-chart-form.tsx +248 -0
- package/components/data-stats.tsx +9 -7
- package/components/live-indicator.tsx +14 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/label.tsx +24 -0
- package/direct-test.sh +180 -0
- package/dist/cli.js +52 -3
- package/docker-compose.test.yml +69 -0
- package/hooks/use-live-polling.ts +45 -0
- package/jgwill.coaia-visualizer-8--496dca71-d476-4ac9-ba9f-376add118dd8--260208.txt +2612 -0
- package/lib/chart-editor.ts +281 -68
- package/mcp/Dockerfile +21 -0
- package/mcp/README.md +25 -6
- package/mcp/src/api-client.ts +15 -3
- package/mcp/src/index.ts +17 -2
- package/mcp/src/tools/index.ts +21 -1
- package/mcp/test_mcp/.gemini/settings.json +18 -0
- package/mcp-config.json +14 -0
- package/package.json +2 -2
- package/run-mcp-tests.sh +99 -0
- package/samples/tradingchart.jsonl +31 -0
- package/test-data/test-master.jsonl +11 -0
- package/test-run.log +101 -0
- package/test-scripts/README.md +239 -0
- package/test-scripts/run-all-tests.sh +38 -0
- package/test-scripts/test-01-basic-operations.sh +87 -0
- package/test-scripts/test-02-telescope-creation.sh +91 -0
- package/test-scripts/test-03-navigation.sh +87 -0
- package/validate-mcp.sh +136 -0
package/cli.ts
CHANGED
|
@@ -21,13 +21,21 @@ interface Config {
|
|
|
21
21
|
memoryPath: string;
|
|
22
22
|
port: number;
|
|
23
23
|
noOpen: boolean;
|
|
24
|
+
live: boolean;
|
|
25
|
+
pollInterval: number;
|
|
26
|
+
autoPlay: boolean;
|
|
27
|
+
audioDir: string;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
function loadConfig(args: minimist.ParsedArgs): Config {
|
|
27
31
|
let config: Config = {
|
|
28
32
|
memoryPath: path.join(process.cwd(), 'memory.jsonl'),
|
|
29
33
|
port: 3000,
|
|
30
|
-
noOpen: false
|
|
34
|
+
noOpen: false,
|
|
35
|
+
live: false,
|
|
36
|
+
pollInterval: 2000,
|
|
37
|
+
autoPlay: false,
|
|
38
|
+
audioDir: path.join(process.cwd(), 'audio')
|
|
31
39
|
};
|
|
32
40
|
|
|
33
41
|
// Load .env files
|
|
@@ -50,6 +58,18 @@ function loadConfig(args: minimist.ParsedArgs): Config {
|
|
|
50
58
|
if (process.env.COAIAV_PORT) {
|
|
51
59
|
config.port = parseInt(process.env.COAIAV_PORT, 10);
|
|
52
60
|
}
|
|
61
|
+
if (process.env.COAIAV_LIVE === 'true') {
|
|
62
|
+
config.live = true;
|
|
63
|
+
}
|
|
64
|
+
if (process.env.COAIAV_POLL_INTERVAL) {
|
|
65
|
+
config.pollInterval = parseInt(process.env.COAIAV_POLL_INTERVAL, 10);
|
|
66
|
+
}
|
|
67
|
+
if (process.env.COAIAV_AUTO_PLAY === 'true') {
|
|
68
|
+
config.autoPlay = true;
|
|
69
|
+
}
|
|
70
|
+
if (process.env.COAIAV_AUDIO_DIR) {
|
|
71
|
+
config.audioDir = process.env.COAIAV_AUDIO_DIR;
|
|
72
|
+
}
|
|
53
73
|
|
|
54
74
|
// Command-line flags override everything
|
|
55
75
|
if (args['memory-path'] || args['M']) {
|
|
@@ -61,6 +81,18 @@ function loadConfig(args: minimist.ParsedArgs): Config {
|
|
|
61
81
|
if (args['no-open']) {
|
|
62
82
|
config.noOpen = true;
|
|
63
83
|
}
|
|
84
|
+
if (args['live']) {
|
|
85
|
+
config.live = true;
|
|
86
|
+
}
|
|
87
|
+
if (args['poll-interval']) {
|
|
88
|
+
config.pollInterval = parseInt(args['poll-interval'], 10);
|
|
89
|
+
}
|
|
90
|
+
if (args['auto-play']) {
|
|
91
|
+
config.autoPlay = true;
|
|
92
|
+
}
|
|
93
|
+
if (args['audio-dir']) {
|
|
94
|
+
config.audioDir = args['audio-dir'];
|
|
95
|
+
}
|
|
64
96
|
|
|
65
97
|
return config;
|
|
66
98
|
}
|
|
@@ -83,12 +115,20 @@ USAGE:
|
|
|
83
115
|
OPTIONS:
|
|
84
116
|
--memory-path PATH, -M PATH Path to memory.jsonl file (default: ./memory.jsonl)
|
|
85
117
|
--port PORT, -p PORT Server port (default: 3000)
|
|
118
|
+
--live Enable live monitoring mode
|
|
119
|
+
--poll-interval MS Polling interval in ms (default: 2000)
|
|
120
|
+
--auto-play Auto-play audio for new beats
|
|
121
|
+
--audio-dir PATH Audio directory path (default: ./audio)
|
|
86
122
|
--no-open Don't auto-open browser
|
|
87
123
|
--help, -h Show this help message
|
|
88
124
|
|
|
89
125
|
ENVIRONMENT VARIABLES:
|
|
90
126
|
COAIAN_MF Default memory file path
|
|
91
127
|
COAIAV_PORT Default server port
|
|
128
|
+
COAIAV_LIVE Enable live mode (true/false)
|
|
129
|
+
COAIAV_POLL_INTERVAL Polling interval in ms
|
|
130
|
+
COAIAV_AUTO_PLAY Enable auto-play (true/false)
|
|
131
|
+
COAIAV_AUDIO_DIR Audio directory path
|
|
92
132
|
|
|
93
133
|
EXAMPLES:
|
|
94
134
|
# Launch with default memory.jsonl
|
|
@@ -117,14 +157,23 @@ EXAMPLES:
|
|
|
117
157
|
process.exit(1);
|
|
118
158
|
}
|
|
119
159
|
|
|
120
|
-
console.log(`🎨 COAIA Visualizer`);
|
|
160
|
+
console.log(`🎨 COAIA Visualizer${config.live ? ' [LIVE MODE]' : ''}`);
|
|
121
161
|
console.log(`📁 Memory file: ${config.memoryPath}`);
|
|
122
162
|
console.log(`🌐 Port: ${config.port}`);
|
|
163
|
+
if (config.live) {
|
|
164
|
+
console.log(`🔄 Polling: ${config.pollInterval}ms`);
|
|
165
|
+
console.log(`🎵 Audio: ${config.audioDir}`);
|
|
166
|
+
console.log(`🔊 Auto-play: ${config.autoPlay ? 'enabled' : 'disabled'}`);
|
|
167
|
+
}
|
|
123
168
|
console.log();
|
|
124
169
|
|
|
125
170
|
// Set environment variables for Next.js
|
|
126
171
|
process.env.COAIAV_MEMORY_PATH = path.resolve(config.memoryPath);
|
|
127
172
|
process.env.PORT = config.port.toString();
|
|
173
|
+
process.env.NEXT_PUBLIC_LIVE_MODE = config.live.toString();
|
|
174
|
+
process.env.NEXT_PUBLIC_POLL_INTERVAL = config.pollInterval.toString();
|
|
175
|
+
process.env.NEXT_PUBLIC_AUTO_PLAY = config.autoPlay.toString();
|
|
176
|
+
process.env.COAIAV_AUDIO_DIR = path.resolve(config.audioDir);
|
|
128
177
|
|
|
129
178
|
// Navigate to visualizer root
|
|
130
179
|
const visualizerRoot = path.resolve(__dirname, '..');
|
|
@@ -136,7 +185,11 @@ EXAMPLES:
|
|
|
136
185
|
env: {
|
|
137
186
|
...process.env,
|
|
138
187
|
COAIAV_MEMORY_PATH: path.resolve(config.memoryPath),
|
|
139
|
-
PORT: config.port.toString()
|
|
188
|
+
PORT: config.port.toString(),
|
|
189
|
+
NEXT_PUBLIC_LIVE_MODE: config.live.toString(),
|
|
190
|
+
NEXT_PUBLIC_POLL_INTERVAL: config.pollInterval.toString(),
|
|
191
|
+
NEXT_PUBLIC_AUTO_PLAY: config.autoPlay.toString(),
|
|
192
|
+
COAIAV_AUDIO_DIR: path.resolve(config.audioDir)
|
|
140
193
|
}
|
|
141
194
|
});
|
|
142
195
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type React from "react"
|
|
4
|
+
|
|
5
|
+
import { useState } from "react"
|
|
6
|
+
import { Button } from "@/components/ui/button"
|
|
7
|
+
import {
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogContent,
|
|
10
|
+
DialogDescription,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
DialogTrigger,
|
|
14
|
+
} from "@/components/ui/dialog"
|
|
15
|
+
import { Input } from "@/components/ui/input"
|
|
16
|
+
import { Label } from "@/components/ui/label"
|
|
17
|
+
import { Textarea } from "@/components/ui/textarea"
|
|
18
|
+
import { Plus, X } from "lucide-react"
|
|
19
|
+
import type { ParsedData, EntityRecord, RelationRecord } from "@/lib/types"
|
|
20
|
+
|
|
21
|
+
interface AddMasterChartProps {
|
|
22
|
+
data: ParsedData
|
|
23
|
+
onChartAdded: (updatedData: ParsedData) => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function AddMasterChart({ data, onChartAdded }: AddMasterChartProps) {
|
|
27
|
+
const [open, setOpen] = useState(false)
|
|
28
|
+
const [desiredOutcome, setDesiredOutcome] = useState("")
|
|
29
|
+
const [currentReality, setCurrentReality] = useState<string[]>([""])
|
|
30
|
+
const [dueDate, setDueDate] = useState("")
|
|
31
|
+
|
|
32
|
+
const handleAddObservation = () => {
|
|
33
|
+
setCurrentReality([...currentReality, ""])
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleRemoveObservation = (index: number) => {
|
|
37
|
+
setCurrentReality(currentReality.filter((_, i) => i !== index))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const handleObservationChange = (index: number, value: string) => {
|
|
41
|
+
const updated = [...currentReality]
|
|
42
|
+
updated[index] = value
|
|
43
|
+
setCurrentReality(updated)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
47
|
+
e.preventDefault()
|
|
48
|
+
|
|
49
|
+
if (!desiredOutcome.trim()) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const filteredReality = currentReality.filter((obs) => obs.trim())
|
|
54
|
+
if (filteredReality.length === 0) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Generate new chart ID
|
|
59
|
+
const existingCharts = Array.from(data.entities.values()).filter((e) => e.entityType === "structural_tension_chart")
|
|
60
|
+
const maxId = existingCharts.reduce((max, e) => {
|
|
61
|
+
const id = Number.parseInt(e.metadata?.chartId?.replace("chart_", "") || "0")
|
|
62
|
+
return Math.max(max, id)
|
|
63
|
+
}, 0)
|
|
64
|
+
|
|
65
|
+
const newChartId = `chart_${maxId + 1}`
|
|
66
|
+
const timestamp = new Date().toISOString()
|
|
67
|
+
|
|
68
|
+
// Clone existing data
|
|
69
|
+
const newEntities = new Map(data.entities)
|
|
70
|
+
const newRelations = [...data.relations]
|
|
71
|
+
|
|
72
|
+
const chartEntity: EntityRecord = {
|
|
73
|
+
type: "entity",
|
|
74
|
+
name: newChartId,
|
|
75
|
+
entityType: "structural_tension_chart",
|
|
76
|
+
observations: [`Master chart: ${desiredOutcome}`],
|
|
77
|
+
metadata: {
|
|
78
|
+
chartId: newChartId,
|
|
79
|
+
level: 0,
|
|
80
|
+
completionStatus: false,
|
|
81
|
+
createdAt: timestamp,
|
|
82
|
+
updatedAt: timestamp,
|
|
83
|
+
...(dueDate && { dueDate: new Date(dueDate).toISOString() }),
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
newEntities.set(newChartId, chartEntity)
|
|
87
|
+
|
|
88
|
+
const desiredOutcomeName = `${newChartId}_desired_outcome`
|
|
89
|
+
const desiredOutcomeEntity: EntityRecord = {
|
|
90
|
+
type: "entity",
|
|
91
|
+
name: desiredOutcomeName,
|
|
92
|
+
entityType: "desired_outcome",
|
|
93
|
+
observations: [desiredOutcome.trim()],
|
|
94
|
+
metadata: {
|
|
95
|
+
chartId: newChartId,
|
|
96
|
+
createdAt: timestamp,
|
|
97
|
+
updatedAt: timestamp,
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
newEntities.set(desiredOutcomeName, desiredOutcomeEntity)
|
|
101
|
+
|
|
102
|
+
const currentRealityName = `${newChartId}_current_reality`
|
|
103
|
+
const currentRealityEntity: EntityRecord = {
|
|
104
|
+
type: "entity",
|
|
105
|
+
name: currentRealityName,
|
|
106
|
+
entityType: "current_reality",
|
|
107
|
+
observations: filteredReality,
|
|
108
|
+
metadata: {
|
|
109
|
+
chartId: newChartId,
|
|
110
|
+
createdAt: timestamp,
|
|
111
|
+
updatedAt: timestamp,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
newEntities.set(currentRealityName, currentRealityEntity)
|
|
115
|
+
|
|
116
|
+
const containsDesiredOutcome: RelationRecord = {
|
|
117
|
+
type: "relation",
|
|
118
|
+
from: newChartId,
|
|
119
|
+
to: desiredOutcomeName,
|
|
120
|
+
relationType: "contains",
|
|
121
|
+
metadata: { createdAt: timestamp },
|
|
122
|
+
}
|
|
123
|
+
newRelations.push(containsDesiredOutcome)
|
|
124
|
+
|
|
125
|
+
const containsCurrentReality: RelationRecord = {
|
|
126
|
+
type: "relation",
|
|
127
|
+
from: newChartId,
|
|
128
|
+
to: currentRealityName,
|
|
129
|
+
relationType: "contains",
|
|
130
|
+
metadata: { createdAt: timestamp },
|
|
131
|
+
}
|
|
132
|
+
newRelations.push(containsCurrentReality)
|
|
133
|
+
|
|
134
|
+
const tensionRelation: RelationRecord = {
|
|
135
|
+
type: "relation",
|
|
136
|
+
from: currentRealityName,
|
|
137
|
+
to: desiredOutcomeName,
|
|
138
|
+
relationType: "creates_tension_with",
|
|
139
|
+
metadata: { createdAt: timestamp },
|
|
140
|
+
}
|
|
141
|
+
newRelations.push(tensionRelation)
|
|
142
|
+
|
|
143
|
+
// Re-organize data to update chart hierarchy
|
|
144
|
+
const { organizeData } = require("@/lib/jsonl-parser")
|
|
145
|
+
const records = [...Array.from(newEntities.values()), ...newRelations]
|
|
146
|
+
const updatedData = organizeData(records)
|
|
147
|
+
|
|
148
|
+
onChartAdded(updatedData)
|
|
149
|
+
|
|
150
|
+
// Reset form
|
|
151
|
+
setDesiredOutcome("")
|
|
152
|
+
setCurrentReality([""])
|
|
153
|
+
setDueDate("")
|
|
154
|
+
setOpen(false)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
159
|
+
<DialogTrigger asChild>
|
|
160
|
+
<Button variant="outline" size="sm" className="w-full bg-transparent">
|
|
161
|
+
<Plus className="w-4 h-4 mr-2" />
|
|
162
|
+
Add Master Chart
|
|
163
|
+
</Button>
|
|
164
|
+
</DialogTrigger>
|
|
165
|
+
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
166
|
+
<DialogHeader>
|
|
167
|
+
<DialogTitle>Add New Master Chart</DialogTitle>
|
|
168
|
+
<DialogDescription>Create a new top-level structural tension chart</DialogDescription>
|
|
169
|
+
</DialogHeader>
|
|
170
|
+
<form onSubmit={handleSubmit} className="space-y-6">
|
|
171
|
+
<div className="space-y-2">
|
|
172
|
+
<Label htmlFor="desired-outcome">Desired Outcome *</Label>
|
|
173
|
+
<Textarea
|
|
174
|
+
id="desired-outcome"
|
|
175
|
+
placeholder="What do you want to create or achieve?"
|
|
176
|
+
value={desiredOutcome}
|
|
177
|
+
onChange={(e) => setDesiredOutcome(e.target.value)}
|
|
178
|
+
required
|
|
179
|
+
rows={3}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div className="space-y-3">
|
|
184
|
+
<div className="flex items-center justify-between">
|
|
185
|
+
<Label>Current Reality Observations *</Label>
|
|
186
|
+
<Button type="button" variant="ghost" size="sm" onClick={handleAddObservation}>
|
|
187
|
+
<Plus className="w-4 h-4 mr-1" />
|
|
188
|
+
Add Observation
|
|
189
|
+
</Button>
|
|
190
|
+
</div>
|
|
191
|
+
{currentReality.map((obs, index) => (
|
|
192
|
+
<div key={index} className="flex gap-2">
|
|
193
|
+
<Textarea
|
|
194
|
+
placeholder={`Observation ${index + 1}: What's actually true right now?`}
|
|
195
|
+
value={obs}
|
|
196
|
+
onChange={(e) => handleObservationChange(index, e.target.value)}
|
|
197
|
+
rows={2}
|
|
198
|
+
className="flex-1"
|
|
199
|
+
/>
|
|
200
|
+
{currentReality.length > 1 && (
|
|
201
|
+
<Button
|
|
202
|
+
type="button"
|
|
203
|
+
variant="ghost"
|
|
204
|
+
size="sm"
|
|
205
|
+
onClick={() => handleRemoveObservation(index)}
|
|
206
|
+
className="flex-shrink-0"
|
|
207
|
+
>
|
|
208
|
+
<X className="w-4 h-4" />
|
|
209
|
+
</Button>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
))}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<div className="space-y-2">
|
|
216
|
+
<Label htmlFor="due-date">Due Date (Optional)</Label>
|
|
217
|
+
<Input id="due-date" type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} />
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div className="flex gap-3 justify-end">
|
|
221
|
+
<Button type="button" variant="outline" onClick={() => setOpen(false)}>
|
|
222
|
+
Cancel
|
|
223
|
+
</Button>
|
|
224
|
+
<Button type="submit">Create Master Chart</Button>
|
|
225
|
+
</div>
|
|
226
|
+
</form>
|
|
227
|
+
</DialogContent>
|
|
228
|
+
</Dialog>
|
|
229
|
+
)
|
|
230
|
+
}
|
|
@@ -41,6 +41,30 @@ export function ChartDetailEditable({
|
|
|
41
41
|
onUpdate(updatedData)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
const handleCreateTelescopedChart = (actionName: string) => {
|
|
45
|
+
console.log("[v0] Creating telescoped chart from action:", actionName)
|
|
46
|
+
const editor = new ChartEditor(data)
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const newChartId = editor.createTelescopedChartFromAction(chart.chartEntity.name, actionName)
|
|
50
|
+
console.log("[v0] Created new chart:", newChartId)
|
|
51
|
+
|
|
52
|
+
const updatedData = editor.getUpdatedData()
|
|
53
|
+
onUpdate(updatedData)
|
|
54
|
+
|
|
55
|
+
// Navigate to the newly created chart
|
|
56
|
+
if (onNavigateToSubChart) {
|
|
57
|
+
const newChart = updatedData.charts.find((c) => c.id === newChartId)
|
|
58
|
+
if (newChart) {
|
|
59
|
+
console.log("[v0] Navigating to new chart:", newChartId)
|
|
60
|
+
setTimeout(() => onNavigateToSubChart(newChart), 150)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("[v0] Error creating telescoped chart:", error)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
44
68
|
return (
|
|
45
69
|
<div className="space-y-6">
|
|
46
70
|
{/* Back button for sub-charts */}
|
|
@@ -157,7 +181,7 @@ export function ChartDetailEditable({
|
|
|
157
181
|
<CardContent className="space-y-3">
|
|
158
182
|
{chart.actions.map((action, idx) => {
|
|
159
183
|
const subChart = action.metadata?.isTelescopedChart
|
|
160
|
-
? chart.subCharts.find((sc) => sc.desiredOutcome?.
|
|
184
|
+
? chart.subCharts.find((sc) => sc.desiredOutcome?.observations[0] === action.observations[0])
|
|
161
185
|
: undefined
|
|
162
186
|
|
|
163
187
|
return (
|
|
@@ -179,27 +203,14 @@ export function ChartDetailEditable({
|
|
|
179
203
|
onNavigateToChart={
|
|
180
204
|
subChart && onNavigateToSubChart ? () => onNavigateToSubChart(subChart) : undefined
|
|
181
205
|
}
|
|
182
|
-
onCreateTelescopedChart={() =>
|
|
183
|
-
applyEdit((editor) => editor.createTelescopedChart(chart.id, action.name))
|
|
184
|
-
// After creating, find and navigate to the new subchart
|
|
185
|
-
const editor = new ChartEditor(data)
|
|
186
|
-
editor.createTelescopedChart(chart.id, action.name)
|
|
187
|
-
const updatedData = editor.getUpdatedData()
|
|
188
|
-
const newSubChart = updatedData.charts
|
|
189
|
-
.find((c) => c.id === chart.id)
|
|
190
|
-
?.subCharts.find((sc) => sc.desiredOutcome?.name === action.name)
|
|
191
|
-
if (newSubChart && onNavigateToSubChart) {
|
|
192
|
-
onUpdate(updatedData)
|
|
193
|
-
setTimeout(() => onNavigateToSubChart(newSubChart), 100)
|
|
194
|
-
}
|
|
195
|
-
}}
|
|
206
|
+
onCreateTelescopedChart={() => handleCreateTelescopedChart(action.name)}
|
|
196
207
|
/>
|
|
197
208
|
)
|
|
198
209
|
})}
|
|
199
210
|
|
|
200
211
|
<AddActionStep
|
|
201
212
|
onAdd={(description, currentReality, dueDate) =>
|
|
202
|
-
applyEdit((editor) => editor.addActionStep(chart.
|
|
213
|
+
applyEdit((editor) => editor.addActionStep(chart.chartEntity.name, description, currentReality, dueDate))
|
|
203
214
|
}
|
|
204
215
|
/>
|
|
205
216
|
</CardContent>
|
|
@@ -8,15 +8,17 @@ import { getChartSummary, getChartProgress } from "@/lib/jsonl-parser"
|
|
|
8
8
|
import { ChevronRight, ChevronDown, Target, Calendar, BookOpen } from "lucide-react"
|
|
9
9
|
import { useState } from "react"
|
|
10
10
|
import { cn } from "@/lib/utils"
|
|
11
|
+
import { AddMasterChart } from "@/components/add-master-chart"
|
|
11
12
|
|
|
12
13
|
interface ChartListProps {
|
|
13
14
|
data: ParsedData
|
|
14
15
|
selectedChart: Chart | null
|
|
15
16
|
onSelectChart: (chart: Chart) => void
|
|
16
17
|
mode: "hierarchy" | "list"
|
|
18
|
+
onDataUpdate?: (updatedData: ParsedData) => void
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
export function ChartList({ data, selectedChart, onSelectChart, mode }: ChartListProps) {
|
|
21
|
+
export function ChartList({ data, selectedChart, onSelectChart, mode, onDataUpdate }: ChartListProps) {
|
|
20
22
|
if (mode === "list") {
|
|
21
23
|
return (
|
|
22
24
|
<Card>
|
|
@@ -25,6 +27,11 @@ export function ChartList({ data, selectedChart, onSelectChart, mode }: ChartLis
|
|
|
25
27
|
<CardDescription className="text-xs md:text-sm">{data.charts.length} total charts</CardDescription>
|
|
26
28
|
</CardHeader>
|
|
27
29
|
<CardContent>
|
|
30
|
+
{onDataUpdate && (
|
|
31
|
+
<div className="mb-4">
|
|
32
|
+
<AddMasterChart data={data} onChartAdded={onDataUpdate} />
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
28
35
|
<ScrollArea className="h-[400px] md:h-[600px] pr-2 md:pr-4">
|
|
29
36
|
<div className="space-y-2">
|
|
30
37
|
{data.charts.map((chart) => (
|
|
@@ -50,6 +57,11 @@ export function ChartList({ data, selectedChart, onSelectChart, mode }: ChartLis
|
|
|
50
57
|
<CardDescription className="text-xs md:text-sm">{data.rootCharts.length} root charts</CardDescription>
|
|
51
58
|
</CardHeader>
|
|
52
59
|
<CardContent>
|
|
60
|
+
{onDataUpdate && (
|
|
61
|
+
<div className="mb-4">
|
|
62
|
+
<AddMasterChart data={data} onChartAdded={onDataUpdate} />
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
53
65
|
<ScrollArea className="h-[400px] md:h-[600px] pr-2 md:pr-4">
|
|
54
66
|
<div className="space-y-2">
|
|
55
67
|
{data.rootCharts.map((chart) => (
|