ggaction 0.0.18 → 0.0.19

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 (155) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +6 -2
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/package.json +1 -1
  7. package/src/actions/canvas/fitting.js +6 -2
  8. package/src/actions/charts/heatmap.js +4 -3
  9. package/src/actions/data/derived.js +11 -1
  10. package/src/actions/data/edit.js +2 -0
  11. package/src/actions/data/index.js +4 -0
  12. package/src/actions/data/regression.js +24 -16
  13. package/src/actions/data/regressionStatistics.js +73 -0
  14. package/src/actions/data/shared.js +5 -2
  15. package/src/actions/distributions/revision.js +24 -18
  16. package/src/actions/encodings/remove.js +11 -6
  17. package/src/actions/encodings/shared.js +26 -24
  18. package/src/actions/facets/actions.js +18 -7
  19. package/src/actions/facets/guides.js +2 -1
  20. package/src/actions/guides/axes/axes.js +30 -11
  21. package/src/actions/guides/axes/axis.js +58 -8
  22. package/src/actions/guides/axes/components.js +13 -13
  23. package/src/actions/guides/axes/edit.js +36 -24
  24. package/src/actions/guides/axes/labels.js +37 -27
  25. package/src/actions/guides/axes/lines.js +22 -20
  26. package/src/actions/guides/axes/remove.js +10 -6
  27. package/src/actions/guides/axes/tickGroups.js +8 -7
  28. package/src/actions/guides/axes/ticks.js +31 -22
  29. package/src/actions/guides/axes/titles.js +53 -60
  30. package/src/actions/guides/facadeAxes.js +33 -24
  31. package/src/actions/guides/facadeLegend.js +3 -3
  32. package/src/actions/guides/grids/grid.js +1 -1
  33. package/src/actions/guides/grids/resolve.js +48 -51
  34. package/src/actions/guides/legends/blocks.js +43 -19
  35. package/src/actions/guides/legends/categorical/actions.js +30 -7
  36. package/src/actions/guides/legends/categorical/components.js +3 -10
  37. package/src/actions/guides/legends/categorical/index.js +2 -0
  38. package/src/actions/guides/legends/categorical/layout.js +9 -13
  39. package/src/actions/guides/legends/categorical/options.js +8 -2
  40. package/src/actions/guides/legends/categorical/recipes.js +7 -3
  41. package/src/actions/guides/legends/categorical/resolve.js +9 -2
  42. package/src/actions/guides/legends/categorical/symbols.js +22 -8
  43. package/src/actions/guides/legends/colorSize.js +110 -0
  44. package/src/actions/guides/legends/combine.js +37 -0
  45. package/src/actions/guides/legends/continuous/colorSymbols.js +1 -1
  46. package/src/actions/guides/legends/continuous/common.js +1 -1
  47. package/src/actions/guides/legends/continuous/gradient.js +27 -13
  48. package/src/actions/guides/legends/continuous/opacity.js +16 -11
  49. package/src/actions/guides/legends/creation.js +2 -0
  50. package/src/actions/guides/legends/edit.js +1 -1
  51. package/src/actions/guides/legends/focused.js +6 -5
  52. package/src/actions/guides/legends/lane.js +1 -1
  53. package/src/actions/guides/legends/lifecycle.js +2 -1
  54. package/src/actions/guides/legends/remove.js +6 -3
  55. package/src/actions/guides/legends/size.js +16 -6
  56. package/src/actions/guides/legends/strokeWidth.js +2 -0
  57. package/src/actions/guides/legends/target.js +2 -79
  58. package/src/actions/guides/legends/transition.js +24 -5
  59. package/src/actions/guides/tickValues.js +8 -5
  60. package/src/actions/marks/bar/edit.js +23 -12
  61. package/src/actions/marks/bar/materialize.js +6 -1
  62. package/src/actions/marks/point/materialize.js +5 -1
  63. package/src/actions/marks/remove.js +10 -10
  64. package/src/actions/primitives/createGraphics.js +3 -2
  65. package/src/actions/primitives/editGraphics.js +15 -11
  66. package/src/actions/primitives/graphicProperties.js +1 -6
  67. package/src/actions/primitives/semanticAction.js +27 -1
  68. package/src/actions/primitives/semanticValidation/guide.js +11 -1
  69. package/src/actions/primitives/semanticValidation/layer.js +4 -0
  70. package/src/actions/regression/edit.js +15 -3
  71. package/src/actions/scales/channels.js +2 -2
  72. package/src/actions/scales/create.js +3 -3
  73. package/src/actions/scales/definitions.js +1 -1
  74. package/src/actions/scales/edit.js +1 -1
  75. package/src/actions/scales/editPolicy.js +3 -0
  76. package/src/actions/scales/patch.js +1 -1
  77. package/src/actions/scales/quantitativeColor.js +8 -3
  78. package/src/actions/theme/reconcile.js +50 -23
  79. package/src/core/compositionState.js +2 -2
  80. package/src/core/graphicProperties.js +1 -0
  81. package/src/core/programState.js +4 -0
  82. package/src/core/resourceReferences.js +2 -2
  83. package/src/core/validation.js +9 -0
  84. package/src/core/vocabulary.js +3 -0
  85. package/src/grammar/axisTextPlacement.js +1 -0
  86. package/src/grammar/colorSizeLegend.js +39 -0
  87. package/src/grammar/computed.js +23 -1
  88. package/src/grammar/datasetSchema.js +10 -2
  89. package/src/grammar/displayLabels.js +8 -0
  90. package/src/grammar/facets/guides.js +54 -31
  91. package/src/grammar/facets/legendIntent.js +65 -0
  92. package/src/grammar/guideDash.js +1 -0
  93. package/src/grammar/legendBlocks.js +1 -0
  94. package/src/grammar/legendBorder.js +9 -0
  95. package/src/grammar/legendClip.js +19 -0
  96. package/src/grammar/legendCombinations.js +45 -0
  97. package/src/grammar/legendGradient.js +9 -0
  98. package/src/grammar/legendGroups.js +1 -1
  99. package/src/grammar/legendOpacity.js +6 -0
  100. package/src/grammar/pointShapes.js +16 -2
  101. package/src/grammar/regression/derive.js +9 -1
  102. package/src/grammar/regression/models.js +60 -0
  103. package/src/grammar/regression/parameters.js +9 -1
  104. package/src/grammar/regression/statistics.js +64 -0
  105. package/src/grammar/scales/color.js +1 -1
  106. package/src/grammar/scales/colorConsumers.js +1 -1
  107. package/src/grammar/scales/definition.js +1 -1
  108. package/src/grammar/scales/types.js +1 -1
  109. package/src/grammar/schemas/concreteGraphic.js +3 -0
  110. package/src/grammar/schemas/graphic.js +3 -0
  111. package/src/grammar/schemas/graphicBounds.js +31 -7
  112. package/src/grammar/schemas/semanticPath.js +17 -2
  113. package/src/grammar/summary.js +1 -1
  114. package/src/grammar/timeUnit.js +6 -4
  115. package/src/grammar/transformTopology.js +1 -0
  116. package/src/grammar/transforms.js +11 -1
  117. package/src/inspectionDescriptors.js +1 -1
  118. package/src/layout/guideCollisions.js +9 -1
  119. package/src/layout/legendGroup.js +1 -1
  120. package/src/materialization/compositionSnapshot.js +2 -7
  121. package/src/materialization/coordinateBounds.js +17 -4
  122. package/src/materialization/facetGuides/edit.js +30 -0
  123. package/src/materialization/facetGuides/layout.js +1 -1
  124. package/src/materialization/facetGuides/owner.js +125 -0
  125. package/src/materialization/facetGuides/ownerLayout.js +71 -0
  126. package/src/materialization/facetGuides/placement.js +52 -6
  127. package/src/materialization/facetGuides/preparation.js +7 -0
  128. package/src/materialization/facets.js +1 -1
  129. package/src/materialization/graphicHierarchy.js +5 -1
  130. package/src/materialization/graphicTranslation.js +73 -0
  131. package/src/materialization/guides/layout.js +1 -1
  132. package/src/materialization/guides/resources.js +1 -1
  133. package/src/materialization/legendBorder.js +28 -0
  134. package/src/materialization/legendClip.js +76 -0
  135. package/src/materialization/legendGroups.js +1 -1
  136. package/src/materialization/legends.js +1 -1
  137. package/src/materialization/scaleGuideDependencies.js +13 -14
  138. package/src/materialization/scales/map.js +3 -0
  139. package/src/materialization/scales/resolve.js +2 -0
  140. package/src/materialization/typography.js +10 -2
  141. package/src/persistence/validation.js +174 -1
  142. package/src/persistence.js +1 -1
  143. package/src/renderers/canvas/index.js +20 -10
  144. package/src/renderers/canvas/isolated.js +166 -0
  145. package/src/renderers/canvas/limits.js +3 -0
  146. package/src/renderers/canvas/rect.js +1 -1
  147. package/src/renderers/pdf.js +2 -2
  148. package/src/renderers/png.js +2 -2
  149. package/src/renderers/svg.js +10 -3
  150. package/src/selectors/axes.js +1 -0
  151. package/src/selectors/graphicCapacity.js +38 -0
  152. package/src/version.js +1 -1
  153. package/types/basic.d.ts +2 -0
  154. package/types/index.d.ts +5 -0
  155. package/types/program.d.ts +206 -61
@@ -0,0 +1,166 @@
1
+ import { resolvePrimitiveGraphicBounds } from "../../grammar/schemas/graphicBounds.js";
2
+ import { walkGraphicTreeEvents } from "../../grammar/schemas/graphicTree.js";
3
+ import { MAX_RASTER_DIMENSION, MAX_RASTER_PIXELS } from "./limits.js";
4
+
5
+ function canvasFactory(context, supplied) {
6
+ if (supplied !== undefined) return supplied;
7
+ const canvas = context.canvas;
8
+ if (typeof canvas?.ownerDocument?.createElement === "function") {
9
+ return (width, height) => {
10
+ const source = canvas.ownerDocument.createElement("canvas");
11
+ source.width = width;
12
+ source.height = height;
13
+ return source;
14
+ };
15
+ }
16
+ if (canvas?.constructor && canvas.constructor !== Object) {
17
+ return (width, height) => new canvas.constructor(width, height);
18
+ }
19
+ throw new TypeError("Isolated glyph painting requires a compatible Canvas surface.");
20
+ }
21
+
22
+ function requireSurface(surface) {
23
+ const context = surface?.getContext?.("2d");
24
+ if (!context || ["save", "restore", "scale", "translate", "beginPath", "fillRect",
25
+ "fill", "stroke", "moveTo", "lineTo", "bezierCurveTo", "closePath", "setLineDash"]
26
+ .some(method => typeof context[method] !== "function")) {
27
+ throw new TypeError("Isolated glyph painting requires a compatible Canvas 2D surface.");
28
+ }
29
+ return context;
30
+ }
31
+
32
+ // This is renderer-private state: no surface or backend object enters graphicSpec.
33
+ export function prepareIsolatedGlyphs(target, context, { pixelRatio = 1, createCanvas } = {}) {
34
+ const requested = Object.values(target.graphicSpec.objects).some(object =>
35
+ ["normal","difference"].includes(object.properties?.blend) ||
36
+ object.items?.some(item => ["normal","difference"].includes(item.properties?.blend)));
37
+ if (!requested) return undefined;
38
+ if (typeof context.drawImage !== "function" || typeof context.globalCompositeOperation !== "string") {
39
+ throw new TypeError("Isolated glyph painting requires Canvas drawImage and compositing support.");
40
+ }
41
+ const factory = canvasFactory(context, createCanvas);
42
+ let surface;
43
+ try {
44
+ surface = factory(1, 1);
45
+ const source = requireSurface(surface);
46
+ const hasGradient = Object.values(target.graphicSpec.objects).some(object =>
47
+ [object, ...(object.items ?? [])].some(item =>
48
+ ["normal","difference"].includes(item.properties?.blend) && item.properties.fill?.type === "linear-gradient"));
49
+ const normal=Object.values(target.graphicSpec.objects).some(object=>
50
+ [object,...(object.items??[])].some(item=>item.properties?.blend==="normal"));
51
+ if(normal){
52
+ if(typeof source.setTransform!=="function")throw new TypeError("Normal glyph opacity requires Canvas transform support.");
53
+ source.save();try{
54
+ source.globalCompositeOperation="destination-in";
55
+ if(source.globalCompositeOperation!=="destination-in")throw new TypeError("Normal glyph opacity requires source attenuation.");
56
+ }finally{source.restore();}
57
+ const circle=Object.values(target.graphicSpec.objects).some(object=>
58
+ [object,...(object.items??[])].some(item=>item.properties?.blend==="normal"&&(item.type??object.type)==="circle"));
59
+ if(circle&&typeof source.arc!=="function")throw new TypeError("Normal circle painting requires Canvas arcs.");
60
+ }
61
+ if (hasGradient && (typeof source.createLinearGradient !== "function" ||
62
+ typeof source.createLinearGradient(0, 0, 1, 0)?.addColorStop !== "function")) {
63
+ throw new TypeError("Isolated gradient painting requires Canvas linear gradients.");
64
+ }
65
+ } catch (error) {
66
+ throw new TypeError("Isolated glyph painting requires a compatible Canvas surface.", {cause:error});
67
+ }
68
+ context.save();
69
+ try {
70
+ const modes=new Set();
71
+ for(const object of Object.values(target.graphicSpec.objects))for(const item of [object,...(object.items??[])]){
72
+ if(item.properties?.blend!==undefined)modes.add(item.properties.blend==="normal"?"source-over":item.properties.blend);
73
+ }
74
+ for(const mode of modes){
75
+ context.globalCompositeOperation=mode;
76
+ if(context.globalCompositeOperation!==mode)throw new TypeError(`Canvas context does not support ${mode} compositing.`);
77
+ }
78
+ } finally { context.restore(); }
79
+
80
+ const plans = new Map();
81
+ // Ordered root graphics may be siblings of the Canvas, not its descendants.
82
+ const frames = [{offset:[0,0],clip:{left:0,top:0,right:target.width,bottom:target.height}}];
83
+ function plan(id, object, type) {
84
+ const properties = object.properties;
85
+ if (!["normal","difference"].includes(properties?.blend)) return;
86
+ const bounds = resolvePrimitiveGraphicBounds(type, properties);
87
+ if (!bounds) throw new Error(`Graphic "${id}" requires finite isolated paint bounds.`);
88
+ const {offset: [x, y], clip} = frames.at(-1);
89
+ const clipped = {
90
+ left: Math.max(bounds.left + x, clip.left),
91
+ right: Math.min(bounds.right + x, clip.right),
92
+ top: Math.max(bounds.top + y, clip.top),
93
+ bottom: Math.min(bounds.bottom + y, clip.bottom)
94
+ };
95
+ if (clipped.right <= clipped.left || clipped.bottom <= clipped.top) {
96
+ plans.set(id, {empty: true});
97
+ return;
98
+ }
99
+ // Align to destination pixels, including fractional nested translations.
100
+ const left = Math.max(Math.floor(clip.left * pixelRatio), Math.floor(clipped.left * pixelRatio) - 1);
101
+ const top = Math.max(Math.floor(clip.top * pixelRatio), Math.floor(clipped.top * pixelRatio) - 1);
102
+ const right = Math.min(Math.ceil(clip.right * pixelRatio), Math.ceil(clipped.right * pixelRatio) + 1);
103
+ const bottom = Math.min(Math.ceil(clip.bottom * pixelRatio), Math.ceil(clipped.bottom * pixelRatio) + 1);
104
+ const width = right - left, height = bottom - top;
105
+ if (![width, height].every(Number.isSafeInteger) || width < 1 || height < 1 ||
106
+ width > MAX_RASTER_DIMENSION || height > MAX_RASTER_DIMENSION ||
107
+ width * height > MAX_RASTER_PIXELS) {
108
+ throw new RangeError(`Graphic "${id}" isolated paint surface exceeds the raster allocation limit.`);
109
+ }
110
+ plans.set(id, {width, height, left: left / pixelRatio - x, top: top / pixelRatio - y});
111
+ }
112
+ walkGraphicTreeEvents(target.graphicSpec, {
113
+ enter({id, object}) {
114
+ if (object.type === "canvas") {
115
+ const parent = frames.at(-1);
116
+ const offset = id === target.canvasId ? [0, 0] :
117
+ [parent.offset[0] + object.properties.x, parent.offset[1] + object.properties.y];
118
+ const properties = object.properties;
119
+ const local = {left: offset[0], top: offset[1],
120
+ right: offset[0] + properties.width, bottom: offset[1] + properties.height};
121
+ const clip = {
122
+ left: Math.max(parent.clip.left, local.left), top: Math.max(parent.clip.top, local.top),
123
+ right: Math.min(parent.clip.right, local.right), bottom: Math.min(parent.clip.bottom, local.bottom)
124
+ };
125
+ frames.push({offset, clip});
126
+ }
127
+ if (!object.items) plan(id, object, object.type);
128
+ },
129
+ item({id, object, owner}) { plan(id, object, object.type ?? owner.type); },
130
+ exit({object}) { if (object.type === "canvas") frames.pop(); }
131
+ });
132
+ return {plans, surface, pixelRatio};
133
+ }
134
+
135
+ // Paint fill and outline together with opacity 1, then composite the source once.
136
+ export function drawIsolatedGlyph(context, id, graphic, draw, prepared) {
137
+ const plan = prepared?.plans.get(id);
138
+ if (!plan) throw new Error(`Graphic "${id}" has no prepared isolated paint source.`);
139
+ if (plan.empty) return;
140
+ const {surface, pixelRatio} = prepared;
141
+ surface.width = plan.width;
142
+ surface.height = plan.height;
143
+ const source = requireSurface(surface);
144
+ source.scale(pixelRatio, pixelRatio);
145
+ source.translate(-plan.left, -plan.top);
146
+ const {blend, opacity = 1, ...properties} = graphic.properties;
147
+ draw(source, id, {...graphic, properties: {...properties, opacity: 1}});
148
+ if(blend==="normal"){
149
+ // Attenuate the complete source once. drawImage(globalAlpha) introduces
150
+ // additional native image-alpha quantization; source masking avoids it.
151
+ source.save();
152
+ try{
153
+ source.setTransform(1,0,0,1,0,0);
154
+ source.globalCompositeOperation="destination-in";
155
+ source.globalAlpha=opacity;
156
+ source.fillStyle="#fff";
157
+ source.fillRect(0,0,plan.width,plan.height);
158
+ }finally{source.restore();}
159
+ }
160
+ context.save();
161
+ try {
162
+ context.globalAlpha = blend==="normal"?1:opacity;
163
+ context.globalCompositeOperation = blend==="normal"?"source-over":blend;
164
+ context.drawImage(surface, plan.left, plan.top, plan.width / pixelRatio, plan.height / pixelRatio);
165
+ } finally { context.restore(); }
166
+ }
@@ -0,0 +1,3 @@
1
+ // Shared raster allocation limits for root canvases and isolated glyph sources.
2
+ export const MAX_RASTER_DIMENSION = 32_767;
3
+ export const MAX_RASTER_PIXELS = 16_777_216;
@@ -51,7 +51,7 @@ function drawRect(context, child, collectionId) {
51
51
  context.lineTo(x + width, y);
52
52
  context.lineTo(x + width, y + height);
53
53
  context.lineTo(x, y + height);
54
- context.lineTo(x, y);
54
+ context.closePath();
55
55
  context.stroke();
56
56
  }
57
57
  }
@@ -73,10 +73,10 @@ export async function renderToPDFBuffer(program, options = {}) {
73
73
  const target = resolveGraphicRenderTarget(graphicSpec);
74
74
  requirePDFPageDimensions(target);
75
75
  preflightCanvasGraphicSpec(target);
76
- const { PDFDocument } = await loadNativeBackend();
76
+ const { PDFDocument, createCanvas } = await loadNativeBackend();
77
77
  const document = new PDFDocument(metadata);
78
78
  const context = document.beginPage(target.width, target.height);
79
- drawResolvedGraphicSpec(target, context);
79
+ drawResolvedGraphicSpec(target, context, {createCanvas});
80
80
  document.endPage();
81
81
  const buffer = document.close();
82
82
  return Object.freeze({
@@ -1,6 +1,6 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import { dirname, resolve } from "node:path";
3
- import { render } from "./canvas/index.js";
3
+ import { renderCanvasProgram } from "./canvas/index.js";
4
4
  import { validateRendererOptions } from "./options.js";
5
5
  import { loadNativeBackend } from "./nativeBackend.js";
6
6
 
@@ -12,7 +12,7 @@ export async function renderToPNGBuffer(program, options = {}) {
12
12
  const { pixelRatio = 1 } = options;
13
13
  const { createCanvas } = await loadNativeBackend();
14
14
  const canvas = createCanvas(1, 1);
15
- render(program, canvas.getContext("2d"), { pixelRatio });
15
+ renderCanvasProgram(program, canvas.getContext("2d"), { pixelRatio }, createCanvas);
16
16
  // Canvas drawing is synchronous; encoding uses the native async worker.
17
17
  const buffer = await canvas.encode("png");
18
18
  return Object.freeze({
@@ -265,7 +265,8 @@ function serializeRect(state, graphicId, properties) {
265
265
  ["stroke", properties.stroke],
266
266
  ["stroke-width", formatNumber(strokeWidth)],
267
267
  ...strokeDetailAttributes(properties, strokeWidth),
268
- ["opacity", formatNumber(requireOpacity(properties, graphicId))]
268
+ ["opacity", formatNumber(requireOpacity(properties, graphicId))],
269
+ ["style", properties.blend === "difference" ? "mix-blend-mode:difference" : undefined]
269
270
  ]);
270
271
  }
271
272
 
@@ -362,7 +363,8 @@ function serializePath(state, graphicId, properties) {
362
363
  ["stroke-dasharray", strokeDash.length === 0
363
364
  ? undefined
364
365
  : strokeDash.map(formatNumber).join(" ")],
365
- ["opacity", formatNumber(requireOpacity(properties, graphicId))]
366
+ ["opacity", formatNumber(requireOpacity(properties, graphicId))],
367
+ ["style", properties.blend === "difference" ? "mix-blend-mode:difference" : undefined]
366
368
  ]);
367
369
  }
368
370
 
@@ -582,6 +584,10 @@ export function renderToSVG(program, options = {}) {
582
584
  resourceNamespace:
583
585
  resolvedOptions.resourceNamespace ?? resourceNamespace(graphicSpec)
584
586
  };
587
+ walkGraphicTreeEvents(graphicSpec, {
588
+ enter({object}) { if (object.properties?.blend === "difference") state.hasBlend = true; },
589
+ item({object}) { if (object.properties?.blend === "difference") state.hasBlend = true; }
590
+ });
585
591
  const body = serializeBody(
586
592
  graphicSpec,
587
593
  rootId,
@@ -605,6 +611,7 @@ export function renderToSVG(program, options = {}) {
605
611
  ["xmlns", SVG_NAMESPACE],
606
612
  ["width", formatNumber(width)],
607
613
  ["height", formatNumber(height)],
608
- ["viewBox", `0 0 ${formatNumber(width)} ${formatNumber(height)}`]
614
+ ["viewBox", `0 0 ${formatNumber(width)} ${formatNumber(height)}`],
615
+ ["style", state.hasBlend ? "isolation:isolate" : undefined]
609
616
  ], `${accessible}${definitions}${body}`);
610
617
  }
@@ -0,0 +1 @@
1
+ const p=Object.freeze(["x","y"]),w=Object.freeze(["line","ticks","labels","title"]);function x(n){const e=n.semanticSpec?.guides?.axis??{},a=(n.guideConfigs??n.materializationConfigs?.guides)?.axis??{};return[...new Set([...Object.keys(e),...Object.keys(a)])].flatMap(i=>{const s=Object.hasOwn(e,i)?e[i]:void 0,c=Object.hasOwn(a,i)?a[i]:void 0,d=p.includes(i)?i:s?.channel;if(!p.includes(d))return[];const t=[...new Set((n.semanticSpec.layers??[]).filter(f=>f.encoding?.[d]?.scale===s?.scale).map(f=>f.coordinate).filter(Boolean))];return[{id:i,channel:d,semantic:s,config:c,scale:s?.scale,coordinate:s?.coordinate??(t.length===1?t[0]:void 0),named:i!==d,sides:[...new Set(i!==d?[s.position]:w.map(f=>c?.[f]?.position).filter(Boolean))]}]})}function g(n,e){return n===e?{}:{target:n}}function O(n,e,a={},{create:i=!1,whole:s=!1}={}){const c=x(n),d=c.filter(r=>r.channel===e);let t;if(Object.hasOwn(a,"target")){const r=a.target;if(t=c.find(l=>l.id===r),t===void 0||t.channel!==e)throw new Error(`Unknown ${e}-axis target "${r}".`)}else if(t=d.find(r=>r.id===e),t===void 0){if(d.length>1)throw new Error(`Multiple ${e}-axes require an explicit target.`);t=d[0]}const{target:f,...o}=a,u=t?.id??e;if(!t?.named&&o.position!==void 0&&c.some(r=>r.named)){const r=[...new Set((n.semanticSpec.layers??[]).filter(l=>l.encoding?.[e]?.scale===(o.scale??t?.scale??e)).map(l=>l.coordinate).filter(Boolean))];m(n,{id:u,channel:e,coordinate:t?.coordinate??(r.length===1?r[0]:void 0),position:o.position})}if(t?.named){if(o.scale!==void 0&&o.scale!==t.scale||o.coordinate!==void 0&&o.coordinate!==t.coordinate)throw new Error(`Axis "${u}" resource binding does not match the requested filter.`);if(!s&&o.position!==void 0&&o.position!==t.semantic.position)throw new Error(`Named axis "${u}" components share one side; edit the complete axis position.`);i&&(o.scale??=t.scale),s||(o.position??=t.semantic.position)}return{id:u,owner:t,options:o}}function m(n,{id:e,channel:a,coordinate:i,position:s}){for(const c of x(n))if(c.id!==e&&c.coordinate===i&&c.sides.includes(s))throw new Error(`Axis side "${s}" is already occupied on coordinate "${i}".`)}function b(n,e=n){const a=["Line","Ticks","Labels","Title"];if(e!==n)return a.map(s=>`axis_${e}__${s}`);const i=n==="radius"?"radial":n;return a.map(s=>`${i}Axis${s}`)}export{m as assertCartesianAxisSideAvailable,b as axisGraphicIds,g as axisTargetArgs,x as cartesianAxisOwners,O as selectCartesianAxis};
@@ -0,0 +1,38 @@
1
+ import { annotateError } from "../core/diagnostics.js";
2
+ import { findLayer } from "./layers.js";
3
+ import { findDataset } from "./datasets.js";
4
+
5
+ export function namespaceGraphicId(namespace, id) {
6
+ const encode = value => Array.from(value, character =>
7
+ character.codePointAt(0).toString(16).padStart(6, "0")
8
+ ).join("");
9
+ return `g${encode(namespace)}_${id}`;
10
+ }
11
+
12
+ function pointOwners(program, id) {
13
+ const layer = findLayer(program, id);
14
+ const dataset = layer?.mark?.type === "point" ? findDataset(program, layer.data) : undefined;
15
+ const owners = Array.isArray(dataset?.values) ? [{ layer, dataset }] : [];
16
+ for (const childId of program.compositionSpec?.children ?? []) {
17
+ const prefix = namespaceGraphicId(`${program.compositionSpec.id}-${childId}`, "");
18
+ const child = program.children[childId];
19
+ if (!id.startsWith(prefix) || child === undefined) continue;
20
+ const localId = id.slice(prefix.length);
21
+ if (child.graphicSpec.objects[localId] === undefined) continue;
22
+ owners.push(...pointOwners(child, localId));
23
+ }
24
+ return owners;
25
+ }
26
+
27
+ export function resolveGraphicItemCapacity(program, { id, label, type }) {
28
+ const owners = pointOwners(program, id);
29
+ if (owners.length > 1) throw annotateError(new Error("Point graphic ownership is ambiguous."), {
30
+ code: "ambiguous-resource", resourceId: id, candidates: owners.map(owner => owner.layer.id)
31
+ });
32
+ return {
33
+ pointOwned: owners.length === 1,
34
+ rawCount: owners[0]?.dataset.values.length,
35
+ graphicType: type ?? program.graphicSpec.objects[id]?.type,
36
+ label
37
+ };
38
+ }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Must match package.json; enforced by the snapshot producer-version test.
2
- export const packageVersion = "0.0.18";
2
+ export const packageVersion = "0.0.19";
package/types/basic.d.ts CHANGED
@@ -4,6 +4,7 @@ export type {
4
4
  AxisLabelOptions,
5
5
  AxisLineStyleOptions,
6
6
  AxisTickOptions,
7
+ CartesianGuideDashMap,
7
8
  AxisTicksAndLabelsOptions,
8
9
  AxisTitleOptions,
9
10
  DisplayLabelMap,
@@ -15,6 +16,7 @@ export type {
15
16
  CanvasOptions,
16
17
  ColorEncodingOptions,
17
18
  CompleteAxisOptions,
19
+ NamedCompleteAxis,
18
20
  CreateAxesOptions,
19
21
  CreateBarPlotOptions,
20
22
  BarRangePositionChannel,
package/types/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export type {
21
21
  AxisLabelStyleOptions,
22
22
  AxisLineStyleOptions,
23
23
  AxisTickOptions,
24
+ CartesianGuideDashMap,
24
25
  AxisTicksAndLabelsOptions,
25
26
  AxisTickStyleOptions,
26
27
  AxisTitleOptions,
@@ -65,6 +66,7 @@ export type {
65
66
  ShapeEncodingOptions,
66
67
  AngleEncodingOptions,
67
68
  CompleteAxisOptions,
69
+ NamedCompleteAxis,
68
70
  ConcatCompositionSpec,
69
71
  CompositionAlign,
70
72
  CompositionOptions,
@@ -219,6 +221,7 @@ export type {
219
221
  DurationWindowFrame,
220
222
  DurationWindowUnit,
221
223
  DatasetRegressionTransform,
224
+ DatasetRegressionStatisticsTransform,
222
225
  DatasetScalar,
223
226
  StatisticalWeight,
224
227
  DatasetStackTransform,
@@ -238,6 +241,7 @@ export type {
238
241
  EditDensityDataOptions,
239
242
  EditStackDataOptions,
240
243
  EditRegressionDataOptions,
244
+ EditRegressionStatisticsDataOptions,
241
245
  EditIntervalDataOptions,
242
246
  EditECDFDataOptions,
243
247
  EditNormalizedDataOptions,
@@ -357,6 +361,7 @@ export type {
357
361
  SecondaryPositionEncodingOptions,
358
362
  RegressionBandOptions,
359
363
  RegressionDataOptions,
364
+ RegressionStatisticsDataOptions,
360
365
  RegressionInterval,
361
366
  RegressionMethod,
362
367
  RegressionOptions,