@taylorwong/ichartjs 2.0.13 → 2.0.15
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/CHANGELOG.md +14 -0
- package/README.md +2 -2
- package/docs/agent/charting-scenario.md +1 -1
- package/docs/agent/coding-agent-integration.md +1 -1
- package/docs/agent/development/2.0-release-readiness.md +4 -0
- package/docs/agent/development/iteration-13.md +318 -0
- package/docs/agent/development/roadmap.md +2 -1
- package/docs/agent/editing-contract.md +3 -0
- package/docs/agent/frontend-integration.md +1 -1
- package/docs/agent/runtime-contract.md +4 -0
- package/docs/agent/usage-scenarios.md +1 -1
- package/docs/agent/zh-CN/charting-scenario.md +1 -1
- package/docs/agent/zh-CN/coding-agent-integration.md +1 -1
- package/docs/agent/zh-CN/editing-contract.md +2 -0
- package/docs/agent/zh-CN/frontend-integration.md +1 -1
- package/docs/agent/zh-CN/runtime-contract.md +3 -1
- package/docs/agent/zh-CN/usage-scenarios.md +1 -1
- package/docs/manifests/capabilities.json +1310 -32
- package/docs/manifests/commands.json +7 -7
- package/docs/manifests/schemas.json +182 -10
- package/examples/agent-workflow.mjs +10 -0
- package/package.json +21 -5
- package/skills/ichartjs/SKILL.md +1 -1
- package/src/capabilities.mjs +10 -36
- package/src/chart-lifecycle.mjs +31 -0
- package/src/charts.mjs +56 -25
- package/src/command.mjs +3 -2
- package/src/contract-registry.mjs +59 -0
- package/src/edit-controller.mjs +1 -1
- package/src/errors.mjs +31 -0
- package/src/index.mjs +144 -12
- package/src/schema.mjs +1 -0
- package/src/spec.mjs +45 -3
- package/types/consumer-fixture.ts +35 -0
- package/types/index.d.ts +33 -3
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"version": "1.0",
|
|
3
3
|
"source": "src/command.mjs#operationTypes",
|
|
4
4
|
"commands": {
|
|
5
|
-
"updateField": { "models": ["project-task", "timeline-event", "milestone", "burndown-sample", "flow-node", "flow-edge", "swimlane", "architecture-node", "architecture-edge", "mindmap-node"], "previewRequired": true, "undoable": true },
|
|
6
|
-
"updateRecord": { "models": ["project-task", "timeline-event", "milestone", "burndown-sample", "flow-node", "flow-edge", "swimlane", "architecture-node", "architecture-edge", "mindmap-node"], "previewRequired": true, "undoable": true },
|
|
5
|
+
"updateField": { "models": ["project-task", "timeline-event", "milestone", "burndown-sample", "flow-node", "flow-edge", "swimlane", "architecture-node", "architecture-edge", "mindmap-node", "mindmap-edge"], "previewRequired": true, "undoable": true },
|
|
6
|
+
"updateRecord": { "models": ["project-task", "timeline-event", "milestone", "burndown-sample", "flow-node", "flow-edge", "swimlane", "architecture-node", "architecture-edge", "mindmap-node", "mindmap-edge"], "previewRequired": true, "undoable": true },
|
|
7
7
|
"updateTask": { "models": ["project-task"], "previewRequired": true, "undoable": true },
|
|
8
8
|
"shiftTask": { "models": ["project-task"], "required": ["taskId", "days"], "previewRequired": true, "undoable": true },
|
|
9
9
|
"updateProgress": { "models": ["project-task"], "required": ["taskId", "progress"], "previewRequired": true, "undoable": true },
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"assignNodesToGroup": { "models": ["flow-node"], "required": ["nodeIds", "groupId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
22
22
|
"duplicateGroup": { "models": ["flow-node", "flow-edge"], "required": ["groupId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
23
23
|
"deleteGroup": { "models": ["flow-node", "flow-edge"], "required": ["groupId", "policy"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
24
|
-
"updateEdge": { "models": ["flow-edge", "architecture-edge"], "required": ["edgeId", "changes"], "previewRequired": true, "undoable": true },
|
|
25
|
-
"removeEdge": { "models": ["flow-edge", "architecture-edge"], "required": ["edgeId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
26
|
-
"addEdge": { "models": ["flow-edge", "architecture-edge"], "required": ["from", "to"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
24
|
+
"updateEdge": { "models": ["flow-edge", "architecture-edge", "mindmap-edge"], "required": ["edgeId", "changes"], "previewRequired": true, "undoable": true },
|
|
25
|
+
"removeEdge": { "models": ["flow-edge", "architecture-edge", "mindmap-edge"], "required": ["edgeId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
26
|
+
"addEdge": { "models": ["flow-edge", "architecture-edge", "mindmap-edge"], "required": ["from", "to"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
27
27
|
"toggleGroupCollapse": { "models": ["flow-node"], "required": ["groupId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
28
|
-
"duplicateSelection": { "models": ["flow-node", "flow-edge"], "required": ["nodeIds"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
29
|
-
"pasteSelection": { "models": ["flow-node", "flow-edge"], "required": ["nodes"], "previewRequired": true, "confirmationRequired": true, "undoable": true }
|
|
28
|
+
"duplicateSelection": { "models": ["flow-node", "flow-edge", "mindmap-edge"], "required": ["nodeIds"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
|
|
29
|
+
"pasteSelection": { "models": ["flow-node", "flow-edge", "mindmap-edge"], "required": ["nodes"], "previewRequired": true, "confirmationRequired": true, "undoable": true }
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -2,15 +2,187 @@
|
|
|
2
2
|
"version": "1.0",
|
|
3
3
|
"source": "src/schema.mjs#getBusinessSchema",
|
|
4
4
|
"models": {
|
|
5
|
-
"project-task": {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
"project-task": {
|
|
6
|
+
"key": "id",
|
|
7
|
+
"required": [
|
|
8
|
+
"id",
|
|
9
|
+
"name",
|
|
10
|
+
"start",
|
|
11
|
+
"end"
|
|
12
|
+
],
|
|
13
|
+
"editable": [
|
|
14
|
+
"name",
|
|
15
|
+
"start",
|
|
16
|
+
"end",
|
|
17
|
+
"baselineStart",
|
|
18
|
+
"baselineEnd",
|
|
19
|
+
"actualStart",
|
|
20
|
+
"actualEnd",
|
|
21
|
+
"baselineDate",
|
|
22
|
+
"actualDate",
|
|
23
|
+
"progress",
|
|
24
|
+
"status",
|
|
25
|
+
"owner",
|
|
26
|
+
"resource",
|
|
27
|
+
"capacity",
|
|
28
|
+
"load",
|
|
29
|
+
"points",
|
|
30
|
+
"sprint",
|
|
31
|
+
"priority",
|
|
32
|
+
"labels",
|
|
33
|
+
"dependencies"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"timeline-event": {
|
|
37
|
+
"key": "id",
|
|
38
|
+
"required": [
|
|
39
|
+
"id",
|
|
40
|
+
"title",
|
|
41
|
+
"date"
|
|
42
|
+
],
|
|
43
|
+
"editable": [
|
|
44
|
+
"title",
|
|
45
|
+
"date",
|
|
46
|
+
"description",
|
|
47
|
+
"status"
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"milestone": {
|
|
51
|
+
"key": "id",
|
|
52
|
+
"required": [
|
|
53
|
+
"id",
|
|
54
|
+
"title",
|
|
55
|
+
"date"
|
|
56
|
+
],
|
|
57
|
+
"editable": [
|
|
58
|
+
"title",
|
|
59
|
+
"date",
|
|
60
|
+
"status"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"burndown-sample": {
|
|
64
|
+
"key": "id",
|
|
65
|
+
"required": [
|
|
66
|
+
"id",
|
|
67
|
+
"date",
|
|
68
|
+
"remaining"
|
|
69
|
+
],
|
|
70
|
+
"editable": [
|
|
71
|
+
"date",
|
|
72
|
+
"remaining",
|
|
73
|
+
"scopeChange"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"flow-node": {
|
|
77
|
+
"key": "id",
|
|
78
|
+
"required": [
|
|
79
|
+
"id",
|
|
80
|
+
"label"
|
|
81
|
+
],
|
|
82
|
+
"editable": [
|
|
83
|
+
"label",
|
|
84
|
+
"position",
|
|
85
|
+
"size",
|
|
86
|
+
"laneId",
|
|
87
|
+
"description",
|
|
88
|
+
"status"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"flow-edge": {
|
|
92
|
+
"key": "id",
|
|
93
|
+
"required": [
|
|
94
|
+
"id",
|
|
95
|
+
"from",
|
|
96
|
+
"to"
|
|
97
|
+
],
|
|
98
|
+
"editable": [
|
|
99
|
+
"from",
|
|
100
|
+
"to",
|
|
101
|
+
"fromPort",
|
|
102
|
+
"toPort",
|
|
103
|
+
"label",
|
|
104
|
+
"status",
|
|
105
|
+
"routing",
|
|
106
|
+
"curveTension",
|
|
107
|
+
"waypoints"
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
"swimlane": {
|
|
111
|
+
"key": "id",
|
|
112
|
+
"required": [
|
|
113
|
+
"id",
|
|
114
|
+
"label"
|
|
115
|
+
],
|
|
116
|
+
"editable": [
|
|
117
|
+
"label"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
"architecture-node": {
|
|
121
|
+
"key": "id",
|
|
122
|
+
"required": [
|
|
123
|
+
"id",
|
|
124
|
+
"label"
|
|
125
|
+
],
|
|
126
|
+
"editable": [
|
|
127
|
+
"label",
|
|
128
|
+
"layerId",
|
|
129
|
+
"boundaryId",
|
|
130
|
+
"position",
|
|
131
|
+
"size",
|
|
132
|
+
"description",
|
|
133
|
+
"status"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"architecture-edge": {
|
|
137
|
+
"key": "id",
|
|
138
|
+
"required": [
|
|
139
|
+
"id",
|
|
140
|
+
"from",
|
|
141
|
+
"to"
|
|
142
|
+
],
|
|
143
|
+
"editable": [
|
|
144
|
+
"from",
|
|
145
|
+
"to",
|
|
146
|
+
"label",
|
|
147
|
+
"relation",
|
|
148
|
+
"routing",
|
|
149
|
+
"curveTension",
|
|
150
|
+
"waypoints",
|
|
151
|
+
"status"
|
|
152
|
+
]
|
|
153
|
+
},
|
|
154
|
+
"mindmap-edge": {
|
|
155
|
+
"key": "id",
|
|
156
|
+
"required": [
|
|
157
|
+
"id",
|
|
158
|
+
"from",
|
|
159
|
+
"to"
|
|
160
|
+
],
|
|
161
|
+
"editable": [
|
|
162
|
+
"from",
|
|
163
|
+
"to",
|
|
164
|
+
"label",
|
|
165
|
+
"routing",
|
|
166
|
+
"curveTension",
|
|
167
|
+
"waypoints",
|
|
168
|
+
"status"
|
|
169
|
+
]
|
|
170
|
+
},
|
|
171
|
+
"mindmap-node": {
|
|
172
|
+
"key": "id",
|
|
173
|
+
"required": [
|
|
174
|
+
"id",
|
|
175
|
+
"label"
|
|
176
|
+
],
|
|
177
|
+
"editable": [
|
|
178
|
+
"label",
|
|
179
|
+
"parentId",
|
|
180
|
+
"position",
|
|
181
|
+
"size",
|
|
182
|
+
"branch",
|
|
183
|
+
"description",
|
|
184
|
+
"status"
|
|
185
|
+
]
|
|
186
|
+
}
|
|
15
187
|
}
|
|
16
188
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// Executable Agent integration example covering the full inspect-to-destroy lifecycle.
|
|
1
2
|
import { pathToFileURL } from 'node:url';
|
|
2
3
|
import {
|
|
3
4
|
createChart,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
recommend,
|
|
9
10
|
validateSpec
|
|
10
11
|
} from '@taylorwong/ichartjs';
|
|
12
|
+
// Small deterministic fixture with stable IDs for reproducible lineage checks.
|
|
11
13
|
export const sampleRows = [
|
|
12
14
|
{ id: 'jan', month: 'Jan', revenue: 120, cost: 82 },
|
|
13
15
|
{ id: 'feb', month: 'Feb', revenue: 148, cost: 91 },
|
|
@@ -15,6 +17,7 @@ export const sampleRows = [
|
|
|
15
17
|
{ id: 'apr', month: 'Apr', revenue: 176, cost: 105 }
|
|
16
18
|
];
|
|
17
19
|
|
|
20
|
+
// Convert the planning result into a candidate Chart Spec for validateSpec().
|
|
18
21
|
function buildCandidate(rows, plan, renderer) {
|
|
19
22
|
const measureFields = [plan.suggestedEncodings.measure, plan.suggestedEncodings.secondaryMeasure].filter(Boolean);
|
|
20
23
|
return {
|
|
@@ -36,17 +39,21 @@ function buildCandidate(rows, plan, renderer) {
|
|
|
36
39
|
};
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
// Run the complete Agent workflow and return structured results for inspection or presentation.
|
|
39
43
|
export function runAgentWorkflow(rows, options = {}) {
|
|
40
44
|
const intent = options.intent || 'trend';
|
|
41
45
|
const renderer = options.renderer || 'svg';
|
|
46
|
+
// Discover the capability contract before inspecting data and planning the chart.
|
|
42
47
|
const capabilities = getCapabilities();
|
|
43
48
|
const inspection = inspectData(rows);
|
|
44
49
|
const plan = planChart(rows, { intent, renderer, context: options.context || 'analysis' });
|
|
45
50
|
|
|
51
|
+
// Return early when required planning fields are missing instead of creating an invalid chart.
|
|
46
52
|
if (plan.requiredFields.length) {
|
|
47
53
|
return { ok: false, stage: 'planning', capabilitiesContract: capabilities.contractVersion, inspection, plan };
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
// Build and validate the candidate Spec before creating the chart.
|
|
50
57
|
const candidate = buildCandidate(rows, plan, renderer);
|
|
51
58
|
const validation = validateSpec(candidate);
|
|
52
59
|
if (!validation.valid) {
|
|
@@ -55,6 +62,7 @@ export function runAgentWorkflow(rows, options = {}) {
|
|
|
55
62
|
|
|
56
63
|
const chart = createChart(validation.spec);
|
|
57
64
|
try {
|
|
65
|
+
// Read explanation, state, and export output to verify observability and data lineage.
|
|
58
66
|
const explanation = chart.explain();
|
|
59
67
|
const state = chart.getState();
|
|
60
68
|
const expectedRecordIds = rows.map((row, index) => String(row.id ?? row.key ?? `record-${index}`));
|
|
@@ -77,10 +85,12 @@ export function runAgentWorkflow(rows, options = {}) {
|
|
|
77
85
|
export: JSON.parse(chart.export({ type: 'json' }))
|
|
78
86
|
};
|
|
79
87
|
} finally {
|
|
88
|
+
// Always release chart resources and listeners, whether the workflow succeeds or fails.
|
|
80
89
|
chart.destroy();
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
// Print a compact result when run directly; expose reusable workflow functions when imported.
|
|
84
94
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
85
95
|
const result = runAgentWorkflow(sampleRows);
|
|
86
96
|
console.log(JSON.stringify({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taylorwong/ichartjs",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"description": "Agent-first, renderer-independent charting and project visualization runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -37,14 +37,20 @@
|
|
|
37
37
|
"test": "node --test tests/*.test.mjs",
|
|
38
38
|
"test:browser": "node --test tests/browser/*.test.mjs",
|
|
39
39
|
"check": "for file in src/*.mjs; do node --check \"$file\" || exit 1; done",
|
|
40
|
+
"contracts:generate": "node scripts/generate-contract-manifests.mjs",
|
|
41
|
+
"contracts:check": "node scripts/check-contracts.mjs",
|
|
42
|
+
"types:check": "node scripts/check-types.mjs",
|
|
43
|
+
"deps:check": "node scripts/check-cycles.mjs",
|
|
44
|
+
"docs:examples": "node examples/agent-workflow.mjs",
|
|
40
45
|
"docs:check": "node scripts/check-agent-docs.mjs",
|
|
41
46
|
"example:agent": "node examples/agent-workflow.mjs",
|
|
42
47
|
"playground": "node scripts/serve-playground.mjs",
|
|
43
|
-
"agent:check": "npm run docs:check && npm run check && npm test",
|
|
48
|
+
"agent:check": "npm run contracts:check && npm run types:check && npm run deps:check && npm run docs:check && npm run docs:examples && npm run check && npm test",
|
|
44
49
|
"rc:check": "npm test && npm run check && git diff --check"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
|
47
|
-
"playwright-core": "^1.63.0"
|
|
52
|
+
"playwright-core": "^1.63.0",
|
|
53
|
+
"typescript": "^5.9.3"
|
|
48
54
|
},
|
|
49
55
|
"repository": {
|
|
50
56
|
"type": "git",
|
|
@@ -52,8 +58,18 @@
|
|
|
52
58
|
},
|
|
53
59
|
"homepage": "https://github.com/wanghetommy/ichartjs#readme",
|
|
54
60
|
"bugs": "https://github.com/wanghetommy/ichartjs/issues",
|
|
55
|
-
"keywords": [
|
|
56
|
-
|
|
61
|
+
"keywords": [
|
|
62
|
+
"chart",
|
|
63
|
+
"visualization",
|
|
64
|
+
"agent",
|
|
65
|
+
"svg",
|
|
66
|
+
"canvas",
|
|
67
|
+
"gantt",
|
|
68
|
+
"diagram"
|
|
69
|
+
],
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=18"
|
|
72
|
+
},
|
|
57
73
|
"author": "taylorwong <wanghetommy@gmail.com>",
|
|
58
74
|
"license": "Apache-2.0"
|
|
59
75
|
}
|
package/skills/ichartjs/SKILL.md
CHANGED
|
@@ -21,7 +21,7 @@ Recommended installation:
|
|
|
21
21
|
npx skills add wanghetommy/ichartjs --skill ichartjs
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
24
|
+
Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.15/skills/ichartjs` when reproducibility matters. WorkBuddy can import the same directory through its Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it.
|
|
25
25
|
|
|
26
26
|
The Skill is a workflow adapter, not the chart runtime. If the current JavaScript or TypeScript project does not already depend on iChart.js, install the matching runtime from GitHub:
|
|
27
27
|
|
package/src/capabilities.mjs
CHANGED
|
@@ -4,44 +4,13 @@
|
|
|
4
4
|
import { inspectData } from './data.mjs';
|
|
5
5
|
import { planStyle, styleCapabilities } from './theme.mjs';
|
|
6
6
|
import { defaultPreferences, preferenceDensities, preferenceMotions, preferenceTriStates } from './preferences.mjs';
|
|
7
|
+
import { businessModels, chartProfiles, chartTypes, commandTypes, contractVersion, diagramEdgeModels, diagramOperations, exportTypes, interactionDefaults, renderers } from './contract-registry.mjs';
|
|
7
8
|
|
|
8
|
-
export
|
|
9
|
+
export { chartProfiles, chartTypes };
|
|
9
10
|
|
|
10
11
|
const cartesian = ['line', 'area', 'bar', 'column', 'scatter'];
|
|
11
12
|
const project = ['gantt', 'timeline', 'milestone', 'burndown'];
|
|
12
13
|
const diagrams = ['flow', 'swimlane', 'architecture', 'mindmap'];
|
|
13
|
-
const status = (supported, notApplicable = []) => Object.fromEntries(supported.map(name => [name, 'supported']).concat(notApplicable.map(name => [name, 'not-applicable'])));
|
|
14
|
-
const commonPresentation = ['title', 'subtitle', 'theme', 'responsive', 'empty-state', 'invalid-data-state', 'export', 'branding'];
|
|
15
|
-
|
|
16
|
-
const definitions = {
|
|
17
|
-
line: { family: 'cartesian', intents: ['trend', 'time-series'], required: ['dimension', 'measure'], optional: ['series'], interactions: ['hover', 'tooltip', 'selection', 'crosshair', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'grid', 'legend', 'labels', 'formatting', 'multi-series', 'dual-axis']) },
|
|
18
|
-
area: { family: 'cartesian', intents: ['trend', 'composition'], required: ['dimension', 'measure'], optional: ['series'], interactions: ['hover', 'tooltip', 'selection', 'crosshair', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'grid', 'legend', 'labels', 'formatting', 'multi-series', 'stacked', 'percent-stacked']) },
|
|
19
|
-
bar: { family: 'cartesian', intents: ['comparison', 'ranking'], required: ['dimension', 'measure'], optional: ['series'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'grid', 'legend', 'labels', 'formatting', 'multi-series', 'stacked', 'percent-stacked']) },
|
|
20
|
-
column: { family: 'cartesian', intents: ['comparison', 'distribution', 'composition'], required: ['dimension', 'measure'], optional: ['series'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'grid', 'legend', 'labels', 'formatting', 'multi-series', 'stacked', 'percent-stacked', 'mixed-line-column', 'dual-axis', 'bin']) },
|
|
21
|
-
pie: { family: 'part-to-whole', intents: ['part-to-whole'], required: ['dimension', 'measure'], optional: [], interactions: ['hover', 'tooltip', 'selection', 'keyboard'], features: status([...commonPresentation, 'legend', 'labels', 'formatting', 'donut', 'zero-total'], ['axes', 'grid', 'zoom', 'pan']) },
|
|
22
|
-
scatter: { family: 'cartesian', intents: ['relationship', 'correlation'], required: ['measure', 'measure'], optional: ['dimension'], interactions: ['hover', 'tooltip', 'selection', 'brush', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'grid', 'labels', 'formatting'], ['stacked']) },
|
|
23
|
-
funnel: { family: 'stage', intents: ['funnel', 'conversion'], required: ['dimension', 'measure'], optional: [], interactions: ['hover', 'tooltip', 'selection', 'keyboard'], features: status([...commonPresentation, 'labels', 'formatting'], ['axes', 'grid', 'zoom', 'pan']) },
|
|
24
|
-
gauge: { family: 'indicator', intents: ['progress', 'single-value'], required: ['measure'], optional: [], interactions: ['hover', 'tooltip', 'selection', 'keyboard'], features: status([...commonPresentation, 'labels', 'formatting', 'domain'], ['axes', 'grid', 'legend', 'zoom', 'pan']) },
|
|
25
|
-
heatmap: { family: 'matrix', intents: ['matrix', 'correlation-grid'], required: ['dimension', 'dimension', 'measure'], optional: [], interactions: ['hover', 'tooltip', 'selection', 'keyboard'], features: status([...commonPresentation, 'axes', 'labels', 'formatting', 'color-scale', 'missing-values'], ['zoom', 'pan']) },
|
|
26
|
-
radar: { family: 'radial', intents: ['multidimensional', 'profile'], required: ['three-or-more-measures'], optional: ['series'], interactions: ['hover', 'tooltip', 'selection', 'keyboard'], features: status([...commonPresentation, 'legend', 'labels', 'formatting', 'multi-series', 'indicator-domains', 'mixed-unit-warning'], ['axes', 'grid', 'zoom', 'pan']) },
|
|
27
|
-
gantt: { family: 'project', intents: ['schedule', 'variance'], required: ['id', 'start', 'end'], optional: ['dependencies', 'progress', 'baseline', 'actual'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard', 'linked-filter', 'linked-selection'], features: status([...commonPresentation, 'labels', 'formatting', 'critical-path', 'slack', 'baseline-actual-variance', 'working-calendar', 'dependencies']) },
|
|
28
|
-
timeline: { family: 'project', intents: ['timeline', 'milestone'], required: ['date', 'label'], optional: ['status'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'labels', 'formatting']) },
|
|
29
|
-
milestone: { family: 'project', intents: ['milestone'], required: ['date', 'label'], optional: ['baseline', 'actual'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'labels', 'formatting', 'baseline-actual-variance']) },
|
|
30
|
-
burndown: { family: 'project', intents: ['progress', 'release'], required: ['date', 'remaining'], optional: ['ideal', 'scopeChange'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'axes', 'labels', 'formatting', 'scope-change', 'release-forecast']) },
|
|
31
|
-
flow: { family: 'diagram', intents: ['workflow'], required: ['nodes'], optional: ['edges', 'groups', 'ports'], interactions: ['hover', 'tooltip', 'selection', 'edge-selection', 'drag', 'edgeDrag', 'zoom', 'pan', 'keyboard', 'keyboard-edit', 'keyboard-port-connect', 'portConnect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'manual-routing', 'layout', 'groups', 'ports', 'history'], ['axes', 'grid', 'legend']) },
|
|
32
|
-
swimlane: { family: 'diagram', intents: ['responsibility'], required: ['nodes', 'lanes'], optional: ['edges', 'groups', 'ports'], interactions: ['hover', 'tooltip', 'selection', 'edge-selection', 'drag', 'edgeDrag', 'zoom', 'pan', 'keyboard', 'keyboard-edit', 'keyboard-port-connect', 'portConnect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'manual-routing', 'layout', 'groups', 'ports', 'lanes', 'history'], ['axes', 'grid', 'legend']) },
|
|
33
|
-
architecture: { family: 'diagram', intents: ['architecture', 'business-architecture', 'data-architecture', 'technical-architecture'], required: ['nodes'], optional: ['edges', 'layers', 'boundaries', 'groups', 'ports'], interactions: ['hover', 'tooltip', 'selection', 'edge-selection', 'drag', 'edgeDrag', 'zoom', 'pan', 'keyboard', 'keyboard-edit', 'keyboard-port-connect', 'portConnect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'manual-routing', 'layout', 'layers', 'boundaries', 'groups', 'ports', 'history'], ['axes', 'grid', 'legend']) },
|
|
34
|
-
mindmap: { family: 'diagram', intents: ['mindmap', 'hierarchy', 'brainstorm'], required: ['nodes'], optional: ['parentId', 'edges', 'groups'], interactions: ['hover', 'tooltip', 'selection', 'edge-selection', 'drag', 'edgeDrag', 'zoom', 'pan', 'keyboard', 'keyboard-edit', 'keyboard-port-connect', 'portConnect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'cubic-bezier', 'curve-tension', 'obstacle-fallback', 'manual-routing', 'layout', 'tree-layout', 'radial-layout', 'history'], ['axes', 'grid', 'legend']) }
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export const chartProfiles = Object.fromEntries(chartTypes.map(type => [type, {
|
|
38
|
-
type,
|
|
39
|
-
...definitions[type],
|
|
40
|
-
dataShapes: definitions[type].family === 'diagram' ? ['diagram-spec'] : definitions[type].family === 'project' ? ['records'] : ['records', 'data-table'],
|
|
41
|
-
renderers: ['canvas', 'svg'],
|
|
42
|
-
exports: ['png', 'svg', 'json'],
|
|
43
|
-
limits: type === 'pie' ? { recommendedCategories: 8 } : type === 'radar' ? { minimumIndicators: 3, recommendedIndicators: 8 } : type === 'heatmap' ? { recommendedCells: 2500 } : {}
|
|
44
|
-
}]));
|
|
45
14
|
|
|
46
15
|
const intentMap = {
|
|
47
16
|
schedule: 'gantt', variance: 'gantt', timeline: 'timeline', milestone: 'milestone', progress: 'burndown', release: 'burndown', capacity: 'column', risk: 'scatter', aging: 'bar', matrix: 'heatmap', 'correlation-grid': 'heatmap', multidimensional: 'radar', profile: 'radar', workflow: 'flow', responsibility: 'swimlane', architecture: 'architecture', 'business-architecture': 'architecture', 'data-architecture': 'architecture', 'technical-architecture': 'architecture', mindmap: 'mindmap', hierarchy: 'mindmap', brainstorm: 'mindmap', trend: 'line', 'time-series': 'line', 'part-to-whole': 'pie', distribution: 'column', relationship: 'scatter', correlation: 'scatter', funnel: 'funnel', conversion: 'funnel', 'single-value': 'gauge', ranking: 'bar', comparison: 'bar', composition: 'column'
|
|
@@ -225,7 +194,7 @@ export function getCapabilities() {
|
|
|
225
194
|
};
|
|
226
195
|
return {
|
|
227
196
|
version: '2.0',
|
|
228
|
-
contractVersion
|
|
197
|
+
contractVersion,
|
|
229
198
|
chartTypes,
|
|
230
199
|
charts: JSON.parse(JSON.stringify(chartProfiles)),
|
|
231
200
|
intents,
|
|
@@ -240,9 +209,10 @@ export function getCapabilities() {
|
|
|
240
209
|
},
|
|
241
210
|
diagrams,
|
|
242
211
|
excluded: ['map', '3d'],
|
|
243
|
-
renderers: [
|
|
212
|
+
renderers: [...renderers],
|
|
213
|
+
interactionDefaults: { ...interactionDefaults },
|
|
244
214
|
interactions: [...new Set(Object.values(chartProfiles).flatMap(profile => profile.interactions))],
|
|
245
|
-
exports: [
|
|
215
|
+
exports: [...exportTypes],
|
|
246
216
|
headless: {
|
|
247
217
|
preview: true,
|
|
248
218
|
json: true,
|
|
@@ -263,6 +233,10 @@ export function getCapabilities() {
|
|
|
263
233
|
precedence: preferenceCapabilities.precedence,
|
|
264
234
|
schema: preferenceCapabilities
|
|
265
235
|
},
|
|
236
|
+
commands: [...commandTypes],
|
|
237
|
+
diagramOperations: [...diagramOperations],
|
|
238
|
+
businessModels: [...businessModels],
|
|
239
|
+
diagramEdgeModels: [...diagramEdgeModels],
|
|
266
240
|
plugins: true,
|
|
267
241
|
branding: { defaultEnabled: true, signature: 'Powered by iChart.js', options: [{ name: 'enabled', type: 'boolean', default: true }] },
|
|
268
242
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Renderer-independent chart lifecycle cleanup. */
|
|
2
|
+
export function destroyChart(chart) {
|
|
3
|
+
if (chart._destroyed) return;
|
|
4
|
+
chart._destroyed = true;
|
|
5
|
+
chart._preferencesUnsubscribe?.();
|
|
6
|
+
chart._resizeObserver?.disconnect();
|
|
7
|
+
chart._colorSchemeQuery?.removeEventListener?.('change', chart._colorSchemeHandler);
|
|
8
|
+
if (chart._eventsBound) {
|
|
9
|
+
const { target, handler, start, move, end, leave, touchStart, touchMove, touchEnd, wheel } = chart._eventsBound;
|
|
10
|
+
target.removeEventListener('mousemove', handler);
|
|
11
|
+
target.removeEventListener('click', handler);
|
|
12
|
+
target.removeEventListener('pointerdown', start);
|
|
13
|
+
target.removeEventListener('pointermove', move);
|
|
14
|
+
target.removeEventListener('pointerup', end);
|
|
15
|
+
target.removeEventListener('pointercancel', end);
|
|
16
|
+
target.removeEventListener('pointerleave', leave);
|
|
17
|
+
target.removeEventListener('touchstart', touchStart);
|
|
18
|
+
target.removeEventListener('touchmove', touchMove);
|
|
19
|
+
target.removeEventListener('touchend', touchEnd);
|
|
20
|
+
target.removeEventListener('wheel', wheel);
|
|
21
|
+
}
|
|
22
|
+
const target = chart.renderer.svg || chart.renderer.canvas;
|
|
23
|
+
if (chart._keyboardHandler) target?.removeEventListener('keydown', chart._keyboardHandler);
|
|
24
|
+
chart._tooltip?.remove();
|
|
25
|
+
chart.plugins.destroy();
|
|
26
|
+
chart.renderer.destroy();
|
|
27
|
+
chart.listeners.clear();
|
|
28
|
+
chart._eventsBound = null;
|
|
29
|
+
chart._selected.clear();
|
|
30
|
+
chart._clipboard = { nodes: [], edges: [] };
|
|
31
|
+
}
|