@taylorwong/ichartjs 2.0.6 → 2.0.7

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 (44) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +2 -2
  3. package/docs/agent/README.md +3 -1
  4. package/docs/agent/coding-agent-integration.md +1 -1
  5. package/docs/agent/development/iteration-12.md +101 -0
  6. package/docs/agent/development/roadmap.md +2 -1
  7. package/docs/agent/development-guide.md +1 -1
  8. package/docs/agent/diagram-scenario.md +39 -11
  9. package/docs/agent/editing-contract.md +5 -0
  10. package/docs/agent/frontend-integration.md +1 -1
  11. package/docs/agent/runtime-contract.md +11 -1
  12. package/docs/agent/theme-guide.md +33 -1
  13. package/docs/agent/usage-scenarios.md +3 -1
  14. package/docs/agent/zh-CN/README.md +1 -1
  15. package/docs/agent/zh-CN/coding-agent-integration.md +1 -1
  16. package/docs/agent/zh-CN/diagram-scenario.md +32 -3
  17. package/docs/agent/zh-CN/editing-contract.md +2 -0
  18. package/docs/agent/zh-CN/frontend-integration.md +1 -1
  19. package/docs/agent/zh-CN/iteration-12.md +98 -0
  20. package/docs/agent/zh-CN/runtime-contract.md +3 -1
  21. package/docs/agent/zh-CN/theme-guide.md +33 -1
  22. package/docs/agent/zh-CN/usage-scenarios.md +3 -1
  23. package/docs/manifests/capabilities.json +30 -3
  24. package/docs/manifests/commands.json +10 -9
  25. package/docs/manifests/schemas.json +5 -2
  26. package/package.json +1 -1
  27. package/skills/ichartjs/SKILL.md +2 -1
  28. package/src/capabilities.mjs +60 -12
  29. package/src/charts.mjs +127 -44
  30. package/src/command.mjs +4 -3
  31. package/src/diagram-interaction.mjs +55 -11
  32. package/src/diagram.mjs +149 -36
  33. package/src/edit-controller.mjs +6 -3
  34. package/src/edit.mjs +17 -7
  35. package/src/index.mjs +34 -15
  36. package/src/layout.mjs +39 -0
  37. package/src/preferences-ui.mjs +73 -24
  38. package/src/project.mjs +64 -24
  39. package/src/renderer.mjs +2 -2
  40. package/src/scene.mjs +32 -1
  41. package/src/schema.mjs +7 -2
  42. package/src/spec.mjs +10 -4
  43. package/src/theme.mjs +1 -1
  44. package/types/index.d.ts +22 -4
@@ -10,6 +10,8 @@ getCapabilities → inspectData → planChart → 创建 Spec → validateSpec
10
10
 
11
11
  Agent 应优先使用 `getCapabilities()`,不要硬编码未声明的图表类型或操作。
12
12
 
13
+ 图表创建后的视觉设置使用 `getPreferenceCapabilities(chartType, { locale }) → chart.getPreferences() → validatePreferences(patch) → chart.setPreferences(patch, { source: 'agent' }) → chart.getState().preferences`。这样 Agent 与内置设置菜单始终使用同一份白名单契约。
14
+
13
15
  Iteration 8 通过 `getChartCapability(type)` 提供逐图表能力档案,包括必需数据角色、支持的交互、Renderer、功能状态、导出和建议限制。Agent 不应猜测未声明能力。
14
16
 
15
17
  `planChart(data, { intent, renderer })` 返回版本化规划结果:主选图表、备选项、置信度、原因、缺失字段、建议编码、假设、警告、不支持请求和安全下一步。规划不会虚构业务含义、单位、日期或缺失字段。
@@ -80,6 +82,6 @@ iChart.js 导出采用**双底层单源架构**,所有产物共享 `buildScene
80
82
  - `DOWNLOAD_HEADLESS`:`chart.download*()` 仅在浏览器有 DOM 时可用,无头用 `export`。
81
83
  - `EXPORT_TYPE_UNSUPPORTED`:不支持的导出类型。
82
84
 
83
- 公共 API:`inspectData`、`normalizeData`、`planChart`、`recommend`、`validateSpec`、`createChart`、`getCapabilities`、`getChartCapability`、`chart.describe`、`chart.explain`、`chart.getState`、`chart.export`、`chart.exportAsync`、`chart.toDataURL`、`chart.toBlob`、`chart.download`、`chart.downloadPNG`、`chart.downloadSVG`、`chart.downloadJSON`。
85
+ 公共 API:`inspectData`、`normalizeData`、`planChart`、`recommend`、`validateSpec`、`createChart`、`getCapabilities`、`getChartCapability`、`getPreferenceCapabilities`、`validatePreferences`、`chart.describe`、`chart.explain`、`chart.getState`、`chart.getPreferences`、`chart.setPreferences`、`chart.resetPreferences`、`chart.selectEdges`、`chart.getSelectedEdgeIds`、`chart.deleteSelectedEdges`、`chart.export`、`chart.exportAsync`、`chart.toDataURL`、`chart.toBlob`、`chart.download`、`chart.downloadPNG`、`chart.downloadSVG`、`chart.downloadJSON`。
84
86
 
85
87
  实现位置:`src/index.mjs`、`src/spec.mjs`、`src/scene.mjs`、`src/renderer.mjs`、`src/plugin.mjs`、`src/scale.mjs`、`src/charts.mjs`、`src/capabilities.mjs`。
@@ -66,7 +66,11 @@ const chart = createChart({
66
66
  data: { values: rows },
67
67
  preferences: pagePreferences
68
68
  });
69
- mountChartSettings(chart, { locale: 'zh-CN' });
69
+ const settings = mountChartSettings(chart, {
70
+ locale: 'zh-CN',
71
+ placement: 'auto',
72
+ preferredPlacements: ['right', 'top', 'bottom']
73
+ });
70
74
 
71
75
  // Agent 对话也使用同一套 API。
72
76
  chart.setPreferences({
@@ -74,8 +78,36 @@ chart.setPreferences({
74
78
  typography: { scale: 1.15 },
75
79
  components: { grid: false }
76
80
  }, { source: 'agent' });
81
+
82
+ // 宿主永久移除图表时同步销毁快捷设置浮层。
83
+ settings.destroy();
84
+ ```
85
+
86
+ 快捷设置面板会通过 Portal 放到图表裁剪区域之外。自动定位优先使用按钮右侧,其次上侧、下侧;都无法完整容纳时限制在浏览器视口内。滚动时保持已经选定的方位并跟随按钮,不再被视口约束拉回;图表或菜单按钮离开视口时直接关闭。窗口缩放和内容变化时可以重新计算方位。
87
+
88
+ ### Agent 配置发现与验证
89
+
90
+ Agent 不应硬编码菜单选项,应先调用 `getPreferenceCapabilities(chartType, { locale })`。该接口会返回每个偏好字段的类型、默认值、候选值、作用域、当前图表适用性和 `menu.visible` 状态;对话调整和宿主自定义设置页应使用同一份契约。
91
+
92
+ ```js
93
+ import { getPreferenceCapabilities, validatePreferences } from '@taylorwong/ichartjs';
94
+
95
+ const capabilities = getPreferenceCapabilities(chart.getSpec().type, { locale: 'zh-CN' });
96
+ const menuFields = capabilities.fields.filter(field => field.menu.visible);
97
+ const current = chart.getPreferences();
98
+ const patch = {
99
+ theme: { preset: 'dashboard', palette: 'status' },
100
+ typography: { scale: 1.15 },
101
+ components: { grid: false }
102
+ };
103
+ const checked = validatePreferences(patch, { partial: true });
104
+ if (!checked.valid) throw new Error(JSON.stringify(checked.errors));
105
+ chart.setPreferences(checked.value, { scope: 'chart', source: 'agent' });
106
+ const applied = chart.getState().preferences;
77
107
  ```
78
108
 
109
+ 快捷菜单字段为 `theme.mode`、`theme.palette`、`typography.scale`,以及当前图表支持的 `components.legend`、`components.labels`、`components.grid`。完整白名单另外包含 `theme.preset`、`density`、`branding.enabled` 和 `motion`。无法识别语言时默认返回英文。
110
+
79
111
  单图表快捷菜单使用紧凑的汉堡图标,只保留高频操作:主题模式、配色、字号,以及当前图表真正支持的图例、数据标签和网格线。能力检测会自动隐藏无效设置;修改即时生效,并支持 `zh-CN`、`en` 和按文档语言自动识别。
80
112
 
81
113
  低频选项和页面级配置应放在图表弹出菜单之外的独立设置页。页面内图表共享同一个 store,并使用 `store.setGlobal()` 或 `chart.setPreferences(patch, { scope: 'global' })` 更新全局。完整设置页可提供 preset、密度、署名等白名单能力,而不挤占每张图表。Node/SSR 默认使用内存,浏览器需要显式选择 `localStorage` 或宿主存储适配器。快捷菜单验收地址:`http://localhost:3000/playground/project-gallery.html`;完整页面设置验收地址:`http://localhost:3000/playground/preferences-lab.html`。
@@ -116,7 +116,7 @@ npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --ye
116
116
  需要固定发布版本时,直接安装已发布的 Skill 目录:
117
117
 
118
118
  ```bash
119
- npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.6/skills/ichartjs \
119
+ npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.7/skills/ichartjs \
120
120
  --agent codex --global --yes
121
121
  ```
122
122
 
@@ -150,6 +150,8 @@ Agent 可以创建一个 ESM 脚本或独立 HTML 页面,输出:
150
150
  - `gantt`、`timeline`、`milestone`、`burndown`:项目进度和交付计划;
151
151
  - 项目分析:capacity、velocity、release forecast、risk、issue aging;
152
152
  - `flow`、`swimlane`:流程、责任、分组、Port 和受控编辑。
153
+ - `architecture`:业务、数据或技术架构,使用明确的层、边界和关系。
154
+ - `mindmap`:想法层级,以 `parentId` 为事实来源,支持树形或放射布局。
153
155
 
154
156
  保留稳定的 record、node、edge、lane、group、port ID。业务编辑先返回 preview,确认后再 commit,并返回审计结果。
155
157
 
@@ -12,11 +12,29 @@
12
12
  "scenarios": {
13
13
  "charting": ["line", "area", "bar", "column", "pie", "scatter", "funnel", "gauge", "heatmap", "radar"],
14
14
  "project": ["gantt", "timeline", "milestone", "burndown"],
15
- "diagram": ["flow", "swimlane"]
15
+ "diagram": ["flow", "swimlane", "architecture", "mindmap"]
16
16
  },
17
17
  "projectIntelligence": ["variance", "capacity", "release", "risk", "aging"],
18
- "chartModes": ["stacked", "percent-stacked", "donut", "mixed-line-column", "bin"],
18
+ "chartModes": ["stacked", "percent-stacked", "donut", "mixed-line-column", "bin", "architecture-layers", "architecture-boundaries", "mindmap-tree", "mindmap-radial", "mindmap-curved-edges"],
19
19
  "renderers": ["canvas", "svg"],
20
+ "interactionDefaults": { "zoom": false, "pan": false, "brush": false, "drag": false, "edgeDrag": false, "portConnect": false, "editing": false },
21
+ "diagramEditing": {
22
+ "renderers": ["canvas", "svg"],
23
+ "edgeSelection": true,
24
+ "waypointDrag": true,
25
+ "segmentDrag": true,
26
+ "persistentWaypoints": true,
27
+ "enabledByDefault": false,
28
+ "activation": ["editing.enabled", "interaction.edgeDrag"]
29
+ },
30
+ "diagramCurves": {
31
+ "renderers": ["canvas", "svg"],
32
+ "type": "cubic-bezier",
33
+ "mindmapDefault": true,
34
+ "curveTension": { "minimum": 0.2, "maximum": 0.8, "default": 0.4 },
35
+ "obstacleFallback": "orthogonal",
36
+ "controlPointEditing": false
37
+ },
20
38
  "excluded": ["map", "3d"],
21
39
  "exports": ["png", "svg", "json"],
22
40
  "headless": {
@@ -65,8 +83,17 @@
65
83
  "version": "1.0",
66
84
  "scopes": ["global", "chart"],
67
85
  "persistence": ["memory", "localStorage", "adapter"],
86
+ "fields": ["theme.mode", "theme.preset", "theme.palette", "typography.scale", "density", "components.legend", "components.labels", "components.grid", "branding.enabled", "motion"],
68
87
  "agentAdjustable": true,
69
- "interactiveSettingsUI": true
88
+ "interactiveSettingsUI": true,
89
+ "precedence": ["defaults", "global", "chart", "temporary-agent-patch"],
90
+ "discovery": {
91
+ "capabilities": "getPreferenceCapabilities(chartType?, { locale? })",
92
+ "current": "chart.getPreferences()",
93
+ "validate": "validatePreferences(patch, { partial: true })",
94
+ "apply": "chart.setPreferences(patch, { scope, source: 'agent' })",
95
+ "verify": "chart.getState().preferences"
96
+ }
70
97
  },
71
98
  "styleSystem": {
72
99
  "modes": ["auto", "light", "dark", "contrast"],
@@ -2,27 +2,28 @@
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"], "previewRequired": true, "undoable": true },
6
- "updateRecord": { "models": ["project-task", "timeline-event", "milestone", "burndown-sample", "flow-node", "flow-edge", "swimlane"], "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"], "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 },
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 },
10
10
  "addDependency": { "models": ["project-task"], "required": ["taskId", "dependencyId"], "previewRequired": true, "undoable": true },
11
11
  "removeDependency": { "models": ["project-task"], "required": ["taskId", "dependencyId"], "previewRequired": true, "undoable": true },
12
12
  "updateMilestone": { "models": ["milestone"], "required": ["recordId"], "previewRequired": true, "undoable": true },
13
- "moveNode": { "models": ["flow-node"], "required": ["nodeId", "position"], "previewRequired": true, "undoable": true },
14
- "moveNodes": { "models": ["flow-node"], "required": ["nodeIds", "delta"], "previewRequired": true, "undoable": true },
13
+ "moveNode": { "models": ["flow-node", "architecture-node", "mindmap-node"], "required": ["nodeId", "position"], "previewRequired": true, "undoable": true },
14
+ "moveNodes": { "models": ["flow-node", "architecture-node", "mindmap-node"], "required": ["nodeIds", "delta"], "previewRequired": true, "undoable": true },
15
15
  "moveNodeToLane": { "models": ["flow-node"], "required": ["nodeId", "laneId"], "previewRequired": true, "undoable": true },
16
- "resizeNode": { "models": ["flow-node"], "required": ["nodeId", "size"], "previewRequired": true, "undoable": true },
17
- "alignNodes": { "models": ["flow-node"], "required": ["nodeIds", "alignment"], "previewRequired": true, "undoable": true },
18
- "snapNodes": { "models": ["flow-node"], "required": ["nodeIds"], "previewRequired": true, "undoable": true },
16
+ "resizeNode": { "models": ["flow-node", "architecture-node", "mindmap-node"], "required": ["nodeId", "size"], "previewRequired": true, "undoable": true },
17
+ "alignNodes": { "models": ["flow-node", "architecture-node", "mindmap-node"], "required": ["nodeIds", "alignment"], "previewRequired": true, "undoable": true },
18
+ "snapNodes": { "models": ["flow-node", "architecture-node", "mindmap-node"], "required": ["nodeIds"], "previewRequired": true, "undoable": true },
19
19
  "moveGroup": { "models": ["flow-node"], "required": ["groupId", "delta"], "previewRequired": true, "undoable": true },
20
20
  "resizeGroup": { "models": ["flow-node"], "required": ["groupId", "size"], "previewRequired": true, "undoable": true },
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"], "required": ["edgeId", "changes"], "previewRequired": true, "undoable": true },
25
- "addEdge": { "models": ["flow-edge"], "required": ["from", "to"], "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 },
26
27
  "toggleGroupCollapse": { "models": ["flow-node"], "required": ["groupId"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
27
28
  "duplicateSelection": { "models": ["flow-node", "flow-edge"], "required": ["nodeIds"], "previewRequired": true, "confirmationRequired": true, "undoable": true },
28
29
  "pasteSelection": { "models": ["flow-node", "flow-edge"], "required": ["nodes"], "previewRequired": true, "confirmationRequired": true, "undoable": true }
@@ -7,7 +7,10 @@
7
7
  "milestone": { "key": "id", "required": ["id", "title", "date"], "editable": ["title", "date", "status"] },
8
8
  "burndown-sample": { "key": "id", "required": ["id", "date", "remaining"], "editable": ["date", "remaining", "scopeChange"] },
9
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", "label"] },
11
- "swimlane": { "key": "id", "required": ["id", "label"], "editable": ["label"] }
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"] }
12
15
  }
13
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taylorwong/ichartjs",
3
- "version": "2.0.6",
3
+ "version": "2.0.7",
4
4
  "description": "Agent-first, renderer-independent charting and project visualization runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -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.6/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.7/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
 
@@ -44,6 +44,7 @@ Do not install the unscoped npm registry package named `ichartjs`; it is current
44
44
  9. Self-check with `chart.explain()`, `chart.getState()`, and JSON export.
45
45
  10. Provide an exact preview URL or artifact path and report assumptions, warnings, and deferred checks.
46
46
  11. Prefer `theme: { mode: 'auto', preset, palette }`; preserve explicit user style choices and use `chart.setTheme()` for live switching.
47
+ 12. For post-creation visual changes, call `getPreferenceCapabilities(chartType, { locale })`, validate the patch with `validatePreferences()`, apply it with `chart.setPreferences(..., { source: 'agent' })`, and verify `chart.getState().preferences`.
47
48
 
48
49
  Use `@taylorwong/ichartjs` for package imports. Use `examples/agent-workflow.mjs` as the executable baseline when working in the repository.
49
50
 
@@ -3,12 +3,13 @@
3
3
  */
4
4
  import { inspectData } from './data.mjs';
5
5
  import { planStyle, styleCapabilities } from './theme.mjs';
6
+ import { defaultPreferences, preferenceDensities, preferenceMotions, preferenceTriStates } from './preferences.mjs';
6
7
 
7
- export const chartTypes = ['line', 'area', 'bar', 'column', 'pie', 'scatter', 'funnel', 'gauge', 'heatmap', 'radar', 'gantt', 'timeline', 'milestone', 'burndown', 'flow', 'swimlane'];
8
+ export const chartTypes = ['line', 'area', 'bar', 'column', 'pie', 'scatter', 'funnel', 'gauge', 'heatmap', 'radar', 'gantt', 'timeline', 'milestone', 'burndown', 'flow', 'swimlane', 'architecture', 'mindmap'];
8
9
 
9
10
  const cartesian = ['line', 'area', 'bar', 'column', 'scatter'];
10
11
  const project = ['gantt', 'timeline', 'milestone', 'burndown'];
11
- const diagrams = ['flow', 'swimlane'];
12
+ const diagrams = ['flow', 'swimlane', 'architecture', 'mindmap'];
12
13
  const status = (supported, notApplicable = []) => Object.fromEntries(supported.map(name => [name, 'supported']).concat(notApplicable.map(name => [name, 'not-applicable'])));
13
14
  const commonPresentation = ['title', 'subtitle', 'theme', 'responsive', 'empty-state', 'invalid-data-state', 'export', 'branding'];
14
15
 
@@ -27,8 +28,10 @@ const definitions = {
27
28
  timeline: { family: 'project', intents: ['timeline', 'milestone'], required: ['date', 'label'], optional: ['status'], interactions: ['hover', 'tooltip', 'selection', 'zoom', 'pan', 'keyboard'], features: status([...commonPresentation, 'labels', 'formatting']) },
28
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']) },
29
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']) },
30
- flow: { family: 'diagram', intents: ['workflow'], required: ['nodes'], optional: ['edges', 'groups', 'ports'], interactions: ['hover', 'selection', 'drag', 'keyboard-edit', 'keyboard-port-connect', 'port-connect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'layout', 'groups', 'ports', 'history'], ['axes', 'grid', 'legend']) },
31
- swimlane: { family: 'diagram', intents: ['responsibility'], required: ['nodes', 'lanes'], optional: ['edges', 'groups', 'ports'], interactions: ['hover', 'selection', 'drag', 'keyboard-edit', 'keyboard-port-connect', 'port-connect', 'copy-paste'], features: status([...commonPresentation, 'labels', 'routing', 'layout', 'groups', 'ports', 'lanes', 'history'], ['axes', 'grid', 'legend']) }
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']) }
32
35
  };
33
36
 
34
37
  export const chartProfiles = Object.fromEntries(chartTypes.map(type => [type, {
@@ -41,11 +44,11 @@ export const chartProfiles = Object.fromEntries(chartTypes.map(type => [type, {
41
44
  }]));
42
45
 
43
46
  const intentMap = {
44
- 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', 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'
47
+ 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'
45
48
  };
46
49
 
47
50
  const alternatives = {
48
- heatmap: ['scatter', 'column'], radar: ['bar', 'line'], gantt: ['timeline', 'milestone'], timeline: ['milestone', 'gantt'], milestone: ['timeline', 'gantt'], burndown: ['line', 'gantt'], column: ['bar', 'line'], scatter: ['bar', 'line'], bar: ['column', 'line'], flow: ['swimlane', 'gantt'], swimlane: ['flow', 'gantt'], line: ['area', 'column'], pie: ['bar', 'column'], funnel: ['bar', 'column'], gauge: ['column', 'bar']
51
+ heatmap: ['scatter', 'column'], radar: ['bar', 'line'], gantt: ['timeline', 'milestone'], timeline: ['milestone', 'gantt'], milestone: ['timeline', 'gantt'], burndown: ['line', 'gantt'], column: ['bar', 'line'], scatter: ['bar', 'line'], bar: ['column', 'line'], flow: ['swimlane', 'architecture'], swimlane: ['flow', 'architecture'], architecture: ['flow', 'mindmap'], mindmap: ['architecture', 'flow'], line: ['area', 'column'], pie: ['bar', 'column'], funnel: ['bar', 'column'], gauge: ['column', 'bar']
49
52
  };
50
53
 
51
54
  function warning(code, path, message, suggestion) { return { code, path, message, ...(suggestion ? { suggestion } : {}) }; }
@@ -54,6 +57,49 @@ export function getChartCapability(type) {
54
57
  return chartProfiles[type] ? JSON.parse(JSON.stringify(chartProfiles[type])) : null;
55
58
  }
56
59
 
60
+ const preferenceMessages = {
61
+ en: {
62
+ groups: { theme: 'Theme', typography: 'Typography', components: 'Components', behavior: 'Behavior' },
63
+ fields: { mode: 'Theme', preset: 'Style preset', palette: 'Palette', scale: 'Font size', density: 'Density', legend: 'Legend', labels: 'Data labels', grid: 'Grid lines', branding: 'Branding', motion: 'Motion' },
64
+ descriptions: { mode: 'Select automatic, light, dark, or high-contrast rendering.', preset: 'Select a semantic visual preset.', palette: 'Select a semantic color palette.', scale: 'Scale chart typography without changing business data.', density: 'Adjust chart spacing and padding density.', legend: 'Show, hide, or automatically resolve the legend.', labels: 'Show, hide, or automatically resolve data labels.', grid: 'Show, hide, or automatically resolve grid lines.', branding: 'Control the Powered by iChart.js signature.', motion: 'Control animation and motion preference.' },
65
+ options: { auto: 'Auto', light: 'Light', dark: 'Dark', contrast: 'High contrast', analysis: 'Analysis', dashboard: 'Dashboard', report: 'Report', presentation: 'Presentation', project: 'Project', diagram: 'Diagram', categorical: 'Categorical', sequential: 'Sequential', diverging: 'Diverging', status: 'Status', compact: 'Compact', comfortable: 'Comfortable', spacious: 'Spacious', full: 'Full', reduced: 'Reduced', off: 'Off', true: 'Show', false: 'Hide', '0.85': 'Small', '1': 'Default', '1.15': 'Large', '1.3': 'Extra large' }
66
+ },
67
+ 'zh-CN': {
68
+ groups: { theme: '主题', typography: '排版', components: '显示内容', behavior: '行为' },
69
+ fields: { mode: '主题', preset: '样式预设', palette: '配色', scale: '字号', density: '布局密度', legend: '图例', labels: '数据标签', grid: '网格线', branding: '品牌署名', motion: '动效' },
70
+ descriptions: { mode: '选择自动、浅色、深色或高对比度主题。', preset: '选择语义化视觉预设。', palette: '选择语义化配色方案。', scale: '在不改变业务数据的情况下缩放图表字体。', density: '调整图表间距与内边距密度。', legend: '显示、隐藏或自动判断图例。', labels: '显示、隐藏或自动判断数据标签。', grid: '显示、隐藏或自动判断网格线。', branding: '控制 Powered by iChart.js 品牌署名。', motion: '控制动画和动效偏好。' },
71
+ options: { auto: '自动', light: '浅色', dark: '深色', contrast: '高对比度', analysis: '分析', dashboard: '仪表盘', report: '报告', presentation: '演示', project: '项目', diagram: '图表结构', categorical: '分类', sequential: '渐变', diverging: '发散', status: '状态', compact: '紧凑', comfortable: '舒适', spacious: '宽松', full: '完整', reduced: '减少', off: '关闭', true: '显示', false: '隐藏', '0.85': '小', '1': '默认', '1.15': '大', '1.3': '特大' }
72
+ }
73
+ };
74
+
75
+ function preferenceLocale(locale) {
76
+ const normalized = String(locale || 'en').toLowerCase();
77
+ return normalized === 'zh' || normalized.startsWith('zh-') ? 'zh-CN' : 'en';
78
+ }
79
+
80
+ export function getPreferenceCapabilities(chartType = null, { locale = 'en' } = {}) {
81
+ const resolvedLocale = preferenceLocale(locale), text = preferenceMessages[resolvedLocale], profile = chartType ? chartProfiles[chartType] || null : null;
82
+ const option = value => ({ value, label: text.options[String(value ?? 'auto')] || String(value ?? 'auto') });
83
+ const feature = name => chartType == null ? { supported: true, applicability: 'chart-dependent' } : { supported: profile?.features?.[name] === 'supported', applicability: profile?.features?.[name] || 'not-applicable' };
84
+ const scopes = ['global', 'chart'];
85
+ const field = ({ path, group, key, type, values, defaultValue, menu = false, control = 'select', support = { supported: true, applicability: 'all-charts' }, minimum, maximum }) => ({
86
+ path, group, groupLabel: text.groups[group], type, label: text.fields[key], description: text.descriptions[key], default: defaultValue, options: values.map(option), scopes: [...scopes], supported: support.supported, applicability: support.applicability, menu: { visible: menu && support.supported, control }, ...(minimum == null ? {} : { minimum }), ...(maximum == null ? {} : { maximum })
87
+ });
88
+ const fields = [
89
+ field({ path: 'theme.mode', group: 'theme', key: 'mode', type: 'enum', values: [null, ...styleCapabilities.modes.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.mode, menu: true }),
90
+ field({ path: 'theme.preset', group: 'theme', key: 'preset', type: 'enum', values: [null, ...styleCapabilities.presets.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.preset }),
91
+ field({ path: 'theme.palette', group: 'theme', key: 'palette', type: 'enum', values: [null, ...styleCapabilities.palettes.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.palette, menu: true }),
92
+ field({ path: 'typography.scale', group: 'typography', key: 'scale', type: 'number', values: [0.85, 1, 1.15, 1.3], defaultValue: defaultPreferences.typography.scale, menu: true, minimum: 0.75, maximum: 1.5 }),
93
+ field({ path: 'density', group: 'typography', key: 'density', type: 'enum', values: preferenceDensities, defaultValue: defaultPreferences.density }),
94
+ field({ path: 'components.legend', group: 'components', key: 'legend', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.legend, menu: true, control: 'toggle', support: feature('legend') }),
95
+ field({ path: 'components.labels', group: 'components', key: 'labels', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.labels, menu: true, control: 'toggle', support: feature('labels') }),
96
+ field({ path: 'components.grid', group: 'components', key: 'grid', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.grid, menu: true, control: 'toggle', support: feature('grid') }),
97
+ field({ path: 'branding.enabled', group: 'components', key: 'branding', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.branding.enabled }),
98
+ field({ path: 'motion', group: 'behavior', key: 'motion', type: 'enum', values: preferenceMotions, defaultValue: defaultPreferences.motion })
99
+ ];
100
+ return { version: '1.0', locale: resolvedLocale, chartType: profile?.type || null, scopes, persistence: ['memory', 'localStorage', 'adapter'], precedence: ['defaults', 'global', 'chart', 'temporary-agent-patch'], fields };
101
+ }
102
+
57
103
  export function planChart(input, options = {}) {
58
104
  const report = inspectData(input);
59
105
  const requestedIntent = options.intent || 'comparison';
@@ -132,6 +178,7 @@ export function explainChart(spec, model = {}) {
132
178
 
133
179
  export function getCapabilities() {
134
180
  const intents = [...new Set(Object.values(chartProfiles).flatMap(profile => profile.intents))];
181
+ const preferenceCapabilities = getPreferenceCapabilities();
135
182
  const exp = {
136
183
  types: ['png', 'jpeg', 'svg', 'json'],
137
184
  mime: { png: 'image/png', jpeg: 'image/jpeg', svg: 'image/svg+xml', json: 'application/json' },
@@ -161,10 +208,10 @@ export function getCapabilities() {
161
208
  chartTypes,
162
209
  charts: JSON.parse(JSON.stringify(chartProfiles)),
163
210
  intents,
164
- chartModes: { stack: ['stacked', 'percent'], pie: ['standard', 'donut'], composition: ['multi-series', 'mixed-line-column', 'dual-axis'], transforms: ['bin'] },
211
+ chartModes: { stack: ['stacked', 'percent'], pie: ['standard', 'donut'], composition: ['multi-series', 'mixed-line-column', 'dual-axis'], transforms: ['bin'], diagrams: ['process', 'architecture', 'mindmap'], mindmapLayouts: ['tree', 'radial'], mindmapEdges: ['curved', 'straight', 'orthogonal'] },
165
212
  projectManagement: project,
166
213
  projectIntelligence: {
167
- intents: ['schedule', 'milestone', 'progress', 'variance', 'capacity', 'release', 'risk', 'aging', 'workflow', 'responsibility'],
214
+ intents: ['schedule', 'milestone', 'progress', 'variance', 'capacity', 'release', 'risk', 'aging', 'workflow', 'responsibility', 'architecture', 'mindmap'],
168
215
  views: ['gantt', 'burndown', 'column', 'area', 'scatter', 'bar'],
169
216
  analytics: ['calendar', 'dependency-normalization', 'critical-path', 'slack', 'baseline-actual-variance', 'capacity', 'cumulative-flow', 'velocity', 'release-forecast', 'risk-matrix', 'issue-aging'],
170
217
  linkedState: ['owner', 'status', 'priority', 'sprint', 'label'],
@@ -186,12 +233,13 @@ export function getCapabilities() {
186
233
  styleSystem: JSON.parse(JSON.stringify(styleCapabilities)),
187
234
  preferences: {
188
235
  version: '1.0',
189
- scopes: ['global', 'chart'],
190
- persistence: ['memory', 'localStorage', 'adapter'],
191
- fields: ['theme', 'typography.scale', 'density', 'components.legend', 'components.labels', 'components.grid', 'branding.enabled', 'motion'],
236
+ scopes: preferenceCapabilities.scopes,
237
+ persistence: preferenceCapabilities.persistence,
238
+ fields: preferenceCapabilities.fields.map(field => field.path),
192
239
  agentAdjustable: true,
193
240
  interactiveSettingsUI: true,
194
- precedence: ['defaults', 'global', 'chart', 'temporary-agent-patch']
241
+ precedence: preferenceCapabilities.precedence,
242
+ schema: preferenceCapabilities
195
243
  },
196
244
  plugins: true,
197
245
  branding: { defaultEnabled: true, signature: 'Powered by iChart.js', options: [{ name: 'enabled', type: 'boolean', default: true }] },