@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.
Files changed (35) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +2 -2
  3. package/docs/agent/charting-scenario.md +1 -1
  4. package/docs/agent/coding-agent-integration.md +1 -1
  5. package/docs/agent/development/2.0-release-readiness.md +4 -0
  6. package/docs/agent/development/iteration-13.md +318 -0
  7. package/docs/agent/development/roadmap.md +2 -1
  8. package/docs/agent/editing-contract.md +3 -0
  9. package/docs/agent/frontend-integration.md +1 -1
  10. package/docs/agent/runtime-contract.md +4 -0
  11. package/docs/agent/usage-scenarios.md +1 -1
  12. package/docs/agent/zh-CN/charting-scenario.md +1 -1
  13. package/docs/agent/zh-CN/coding-agent-integration.md +1 -1
  14. package/docs/agent/zh-CN/editing-contract.md +2 -0
  15. package/docs/agent/zh-CN/frontend-integration.md +1 -1
  16. package/docs/agent/zh-CN/runtime-contract.md +3 -1
  17. package/docs/agent/zh-CN/usage-scenarios.md +1 -1
  18. package/docs/manifests/capabilities.json +1310 -32
  19. package/docs/manifests/commands.json +7 -7
  20. package/docs/manifests/schemas.json +182 -10
  21. package/examples/agent-workflow.mjs +10 -0
  22. package/package.json +21 -5
  23. package/skills/ichartjs/SKILL.md +1 -1
  24. package/src/capabilities.mjs +10 -36
  25. package/src/chart-lifecycle.mjs +31 -0
  26. package/src/charts.mjs +56 -25
  27. package/src/command.mjs +3 -2
  28. package/src/contract-registry.mjs +59 -0
  29. package/src/edit-controller.mjs +1 -1
  30. package/src/errors.mjs +31 -0
  31. package/src/index.mjs +144 -12
  32. package/src/schema.mjs +1 -0
  33. package/src/spec.mjs +45 -3
  34. package/types/consumer-fixture.ts +35 -0
  35. 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": { "key": "id", "required": ["id", "name", "start", "end"], "editable": ["name", "start", "end", "progress", "status", "dependencies"] },
6
- "timeline-event": { "key": "id", "required": ["id", "title", "date"], "editable": ["title", "date", "description", "status"] },
7
- "milestone": { "key": "id", "required": ["id", "title", "date"], "editable": ["title", "date", "status"] },
8
- "burndown-sample": { "key": "id", "required": ["id", "date", "remaining"], "editable": ["date", "remaining", "scopeChange"] },
9
- "flow-node": { "key": "id", "required": ["id", "label"], "editable": ["label", "position", "size", "laneId", "description", "status"] },
10
- "flow-edge": { "key": "id", "required": ["id", "from", "to"], "editable": ["from", "to", "fromPort", "toPort", "label", "status", "routing", "curveTension", "waypoints"] },
11
- "swimlane": { "key": "id", "required": ["id", "label"], "editable": ["label"] },
12
- "architecture-node": { "key": "id", "required": ["id", "label"], "editable": ["label", "layerId", "boundaryId", "position", "size", "description", "status"] },
13
- "architecture-edge": { "key": "id", "required": ["id", "from", "to"], "editable": ["from", "to", "label", "relation", "routing", "curveTension", "waypoints", "status"] },
14
- "mindmap-node": { "key": "id", "required": ["id", "label"], "editable": ["label", "parentId", "position", "size", "branch", "description", "status"] }
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.13",
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": ["chart", "visualization", "agent", "svg", "canvas", "gantt", "diagram"],
56
- "engines": { "node": ">=18" },
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
  }
@@ -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.13/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.
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
 
@@ -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 const chartTypes = ['line', 'area', 'bar', 'column', 'pie', 'scatter', 'funnel', 'gauge', 'heatmap', 'radar', 'gantt', 'timeline', 'milestone', 'burndown', 'flow', 'swimlane', 'architecture', 'mindmap'];
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: '1.0',
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: ['canvas', 'svg'],
212
+ renderers: [...renderers],
213
+ interactionDefaults: { ...interactionDefaults },
244
214
  interactions: [...new Set(Object.values(chartProfiles).flatMap(profile => profile.interactions))],
245
- exports: ['png', 'svg', 'json'],
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
+ }