@jorgerdz/timeview 0.1.0

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 (90) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +263 -0
  4. package/dist/cli/timeview.js +6710 -0
  5. package/dist/timeview.cjs +1 -0
  6. package/dist/timeview.js +5667 -0
  7. package/dist/tokens.css +67 -0
  8. package/dist/types/timeview/BandedTimeline.d.ts +11 -0
  9. package/dist/types/timeview/BandedTimeline.d.ts.map +1 -0
  10. package/dist/types/timeview/DensityHeatmap.d.ts +11 -0
  11. package/dist/types/timeview/DensityHeatmap.d.ts.map +1 -0
  12. package/dist/types/timeview/LaneCalendar.d.ts +11 -0
  13. package/dist/types/timeview/LaneCalendar.d.ts.map +1 -0
  14. package/dist/types/timeview/MetricTimeline.d.ts +8 -0
  15. package/dist/types/timeview/MetricTimeline.d.ts.map +1 -0
  16. package/dist/types/timeview/SpanMatrix.d.ts +8 -0
  17. package/dist/types/timeview/SpanMatrix.d.ts.map +1 -0
  18. package/dist/types/timeview/config.d.ts +22 -0
  19. package/dist/types/timeview/config.d.ts.map +1 -0
  20. package/dist/types/timeview/core/aggregate.d.ts +113 -0
  21. package/dist/types/timeview/core/aggregate.d.ts.map +1 -0
  22. package/dist/types/timeview/core/calendar.d.ts +27 -0
  23. package/dist/types/timeview/core/calendar.d.ts.map +1 -0
  24. package/dist/types/timeview/core/intervals.d.ts +8 -0
  25. package/dist/types/timeview/core/intervals.d.ts.map +1 -0
  26. package/dist/types/timeview/core/labels.d.ts +5 -0
  27. package/dist/types/timeview/core/labels.d.ts.map +1 -0
  28. package/dist/types/timeview/core/metric.d.ts +58 -0
  29. package/dist/types/timeview/core/metric.d.ts.map +1 -0
  30. package/dist/types/timeview/core/time.d.ts +22 -0
  31. package/dist/types/timeview/core/time.d.ts.map +1 -0
  32. package/dist/types/timeview/dashboard.d.ts +17 -0
  33. package/dist/types/timeview/dashboard.d.ts.map +1 -0
  34. package/dist/types/timeview/data.d.ts +21 -0
  35. package/dist/types/timeview/data.d.ts.map +1 -0
  36. package/dist/types/timeview/export.d.ts +14 -0
  37. package/dist/types/timeview/export.d.ts.map +1 -0
  38. package/dist/types/timeview/index.d.ts +28 -0
  39. package/dist/types/timeview/index.d.ts.map +1 -0
  40. package/dist/types/timeview/registry.d.ts +285 -0
  41. package/dist/types/timeview/registry.d.ts.map +1 -0
  42. package/dist/types/timeview/shared/Caption.d.ts +9 -0
  43. package/dist/types/timeview/shared/Caption.d.ts.map +1 -0
  44. package/dist/types/timeview/shared/EmptyState.d.ts +16 -0
  45. package/dist/types/timeview/shared/EmptyState.d.ts.map +1 -0
  46. package/dist/types/timeview/shared/Legend.d.ts +10 -0
  47. package/dist/types/timeview/shared/Legend.d.ts.map +1 -0
  48. package/dist/types/timeview/shared/Tooltip.d.ts +15 -0
  49. package/dist/types/timeview/shared/Tooltip.d.ts.map +1 -0
  50. package/dist/types/timeview/shared/useMeasuredWidth.d.ts +2 -0
  51. package/dist/types/timeview/shared/useMeasuredWidth.d.ts.map +1 -0
  52. package/dist/types/timeview/types.d.ts +158 -0
  53. package/dist/types/timeview/types.d.ts.map +1 -0
  54. package/docs/AGENT-USAGE.md +93 -0
  55. package/docs/COMPATIBILITY.md +134 -0
  56. package/docs/STUDIO.md +41 -0
  57. package/examples/README.md +21 -0
  58. package/examples/configs/bandedTimeline.json +31 -0
  59. package/examples/configs/densityHeatmap.json +33 -0
  60. package/examples/configs/laneCalendar.json +31 -0
  61. package/examples/configs/metricTimeline.json +51 -0
  62. package/examples/configs/spanMatrix.json +31 -0
  63. package/package.json +94 -0
  64. package/render.html +12 -0
  65. package/src/render.tsx +67 -0
  66. package/src/styles/tokens.css +67 -0
  67. package/src/timeview/BandedTimeline.tsx +620 -0
  68. package/src/timeview/DensityHeatmap.tsx +513 -0
  69. package/src/timeview/LaneCalendar.tsx +496 -0
  70. package/src/timeview/MetricTimeline.tsx +993 -0
  71. package/src/timeview/SpanMatrix.tsx +721 -0
  72. package/src/timeview/config.ts +399 -0
  73. package/src/timeview/core/aggregate.ts +317 -0
  74. package/src/timeview/core/calendar.ts +81 -0
  75. package/src/timeview/core/intervals.ts +52 -0
  76. package/src/timeview/core/labels.ts +19 -0
  77. package/src/timeview/core/metric.ts +263 -0
  78. package/src/timeview/core/time.ts +103 -0
  79. package/src/timeview/dashboard.ts +80 -0
  80. package/src/timeview/data.ts +242 -0
  81. package/src/timeview/export.ts +48 -0
  82. package/src/timeview/index.ts +106 -0
  83. package/src/timeview/registry.ts +207 -0
  84. package/src/timeview/shared/Caption.tsx +40 -0
  85. package/src/timeview/shared/EmptyState.tsx +90 -0
  86. package/src/timeview/shared/Legend.tsx +67 -0
  87. package/src/timeview/shared/Tooltip.tsx +59 -0
  88. package/src/timeview/shared/useMeasuredWidth.ts +21 -0
  89. package/src/timeview/types.ts +159 -0
  90. package/vite.config.ts +11 -0
@@ -0,0 +1,134 @@
1
+ # Timeview Compatibility And Versioning
2
+
3
+ Timeview has three versioned surfaces that can change at different speeds:
4
+
5
+ - The npm package version in `package.json`.
6
+ - The dataset schema version, currently `timeview.dataset.v1`.
7
+ - The hosted/local config version, currently `TimeviewConfigV1` with `v: 1`.
8
+
9
+ Until the package is published, the source tree is still pre-1.0. Treat local changes as
10
+ eligible for cleanup, but keep saved configs and agent output compatible whenever practical.
11
+ After the first published package, Timeview should follow semver for the package and keep
12
+ schema/config migrations explicit.
13
+
14
+ ## Stable Surfaces
15
+
16
+ These are compatibility commitments for the current V1 runtime:
17
+
18
+ - `TimeDataset.schemaVersion` remains `"timeview.dataset.v1"` for event, interval, label,
19
+ and optional metric-series datasets accepted by `validateTimeDataset`.
20
+ - `TimeviewConfigV1.v` remains `1` for URL hash configs, local render configs, and
21
+ dashboard panel configs shaped as `{ visualizer, dataset, spec, palette }`.
22
+ - Future stored short links must resolve to the same versioned config contract as URL hash
23
+ links. The storage id is transport metadata, not a replacement config shape.
24
+ - Visualizer IDs are stable string values: `bandedTimeline`, `laneCalendar`,
25
+ `densityHeatmap`, `metricTimeline`, and `spanMatrix`.
26
+ - A `spec.kind` value must match the selected visualizer. Missing supported fields are
27
+ normalized from registry defaults.
28
+ - Unknown top-level config fields are not part of the public contract. Callers should not
29
+ depend on them being preserved by normalization.
30
+
31
+ The public TypeScript exports in `src/timeview/index.ts` are the intended library surface.
32
+ Internal component helpers and demo files may change unless they are exported there.
33
+
34
+ ## Non-Breaking Changes
35
+
36
+ The following changes can land without a dataset or config version bump:
37
+
38
+ - Adding a new optional field to `TimeDataset`, a visualizer spec, or a dashboard panel.
39
+ - Adding a new visualizer ID, as long as existing IDs keep their behavior.
40
+ - Adding a new registry metadata field for docs, studio controls, or agent selection.
41
+ - Expanding validation to accept a previously undocumented but compatible value.
42
+ - Improving rendering, responsive behavior, accessibility, or export determinism without
43
+ changing accepted input meaning.
44
+ - Adding package exports while keeping existing exports available.
45
+
46
+ Optional fields must have safe defaults in `normalizeViewSpec`, the registry, or the
47
+ renderer path before docs or examples depend on them.
48
+
49
+ ## Breaking Changes
50
+
51
+ These changes require a versioned migration plan:
52
+
53
+ - Renaming or removing a required `TimeDataset` field.
54
+ - Changing `TimeDataset.schemaVersion` semantics while keeping the same string.
55
+ - Renaming or removing a visualizer ID.
56
+ - Changing the meaning of an existing `ViewSpec` field.
57
+ - Changing `TimeviewConfigV1.v` shape or requiring fields that older saved configs do not
58
+ have.
59
+ - Removing an exported component, type, helper, or CLI command after publication.
60
+ - Making deterministic exports depend on browser-local state unless that state is explicit
61
+ in the config.
62
+
63
+ Prefer additive schemas over breaking replacements. If a replacement is unavoidable, add a
64
+ new schema or config version and keep a decoder/migration path for old saved links and agent
65
+ artifacts.
66
+
67
+ ## Version Bump Rules
68
+
69
+ Before the first published package:
70
+
71
+ - Patch changes can update docs, fixtures, rendering polish, and validation messages.
72
+ - Minor changes can add visualizers, optional fields, CLI capabilities, and export formats.
73
+ - Breaking cleanup is allowed, but update docs and examples in the same change.
74
+
75
+ After publication:
76
+
77
+ - Patch: bug fixes, docs, visual polish, validation-message improvements, and compatible
78
+ export fixes.
79
+ - Minor: additive public API, optional schema/spec fields, new visualizers, and new CLI
80
+ commands.
81
+ - Major: breaking public API, schema, config, visualizer ID, or deterministic export
82
+ behavior changes.
83
+
84
+ ## Migration Requirements
85
+
86
+ When introducing `timeview.dataset.v2` or `TimeviewConfigV2`:
87
+
88
+ 1. Keep the V1 decoder available until a documented removal release.
89
+ 2. Add a migration helper that returns the newest supported shape.
90
+ 3. Update `npm run timeview -- describe --json` so agents can discover supported versions.
91
+ 4. Document changed fields with before/after JSON examples.
92
+ 5. Keep old URL hash links readable if the local code can infer an equivalent V2 config.
93
+
94
+ Migrations should be deterministic and side-effect free. They should not depend on current
95
+ date, browser timezone, network state, or hosted storage.
96
+
97
+ ## Agent And Export Compatibility
98
+
99
+ Agent-generated charts and saved exports should be reproducible:
100
+
101
+ - Use explicit dates for `today` fields, or use `null`/omission to disable live markers.
102
+ - Use `today: "auto"` only in interactive browser configs; the CLI freezes it during
103
+ export.
104
+ - Keep palette arrays in config. Palette order maps to `dataset.labels` by index.
105
+ - Include complete datasets in URL/config artifacts rather than relying on fixture names.
106
+ - Freeze navigable viewport state before static export when a visualizer supports pan/zoom.
107
+
108
+ The CLI `describe --json` output is the machine-readable capability guide for agents. Update
109
+ it with any new schema, config, visualizer, or export capability before documenting that
110
+ capability for agent workflows.
111
+
112
+ ## Stored Link Compatibility
113
+
114
+ Short-link/backend persistence is future hosted work, documented in `docs/PERSISTENCE.md`.
115
+ When it lands, stored links should remain additive to the URL hash flow:
116
+
117
+ - Keep `#tv=<token>` links decodable without network access.
118
+ - Store normalized `TimeviewConfigV1` payloads, not fixture names or partial studio state.
119
+ - Revalidate fetched configs before rendering.
120
+ - Treat the opaque id and timestamps as persistence metadata outside the public chart
121
+ config.
122
+ - Require a migration plan before writing or serving a newer config version.
123
+ - Fall back to encoded hash links when the persistence service is unavailable.
124
+
125
+ ## Release Checklist
126
+
127
+ Before tagging or publishing a release:
128
+
129
+ 1. Run `npm run typecheck`.
130
+ 2. Run `npm run build`.
131
+ 3. Validate at least one config per registered visualizer with `npm run timeview -- validate`.
132
+ 4. Render a deterministic PNG or HTML export for a representative config.
133
+ 5. Update `README.md`, `docs/ROADMAP.md`, and any agent docs that mention changed surfaces.
134
+ 6. Note whether the release is patch, minor, or major by the rules above.
package/docs/STUDIO.md ADDED
@@ -0,0 +1,41 @@
1
+ # Timeview Studio
2
+
3
+ The studio is the hosted/demo surface for Timeview. It is separate from the React package
4
+ API and the CLI contract, but it uses the same `TimeviewConfigV1` config shape.
5
+
6
+ ## Local Development
7
+
8
+ ```bash
9
+ npm install
10
+ npm run dev
11
+ ```
12
+
13
+ The dev page lets users inspect visualizers, edit dataset/spec JSON, copy configs, copy
14
+ React snippets, export PNG/HTML, and create shareable hash links.
15
+
16
+ ## Production Build
17
+
18
+ ```bash
19
+ npm run build
20
+ ```
21
+
22
+ The studio build is written to `dist/studio`. The library bundle, CLI bundle, CSS tokens,
23
+ and declaration files live at the top of `dist` and are the package artifacts.
24
+
25
+ ## Deployment
26
+
27
+ Any static host can serve `dist/studio`:
28
+
29
+ ```bash
30
+ npx vite preview --outDir dist/studio
31
+ ```
32
+
33
+ Recommended production settings:
34
+
35
+ - Serve `index.html` for unknown routes if route-based studio pages are added later.
36
+ - Keep encoded `#tv=<token>` hash links intact; the hash is read client-side.
37
+ - Do not require a backend for encoded hash links.
38
+ - If short-link persistence is added later, store normalized `TimeviewConfigV1` payloads
39
+ and keep encoded hash links as the offline fallback.
40
+
41
+ See `docs/PERSISTENCE.md` for the future short-link storage contract.
@@ -0,0 +1,21 @@
1
+ # Timeview Examples
2
+
3
+ `examples/configs` contains one complete `TimeviewConfigV1` file per registered visualizer.
4
+ Each file is intentionally self-contained: it includes the dataset, matching view spec, and
5
+ palette needed by the React components, CLI renderer, hosted links, or agents.
6
+
7
+ Validate an example:
8
+
9
+ ```bash
10
+ npm run timeview -- validate examples/configs/bandedTimeline.json
11
+ ```
12
+
13
+ Render an example:
14
+
15
+ ```bash
16
+ npm run timeview -- render examples/configs/metricTimeline.json \
17
+ --format html \
18
+ --preset 1600x900 \
19
+ --out .context/metricTimeline.html \
20
+ --today 2026-10-29
21
+ ```
@@ -0,0 +1,31 @@
1
+ {
2
+ "v": 1,
3
+ "visualizer": "bandedTimeline",
4
+ "dataset": {
5
+ "schemaVersion": "timeview.dataset.v1",
6
+ "timezone": "UTC",
7
+ "meta": { "title": "Launch Plan" },
8
+ "labels": [
9
+ { "id": "design", "name": "Design" },
10
+ { "id": "build", "name": "Build" },
11
+ { "id": "launch", "name": "Launch" }
12
+ ],
13
+ "events": [
14
+ { "id": "kickoff", "title": "Kickoff", "at": "2026-06-01", "labelIds": ["design"] },
15
+ { "id": "ship", "title": "Ship", "at": "2026-06-28", "labelIds": ["launch"] }
16
+ ],
17
+ "intervals": [
18
+ { "id": "research", "title": "Research", "range": { "start": "2026-06-01", "end": "2026-06-08" }, "labelIds": ["design"] },
19
+ { "id": "implementation", "title": "Implementation", "range": { "start": "2026-06-06", "end": "2026-06-22" }, "labelIds": ["build"] },
20
+ { "id": "release", "title": "Release prep", "range": { "start": "2026-06-20", "end": "2026-06-28" }, "labelIds": ["launch"] }
21
+ ]
22
+ },
23
+ "spec": {
24
+ "kind": "bandedTimeline",
25
+ "title": "Launch Plan",
26
+ "overlapMode": "lanes",
27
+ "legend": { "position": "bottom" },
28
+ "events": { "showLabels": true }
29
+ },
30
+ "palette": ["#2563eb", "#16a34a", "#dc2626"]
31
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "v": 1,
3
+ "visualizer": "densityHeatmap",
4
+ "dataset": {
5
+ "schemaVersion": "timeview.dataset.v1",
6
+ "timezone": "UTC",
7
+ "meta": { "title": "Support Load" },
8
+ "labels": [
9
+ { "id": "billing", "name": "Billing" },
10
+ { "id": "product", "name": "Product" },
11
+ { "id": "incident", "name": "Incident" }
12
+ ],
13
+ "events": [
14
+ { "id": "incident-1", "title": "Incident opened", "at": "2026-08-04", "labelIds": ["incident"] },
15
+ { "id": "billing-1", "title": "Billing spike", "at": "2026-08-06", "labelIds": ["billing"] }
16
+ ],
17
+ "intervals": [
18
+ { "id": "billing-window", "title": "Billing queue", "range": { "start": "2026-08-01", "end": "2026-08-05" }, "labelIds": ["billing"] },
19
+ { "id": "product-window", "title": "Product queue", "range": { "start": "2026-08-03", "end": "2026-08-09" }, "labelIds": ["product"] },
20
+ { "id": "incident-window", "title": "Incident response", "range": { "start": "2026-08-04", "end": "2026-08-07" }, "labelIds": ["incident"] }
21
+ ]
22
+ },
23
+ "spec": {
24
+ "kind": "densityHeatmap",
25
+ "title": "Support Load",
26
+ "bucket": "day",
27
+ "measure": "duration",
28
+ "groupBy": "category",
29
+ "scaleMode": "category",
30
+ "showValues": true
31
+ },
32
+ "palette": ["#2563eb", "#16a34a", "#dc2626"]
33
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "v": 1,
3
+ "visualizer": "laneCalendar",
4
+ "dataset": {
5
+ "schemaVersion": "timeview.dataset.v1",
6
+ "timezone": "UTC",
7
+ "meta": { "title": "Editorial Calendar" },
8
+ "labels": [
9
+ { "id": "draft", "name": "Draft" },
10
+ { "id": "review", "name": "Review" },
11
+ { "id": "publish", "name": "Publish" }
12
+ ],
13
+ "events": [
14
+ { "id": "newsletter", "title": "Newsletter", "at": "2026-07-10", "labelIds": ["publish"] }
15
+ ],
16
+ "intervals": [
17
+ { "id": "article", "title": "Article draft", "range": { "start": "2026-07-01", "end": "2026-07-04" }, "labelIds": ["draft"] },
18
+ { "id": "review-window", "title": "Review window", "range": { "start": "2026-07-06", "end": "2026-07-09" }, "labelIds": ["review"] },
19
+ { "id": "launch-week", "title": "Launch week", "range": { "start": "2026-07-10", "end": "2026-07-13" }, "labelIds": ["publish"] }
20
+ ]
21
+ },
22
+ "spec": {
23
+ "kind": "laneCalendar",
24
+ "title": "Editorial Calendar",
25
+ "laneMode": "packed",
26
+ "today": "2026-07-08",
27
+ "legend": { "position": "bottom" },
28
+ "events": { "showLabels": true }
29
+ },
30
+ "palette": ["#2563eb", "#f59e0b", "#16a34a"]
31
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "v": 1,
3
+ "visualizer": "metricTimeline",
4
+ "dataset": {
5
+ "schemaVersion": "timeview.dataset.v1",
6
+ "timezone": "UTC",
7
+ "meta": { "title": "Weekly Weight Trend" },
8
+ "labels": [
9
+ { "id": "deficit", "name": "Deficit" },
10
+ { "id": "maintenance", "name": "Maintenance" },
11
+ { "id": "travel", "name": "Travel" }
12
+ ],
13
+ "events": [
14
+ { "id": "checkin", "title": "Coach check-in", "at": "2026-10-15", "labelIds": ["maintenance"] }
15
+ ],
16
+ "intervals": [
17
+ { "id": "deficit-1", "title": "Calorie deficit", "range": { "start": "2026-09-01", "end": "2026-10-01" }, "labelIds": ["deficit"] },
18
+ { "id": "maintenance-1", "title": "Maintenance", "range": { "start": "2026-10-02", "end": "2026-10-20" }, "labelIds": ["maintenance"] },
19
+ { "id": "travel-1", "title": "Travel", "range": { "start": "2026-10-21", "end": "2026-10-28" }, "labelIds": ["travel"] }
20
+ ],
21
+ "series": {
22
+ "id": "weight",
23
+ "name": "Weight",
24
+ "unit": "lb",
25
+ "target": { "value": 182, "label": "Target" },
26
+ "samples": [
27
+ { "at": "2026-09-01", "value": 190.2 },
28
+ { "at": "2026-09-08", "value": 188.9 },
29
+ { "at": "2026-09-15", "value": 187.1 },
30
+ { "at": "2026-09-22", "value": 185.8 },
31
+ { "at": "2026-10-01", "value": 184.7 },
32
+ { "at": "2026-10-08", "value": 184.4 },
33
+ { "at": "2026-10-15", "value": 183.9 },
34
+ { "at": "2026-10-22", "value": 184.8 },
35
+ { "at": "2026-10-29", "value": 184.1 }
36
+ ]
37
+ }
38
+ },
39
+ "spec": {
40
+ "kind": "metricTimeline",
41
+ "title": "Weekly Weight Trend",
42
+ "stateMode": "band",
43
+ "yAxis": "auto",
44
+ "defaultDays": 90,
45
+ "today": "2026-10-29",
46
+ "showPoints": true,
47
+ "showTarget": true,
48
+ "minimap": true
49
+ },
50
+ "palette": ["#2563eb", "#16a34a", "#f59e0b"]
51
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "v": 1,
3
+ "visualizer": "spanMatrix",
4
+ "dataset": {
5
+ "schemaVersion": "timeview.dataset.v1",
6
+ "timezone": "UTC",
7
+ "meta": { "title": "Coverage Matrix" },
8
+ "labels": [
9
+ { "id": "coverage", "name": "Coverage" },
10
+ { "id": "handoff", "name": "Handoff" },
11
+ { "id": "risk", "name": "Risk" }
12
+ ],
13
+ "events": [
14
+ { "id": "review", "title": "Review", "at": "2026-09-12", "labelIds": ["risk"] }
15
+ ],
16
+ "intervals": [
17
+ { "id": "north", "title": "North team", "range": { "start": "2026-09-01", "end": "2026-09-10" }, "labelIds": ["coverage"] },
18
+ { "id": "south", "title": "South team", "range": { "start": "2026-09-06", "end": "2026-09-16" }, "labelIds": ["coverage"] },
19
+ { "id": "handoff-a", "title": "Handoff", "range": { "start": "2026-09-10", "end": "2026-09-13" }, "labelIds": ["handoff"] }
20
+ ]
21
+ },
22
+ "spec": {
23
+ "kind": "spanMatrix",
24
+ "title": "Coverage Matrix",
25
+ "bucket": "day",
26
+ "groupBy": "category",
27
+ "showCounts": true,
28
+ "today": "2026-09-12"
29
+ },
30
+ "palette": ["#2563eb", "#f59e0b", "#dc2626"]
31
+ }
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@jorgerdz/timeview",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Timeview — a library of reusable, time-based visualization components.",
6
+ "license": "MIT",
7
+ "author": "Timeview contributors",
8
+ "homepage": "https://github.com/jorgerdz/timeview#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/jorgerdz/timeview.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/jorgerdz/timeview/issues"
15
+ },
16
+ "keywords": [
17
+ "react",
18
+ "timeline",
19
+ "calendar",
20
+ "visualization",
21
+ "charts",
22
+ "time-series"
23
+ ],
24
+ "sideEffects": [
25
+ "*.css"
26
+ ],
27
+ "files": [
28
+ "dist/cli",
29
+ "dist/timeview.cjs",
30
+ "dist/timeview.js",
31
+ "dist/tokens.css",
32
+ "dist/types",
33
+ "src/timeview",
34
+ "src/render.tsx",
35
+ "src/styles/tokens.css",
36
+ "docs/AGENT-USAGE.md",
37
+ "docs/COMPATIBILITY.md",
38
+ "docs/STUDIO.md",
39
+ "examples",
40
+ "render.html",
41
+ "vite.config.ts",
42
+ "CHANGELOG.md",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "exports": {
47
+ ".": {
48
+ "types": "./dist/types/timeview/index.d.ts",
49
+ "import": "./dist/timeview.js",
50
+ "require": "./dist/timeview.cjs"
51
+ },
52
+ "./tokens.css": "./dist/tokens.css",
53
+ "./package.json": "./package.json"
54
+ },
55
+ "main": "./dist/timeview.cjs",
56
+ "module": "./dist/timeview.js",
57
+ "types": "./dist/types/timeview/index.d.ts",
58
+ "bin": {
59
+ "timeview": "dist/cli/timeview.js"
60
+ },
61
+ "scripts": {
62
+ "dev": "vite",
63
+ "timeview": "tsx scripts/timeview.ts",
64
+ "build": "npm run clean && npm run build:types && npm run build:lib && npm run build:cli && npm run build:assets && vite build",
65
+ "build:assets": "mkdir -p dist && cp src/styles/tokens.css dist/tokens.css",
66
+ "build:cli": "vite build --config vite.cli.config.ts && node scripts/postbuild-cli.js",
67
+ "build:lib": "vite build --config vite.lib.config.ts",
68
+ "build:types": "tsc -p tsconfig.lib.json",
69
+ "clean": "rm -rf dist node_modules/.tmp/tsconfig.lib.tsbuildinfo",
70
+ "test": "npm run typecheck && npm run test:config && npm run test:examples && npm run test:cli",
71
+ "test:cli": "tsx tests/cli.test.ts",
72
+ "test:config": "tsx tests/config.test.ts",
73
+ "test:examples": "tsx tests/examples.test.ts",
74
+ "smoke:render": "node dist/cli/timeview.js render examples/configs/bandedTimeline.json --format html --preset 1200x720 --out .context/render-smoke.html --json",
75
+ "preview": "vite preview",
76
+ "typecheck": "tsc -b --noEmit"
77
+ },
78
+ "peerDependencies": {
79
+ "react": "^18.3.1",
80
+ "react-dom": "^18.3.1"
81
+ },
82
+ "dependencies": {
83
+ "playwright": "^1.60.0",
84
+ "vite": "^8.0.16"
85
+ },
86
+ "devDependencies": {
87
+ "@types/node": "^25.9.2",
88
+ "@types/react": "^18.3.12",
89
+ "@types/react-dom": "^18.3.1",
90
+ "@vitejs/plugin-react": "^6.0.2",
91
+ "tsx": "^4.22.4",
92
+ "typescript": "^5.6.3"
93
+ }
94
+ }
package/render.html ADDED
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Timeview Render</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/render.tsx"></script>
11
+ </body>
12
+ </html>
package/src/render.tsx ADDED
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import {
4
+ normalizePalette,
5
+ normalizeTimeviewConfig,
6
+ TV_VISUALIZER_BY_ID,
7
+ type TimeviewConfigV1,
8
+ } from "./timeview";
9
+ import "./styles/tokens.css";
10
+
11
+ type RenderResult = { ok: true } | { ok: false; errors: string[] };
12
+
13
+ declare global {
14
+ interface Window {
15
+ renderTimeview: (config: TimeviewConfigV1, width: number, height: number) => Promise<RenderResult>;
16
+ }
17
+ }
18
+
19
+ const rootElement = document.getElementById("root");
20
+ if (!rootElement) throw new Error("Missing #root.");
21
+
22
+ const root = ReactDOM.createRoot(rootElement);
23
+
24
+ function RenderFrame({ config, width, height }: { config: TimeviewConfigV1; width: number; height: number }) {
25
+ const visualizer = TV_VISUALIZER_BY_ID[config.visualizer];
26
+ const ActiveComponent = visualizer.Component;
27
+
28
+ return (
29
+ <main
30
+ id="timeview-render-frame"
31
+ style={{
32
+ width,
33
+ minHeight: height,
34
+ margin: 0,
35
+ padding: 0,
36
+ background: "#ffffff",
37
+ color: "var(--tv-ink)",
38
+ fontFamily: "var(--tv-font)",
39
+ overflow: "hidden",
40
+ }}
41
+ >
42
+ <ActiveComponent dataset={config.dataset} spec={config.spec} palette={normalizePalette(config.palette)} />
43
+ </main>
44
+ );
45
+ }
46
+
47
+ function nextFrame(): Promise<void> {
48
+ return new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
49
+ }
50
+
51
+ document.body.style.margin = "0";
52
+ document.body.style.background = "#ffffff";
53
+
54
+ window.renderTimeview = async (rawConfig, width, height) => {
55
+ const normalized = normalizeTimeviewConfig(rawConfig);
56
+ if (!normalized.value) return { ok: false, errors: normalized.errors };
57
+
58
+ root.render(
59
+ <React.StrictMode>
60
+ <RenderFrame config={normalized.value} width={width} height={height} />
61
+ </React.StrictMode>,
62
+ );
63
+
64
+ await document.fonts.ready;
65
+ await nextFrame();
66
+ return { ok: true };
67
+ };
@@ -0,0 +1,67 @@
1
+ /* ──────────────────────────────────────────────────────────────────
2
+ Timeview — design tokens
3
+ A fresh, restrained developer-tool / analytics visual language.
4
+ White plot canvas · cool-neutral slate ink · hairline gridlines ·
5
+ Geist + Geist Mono · indigo focus accent · categorical mark palette.
6
+ ────────────────────────────────────────────────────────────────── */
7
+
8
+ @import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap");
9
+
10
+ :root {
11
+ /* ── Ink (cool slate) ───────────────────────────────────────── */
12
+ --tv-ink: #15181e; /* primary text / titles */
13
+ --tv-ink-2: #3b414d; /* band titles, strong labels */
14
+ --tv-ink-3: #6b7280; /* axis labels, secondary */
15
+ --tv-ink-4: #9aa1ad; /* faint meta, muted ticks */
16
+ --tv-ink-5: #c2c7d0; /* very faint */
17
+
18
+ /* ── Surfaces ───────────────────────────────────────────────── */
19
+ --tv-canvas: #ffffff; /* the plot itself */
20
+ --tv-frame: #fbfcfd; /* component frame background */
21
+ --tv-rail: #f5f7f9; /* legend chips, quiet fills */
22
+ --tv-rail-2: #eef1f4; /* secondary-axis week strip */
23
+
24
+ /* ── Lines ──────────────────────────────────────────────────── */
25
+ --tv-line: #e7eaee; /* frame borders, lane separators */
26
+ --tv-grid: #eef1f5; /* day gridlines */
27
+ --tv-grid-week:#dde2e8; /* week boundary gridline (stronger) */
28
+ --tv-grid-today:#cfd5dd;
29
+
30
+ /* ── Chrome accent (selection / focus only — NOT a data color) ─ */
31
+ --tv-accent: #4f46e5; /* indigo */
32
+ --tv-accent-soft: rgba(79, 70, 229, 0.12);
33
+ --tv-focus-ring: 0 0 0 2px #fff, 0 0 0 4px rgba(79, 70, 229, 0.55);
34
+
35
+ /* ── Type ───────────────────────────────────────────────────── */
36
+ --tv-font: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
37
+ --tv-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
38
+
39
+ /* ── Radii ──────────────────────────────────────────────────── */
40
+ --tv-r-xs: 4px;
41
+ --tv-r-sm: 6px;
42
+ --tv-r-md: 8px;
43
+ --tv-r-lg: 12px;
44
+ --tv-r-pill: 9999px;
45
+
46
+ /* ── Shadows (tight, inspectable) ───────────────────────────── */
47
+ --tv-shadow-1: 0 1px 2px rgba(16, 24, 40, 0.04);
48
+ --tv-shadow-pop: 0 6px 20px -6px rgba(16, 24, 40, 0.18), 0 2px 6px rgba(16,24,40,0.06);
49
+
50
+ /* ── Motion ─────────────────────────────────────────────────── */
51
+ --tv-ease: cubic-bezier(0.2, 0.6, 0.2, 1);
52
+ --tv-fast: 0.14s;
53
+ --tv-mid: 0.22s;
54
+ }
55
+
56
+ /* ── Stage (the gray studio backdrop, outside the component) ───── */
57
+ .tv-stage {
58
+ background:
59
+ radial-gradient(circle at 1px 1px, #d9dde3 1px, transparent 0) 0 0 / 22px 22px,
60
+ #eef0f3;
61
+ color: var(--tv-ink);
62
+ font-family: var(--tv-font);
63
+ -webkit-font-smoothing: antialiased;
64
+ -moz-osx-font-smoothing: grayscale;
65
+ }
66
+
67
+ *, *::before, *::after { box-sizing: border-box; }