@taylorwong/ichartjs 2.0.6 → 2.0.8

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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +2 -2
  3. package/agent-recipes/minimal-specs.json +25 -0
  4. package/docs/agent/README.md +3 -1
  5. package/docs/agent/charting-scenario.md +46 -1
  6. package/docs/agent/coding-agent-integration.md +1 -1
  7. package/docs/agent/development/iteration-12.md +101 -0
  8. package/docs/agent/development/roadmap.md +2 -1
  9. package/docs/agent/development-guide.md +1 -1
  10. package/docs/agent/diagram-scenario.md +39 -11
  11. package/docs/agent/editing-contract.md +5 -0
  12. package/docs/agent/frontend-integration.md +1 -1
  13. package/docs/agent/project-scenario.md +1 -0
  14. package/docs/agent/quickstart.md +30 -0
  15. package/docs/agent/runtime-contract.md +19 -2
  16. package/docs/agent/theme-guide.md +33 -1
  17. package/docs/agent/usage-scenarios.md +3 -1
  18. package/docs/agent/zh-CN/README.md +1 -1
  19. package/docs/agent/zh-CN/charting-scenario.md +31 -1
  20. package/docs/agent/zh-CN/coding-agent-integration.md +1 -1
  21. package/docs/agent/zh-CN/diagram-scenario.md +32 -3
  22. package/docs/agent/zh-CN/editing-contract.md +2 -0
  23. package/docs/agent/zh-CN/frontend-integration.md +1 -1
  24. package/docs/agent/zh-CN/iteration-12.md +98 -0
  25. package/docs/agent/zh-CN/quickstart.md +30 -0
  26. package/docs/agent/zh-CN/runtime-contract.md +7 -1
  27. package/docs/agent/zh-CN/theme-guide.md +33 -1
  28. package/docs/agent/zh-CN/usage-scenarios.md +3 -1
  29. package/docs/manifests/capabilities.json +42 -3
  30. package/docs/manifests/commands.json +10 -9
  31. package/docs/manifests/schemas.json +5 -2
  32. package/examples/agent-workflow.mjs +2 -1
  33. package/package.json +1 -1
  34. package/skills/ichartjs/SKILL.md +19 -9
  35. package/skills/ichartjs/references/agent-contract.md +5 -2
  36. package/skills/ichartjs/references/chart-selection.md +2 -0
  37. package/src/capabilities.mjs +83 -13
  38. package/src/charts.mjs +202 -58
  39. package/src/command.mjs +4 -3
  40. package/src/diagram-interaction.mjs +55 -11
  41. package/src/diagram.mjs +149 -36
  42. package/src/edit-controller.mjs +6 -3
  43. package/src/edit.mjs +17 -7
  44. package/src/index.mjs +44 -17
  45. package/src/layout.mjs +39 -0
  46. package/src/preferences-ui.mjs +73 -24
  47. package/src/project.mjs +64 -24
  48. package/src/renderer.mjs +2 -2
  49. package/src/scene.mjs +32 -1
  50. package/src/schema.mjs +7 -2
  51. package/src/spec.mjs +88 -6
  52. package/src/theme.mjs +1 -1
  53. package/types/index.d.ts +28 -7
@@ -11,11 +11,11 @@ getCapabilities → inspectData → planChart → build Spec → validateSpec
11
11
  - `getCapabilities()`: global and per-chart discoverability.
12
12
  - `getChartCapability(type)`: required roles, interactions, renderers, features, exports, and limits.
13
13
  - `inspectData(input)`: field roles, identifiers, cardinality, missingness, temporal coverage, and warnings.
14
- - `planChart(input, options)`: primary type, alternatives, confidence, reasons, required fields, suggested encodings, assumptions, warnings, unsupported requests, and next actions.
14
+ - `planChart(input, options)`: primary type, alternatives, confidence, reasons, required fields, suggested encodings, assumptions, warnings, unsupported requests, next actions, and `intentKnown`/`intentSuggestions`/`fallbackUsed` metadata.
15
15
  - `validateSpec(spec)`: errors, warnings, normalizations, and normalized Spec.
16
16
  - `createChart(spec)`: headless or mounted chart lifecycle.
17
17
  - `chart.explain()`: semantics, encodings, interactions, lineage, warnings, and accessibility summary.
18
- - `chart.getState()`: renderer, dimensions, warnings, assumptions, selection, view, project analytics, and linked state.
18
+ - `chart.getState()`: renderer, dimensions, warnings, assumptions, selection, view, project analytics, linked state, and `health`.
19
19
  - `chart.export({ type: 'json' })`: JSON-safe Spec and state.
20
20
 
21
21
  ## Stop conditions
@@ -24,6 +24,8 @@ Do not render when:
24
24
 
25
25
  - `plan.requiredFields` is non-empty;
26
26
  - `validateSpec().valid` is false;
27
+ - chart-specific encoding channels are unsupported or reference missing fields;
28
+ - a Gauge has no explicit `domain`;
27
29
  - the requested chart, renderer, interaction, or export is not declared;
28
30
  - required project dates, diagram endpoints, ports, lanes, or business schema rules are invalid.
29
31
 
@@ -35,5 +37,6 @@ Verify that:
35
37
  - validation passed without ignored errors;
36
38
  - explanation lineage preserves stable source IDs;
37
39
  - warnings and assumptions are visible in the response;
40
+ - `health.renderable` is true and `health.status` is reported; `VALUE_CLAMPED`, `LABELS_SUPPRESSED`, and `ZERO_TOTAL` are not hidden;
38
41
  - the preview uses a maintained URL;
39
42
  - replaced charts are destroyed.
@@ -20,3 +20,5 @@
20
20
  | Responsibility | Swimlane | Require valid lane membership. |
21
21
 
22
22
  Always prefer `planChart()` over this table when runtime capability output is available. Treat alternatives as tradeoffs, not automatic fallbacks.
23
+
24
+ `planChart()` accepts the exact registered intent token, not the user's full sentence. For example, map “show the sales trend over time” to `trend`; if an unknown token is passed, inspect and surface the returned `UNKNOWN_INTENT` warning instead of accepting the fallback silently.
@@ -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,27 +44,87 @@ 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'
48
+ };
49
+
50
+ const intentAliases = {
51
+ trend: ['trend', 'time-series'], time: ['trend', 'time-series'], compare: ['comparison', 'ranking'], rank: ['ranking', 'comparison'],
52
+ distribution: ['distribution', 'comparison'], relationship: ['relationship', 'correlation'], correlation: ['correlation', 'relationship'],
53
+ matrix: ['matrix', 'correlation-grid'], profile: ['multidimensional', 'profile'], architecture: ['architecture', 'business-architecture', 'data-architecture', 'technical-architecture'],
54
+ mind: ['mindmap', 'hierarchy', 'brainstorm']
45
55
  };
46
56
 
47
57
  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']
58
+ 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
59
  };
50
60
 
51
61
  function warning(code, path, message, suggestion) { return { code, path, message, ...(suggestion ? { suggestion } : {}) }; }
52
62
 
63
+ function suggestIntents(requested) {
64
+ const value = String(requested || '').trim().toLowerCase();
65
+ if (!value) return ['comparison'];
66
+ if (intentAliases[value]) return intentAliases[value];
67
+ const tokens = value.split(/[^a-z0-9-]+/).filter(Boolean);
68
+ return [...new Set(tokens.flatMap(token => intentAliases[token] || (intentMap[token] ? [token] : [])))].slice(0, 5);
69
+ }
70
+
53
71
  export function getChartCapability(type) {
54
72
  return chartProfiles[type] ? JSON.parse(JSON.stringify(chartProfiles[type])) : null;
55
73
  }
56
74
 
75
+ const preferenceMessages = {
76
+ en: {
77
+ groups: { theme: 'Theme', typography: 'Typography', components: 'Components', behavior: 'Behavior' },
78
+ 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' },
79
+ 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.' },
80
+ 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' }
81
+ },
82
+ 'zh-CN': {
83
+ groups: { theme: '主题', typography: '排版', components: '显示内容', behavior: '行为' },
84
+ fields: { mode: '主题', preset: '样式预设', palette: '配色', scale: '字号', density: '布局密度', legend: '图例', labels: '数据标签', grid: '网格线', branding: '品牌署名', motion: '动效' },
85
+ descriptions: { mode: '选择自动、浅色、深色或高对比度主题。', preset: '选择语义化视觉预设。', palette: '选择语义化配色方案。', scale: '在不改变业务数据的情况下缩放图表字体。', density: '调整图表间距与内边距密度。', legend: '显示、隐藏或自动判断图例。', labels: '显示、隐藏或自动判断数据标签。', grid: '显示、隐藏或自动判断网格线。', branding: '控制 Powered by iChart.js 品牌署名。', motion: '控制动画和动效偏好。' },
86
+ 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': '特大' }
87
+ }
88
+ };
89
+
90
+ function preferenceLocale(locale) {
91
+ const normalized = String(locale || 'en').toLowerCase();
92
+ return normalized === 'zh' || normalized.startsWith('zh-') ? 'zh-CN' : 'en';
93
+ }
94
+
95
+ export function getPreferenceCapabilities(chartType = null, { locale = 'en' } = {}) {
96
+ const resolvedLocale = preferenceLocale(locale), text = preferenceMessages[resolvedLocale], profile = chartType ? chartProfiles[chartType] || null : null;
97
+ const option = value => ({ value, label: text.options[String(value ?? 'auto')] || String(value ?? 'auto') });
98
+ const feature = name => chartType == null ? { supported: true, applicability: 'chart-dependent' } : { supported: profile?.features?.[name] === 'supported', applicability: profile?.features?.[name] || 'not-applicable' };
99
+ const scopes = ['global', 'chart'];
100
+ const field = ({ path, group, key, type, values, defaultValue, menu = false, control = 'select', support = { supported: true, applicability: 'all-charts' }, minimum, maximum }) => ({
101
+ 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 })
102
+ });
103
+ const fields = [
104
+ field({ path: 'theme.mode', group: 'theme', key: 'mode', type: 'enum', values: [null, ...styleCapabilities.modes.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.mode, menu: true }),
105
+ field({ path: 'theme.preset', group: 'theme', key: 'preset', type: 'enum', values: [null, ...styleCapabilities.presets.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.preset }),
106
+ field({ path: 'theme.palette', group: 'theme', key: 'palette', type: 'enum', values: [null, ...styleCapabilities.palettes.filter(value => value !== 'auto')], defaultValue: defaultPreferences.theme.palette, menu: true }),
107
+ 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 }),
108
+ field({ path: 'density', group: 'typography', key: 'density', type: 'enum', values: preferenceDensities, defaultValue: defaultPreferences.density }),
109
+ field({ path: 'components.legend', group: 'components', key: 'legend', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.legend, menu: true, control: 'toggle', support: feature('legend') }),
110
+ field({ path: 'components.labels', group: 'components', key: 'labels', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.labels, menu: true, control: 'toggle', support: feature('labels') }),
111
+ field({ path: 'components.grid', group: 'components', key: 'grid', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.components.grid, menu: true, control: 'toggle', support: feature('grid') }),
112
+ field({ path: 'branding.enabled', group: 'components', key: 'branding', type: 'tri-state', values: preferenceTriStates, defaultValue: defaultPreferences.branding.enabled }),
113
+ field({ path: 'motion', group: 'behavior', key: 'motion', type: 'enum', values: preferenceMotions, defaultValue: defaultPreferences.motion })
114
+ ];
115
+ return { version: '1.0', locale: resolvedLocale, chartType: profile?.type || null, scopes, persistence: ['memory', 'localStorage', 'adapter'], precedence: ['defaults', 'global', 'chart', 'temporary-agent-patch'], fields };
116
+ }
117
+
57
118
  export function planChart(input, options = {}) {
58
119
  const report = inspectData(input);
59
120
  const requestedIntent = options.intent || 'comparison';
60
121
  const warnings = [...report.warnings];
122
+ const intentKnown = Boolean(intentMap[requestedIntent]);
123
+ const intentSuggestions = intentKnown ? [] : suggestIntents(requestedIntent);
61
124
  let primary = intentMap[requestedIntent];
62
125
  if (!primary) {
63
126
  primary = report.fields.some(field => field.type === 'temporal') ? 'line' : report.measures.length >= 2 && report.dimensions.length === 0 ? 'scatter' : 'bar';
64
- warnings.push(warning('UNKNOWN_INTENT', 'intent', `Intent ${requestedIntent} is not registered.`, 'Use an intent returned by getCapabilities().intents.'));
127
+ warnings.push(warning('UNKNOWN_INTENT', 'intent', `Intent ${requestedIntent} is not registered.`, intentSuggestions.length ? `Use one of: ${intentSuggestions.join(', ')}.` : 'Use an intent returned by getCapabilities().intents.'));
65
128
  }
66
129
  const profile = chartProfiles[primary];
67
130
  const requiredFields = [];
@@ -92,6 +155,9 @@ export function planChart(input, options = {}) {
92
155
  return {
93
156
  version: '1.0',
94
157
  intent: requestedIntent,
158
+ intentKnown,
159
+ intentSuggestions,
160
+ fallbackUsed: !intentKnown,
95
161
  primary,
96
162
  alternatives: alternatives[primary] || ['column', 'line'],
97
163
  confidence,
@@ -124,6 +190,7 @@ export function explainChart(spec, model = {}) {
124
190
  interactions: Object.keys(spec.interaction || {}).filter(key => spec.interaction[key]),
125
191
  assumptions: [...(model.data?.assumptions || []), ...(model.state?.projectAnalytics?.assumptions || [])],
126
192
  warnings,
193
+ axes: model.state?.axes || null,
127
194
  style: spec.theme && typeof spec.theme === 'object' ? { name: spec.theme.name, preset: spec.theme.preset, mode: spec.theme.mode, resolvedMode: spec.theme.resolvedMode, palette: spec.theme.palette, reasons: spec.theme.reasons || [], warnings: spec.theme.warnings || [] } : planStyle(spec),
128
195
  lineage: { recordIds: (model.data?.rows || []).map((row, index) => String(row.id ?? row.key ?? `record-${index}`)), sourcePreserved: true },
129
196
  accessibility: { enabled: Boolean(spec.accessibility?.enabled), summary: spec.accessibility?.description || spec.title?.text || `${spec.type} chart with ${model.data?.rows?.length || 0} data items.` }
@@ -132,6 +199,7 @@ export function explainChart(spec, model = {}) {
132
199
 
133
200
  export function getCapabilities() {
134
201
  const intents = [...new Set(Object.values(chartProfiles).flatMap(profile => profile.intents))];
202
+ const preferenceCapabilities = getPreferenceCapabilities();
135
203
  const exp = {
136
204
  types: ['png', 'jpeg', 'svg', 'json'],
137
205
  mime: { png: 'image/png', jpeg: 'image/jpeg', svg: 'image/svg+xml', json: 'application/json' },
@@ -161,10 +229,11 @@ export function getCapabilities() {
161
229
  chartTypes,
162
230
  charts: JSON.parse(JSON.stringify(chartProfiles)),
163
231
  intents,
164
- chartModes: { stack: ['stacked', 'percent'], pie: ['standard', 'donut'], composition: ['multi-series', 'mixed-line-column', 'dual-axis'], transforms: ['bin'] },
232
+ locale: { default: 'en-US', recommended: ['en-US', 'zh-CN'], appliesTo: ['axis', 'labels', 'tooltip', 'export'], inputDates: 'ISO-8601 strings; natural-language date parsing is not supported.' },
233
+ 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
234
  projectManagement: project,
166
235
  projectIntelligence: {
167
- intents: ['schedule', 'milestone', 'progress', 'variance', 'capacity', 'release', 'risk', 'aging', 'workflow', 'responsibility'],
236
+ intents: ['schedule', 'milestone', 'progress', 'variance', 'capacity', 'release', 'risk', 'aging', 'workflow', 'responsibility', 'architecture', 'mindmap'],
168
237
  views: ['gantt', 'burndown', 'column', 'area', 'scatter', 'bar'],
169
238
  analytics: ['calendar', 'dependency-normalization', 'critical-path', 'slack', 'baseline-actual-variance', 'capacity', 'cumulative-flow', 'velocity', 'release-forecast', 'risk-matrix', 'issue-aging'],
170
239
  linkedState: ['owner', 'status', 'priority', 'sprint', 'label'],
@@ -186,12 +255,13 @@ export function getCapabilities() {
186
255
  styleSystem: JSON.parse(JSON.stringify(styleCapabilities)),
187
256
  preferences: {
188
257
  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'],
258
+ scopes: preferenceCapabilities.scopes,
259
+ persistence: preferenceCapabilities.persistence,
260
+ fields: preferenceCapabilities.fields.map(field => field.path),
192
261
  agentAdjustable: true,
193
262
  interactiveSettingsUI: true,
194
- precedence: ['defaults', 'global', 'chart', 'temporary-agent-patch']
263
+ precedence: preferenceCapabilities.precedence,
264
+ schema: preferenceCapabilities
195
265
  },
196
266
  plugins: true,
197
267
  branding: { defaultEnabled: true, signature: 'Powered by iChart.js', options: [{ name: 'enabled', type: 'boolean', default: true }] },