@taylorwong/ichartjs 2.0.20 → 2.0.21
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 +5 -0
- package/README.md +2 -2
- package/docs/agent/coding-agent-integration.md +1 -1
- package/docs/agent/development/2.0-release-readiness.md +1 -1
- package/docs/agent/development/iteration-17.md +55 -0
- package/docs/agent/development/roadmap.md +2 -1
- package/docs/agent/frontend-integration.md +1 -1
- package/docs/agent/runtime-contract.md +1 -1
- package/docs/agent/usage-scenarios.md +1 -1
- package/docs/agent/zh-CN/coding-agent-integration.md +1 -1
- package/docs/agent/zh-CN/frontend-integration.md +1 -1
- package/docs/agent/zh-CN/iteration-17.md +50 -0
- package/docs/agent/zh-CN/runtime-contract.md +1 -1
- package/docs/agent/zh-CN/usage-scenarios.md +1 -1
- package/docs/manifests/capabilities.json +29 -2
- package/package.json +3 -2
- package/skills/ichartjs/SKILL.md +1 -1
- package/src/capabilities.mjs +14 -1
- package/src/index.mjs +2 -2
- package/src/spec.mjs +4 -2
- package/src/validation.mjs +2 -2
- package/types/index.d.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.21 - 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Completed Iteration 17 production trust and Agent reliability hardening with unknown-option diagnostics, effective Spec explanations, unsupported-renderer planning diagnostics, and stable normalization records.
|
|
6
|
+
- Added `agentReliability` capability metadata, npm package-content checks, bilingual production-trust guidance, and release-gate coverage for the ESM, TypeScript, headless, SVG, and Canvas consumer paths.
|
|
7
|
+
|
|
3
8
|
## 2.0.20 - 2026-09-30
|
|
4
9
|
|
|
5
10
|
- Completed Iteration 16 preference precedence, Agent source resolution, conversational workflow guidance, capability discovery, and Gallery acceptance coverage.
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ getCapabilities
|
|
|
45
45
|
npm install @taylorwong/ichartjs@^2
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
As a fallback for environments without npm access, install directly from GitHub: `npm install github:wanghetommy/ichartjs#v2.0.
|
|
48
|
+
As a fallback for environments without npm access, install directly from GitHub: `npm install github:wanghetommy/ichartjs#v2.0.21`.
|
|
49
49
|
|
|
50
50
|
### Optional Agent Skill
|
|
51
51
|
|
|
@@ -67,7 +67,7 @@ For a non-interactive global Codex installation:
|
|
|
67
67
|
npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --yes
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
For a release-pinned installation, use `npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
70
|
+
For a release-pinned installation, use `npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs --agent codex --global --yes`. WorkBuddy users can import the same tagged `skills/ichartjs` URL through the host's Skill interface; do not assume a `--agent workbuddy` adapter unless the installed CLI declares it. Package consumers can still copy `node_modules/@taylorwong/ichartjs/skills/ichartjs` as a manual fallback. After installation, invoke `$ichartjs` when named Skill invocation is supported, or select `ichartjs` in the host UI.
|
|
71
71
|
|
|
72
72
|
### Agent workflow
|
|
73
73
|
|
|
@@ -33,7 +33,7 @@ Install the official Skill with the standard Agent Skills CLI:
|
|
|
33
33
|
npx skills add wanghetommy/ichartjs --skill ichartjs
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
For global non-interactive Codex setup, append `--agent codex --global --yes`. To pin the released workflow, install `https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
36
|
+
For global non-interactive Codex setup, append `--agent codex --global --yes`. To pin the released workflow, install `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs`. WorkBuddy can import that tagged directory through its Skill interface; only use a host-specific `--agent` value when the installed CLI declares it.
|
|
37
37
|
|
|
38
38
|
Verify discovery with `npx skills add wanghetommy/ichartjs --list`; the result should include `ichartjs`.
|
|
39
39
|
|
|
@@ -40,7 +40,7 @@ iChart.js 2.0 is a new Agent-first product line. Compatibility with 1.x and a 1.
|
|
|
40
40
|
|
|
41
41
|
## Iteration 13 Addendum — 2026-09-23
|
|
42
42
|
|
|
43
|
-
The historical 2.0.0 release decision remains unchanged. Release `v2.0.
|
|
43
|
+
The historical 2.0.0 release decision remains unchanged. Release `v2.0.21` includes Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, Iteration 16 Agent presentation and acceptance hardening, and Iteration 17 production trust and Agent reliability hardening without adding a chart type: 18 chart profiles, 25 commands, 11 schemas, contract version `1.1`, atomic mutation validation, Mindmap edge discovery, strict TypeScript consumer compilation, no active source cycles, Node tests, Chromium browser acceptance, package dry-run verification, the 13F axis-free layout strategy, Timeline/Milestone layout hardening, renderer-parity Flow semantic shapes, native Flow ellipse primitives, adaptive diagram labels, diagram-only edge-label plates, polar label layout diagnostics for Pie, Donut, and Gauge, preference source resolution, conversational workflow guidance, effective-option explanations, stable normalization diagnostics, unsupported-renderer planning diagnostics, package-content checks, and Gallery browser acceptance.
|
|
44
44
|
|
|
45
45
|
## Deferred npm Publication
|
|
46
46
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Iteration 17 — Production Trust and Agent Reliability
|
|
2
|
+
|
|
3
|
+
Iteration 17 hardens the existing chart runtime for production Agent use. It adds no chart type, no CLI, no MCP server, and no HTTP service.
|
|
4
|
+
|
|
5
|
+
## 17A — Contract Reliability
|
|
6
|
+
|
|
7
|
+
- Diagnose unknown top-level Spec options instead of silently ignoring them.
|
|
8
|
+
- Keep `validateSpec()`, normalization, `createChart()`, capabilities, TypeScript, and manifests aligned.
|
|
9
|
+
- Preserve stable diagnostic codes, JSON paths, expected values, and actionable suggestions.
|
|
10
|
+
|
|
11
|
+
## 17B — Agent Self-check
|
|
12
|
+
|
|
13
|
+
- Expose effective rendering options and normalization results through `chart.explain()`.
|
|
14
|
+
- Report unsupported renderer requests during `planChart()` instead of returning only a fallback.
|
|
15
|
+
- Publish `agentReliability` in `getCapabilities()` and `capabilities.json`.
|
|
16
|
+
- Keep navigation, editing, and motion safe by default.
|
|
17
|
+
|
|
18
|
+
## 17C — Package and Integration Confidence
|
|
19
|
+
|
|
20
|
+
- Add a package dry-run gate for required runtime, TypeScript, Skill, recipes, and capability files.
|
|
21
|
+
- Reject accidental publication of `.trae`, `.github`, tests, and Playground development paths.
|
|
22
|
+
- Keep the consumer TypeScript fixture and ESM/headless examples in the release gate.
|
|
23
|
+
|
|
24
|
+
## 17D — Rendering and Accessibility Acceptance
|
|
25
|
+
|
|
26
|
+
- Continue SVG/Canvas parity checks for labels, diagrams, exports, and responsive layouts.
|
|
27
|
+
- Keep browser acceptance separate from headless checks and require explicit local-server evidence.
|
|
28
|
+
- Preserve static, safe defaults while testing opt-in navigation and editing.
|
|
29
|
+
|
|
30
|
+
## 17E — Open Source Maintenance
|
|
31
|
+
|
|
32
|
+
- Keep `CONTRIBUTING.md`, `SECURITY.md`, CI, release SOP, Skill, and Agent guides aligned.
|
|
33
|
+
- Record package and contract checks as mandatory contribution and release gates.
|
|
34
|
+
|
|
35
|
+
## Acceptance
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
inspect data
|
|
39
|
+
→ plan chart
|
|
40
|
+
→ validate Spec
|
|
41
|
+
→ create chart
|
|
42
|
+
→ inspect effective options and health
|
|
43
|
+
→ preview/export
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Invalid or ignored configuration is diagnosable.
|
|
47
|
+
- An Agent can verify what actually rendered without reading renderer internals.
|
|
48
|
+
- The npm tarball contains only supported consumer artifacts.
|
|
49
|
+
- `npm run agent:check`, browser acceptance, and `git diff --check` pass.
|
|
50
|
+
|
|
51
|
+
## Out of Scope
|
|
52
|
+
|
|
53
|
+
- New chart types.
|
|
54
|
+
- NLP parsing inside the runtime.
|
|
55
|
+
- CLI, MCP, HTTP, Python, or 1.x compatibility layers.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# iChart.js 2.0 Roadmap
|
|
2
2
|
|
|
3
|
-
> Roadmap baseline: 2026-09-14. Current release status: `v2.0.
|
|
3
|
+
> Roadmap baseline: 2026-09-14. Current release status: `v2.0.21` includes the completed Iteration 12 structured-diagram work, Iteration 13 contract hardening, Iteration 14 lightweight Flow semantics, Iteration 15 text-readability hardening, Iteration 16 Agent presentation and acceptance hardening, Iteration 17 production trust and Agent reliability hardening, native Flow ellipse primitives, sector-aware Pie/Donut labels, radius-aware Gauge metrics, and the preceding layout and diagnostic improvements. Geographic charts and 3D rendering remain out of scope until explicitly reintroduced.
|
|
4
4
|
|
|
5
5
|
## Current Status
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- Iteration 15A–15D is complete and included in `v2.0.19`: adaptive diagram node labels, semantic inline-first edge labels, diagram edge-label background plates, plate-free generic chart labels, shared SVG/Canvas fitting, unified label diagnostics, Agent/diagram documentation, and polar label layout hardening for Pie, Donut, and Gauge.
|
|
21
21
|
- Iteration 16A–16E is complete and included in `v2.0.20`: Gallery preference precedence, preference source resolution, conversational Agent routing, bilingual workflow guidance, manifest discovery, lifecycle regression coverage, and browser acceptance for Gallery theme controls.
|
|
22
22
|
- Flow start/end nodes now use native ellipse geometry across Scene Graph, SVG, Canvas, export, scaling, and hit testing.
|
|
23
|
+
- Iteration 17A–17E is complete and included in `v2.0.21`: unknown-option diagnostics, effective Agent explanations, unsupported renderer planning diagnostics, `agentReliability` discovery, npm package-content checks, and production-trust documentation. See `docs/agent/development/iteration-17.md`.
|
|
23
24
|
- The original `2.0.0` readiness record is historical and superseded by the published `2.0.x` releases. Current release checks are defined by `docs/agent/development/release-sop.md`.
|
|
24
25
|
|
|
25
26
|
## Iteration 4 — Agent Data Contract and Business Editing
|
|
@@ -10,7 +10,7 @@ This is the production component path. For one-off files or Agent-led repository
|
|
|
10
10
|
npm install @taylorwong/ichartjs@^2
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
For environments without npm registry access, install from GitHub as a fallback: `npm install github:wanghetommy/ichartjs#v2.0.
|
|
13
|
+
For environments without npm registry access, install from GitHub as a fallback: `npm install github:wanghetommy/ichartjs#v2.0.21`.
|
|
14
14
|
|
|
15
15
|
Use the package through a bundler or another environment that resolves npm ESM imports:
|
|
16
16
|
|
|
@@ -129,7 +129,7 @@ chart.downloadSVG()
|
|
|
129
129
|
chart.downloadJSON()
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
Validation results contain separate `errors`, `warnings`, and `normalizations`. Diagnostics use stable codes, JSON-oriented paths, expected values where useful, and actionable suggestions. `chart.explain()` returns encodings, transforms, interactions, assumptions, warnings, stable record lineage, and an accessibility summary.
|
|
132
|
+
Validation results contain separate `errors`, `warnings`, and `normalizations`. Diagnostics use stable codes, JSON-oriented paths, expected values where useful, and actionable suggestions. Unknown top-level options return `UNKNOWN_SPEC_OPTION` instead of being silently treated as valid configuration. `chart.explain()` returns encodings, transforms, interactions, effective options, normalizations, assumptions, warnings, stable record lineage, and an accessibility summary.
|
|
133
133
|
|
|
134
134
|
For lineage checks and linked updates, provide stable string `id` values on input rows. Without one, the runtime uses deterministic positional IDs such as `record-0`; these are suitable for a local self-check but not for durable business identity.
|
|
135
135
|
|
|
@@ -125,7 +125,7 @@ npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --ye
|
|
|
125
125
|
For reproducible installation, pin the released Skill directory:
|
|
126
126
|
|
|
127
127
|
```bash
|
|
128
|
-
npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
128
|
+
npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs \
|
|
129
129
|
--agent codex --global --yes
|
|
130
130
|
```
|
|
131
131
|
|
|
@@ -22,7 +22,7 @@ npm Registry 中无作用域的 `ichartjs` 是安全占位包,并非本项目
|
|
|
22
22
|
npx skills add wanghetommy/ichartjs --skill ichartjs
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Codex 全局无交互安装可追加 `--agent codex --global --yes`。需要固定发布版本时,安装 `https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
25
|
+
Codex 全局无交互安装可追加 `--agent codex --global --yes`。需要固定发布版本时,安装 `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs`。WorkBuddy 可通过自身 Skill 界面导入该带 Tag 的目录;只有当前 CLI 明确声明对应适配器时才使用宿主专用 `--agent` 参数。
|
|
26
26
|
|
|
27
27
|
使用 `npx skills add wanghetommy/ichartjs --list` 验证发现结果,其中应包含 `ichartjs`。
|
|
28
28
|
|
|
@@ -6,7 +6,7 @@ iChart.js 应作为普通 JavaScript UI 组件运行在浏览器应用中。数
|
|
|
6
6
|
npm install @taylorwong/ichartjs@^2
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
无法访问 npm Registry 的环境请用 GitHub 源作为后备:`npm install github:wanghetommy/ichartjs#v2.0.
|
|
9
|
+
无法访问 npm Registry 的环境请用 GitHub 源作为后备:`npm install github:wanghetommy/ichartjs#v2.0.21`。
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
12
|
import { createChart } from '@taylorwong/ichartjs';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Iteration 17 — 生产可信度与 Agent 可靠性
|
|
2
|
+
|
|
3
|
+
Iteration 17 面向生产环境强化现有 Runtime 的 Agent 使用体验。不新增图表类型,也不新增 CLI、MCP 或 HTTP 服务。
|
|
4
|
+
|
|
5
|
+
## 17A — 契约可靠性
|
|
6
|
+
|
|
7
|
+
- 对未知顶层 Spec 配置提供诊断,不再静默忽略。
|
|
8
|
+
- 保持 `validateSpec()`、规范化、`createChart()`、Capabilities、TypeScript 和 Manifest 一致。
|
|
9
|
+
- 保留稳定诊断编码、JSON 路径、期望值和可执行建议。
|
|
10
|
+
|
|
11
|
+
## 17B — Agent 自检
|
|
12
|
+
|
|
13
|
+
- 通过 `chart.explain()` 暴露实际生效的渲染选项和规范化结果。
|
|
14
|
+
- `planChart()` 遇到不支持的 Renderer 时直接报告风险,而不是只返回隐式 fallback。
|
|
15
|
+
- 在 `getCapabilities()` 和 `capabilities.json` 中发布 `agentReliability`。
|
|
16
|
+
- 保持导航、编辑和动效默认安全关闭。
|
|
17
|
+
|
|
18
|
+
## 17C — 包与集成可信度
|
|
19
|
+
|
|
20
|
+
- 增加 npm 包 dry-run 检查,确认 Runtime、TypeScript、Skill、recipes 和能力清单均存在。
|
|
21
|
+
- 禁止 `.trae`、`.github`、测试和 Playground 开发文件进入发布包。
|
|
22
|
+
- 将 TypeScript consumer fixture 与 ESM/headless 示例纳入发布检查。
|
|
23
|
+
|
|
24
|
+
## 17D — 渲染与无障碍验收
|
|
25
|
+
|
|
26
|
+
- 持续验证 SVG/Canvas 在标签、图表、导出和响应式布局上的一致性。
|
|
27
|
+
- 将浏览器验收与 headless 检查分开,并保留明确的本地服务证据。
|
|
28
|
+
- 在测试显式开启导航和编辑的同时,保持默认静态安全行为。
|
|
29
|
+
|
|
30
|
+
## 验收标准
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
检查数据
|
|
34
|
+
→ 规划图表
|
|
35
|
+
→ 校验 Spec
|
|
36
|
+
→ 创建图表
|
|
37
|
+
→ 检查生效配置和健康状态
|
|
38
|
+
→ 预览或导出
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- 无效或被忽略的配置都可诊断。
|
|
42
|
+
- Agent 无需阅读 Renderer 内部代码即可确认实际渲染结果。
|
|
43
|
+
- npm 包只包含支持用户使用的产物。
|
|
44
|
+
- `npm run agent:check`、浏览器验收和 `git diff --check` 通过。
|
|
45
|
+
|
|
46
|
+
## 不纳入本轮
|
|
47
|
+
|
|
48
|
+
- 新图表类型。
|
|
49
|
+
- Runtime 内置 NLP。
|
|
50
|
+
- CLI、MCP、HTTP、Python 或 1.x 兼容层。
|
|
@@ -20,7 +20,7 @@ Iteration 8 通过 `getChartCapability(type)` 提供逐图表能力档案,包
|
|
|
20
20
|
|
|
21
21
|
未知 intent 还会返回 `intentKnown`、`intentSuggestions` 和 `fallbackUsed`。Spec 的通道按图表类型约束:笛卡尔图表是 `x`/`y`,Pie/Funnel 是 `category`/`value`,Gauge 只使用 `value`,Heatmap 是 `x`/`y`/`color`,Radar 字段位于 `indicators`。`validateSpec()` 会拒绝不支持的通道和缺失字段。Gauge 必须显式声明 `domain`,超出范围时会报告 `VALUE_CLAMPED`;Pie 的负值报告 `NEGATIVE_VALUE_DROPPED`,没有正数占比时 `ZERO_TOTAL` 会使校验失败。
|
|
22
22
|
|
|
23
|
-
`validateSpec()` 分开返回 `errors`、`warnings` 和 `normalizations`;诊断包含稳定代码、JSON
|
|
23
|
+
`validateSpec()` 分开返回 `errors`、`warnings` 和 `normalizations`;诊断包含稳定代码、JSON 路径、期望值和修复建议。未知顶层选项会返回 `UNKNOWN_SPEC_OPTION`,不会静默当作有效配置。`chart.explain()` 返回编码、转换、交互、实际生效选项、规范化结果、假设、警告、稳定记录血缘和无障碍摘要。
|
|
24
24
|
|
|
25
25
|
## 关键规则
|
|
26
26
|
|
|
@@ -120,7 +120,7 @@ npx skills add wanghetommy/ichartjs --skill ichartjs --agent codex --global --ye
|
|
|
120
120
|
需要固定发布版本时,直接安装已发布的 Skill 目录:
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
|
-
npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
123
|
+
npx skills add https://github.com/wanghetommy/ichartjs/tree/v2.0.21/skills/ichartjs \
|
|
124
124
|
--agent codex --global --yes
|
|
125
125
|
```
|
|
126
126
|
|
|
@@ -282,8 +282,8 @@
|
|
|
282
282
|
"Short form `branding: false` and object form `branding: { enabled: false }` are both supported. Custom text, link, position, or font are intentionally NOT exposed in the current iteration."
|
|
283
283
|
]
|
|
284
284
|
},
|
|
285
|
-
"packageVersion": "2.0.
|
|
286
|
-
"runtimeVersion": "2.0.
|
|
285
|
+
"packageVersion": "2.0.21",
|
|
286
|
+
"runtimeVersion": "2.0.21",
|
|
287
287
|
"chartTypes": [
|
|
288
288
|
"line",
|
|
289
289
|
"area",
|
|
@@ -1438,5 +1438,32 @@
|
|
|
1438
1438
|
"chart.getState()",
|
|
1439
1439
|
"chart.getState().health"
|
|
1440
1440
|
]
|
|
1441
|
+
},
|
|
1442
|
+
"agentReliability": {
|
|
1443
|
+
"contract": {
|
|
1444
|
+
"validationBeforeRender": true,
|
|
1445
|
+
"normalizedSpecReturned": true,
|
|
1446
|
+
"unknownOptionsDiagnosed": true,
|
|
1447
|
+
"stableDiagnosticCodes": true
|
|
1448
|
+
},
|
|
1449
|
+
"selfCheck": [
|
|
1450
|
+
"chart.explain().effective",
|
|
1451
|
+
"chart.explain().normalizations",
|
|
1452
|
+
"chart.getState().health",
|
|
1453
|
+
"chart.getState().warnings"
|
|
1454
|
+
],
|
|
1455
|
+
"integration": {
|
|
1456
|
+
"esm": true,
|
|
1457
|
+
"typescript": true,
|
|
1458
|
+
"headless": true,
|
|
1459
|
+
"svg": true,
|
|
1460
|
+
"canvas": true,
|
|
1461
|
+
"packageTarball": true
|
|
1462
|
+
},
|
|
1463
|
+
"defaults": {
|
|
1464
|
+
"navigation": false,
|
|
1465
|
+
"editing": false,
|
|
1466
|
+
"motion": "auto"
|
|
1467
|
+
}
|
|
1441
1468
|
}
|
|
1442
1469
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taylorwong/ichartjs",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.21",
|
|
4
4
|
"description": "Agent-first, renderer-independent charting and project visualization runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
"deps:check": "node scripts/check-cycles.mjs",
|
|
44
44
|
"docs:examples": "node examples/agent-workflow.mjs",
|
|
45
45
|
"docs:check": "node scripts/check-agent-docs.mjs",
|
|
46
|
+
"package:check": "node scripts/check-package.mjs",
|
|
46
47
|
"example:agent": "node examples/agent-workflow.mjs",
|
|
47
48
|
"playground": "node scripts/serve-playground.mjs",
|
|
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",
|
|
49
|
+
"agent:check": "npm run contracts:check && npm run types:check && npm run deps:check && npm run docs:check && npm run package:check && npm run docs:examples && npm run check && npm test",
|
|
49
50
|
"rc:check": "npm test && npm run check && git diff --check"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
package/skills/ichartjs/SKILL.md
CHANGED
|
@@ -22,7 +22,7 @@ Recommended installation:
|
|
|
22
22
|
npx skills add wanghetommy/ichartjs --skill ichartjs
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.
|
|
25
|
+
Use `--agent codex --global --yes` for global non-interactive Codex installation. Use the tagged directory `https://github.com/wanghetommy/ichartjs/tree/v2.0.21/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.
|
|
26
26
|
|
|
27
27
|
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 npm:
|
|
28
28
|
|
package/src/capabilities.mjs
CHANGED
|
@@ -121,6 +121,11 @@ export function planChart(input, options = {}) {
|
|
|
121
121
|
const confidence = !report.rows || requiredFields.length ? 0.35 : warnings.some(item => item.code !== 'MISSING_VALUE') ? 0.65 : 0.9;
|
|
122
122
|
const styleRecommendation = planStyle({ type: primary, intent: requestedIntent, data: Array.isArray(input) ? input : input?.values || input, encoding: { x: report.dimensions[0] ? { field: report.dimensions[0] } : undefined, y: report.measures.map(field => ({ field })) } }, options);
|
|
123
123
|
warnings.push(...styleRecommendation.warnings);
|
|
124
|
+
const unsupportedRequests = options.renderer && options.renderer !== 'auto' && !profile.renderers.includes(options.renderer) ? [`renderer:${options.renderer}`] : [];
|
|
125
|
+
if (unsupportedRequests.length) {
|
|
126
|
+
warnings.push(warning('UNSUPPORTED_RENDERER', 'renderer', `${primary} does not support renderer ${options.renderer}.`, `Use one of: ${profile.renderers.join(', ')}.`));
|
|
127
|
+
nextActions.push(`Change renderer to ${profile.renderers[0]}.`);
|
|
128
|
+
}
|
|
124
129
|
return {
|
|
125
130
|
version: '1.0',
|
|
126
131
|
intent: requestedIntent,
|
|
@@ -135,7 +140,7 @@ export function planChart(input, options = {}) {
|
|
|
135
140
|
suggestedEncodings: { dimension: report.dimensions[0] || null, measure: report.measures[0] || null, secondaryMeasure: report.measures[1] || null },
|
|
136
141
|
assumptions: ['Field roles are inferred from provided values; business meaning and units are not inferred.'],
|
|
137
142
|
warnings,
|
|
138
|
-
unsupportedRequests
|
|
143
|
+
unsupportedRequests,
|
|
139
144
|
nextActions,
|
|
140
145
|
capability: getChartCapability(primary),
|
|
141
146
|
styleRecommendation,
|
|
@@ -157,7 +162,9 @@ export function explainChart(spec, model = {}) {
|
|
|
157
162
|
encodings,
|
|
158
163
|
transforms: spec.transform ? (Array.isArray(spec.transform) ? spec.transform : [spec.transform]).map(item => item.type) : [],
|
|
159
164
|
interactions: Object.keys(spec.interaction || {}).filter(key => spec.interaction[key]),
|
|
165
|
+
effective: { renderer: spec.renderer, title: spec.title, legend: spec.legend, grid: spec.grid, labels: spec.labels, interaction: spec.interaction, branding: spec.branding },
|
|
160
166
|
assumptions: [...(model.data?.assumptions || []), ...(model.state?.projectAnalytics?.assumptions || [])],
|
|
167
|
+
normalizations: model.normalizations || [],
|
|
161
168
|
warnings,
|
|
162
169
|
axes: model.state?.axes || null,
|
|
163
170
|
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),
|
|
@@ -237,6 +244,12 @@ export function getCapabilities() {
|
|
|
237
244
|
confirmation: ['deletion', 'structural-change', 'bulk-edit'],
|
|
238
245
|
selfCheck: ['chart.explain()', 'chart.getState()', 'chart.getState().health']
|
|
239
246
|
},
|
|
247
|
+
agentReliability: {
|
|
248
|
+
contract: { validationBeforeRender: true, normalizedSpecReturned: true, unknownOptionsDiagnosed: true, stableDiagnosticCodes: true },
|
|
249
|
+
selfCheck: ['chart.explain().effective', 'chart.explain().normalizations', 'chart.getState().health', 'chart.getState().warnings'],
|
|
250
|
+
integration: { esm: true, typescript: true, headless: true, svg: true, canvas: true, packageTarball: true },
|
|
251
|
+
defaults: { navigation: false, editing: false, motion: 'auto' }
|
|
252
|
+
},
|
|
240
253
|
preferences: {
|
|
241
254
|
version: '1.0',
|
|
242
255
|
scopes: preferenceCapabilities.scopes,
|
package/src/index.mjs
CHANGED
|
@@ -465,7 +465,7 @@ export class Chart {
|
|
|
465
465
|
setLinkedFilters(filters = {}) { this.spec.project = { ...(this.spec.project || {}), linked: { ...(this.spec.project?.linked || {}), filters: normalizeLinkedFilters(filters) } }; this.emit('linkedstatechange', { chart: this, linked: this.spec.project.linked }); return this.render(); }
|
|
466
466
|
setLinkedSelection(selection = []) { this.spec.project = { ...(this.spec.project || {}), linked: { ...(this.spec.project?.linked || {}), selection: normalizeLinkedSelection(selection) } }; this.emit('linkedstatechange', { chart: this, linked: this.spec.project.linked }); return this.render(); }
|
|
467
467
|
describe() { return { type: this.spec.type, renderer: this.renderer.constructor.name, dimensions: [this.spec.encoding.x?.field || this.spec.encoding.category?.field], measures: (Array.isArray(this.spec.encoding.y) ? this.spec.encoding.y : [this.spec.encoding.y || this.spec.encoding.value]).filter(Boolean).map(encoding => encoding.field), dataCount: this.model.data.rows.length, theme: this.spec.theme?.name || 'custom', interactions: Object.keys(this.spec.interaction || {}).filter(key => this.spec.interaction[key]) }; }
|
|
468
|
-
explain() { const explanation = explainChart(this.spec, this.model), state = this.getState(); return { ...explanation, layout: state.layout, timeAxis: state.timeAxis, warnings: state.warnings, health: state.health }; }
|
|
468
|
+
explain() { const explanation = explainChart(this.spec, { ...this.model, normalizations: this._specDiagnostics?.normalizations || [] }), state = this.getState(); return { ...explanation, layout: state.layout, timeAxis: state.timeAxis, warnings: state.warnings, health: state.health }; }
|
|
469
469
|
getAccessibleDescription() { const description = this.spec.accessibility?.description || this.spec.title?.text || `${this.spec.type} chart`; return `${description}; ${this.model.data.rows.length} data items.`; }
|
|
470
470
|
inspectDataSchema() { return inspectDataSchema(this.spec.data.schema || this.spec.schema); }
|
|
471
471
|
validateData() { return validateData(this.toDataTable(), this.spec.data.schema || this.spec.schema, this.spec.validationOptions); }
|
|
@@ -775,4 +775,4 @@ export { normalizeLinkedFilters, normalizeLinkedSelection, filterProjectRows, cr
|
|
|
775
775
|
|
|
776
776
|
export { contrastRatio, planStyle, resolveTheme, styleCapabilities, themeModes, themePalettes, themePresets, validateThemeContrast, annotationPlugin, dataZoomPlugin, dataLabelsPlugin, accessibilityPlugin };
|
|
777
777
|
export { applyPreferencesToSpec, createPreferencesStore, defaultPreferences, mergePreferences, mergeThemePreference, mountChartSettings, normalizePreferences, validatePreferences };
|
|
778
|
-
export const iChart = { version: '2.0.
|
|
778
|
+
export const iChart = { version: '2.0.21', createChart, ChartValidationError, inspectData, normalizeData, binData, applyTransforms, data, getCapabilities, getChartCapability, getPreferenceCapabilities, planChart, recommend, explainChart, contrastRatio, planStyle, resolveTheme, styleCapabilities, themeModes, themePalettes, themePresets, validateThemeContrast, createPreferencesStore, defaultPreferences, normalizePreferences, mergePreferences, validatePreferences, applyPreferencesToSpec, mountChartSettings, annotationPlugin, dataZoomPlugin, dataLabelsPlugin, accessibilityPlugin, getBusinessSchema, inspectDataSchema, validateData, getEditCapabilities, validateEdit, previewEdit, commitPreview, validateRecipe, normalizeProjectCalendar, applyWorkingCalendar, normalizeDependencies, analyzeSchedule, analyzeBurndownSeries, analyzeCapacity, buildCapacityView, buildCumulativeFlowSeries, buildVelocitySeries, buildReleaseForecast, buildRiskMatrixSeries, buildIssueAgingSeries, normalizeLinkedFilters, normalizeLinkedSelection, filterProjectRows, createLinkedProjectState, linkedRecordId };
|
package/src/spec.mjs
CHANGED
|
@@ -118,6 +118,7 @@ const presentationEncodingKeys = new Set(['title', 'format', 'labels', 'legend']
|
|
|
118
118
|
const diagramTypes = new Set(['flow', 'swimlane', 'architecture', 'mindmap']);
|
|
119
119
|
const diagramFields = ['nodes', 'edges', 'lanes', 'groups', 'layers', 'boundaries'];
|
|
120
120
|
const nonCartesianAnalysisTypes = new Set(['pie', 'funnel', 'gauge', 'heatmap', 'radar']);
|
|
121
|
+
const knownSpecKeys = new Set(['version', 'type', 'renderer', 'container', 'chartId', 'width', 'height', 'padding', 'colors', 'background', 'locale', 'data', 'encoding', 'title', 'legend', 'grid', 'labels', 'xAxis', 'yAxis', 'domain', 'colorScale', 'indicators', 'innerRadius', 'stack', 'transform', 'criticalPath', 'nodes', 'edges', 'lanes', 'layers', 'boundaries', 'groups', 'diagram', 'project', 'interaction', 'editing', 'accessibility', 'branding', 'theme', 'preferences', 'preferencesStore', 'plugins', 'schema', 'validationOptions', 'emptyText', 'responsive', 'view', 'context', 'intent', 'tasks', 'events']);
|
|
121
122
|
|
|
122
123
|
function finiteDomain(domain) {
|
|
123
124
|
return Array.isArray(domain) && domain.length === 2 && domain.every(value => Number.isFinite(Number(value))) && Number(domain[1]) > Number(domain[0]);
|
|
@@ -179,11 +180,12 @@ export function normalizeSpec(input = {}) {
|
|
|
179
180
|
export function validateSpec(input = {}) {
|
|
180
181
|
const spec = normalizeSpec(input);
|
|
181
182
|
const errors = [], warnings = [], normalizations = [];
|
|
183
|
+
Object.keys(input || {}).filter(key => !knownSpecKeys.has(key)).forEach(key => warnings.push({ code: 'UNKNOWN_SPEC_OPTION', path: key, message: `Top-level option ${key} is not part of the iChart.js Spec contract and will be ignored.`, expected: [...knownSpecKeys].sort(), suggestion: 'Remove the option or place host metadata outside the chart Spec.' }));
|
|
182
184
|
if (typeof input.title === 'string') {
|
|
183
|
-
normalizations.push({ path: 'title', from: 'string', to: 'title.text' });
|
|
185
|
+
normalizations.push({ code: 'NORMALIZED_TITLE', path: 'title', from: 'string', to: 'title.text' });
|
|
184
186
|
warnings.push({ code: 'NORMALIZED_TITLE', path: 'title', message: 'String titles are normalized to title.text.', suggestion: 'Prefer title: { text: "..." } for an explicit title contract.' });
|
|
185
187
|
} else if (input.title && typeof input.title === 'object' && !Array.isArray(input.title) && input.title.text === undefined && typeof input.title.label === 'string') {
|
|
186
|
-
normalizations.push({ path: 'title.label', from: 'title.label', to: 'title.text' });
|
|
188
|
+
normalizations.push({ code: 'NORMALIZED_TITLE', path: 'title.label', from: 'title.label', to: 'title.text' });
|
|
187
189
|
warnings.push({ code: 'NORMALIZED_TITLE', path: 'title.label', message: 'title.label is normalized to title.text.', suggestion: 'Use title: { text: "..." }.' });
|
|
188
190
|
} else if (input.title !== undefined && (!input.title || typeof input.title !== 'object' || Array.isArray(input.title) || (input.title.text === undefined && input.title.subtitle === undefined))) {
|
|
189
191
|
warnings.push({ code: 'INVALID_TITLE', path: 'title', message: 'Title must be a string or an object with text and/or subtitle.', suggestion: 'Use title: { text: "...", subtitle: "..." }.' });
|
package/src/validation.mjs
CHANGED
|
@@ -33,7 +33,7 @@ export function validateData(input, schema, options = {}) {
|
|
|
33
33
|
if (!Object.hasOwn(object, name)) {
|
|
34
34
|
if (Object.hasOwn(field, 'default')) {
|
|
35
35
|
object[name] = copyJSON(field.default);
|
|
36
|
-
normalizations.push({ path, beforePresent: false, after: copyJSON(object[name]), reason: 'default' });
|
|
36
|
+
normalizations.push({ code: 'DEFAULT_VALUE', path, beforePresent: false, after: copyJSON(object[name]), reason: 'default' });
|
|
37
37
|
} else { if (field.required) errors.push(issue('REQUIRED', path, 'Required field is missing.')); return; }
|
|
38
38
|
}
|
|
39
39
|
const value = object[name];
|
|
@@ -67,7 +67,7 @@ export function validateData(input, schema, options = {}) {
|
|
|
67
67
|
if (!isRecord(row)) { errors.push(issue('INVALID_RECORD', path, 'Each row must be an object.')); return; }
|
|
68
68
|
if (options.progressUnit === 'ratio' && schema.name === 'project-task' && Object.hasOwn(row, 'progress')) {
|
|
69
69
|
if (typeof row.progress !== 'number' || row.progress < 0 || row.progress > 1) errors.push(issue('PROGRESS_UNIT', `${path}.progress`, 'Ratio progress must be between 0 and 1.'));
|
|
70
|
-
else { const before = row.progress; row.progress *= 100; normalizations.push({ path: `${path}.progress`, beforePresent: true, before, after: row.progress, reason: 'ratio-to-percent' }); }
|
|
70
|
+
else { const before = row.progress; row.progress *= 100; normalizations.push({ code: 'PROGRESS_UNIT_CONVERSION', path: `${path}.progress`, beforePresent: true, before, after: row.progress, reason: 'ratio-to-percent' }); }
|
|
71
71
|
}
|
|
72
72
|
Object.keys(row).filter(key => !Object.hasOwn(schema.fields, key)).forEach(key => errors.push(issue('UNKNOWN_FIELD', `${path}.${key}`, 'Field is not declared in the business schema.')));
|
|
73
73
|
Object.entries(schema.fields).forEach(([name, field]) => normalizeField(row, name, field, `${path}.${name}`));
|
package/types/index.d.ts
CHANGED
|
@@ -27,12 +27,12 @@ export class ChartValidationError extends Error { name: 'ChartValidationError';
|
|
|
27
27
|
export interface DataFieldInfo { name: string; type: 'quantitative' | 'temporal' | 'category' | 'unknown'; role: 'identifier' | 'measure' | 'temporal-dimension' | 'dimension'; unit: string | null; cardinality: number; validCount: number; nullCount: number; min?: number; max?: number; temporalMin?: string; temporalMax?: string; }
|
|
28
28
|
export interface DataInspection { version: '1.0'; rows: number; fields: DataFieldInfo[]; dimensions: string[]; measures: string[]; temporalFields: string[]; missingValueCount: number; warnings: Diagnostic[]; }
|
|
29
29
|
export interface ChartCapability { type: ChartType; family: string; intents: string[]; required: string[]; optional: string[]; dataShapes: string[]; interactions: string[]; features: Record<string, 'supported' | 'not-applicable' | 'degraded'>; renderers: Array<'canvas' | 'svg'>; exports: string[]; limits: Record<string, number>; }
|
|
30
|
-
export interface RuntimeCapabilities { version: '2.0'; contractVersion: '1.1'; chartTypes: ChartType[]; charts: Record<ChartType, ChartCapability>; intents: string[]; locale?: { default: string; recommended: string[]; appliesTo: string[]; inputDates: string }; renderers: Array<'canvas' | 'svg'>; interactions: string[]; exports: Array<'png' | 'svg' | 'json' | 'jpeg'>; chartModes?: Record<string, unknown>; conversationalWorkflow?: { version: '1.0'; steps: string[]; routing: Record<string, string>; confirmation: string[]; selfCheck: string[] }; diagram?: { flowNodeKinds?: FlowNodeKind[]; flowBranchEdges?: { label: string; minimumOutgoing: number }; flowLoops?: string; flowConnectors?: { kind: FlowNodeKind; linking: string }; [key: string]: unknown }; styleSystem: { modes: ThemeMode[]; presets: ThemePreset[]; palettes: ThemePalette[]; switchable: boolean; automatic: boolean; [key: string]: unknown }; preferences?: { version: '1.0'; scopes: string[]; persistence: string[]; fields: string[]; agentAdjustable: boolean; interactiveSettingsUI: boolean; precedence: string[]; schema: PreferenceCapabilities }; headless: { preview?: boolean; json: boolean; svg: boolean; png: boolean | string }; export: { types: string[]; mime: Record<string, string>; browser: Record<string, boolean>; headless: Record<string, string | boolean>; methods: string[]; options: Record<string, unknown>; branding: Record<string, unknown> }; branding: { defaultEnabled: boolean; signature: string; options: Record<string, unknown> }; [key: string]: unknown; }
|
|
30
|
+
export interface RuntimeCapabilities { version: '2.0'; contractVersion: '1.1'; chartTypes: ChartType[]; charts: Record<ChartType, ChartCapability>; intents: string[]; locale?: { default: string; recommended: string[]; appliesTo: string[]; inputDates: string }; renderers: Array<'canvas' | 'svg'>; interactions: string[]; exports: Array<'png' | 'svg' | 'json' | 'jpeg'>; chartModes?: Record<string, unknown>; conversationalWorkflow?: { version: '1.0'; steps: string[]; routing: Record<string, string>; confirmation: string[]; selfCheck: string[] }; agentReliability?: Record<string, unknown>; diagram?: { flowNodeKinds?: FlowNodeKind[]; flowBranchEdges?: { label: string; minimumOutgoing: number }; flowLoops?: string; flowConnectors?: { kind: FlowNodeKind; linking: string }; [key: string]: unknown }; styleSystem: { modes: ThemeMode[]; presets: ThemePreset[]; palettes: ThemePalette[]; switchable: boolean; automatic: boolean; [key: string]: unknown }; preferences?: { version: '1.0'; scopes: string[]; persistence: string[]; fields: string[]; agentAdjustable: boolean; interactiveSettingsUI: boolean; precedence: string[]; schema: PreferenceCapabilities }; headless: { preview?: boolean; json: boolean; svg: boolean; png: boolean | string }; export: { types: string[]; mime: Record<string, string>; browser: Record<string, boolean>; headless: Record<string, string | boolean>; methods: string[]; options: Record<string, unknown>; branding: Record<string, unknown> }; branding: { defaultEnabled: boolean; signature: string; options: Record<string, unknown> }; [key: string]: unknown; }
|
|
31
31
|
export interface ChartPlan { version: '1.0'; intent: string; intentKnown?: boolean; intentSuggestions?: string[]; fallbackUsed?: boolean; primary: ChartType; alternatives: ChartType[]; confidence: number; reasons: string[]; requiredFields: string[]; suggestedEncodings: { dimension: string | null; measure: string | null; secondaryMeasure: string | null }; assumptions: string[]; warnings: Diagnostic[]; unsupportedRequests: string[]; nextActions: string[]; capability: ChartCapability; styleRecommendation: StyleRecommendation; data: DataInspection; }
|
|
32
32
|
export interface AxisState { rawDomain: [number, number]; domain: [number, number]; ticks: number[]; step: number | null; policy: 'nice' | 'raw' | 'explicit'; }
|
|
33
33
|
export interface TimeAxisState { orientation: 'horizontal'; field: 'date'; coordinate: 'x'; rowCoordinate: 'y'; domain: [number, number]; domainISO: [string, string]; }
|
|
34
34
|
export interface ChartHealth { version: '1.0'; status: 'ready' | 'degraded' | 'empty'; renderable: boolean; issues: string[]; metrics: { warnings: number; suppressedLabels: number; clampedValues: number; renderedMarks: number }; }
|
|
35
|
-
export interface ChartExplanation { version: '1.0'; type: ChartType; family: string; purpose: string; renderer: Renderer; dataCount: number; encodings: Record<string, string | string[]>; transforms: string[]; interactions: string[]; assumptions: string[]; warnings: Diagnostic[]; axes?: { y?: AxisState; right?: AxisState } | null; timeAxis?: TimeAxisState | null; health?: ChartHealth; style: Partial<StyleRecommendation> & { name?: string }; lineage: { recordIds: string[]; sourcePreserved: boolean }; accessibility: { enabled: boolean; summary: string }; }
|
|
35
|
+
export interface ChartExplanation { version: '1.0'; type: ChartType; family: string; purpose: string; renderer: Renderer; dataCount: number; encodings: Record<string, string | string[]>; transforms: string[]; interactions: string[]; effective?: { renderer?: Renderer; title?: ChartTitle; legend?: ChartLegend; grid?: Record<string, unknown>; labels?: ChartLabels; interaction?: ChartInteraction; branding?: ChartSpec['branding'] }; assumptions: string[]; normalizations?: Diagnostic[]; warnings: Diagnostic[]; axes?: { y?: AxisState; right?: AxisState } | null; timeAxis?: TimeAxisState | null; health?: ChartHealth; style: Partial<StyleRecommendation> & { name?: string }; lineage: { recordIds: string[]; sourcePreserved: boolean }; accessibility: { enabled: boolean; summary: string }; }
|
|
36
36
|
export interface ChartInteraction { tooltip?: boolean; hover?: boolean; click?: boolean; crosshair?: boolean; zoom?: boolean; pan?: boolean; brush?: boolean; drag?: boolean; edgeDrag?: boolean; portConnect?: boolean; keyboard?: boolean; [key: string]: boolean | undefined; }
|
|
37
37
|
export interface ChartEditing { enabled?: boolean; mode?: 'command' | string; requireConfirmation?: boolean; allowDelete?: boolean; allowStructuralChanges?: boolean; }
|
|
38
38
|
export interface ChartTitle { text?: string; subtitle?: string; label?: string; }
|