ggaction 0.0.9 → 0.0.11
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 +84 -0
- package/README.md +5 -4
- package/knowledge/action-card.schema.json +1 -0
- package/knowledge/action-cards.json +1 -10941
- package/knowledge/action-cards.schema.json +1 -0
- package/knowledge/extension-authoring.md +248 -0
- package/knowledge/intent-taxonomy.json +1 -183
- package/knowledge/intent-taxonomy.schema.json +1 -0
- package/knowledge/mcp-resources.json +1 -95
- package/knowledge/mcp-resources.schema.json +1 -0
- package/knowledge/task-packet.schema.json +1 -159
- package/knowledge/task-resolver.js +1 -1230
- package/package.json +19 -2
- package/src/actions/boxPlots/edit.js +45 -298
- package/src/actions/boxPlots/options.js +46 -77
- package/src/actions/boxPlots/resolve.js +1 -1
- package/src/actions/charts/bar.js +3 -0
- package/src/actions/charts/heatmap.js +73 -83
- package/src/actions/charts/index.js +1 -1
- package/src/actions/charts/parallel.js +35 -1
- package/src/actions/charts/shared.js +20 -0
- package/src/actions/composition/actions.js +46 -49
- package/src/actions/data/bin2d.js +105 -2
- package/src/actions/data/box.js +17 -17
- package/src/actions/data/density.js +42 -78
- package/src/actions/data/gradientProfile.js +22 -52
- package/src/actions/data/horizon.js +16 -41
- package/src/actions/data/index.js +10 -10
- package/src/actions/data/interval.js +16 -38
- package/src/actions/data/intervalEdit.js +55 -0
- package/src/actions/data/regression.js +7 -17
- package/src/actions/data/shared.js +47 -0
- package/src/actions/data/timeUnit.js +14 -38
- package/src/actions/data/window.js +14 -38
- package/src/actions/distributions/revision.js +154 -34
- package/src/actions/encodings/angle.js +9 -10
- package/src/actions/encodings/appearance.js +17 -51
- package/src/actions/encodings/density/resolve.js +3 -1
- package/src/actions/encodings/density.js +1 -15
- package/src/actions/encodings/horizon/resolve.js +13 -3
- package/src/actions/encodings/horizon.js +8 -7
- package/src/actions/encodings/index.js +9 -9
- package/src/actions/encodings/offset.js +8 -24
- package/src/actions/encodings/parallel.js +9 -10
- package/src/actions/encodings/pathOrder.js +6 -13
- package/src/actions/encodings/position/policies/index.js +4 -4
- package/src/actions/encodings/position/policies/line.js +47 -17
- package/src/actions/encodings/position/resolve.js +24 -5
- package/src/actions/encodings/ranged.js +18 -23
- package/src/actions/encodings/remove.js +15 -25
- package/src/actions/encodings/ruleAppearance.js +9 -24
- package/src/actions/encodings/shared.js +37 -0
- package/src/actions/encodings/strokeDash.js +8 -23
- package/src/actions/encodings/text.js +9 -10
- package/src/actions/errorBands/create.js +105 -39
- package/src/actions/errorBands/edit.js +89 -93
- package/src/actions/errorBands/index.js +1 -2
- package/src/actions/errorBars/create.js +78 -27
- package/src/actions/errorBars/edit.js +81 -48
- package/src/actions/errorBars/index.js +1 -2
- package/src/actions/facets/actions.js +45 -69
- package/src/actions/facets/derive.js +38 -53
- package/src/actions/facets/replay.js +0 -1
- package/src/actions/gradientPlots/components.js +59 -48
- package/src/actions/gradientPlots/edit.js +35 -216
- package/src/actions/gradientPlots/index.js +11 -10
- package/src/actions/gradientPlots/materialize.js +21 -35
- package/src/actions/gradientPlots/options.js +5 -7
- package/src/actions/gradientPlots/paint.js +9 -6
- package/src/actions/gradientPlots/resolve.js +1 -1
- package/src/actions/guides/axes/index.js +3 -3
- package/src/actions/guides/axes/labels.js +68 -26
- package/src/actions/guides/axes/lines.js +22 -2
- package/src/actions/guides/axes/policy.js +28 -8
- package/src/actions/guides/axes/ticks.js +27 -9
- package/src/actions/guides/axes/titles.js +113 -65
- package/src/actions/guides/grids/resolve.js +14 -0
- package/src/actions/guides/guides.js +2 -4
- package/src/actions/guides/index.js +2 -2
- package/src/actions/guides/legends/categorical/actions.js +44 -68
- package/src/actions/guides/legends/categorical/components.js +6 -1
- package/src/actions/guides/legends/categorical/index.js +2 -2
- package/src/actions/guides/legends/categorical/layout.js +148 -175
- package/src/actions/guides/legends/categorical/recipes.js +5 -0
- package/src/actions/guides/legends/categorical/resolve.js +8 -1
- package/src/actions/guides/legends/continuous/common.js +117 -66
- package/src/actions/guides/legends/continuous/gradient.js +38 -37
- package/src/actions/guides/legends/continuous/interval.js +97 -76
- package/src/actions/guides/legends/continuous/opacity.js +28 -38
- package/src/actions/guides/legends/edit.js +14 -1
- package/src/actions/guides/legends/index.js +1 -1
- package/src/actions/guides/legends/lane.js +2 -1
- package/src/actions/guides/legends/remove.js +8 -0
- package/src/actions/guides/legends/size.js +36 -43
- package/src/actions/guides/legends/strokeWidth.js +20 -26
- package/src/actions/guides/polar/axes/labels.js +17 -1
- package/src/actions/guides/polar/axes/shared.js +7 -1
- package/src/actions/guides/polar/axes/titles.js +19 -2
- package/src/actions/guides/polar/grids.js +4 -0
- package/src/actions/guides/polar/resolve.js +6 -0
- package/src/actions/index.js +13 -13
- package/src/actions/intervals/resolve.js +87 -117
- package/src/actions/marks/arc/actions.js +31 -36
- package/src/actions/marks/area/actions.js +23 -31
- package/src/actions/marks/index.js +6 -6
- package/src/actions/marks/line/actions.js +33 -52
- package/src/actions/marks/line/materialize.js +7 -5
- package/src/actions/marks/point/materialize.js +5 -3
- package/src/actions/marks/remove.js +21 -0
- package/src/actions/marks/rule/actions.js +53 -53
- package/src/actions/marks/shared.js +8 -4
- package/src/actions/marks/text/actions.js +14 -6
- package/src/actions/marks/text/layout.js +21 -45
- package/src/actions/marks/tick/actions.js +22 -21
- package/src/actions/primitives/createGraphics.js +4 -0
- package/src/actions/primitives/editGraphics.js +4 -0
- package/src/actions/primitives/index.js +3 -3
- package/src/actions/primitives/semantic.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +2 -2
- package/src/actions/primitives/semanticValidation/layer.js +1 -1
- package/src/actions/regression/edit.js +3 -43
- package/src/actions/scales/consumers/families.js +24 -4
- package/src/actions/scales/consumers/index.js +1 -1
- package/src/actions/scales/consumers/seriesLayout.js +17 -0
- package/src/actions/scales/definitions.js +100 -103
- package/src/actions/scales/materialize.js +5 -0
- package/src/actions/selection/actions.js +79 -121
- package/src/actions/titles/actions.js +48 -94
- package/src/actions/titles/resolve.js +14 -27
- package/src/actions/violinPlots/create.js +37 -44
- package/src/core/action.js +20 -5
- package/src/core/extensionRegistry.js +104 -0
- package/src/core/textMetrics.js +74 -37
- package/src/core/validation.js +18 -0
- package/src/extension.js +8 -1
- package/src/grammar/aggregate.js +48 -28
- package/src/grammar/arcs.js +58 -11
- package/src/grammar/areaSeries.js +14 -16
- package/src/grammar/bin2d.js +37 -23
- package/src/grammar/boxPlot.js +14 -13
- package/src/grammar/categoricalDensity.js +14 -14
- package/src/grammar/categoryOrder.js +8 -14
- package/src/grammar/curveCommands.js +87 -20
- package/src/grammar/density.js +162 -141
- package/src/grammar/facets/index.js +14 -0
- package/src/grammar/facets/scales.js +7 -4
- package/src/grammar/gradientProfile.js +12 -15
- package/src/grammar/histogram.js +145 -83
- package/src/grammar/horizon.js +60 -23
- package/src/grammar/interval.js +14 -13
- package/src/grammar/lineSeries.js +91 -61
- package/src/grammar/numeric.js +290 -0
- package/src/grammar/paint.js +3 -4
- package/src/grammar/palettes.js +21 -32
- package/src/grammar/pathCommands.js +6 -0
- package/src/grammar/polarPaths.js +5 -5
- package/src/grammar/regression/derive.js +48 -9
- package/src/grammar/regression/models.js +248 -69
- package/src/grammar/regression/parameters.js +8 -0
- package/src/grammar/scales/color.js +35 -26
- package/src/grammar/scales/continuous.js +71 -24
- package/src/grammar/scales/definition.js +1 -1
- package/src/grammar/scales/discretized.js +42 -12
- package/src/grammar/scales/fields.js +21 -4
- package/src/grammar/scales/ordinal.js +117 -61
- package/src/grammar/scales/temporal.js +40 -11
- package/src/grammar/scales/transformed.js +199 -52
- package/src/grammar/schemas/graphicBounds.js +270 -79
- package/src/grammar/seriesLayout.js +50 -18
- package/src/grammar/ticks.js +123 -53
- package/src/grammar/timeUnit.js +18 -8
- package/src/grammar/transforms.js +4 -1
- package/src/grammar/window.js +70 -57
- package/src/index.js +1 -1
- package/src/layout/canvas.js +4 -8
- package/src/layout/composition.js +39 -40
- package/src/layout/facets.js +10 -14
- package/src/layout/labels.js +91 -15
- package/src/layout/legend.js +22 -20
- package/src/layout/legendLane.js +54 -40
- package/src/layout/title.js +7 -7
- package/src/materialization/bars/aggregate.js +14 -40
- package/src/materialization/bars/grouped.js +7 -18
- package/src/materialization/bars/histogram.js +2 -19
- package/src/materialization/bars/ranged.js +20 -15
- package/src/materialization/bars/resolve.js +21 -0
- package/src/materialization/facetGuides/legacyCategorical.js +2 -1
- package/src/materialization/facetGuides/placement.js +34 -19
- package/src/materialization/facetGuides/preparation.js +9 -5
- package/src/materialization/facets.js +39 -27
- package/src/materialization/guides/resources.js +2 -1
- package/src/materialization/marks/capabilities.js +5 -2
- package/src/materialization/marks/index.js +24 -4
- package/src/materialization/scales/policies/bar.js +61 -59
- package/src/materialization/scales/policies/binnedPosition.js +34 -0
- package/src/materialization/scales/policies/series.js +13 -7
- package/src/materialization/scales/resolve.js +4 -5
- package/src/materialization/selection/items/arc.js +16 -2
- package/src/materialization/selection/items/bar.js +4 -2
- package/src/materialization/selection/items/common.js +21 -1
- package/src/materialization/selection/items/path.js +19 -2
- package/src/materialization/selection/items/point.js +4 -16
- package/src/materialization/selection/items/tick.js +13 -21
- package/src/materialization/selection/point.js +11 -4
- package/src/materialization/selection/styles.js +19 -27
- package/src/materialization/text.js +4 -1
- package/src/mcp/adapter.js +8 -3
- package/src/renderers/canvas/fill.js +2 -2
- package/src/renderers/canvas/index.js +35 -2
- package/src/renderers/canvas/native.js +160 -0
- package/src/renderers/pdf.js +14 -0
- package/src/renderers/svg.js +37 -4
- package/types/extension.d.ts +19 -0
- package/types/index.d.ts +3 -0
- package/types/program.d.ts +509 -131
- package/types/svg.d.ts +1 -0
- package/src/actions/errorBands/components.js +0 -81
- package/src/actions/errorBands/options.js +0 -39
- package/src/actions/errorBands/resolve.js +0 -28
- package/src/actions/errorBars/components.js +0 -62
- package/src/actions/errorBars/options.js +0 -58
- package/src/actions/errorBars/resolve.js +0 -25
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { deriveArcSectors } from "../../../grammar/arcs.js";
|
|
2
|
+
import { resolvePolarPoint } from "../../../grammar/polarPaths.js";
|
|
2
3
|
import { resolvePolarFrame } from "../../../grammar/polar.js";
|
|
3
4
|
import { resolveGraphicBounds } from "../../../layout/canvas.js";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
concreteProperties,
|
|
7
|
+
finalizeItems,
|
|
8
|
+
uniqueFields
|
|
9
|
+
} from "./common.js";
|
|
5
10
|
|
|
6
11
|
export function resolveArcItems(program, layer, dataset) {
|
|
7
12
|
const thetaScale = program.resolvedScales[layer.encoding?.theta?.scale];
|
|
@@ -13,7 +18,8 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
13
18
|
frame,
|
|
14
19
|
innerRadiusRatio: program.markConfigs[layer.id]?.innerRadius ?? 0
|
|
15
20
|
});
|
|
16
|
-
const
|
|
21
|
+
const graphic = program.graphicSpec.objects[layer.id];
|
|
22
|
+
const definitions = derived.sectors.map((sector, index) => {
|
|
17
23
|
const members = sector.sourceIndices.map(index => dataset.values[index]);
|
|
18
24
|
return {
|
|
19
25
|
fields: uniqueFields(members),
|
|
@@ -22,6 +28,14 @@ export function resolveArcItems(program, layer, dataset) {
|
|
|
22
28
|
...(sector.radius === undefined ? {} : { radius: sector.radius }),
|
|
23
29
|
...(sector.color === undefined ? {} : { color: sector.color })
|
|
24
30
|
},
|
|
31
|
+
properties: {
|
|
32
|
+
...concreteProperties(graphic.items[index]?.properties),
|
|
33
|
+
...resolvePolarPoint(
|
|
34
|
+
frame,
|
|
35
|
+
(sector.startTheta + sector.endTheta) / 2,
|
|
36
|
+
(sector.innerRadius + sector.outerRadius) / 2
|
|
37
|
+
)
|
|
38
|
+
},
|
|
25
39
|
members
|
|
26
40
|
};
|
|
27
41
|
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
resolveBarOffsetChannel,
|
|
7
7
|
resolveBarGrain
|
|
8
8
|
} from "../../../grammar/bars/policy.js";
|
|
9
|
+
import { numericExtent } from "../../../grammar/numeric.js";
|
|
9
10
|
import { layoutSeriesPartition } from "../../../grammar/seriesLayout.js";
|
|
10
11
|
import {
|
|
11
12
|
readNominalField,
|
|
@@ -215,14 +216,15 @@ function barStackDefinitions(layer, barGrain, definitions) {
|
|
|
215
216
|
`Bar mark "${layer.id}" stack ${index} has no resolved semantic endpoints.`
|
|
216
217
|
);
|
|
217
218
|
}
|
|
219
|
+
const [minimum, maximum] = numericExtent(endpoints);
|
|
218
220
|
const members = grouped.flatMap(definition => definition.members);
|
|
219
221
|
return {
|
|
220
222
|
key: itemKey(layer, "stack", index),
|
|
221
223
|
fields: uniqueFields(members),
|
|
222
224
|
channels: {
|
|
223
225
|
...sharedChannels(grouped),
|
|
224
|
-
[measure]:
|
|
225
|
-
[secondary]:
|
|
226
|
+
[measure]: minimum,
|
|
227
|
+
[secondary]: maximum
|
|
226
228
|
},
|
|
227
229
|
members,
|
|
228
230
|
graphicIndices: entries.map(entry => entry.index)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { cloneAndFreeze, isPlainObject } from "../../../core/immutable.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
MARK_GRAPHIC_PROPERTIES,
|
|
4
|
+
selectMarkItemKeys
|
|
5
|
+
} from "../../../grammar/markSelection.js";
|
|
3
6
|
import { unionConcreteGraphicBounds } from
|
|
4
7
|
"../../../grammar/schemas/graphicBounds.js";
|
|
5
8
|
|
|
@@ -129,3 +132,20 @@ export function finalizeItems(program, layer, grain, definitions, graphicType) {
|
|
|
129
132
|
});
|
|
130
133
|
}));
|
|
131
134
|
}
|
|
135
|
+
|
|
136
|
+
export function reorderHighlightedItems(program, layer, definitions) {
|
|
137
|
+
let ordered = definitions;
|
|
138
|
+
for (const config of Object.values(
|
|
139
|
+
program.materializationConfigs.highlights ?? {}
|
|
140
|
+
)) {
|
|
141
|
+
if (config.target !== layer.id || config.bringToFront !== true) continue;
|
|
142
|
+
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
143
|
+
if (selection?.target !== layer.id) continue;
|
|
144
|
+
const selected = new Set(selectMarkItemKeys(ordered, selection.selector));
|
|
145
|
+
ordered = [
|
|
146
|
+
...ordered.filter(definition => !selected.has(definition.key)),
|
|
147
|
+
...ordered.filter(definition => selected.has(definition.key))
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
return ordered;
|
|
151
|
+
}
|
|
@@ -3,8 +3,12 @@ import {
|
|
|
3
3
|
deriveAreaSeries,
|
|
4
4
|
deriveDensityAreaSeries
|
|
5
5
|
} from "../../../grammar/areaSeries.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
deriveLineSeries,
|
|
8
|
+
resolveLineBins
|
|
9
|
+
} from "../../../grammar/lineSeries.js";
|
|
7
10
|
import { findUpstreamTransform } from "../../dataProvenance.js";
|
|
11
|
+
import { requireSemanticScale } from "../../../selectors/scales.js";
|
|
8
12
|
import {
|
|
9
13
|
channelMapFromRow,
|
|
10
14
|
finalizeItems,
|
|
@@ -61,7 +65,20 @@ export function resolveLineItems(program, layer, dataset) {
|
|
|
61
65
|
});
|
|
62
66
|
return finalizeItems(program, layer, "row", definitions, "path");
|
|
63
67
|
}
|
|
64
|
-
const
|
|
68
|
+
const x = layer.encoding?.x;
|
|
69
|
+
const derived = deriveLineSeries(
|
|
70
|
+
dataset.values,
|
|
71
|
+
layer,
|
|
72
|
+
x?.bin === undefined
|
|
73
|
+
? undefined
|
|
74
|
+
: {
|
|
75
|
+
xBinBoundaries: resolveLineBins(
|
|
76
|
+
dataset.values,
|
|
77
|
+
layer,
|
|
78
|
+
requireSemanticScale(program, x.scale)
|
|
79
|
+
).boundaries
|
|
80
|
+
}
|
|
81
|
+
);
|
|
65
82
|
return finalizeItems(
|
|
66
83
|
program,
|
|
67
84
|
layer,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { selectMarkItemKeys } from "../../../grammar/markSelection.js";
|
|
2
1
|
import {
|
|
3
2
|
channelMapFromRow,
|
|
4
3
|
concreteProperties,
|
|
5
4
|
finalizeItems,
|
|
6
5
|
itemKey,
|
|
7
|
-
ownFields
|
|
6
|
+
ownFields,
|
|
7
|
+
reorderHighlightedItems
|
|
8
8
|
} from "./common.js";
|
|
9
9
|
|
|
10
10
|
export function resolvePointItems(program, layer, dataset) {
|
|
@@ -22,30 +22,18 @@ export function resolvePointItems(program, layer, dataset) {
|
|
|
22
22
|
) {
|
|
23
23
|
throw new Error(`Point mark "${layer.id}" is incomplete for selection.`);
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
const definitions = dataset.values.map((row, index) => ({
|
|
26
26
|
key: itemKey(layer, "point", index),
|
|
27
27
|
fields: ownFields(row),
|
|
28
28
|
channels: channelMapFromRow(row, layer),
|
|
29
29
|
properties: concreteProperties(graphic.items[index]?.properties),
|
|
30
30
|
members: [row]
|
|
31
31
|
}));
|
|
32
|
-
for (const config of Object.values(
|
|
33
|
-
program.materializationConfigs.highlights ?? {}
|
|
34
|
-
)) {
|
|
35
|
-
if (config.target !== layer.id || config.bringToFront !== true) continue;
|
|
36
|
-
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
37
|
-
if (selection?.target !== layer.id) continue;
|
|
38
|
-
const selected = new Set(selectMarkItemKeys(definitions, selection.selector));
|
|
39
|
-
definitions = [
|
|
40
|
-
...definitions.filter(definition => !selected.has(definition.key)),
|
|
41
|
-
...definitions.filter(definition => selected.has(definition.key))
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
32
|
return finalizeItems(
|
|
45
33
|
program,
|
|
46
34
|
layer,
|
|
47
35
|
"point",
|
|
48
|
-
definitions,
|
|
36
|
+
reorderHighlightedItems(program, layer, definitions),
|
|
49
37
|
program.graphicSpec.objects[layer.id]?.type
|
|
50
38
|
);
|
|
51
39
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { selectMarkItemKeys } from "../../../grammar/markSelection.js";
|
|
2
1
|
import {
|
|
3
2
|
channelMapFromRow,
|
|
4
3
|
concreteProperties,
|
|
5
4
|
finalizeItems,
|
|
6
5
|
itemKey,
|
|
7
|
-
ownFields
|
|
6
|
+
ownFields,
|
|
7
|
+
reorderHighlightedItems
|
|
8
8
|
} from "./common.js";
|
|
9
9
|
|
|
10
10
|
export function resolveTickItems(program, layer, dataset) {
|
|
@@ -15,25 +15,17 @@ export function resolveTickItems(program, layer, dataset) {
|
|
|
15
15
|
if (!Array.isArray(graphic?.items) || !completePosition) {
|
|
16
16
|
throw new Error(`Tick mark "${layer.id}" is incomplete for selection.`);
|
|
17
17
|
}
|
|
18
|
-
let definitions =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const selection = program.materializationConfigs.selections?.[config.selection];
|
|
30
|
-
if (selection?.target !== layer.id) continue;
|
|
31
|
-
const selected = new Set(selectMarkItemKeys(definitions, selection.selector));
|
|
32
|
-
definitions = [
|
|
33
|
-
...definitions.filter(definition => !selected.has(definition.key)),
|
|
34
|
-
...definitions.filter(definition => selected.has(definition.key))
|
|
35
|
-
];
|
|
36
|
-
}
|
|
18
|
+
let definitions = reorderHighlightedItems(
|
|
19
|
+
program,
|
|
20
|
+
layer,
|
|
21
|
+
dataset.values.map((row, index) => ({
|
|
22
|
+
key: itemKey(layer, "tick", index),
|
|
23
|
+
fields: ownFields(row),
|
|
24
|
+
channels: channelMapFromRow(row, layer),
|
|
25
|
+
properties: concreteProperties(graphic.items[index]?.properties),
|
|
26
|
+
members: [row]
|
|
27
|
+
}))
|
|
28
|
+
);
|
|
37
29
|
definitions = definitions.map((definition, index) => ({
|
|
38
30
|
...definition,
|
|
39
31
|
properties: concreteProperties(graphic.items[index]?.properties)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createPointShapeGraphic } from "../../grammar/pointShapes.js";
|
|
2
|
+
import { finiteMidpoint } from "../../grammar/numeric.js";
|
|
2
3
|
|
|
3
4
|
function pointCenterAndArea(child) {
|
|
4
5
|
const properties = child.properties;
|
|
@@ -19,17 +20,23 @@ function pointCenterAndArea(child) {
|
|
|
19
20
|
const points = properties.commands.filter(command =>
|
|
20
21
|
command.op === "M" || command.op === "L"
|
|
21
22
|
);
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
let left = Infinity;
|
|
24
|
+
let right = -Infinity;
|
|
25
|
+
let top = Infinity;
|
|
26
|
+
let bottom = -Infinity;
|
|
24
27
|
let twiceArea = 0;
|
|
25
28
|
for (let index = 0; index < points.length; index += 1) {
|
|
26
29
|
const current = points[index];
|
|
27
30
|
const next = points[(index + 1) % points.length];
|
|
31
|
+
left = Math.min(left, current.x);
|
|
32
|
+
right = Math.max(right, current.x);
|
|
33
|
+
top = Math.min(top, current.y);
|
|
34
|
+
bottom = Math.max(bottom, current.y);
|
|
28
35
|
twiceArea += current.x * next.y - next.x * current.y;
|
|
29
36
|
}
|
|
30
37
|
return {
|
|
31
|
-
x: (
|
|
32
|
-
y: (
|
|
38
|
+
x: finiteMidpoint(left, right),
|
|
39
|
+
y: finiteMidpoint(top, bottom),
|
|
33
40
|
area: Math.abs(twiceArea) / 2
|
|
34
41
|
};
|
|
35
42
|
}
|
|
@@ -29,6 +29,22 @@ function normalizeOffset(offset) {
|
|
|
29
29
|
return { x, y };
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function normalizeOptionalStroke(args, mark) {
|
|
33
|
+
const stroke = args.stroke === undefined
|
|
34
|
+
? undefined
|
|
35
|
+
: validateNonEmptyString(args.stroke, `${mark} highlight stroke`);
|
|
36
|
+
const strokeWidth = args.strokeWidth === undefined
|
|
37
|
+
? undefined
|
|
38
|
+
: validateNonNegativeFinite(
|
|
39
|
+
args.strokeWidth,
|
|
40
|
+
`${mark} highlight strokeWidth`
|
|
41
|
+
);
|
|
42
|
+
if (strokeWidth !== undefined && stroke === undefined) {
|
|
43
|
+
throw new Error(`${mark} highlight strokeWidth requires stroke.`);
|
|
44
|
+
}
|
|
45
|
+
return { stroke, strokeWidth };
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
export function normalizeDimOthers(dimOthers) {
|
|
33
49
|
if (dimOthers === undefined || dimOthers === false) return false;
|
|
34
50
|
if (dimOthers === true) return { opacity: DEFAULT_DIM_OPACITY };
|
|
@@ -58,15 +74,7 @@ export function normalizePointHighlightStyle(args) {
|
|
|
58
74
|
const opacity = args.opacity === undefined
|
|
59
75
|
? undefined
|
|
60
76
|
: validateUnitInterval(args.opacity, "Point highlight opacity");
|
|
61
|
-
const stroke = args
|
|
62
|
-
? undefined
|
|
63
|
-
: validateNonEmptyString(args.stroke, "Point highlight stroke");
|
|
64
|
-
const strokeWidth = args.strokeWidth === undefined
|
|
65
|
-
? undefined
|
|
66
|
-
: validateNonNegativeFinite(args.strokeWidth, "Point highlight strokeWidth");
|
|
67
|
-
if (strokeWidth !== undefined && stroke === undefined) {
|
|
68
|
-
throw new Error("Point highlight strokeWidth requires stroke.");
|
|
69
|
-
}
|
|
77
|
+
const { stroke, strokeWidth } = normalizeOptionalStroke(args, "Point");
|
|
70
78
|
return {
|
|
71
79
|
fill,
|
|
72
80
|
...(opacity === undefined ? {} : { opacity }),
|
|
@@ -96,15 +104,7 @@ function normalizeRectangularHighlightStyle(args, mark, { offset = false } = {})
|
|
|
96
104
|
const opacity = args.opacity === undefined
|
|
97
105
|
? undefined
|
|
98
106
|
: validateUnitInterval(args.opacity, `${mark} highlight opacity`);
|
|
99
|
-
const stroke = args
|
|
100
|
-
? undefined
|
|
101
|
-
: validateNonEmptyString(args.stroke, `${mark} highlight stroke`);
|
|
102
|
-
const strokeWidth = args.strokeWidth === undefined
|
|
103
|
-
? undefined
|
|
104
|
-
: validateNonNegativeFinite(args.strokeWidth, `${mark} highlight strokeWidth`);
|
|
105
|
-
if (strokeWidth !== undefined && stroke === undefined) {
|
|
106
|
-
throw new Error(`${mark} highlight strokeWidth requires stroke.`);
|
|
107
|
-
}
|
|
107
|
+
const { stroke, strokeWidth } = normalizeOptionalStroke(args, mark);
|
|
108
108
|
const hasAppearanceOverride = opacity !== undefined || stroke !== undefined ||
|
|
109
109
|
strokeWidth !== undefined || args.offset !== undefined;
|
|
110
110
|
const fill = explicitFill === undefined && hasAppearanceOverride
|
|
@@ -178,15 +178,7 @@ export function normalizeAreaHighlightStyle(args, mark = "Area") {
|
|
|
178
178
|
if (args.color !== undefined && args.fill !== undefined) {
|
|
179
179
|
throw new Error(`${mark} highlight accepts color or fill, not both.`);
|
|
180
180
|
}
|
|
181
|
-
const stroke = args
|
|
182
|
-
? undefined
|
|
183
|
-
: validateNonEmptyString(args.stroke, `${mark} highlight stroke`);
|
|
184
|
-
const strokeWidth = args.strokeWidth === undefined
|
|
185
|
-
? undefined
|
|
186
|
-
: validateNonNegativeFinite(args.strokeWidth, `${mark} highlight strokeWidth`);
|
|
187
|
-
if (strokeWidth !== undefined && stroke === undefined) {
|
|
188
|
-
throw new Error(`${mark} highlight strokeWidth requires stroke.`);
|
|
189
|
-
}
|
|
181
|
+
const { stroke, strokeWidth } = normalizeOptionalStroke(args, mark);
|
|
190
182
|
return {
|
|
191
183
|
fill: validateNonEmptyString(
|
|
192
184
|
args.fill ?? args.color ?? DEFAULT_COLORS.highlight,
|
|
@@ -104,7 +104,10 @@ function relativeLuminance(color) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
function sourceTextConfig(config, source, item) {
|
|
107
|
-
if (
|
|
107
|
+
if (
|
|
108
|
+
!["arc", "rect"].includes(source.mark.type) ||
|
|
109
|
+
config.fillExplicit === true
|
|
110
|
+
) return config;
|
|
108
111
|
const luminance = relativeLuminance(item.properties.fill);
|
|
109
112
|
if (luminance === undefined) return config;
|
|
110
113
|
return {
|
package/src/mcp/adapter.js
CHANGED
|
@@ -28,7 +28,7 @@ export const OVERVIEW_URI = "ggaction://overview";
|
|
|
28
28
|
export const SEARCH_TOOL = Object.freeze({
|
|
29
29
|
name: SEARCH_TOOL_NAME,
|
|
30
30
|
title: "Search ggaction authoring knowledge",
|
|
31
|
-
description: "Resolve one complete ggaction chart request into a bounded ordered task packet with exact imports, authoring prerequisites,
|
|
31
|
+
description: "Resolve one complete ggaction chart request into a bounded ordered task packet with exact imports, authoring prerequisites, syntax-valid immutable steps, applied options, explicit placeholders and unmatched requirements, terminal unsupported constraints, and unresolved decisions.",
|
|
32
32
|
inputSchema: Object.freeze({
|
|
33
33
|
type: "object",
|
|
34
34
|
additionalProperties: false,
|
|
@@ -164,6 +164,7 @@ export function readKnowledgeResource(uri, { allowedDocs = [] } = {}) {
|
|
|
164
164
|
if (uri === OVERVIEW_URI) {
|
|
165
165
|
return jsonResource(uri, {
|
|
166
166
|
schemaVersion: resourcesArtifact.schemaVersion,
|
|
167
|
+
packageVersion: resourcesArtifact.packageVersion,
|
|
167
168
|
title: resourcesArtifact.overview.title,
|
|
168
169
|
text: resourcesArtifact.overview.text
|
|
169
170
|
});
|
|
@@ -184,6 +185,7 @@ export function readKnowledgeResource(uri, { allowedDocs = [] } = {}) {
|
|
|
184
185
|
if (!recipe) throw new KnowledgeResourceError(`Unknown ggaction recipe: ${id}`);
|
|
185
186
|
return jsonResource(uri, {
|
|
186
187
|
schemaVersion: resourcesArtifact.schemaVersion,
|
|
188
|
+
packageVersion: resourcesArtifact.packageVersion,
|
|
187
189
|
...recipe,
|
|
188
190
|
packet: searchGgaction(recipe.query)
|
|
189
191
|
});
|
|
@@ -201,6 +203,9 @@ export function readKnowledgeResource(uri, { allowedDocs = [] } = {}) {
|
|
|
201
203
|
throw new KnowledgeResourceError(`Unknown knowledge resource URI: ${uri}`);
|
|
202
204
|
}
|
|
203
205
|
|
|
204
|
-
if (cardsArtifact.schemaVersion !==
|
|
205
|
-
throw new Error("MCP action cards must use schemaVersion
|
|
206
|
+
if (cardsArtifact.schemaVersion !== 2 || resourcesArtifact.schemaVersion !== 2) {
|
|
207
|
+
throw new Error("MCP action cards and resources must use schemaVersion 2.");
|
|
208
|
+
}
|
|
209
|
+
if (resourcesArtifact.packageVersion !== cardsArtifact.packageVersion) {
|
|
210
|
+
throw new Error("MCP action cards and resources must use one packageVersion.");
|
|
206
211
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isLinearGradientPaint,
|
|
3
|
-
resolveLinearGradientCoordinates,
|
|
4
3
|
validateFillPaint
|
|
5
4
|
} from "../../grammar/paint.js";
|
|
5
|
+
import { resolveCanvasGradientCoordinates } from "./native.js";
|
|
6
6
|
|
|
7
7
|
export function applyCanvasFill(context, fill, bounds, graphicId) {
|
|
8
8
|
validateFillPaint(fill, `${graphicId}.fill`);
|
|
@@ -15,7 +15,7 @@ export function applyCanvasFill(context, fill, bounds, graphicId) {
|
|
|
15
15
|
`Graphic "${graphicId}" requires Canvas context createLinearGradient().`
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
|
-
const coordinates =
|
|
18
|
+
const coordinates = resolveCanvasGradientCoordinates(fill, bounds, graphicId);
|
|
19
19
|
const gradient = context.createLinearGradient(
|
|
20
20
|
coordinates.from.x,
|
|
21
21
|
coordinates.from.y,
|
|
@@ -4,6 +4,10 @@ import { drawPathGraphic } from "./path.js";
|
|
|
4
4
|
import { drawRectGraphic } from "./rect.js";
|
|
5
5
|
import { drawTextGraphic } from "./text.js";
|
|
6
6
|
import { requireFiniteProperty } from "./validation.js";
|
|
7
|
+
import {
|
|
8
|
+
preflightCanvasGraphicSpec,
|
|
9
|
+
requireCanvasNativeValue
|
|
10
|
+
} from "./native.js";
|
|
7
11
|
import {
|
|
8
12
|
requireSingleOrderedGraphicByType,
|
|
9
13
|
walkGraphicTreeEvents
|
|
@@ -34,6 +38,8 @@ const DRAWING_CONTEXT_METHODS = Object.freeze([
|
|
|
34
38
|
"rotate",
|
|
35
39
|
"fillText"
|
|
36
40
|
]);
|
|
41
|
+
const MAX_RASTER_DIMENSION = 32_767;
|
|
42
|
+
const MAX_RASTER_PIXELS = 16_777_216;
|
|
37
43
|
|
|
38
44
|
function requireDrawingContext(context) {
|
|
39
45
|
if (context === null || typeof context !== "object") {
|
|
@@ -182,12 +188,39 @@ export function render(program, context, { pixelRatio = 1 } = {}) {
|
|
|
182
188
|
if (!Number.isFinite(pixelRatio) || pixelRatio <= 0) {
|
|
183
189
|
throw new RangeError("render pixelRatio must be a positive finite number.");
|
|
184
190
|
}
|
|
191
|
+
requireCanvasNativeValue(pixelRatio, "render Canvas pixelRatio");
|
|
192
|
+
if (Math.fround(pixelRatio) === 0) {
|
|
193
|
+
throw new RangeError(
|
|
194
|
+
"render Canvas pixelRatio must remain positive at native precision."
|
|
195
|
+
);
|
|
196
|
+
}
|
|
185
197
|
|
|
186
198
|
const target = resolveGraphicRenderTarget(graphicSpec);
|
|
187
199
|
const { width, height } = target;
|
|
200
|
+
const physicalWidth = Math.max(1, Math.round(width * pixelRatio));
|
|
201
|
+
const physicalHeight = Math.max(1, Math.round(height * pixelRatio));
|
|
202
|
+
if (![physicalWidth, physicalHeight].every(Number.isSafeInteger)) {
|
|
203
|
+
throw new RangeError(
|
|
204
|
+
"render physical Canvas dimensions must be finite safe integers."
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
if (
|
|
208
|
+
physicalWidth > MAX_RASTER_DIMENSION ||
|
|
209
|
+
physicalHeight > MAX_RASTER_DIMENSION
|
|
210
|
+
) {
|
|
211
|
+
throw new RangeError(
|
|
212
|
+
`render physical Canvas dimensions must not exceed ${MAX_RASTER_DIMENSION}.`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (physicalWidth * physicalHeight > MAX_RASTER_PIXELS) {
|
|
216
|
+
throw new RangeError(
|
|
217
|
+
`render physical Canvas pixel count must not exceed ${MAX_RASTER_PIXELS}.`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
preflightCanvasGraphicSpec(target, pixelRatio);
|
|
188
221
|
|
|
189
|
-
context.canvas.width =
|
|
190
|
-
context.canvas.height =
|
|
222
|
+
context.canvas.width = physicalWidth;
|
|
223
|
+
context.canvas.height = physicalHeight;
|
|
191
224
|
if (
|
|
192
225
|
context.canvas.style !== null &&
|
|
193
226
|
typeof context.canvas.style === "object"
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { resolveLinearGradientCoordinates } from "../../grammar/paint.js";
|
|
2
|
+
import { resolvePathCommandBounds } from
|
|
3
|
+
"../../grammar/schemas/graphicBounds.js";
|
|
4
|
+
import { walkGraphicTreeEvents } from
|
|
5
|
+
"../../grammar/schemas/graphicTree.js";
|
|
6
|
+
import { validateConcreteGraphicProperties } from
|
|
7
|
+
"../../grammar/schemas/concreteGraphic.js";
|
|
8
|
+
|
|
9
|
+
const MAX_NATIVE_VALUE = 16_777_216;
|
|
10
|
+
const NATIVE_PROPERTIES = {
|
|
11
|
+
canvas: ["x", "y", "width", "height"],
|
|
12
|
+
circle: ["x", "y", "radius", "strokeWidth"],
|
|
13
|
+
rect: ["x", "y", "width", "height", "strokeWidth"],
|
|
14
|
+
line: ["x1", "y1", "x2", "y2", "strokeWidth"],
|
|
15
|
+
path: ["strokeWidth"],
|
|
16
|
+
text: ["x", "y", "fontSize"]
|
|
17
|
+
};
|
|
18
|
+
const COMMAND_PROPERTIES = ["x", "y", "x1", "y1", "x2", "y2"];
|
|
19
|
+
|
|
20
|
+
export function requireCanvasNativeValue(value, label, scale = 1, offset = 0) {
|
|
21
|
+
const magnitude = Math.abs(value) + Math.abs(offset);
|
|
22
|
+
const scaled = magnitude * scale;
|
|
23
|
+
if (
|
|
24
|
+
!Number.isFinite(value) || magnitude > MAX_NATIVE_VALUE ||
|
|
25
|
+
!Number.isFinite(scaled) || scaled > MAX_NATIVE_VALUE
|
|
26
|
+
) {
|
|
27
|
+
throw new RangeError(
|
|
28
|
+
`${label} must be finite with magnitude <= ${MAX_NATIVE_VALUE}.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function requireValues(values, label, scale) {
|
|
34
|
+
for (const value of values) {
|
|
35
|
+
requireCanvasNativeValue(value, label, scale);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function requireProperties(properties, keys, label, scale, offset) {
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
const value = properties?.[key];
|
|
42
|
+
if (value !== undefined) {
|
|
43
|
+
requireCanvasNativeValue(
|
|
44
|
+
value,
|
|
45
|
+
label,
|
|
46
|
+
scale,
|
|
47
|
+
offset?.["xy".indexOf(key[0])]
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolveCanvasGradientCoordinates(fill, bounds, graphicId, scale = 1) {
|
|
54
|
+
const coordinates = resolveLinearGradientCoordinates(fill, bounds);
|
|
55
|
+
requireValues([
|
|
56
|
+
coordinates.from.x,
|
|
57
|
+
coordinates.from.y,
|
|
58
|
+
coordinates.to.x,
|
|
59
|
+
coordinates.to.y,
|
|
60
|
+
Math.hypot(
|
|
61
|
+
coordinates.to.x - coordinates.from.x,
|
|
62
|
+
coordinates.to.y - coordinates.from.y
|
|
63
|
+
)
|
|
64
|
+
],
|
|
65
|
+
`Graphic "${graphicId}" Canvas linear gradient geometry`,
|
|
66
|
+
scale
|
|
67
|
+
);
|
|
68
|
+
return coordinates;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function preflightGraphic(id, graphic, scale, type, offset) {
|
|
72
|
+
const properties = graphic.properties ?? {};
|
|
73
|
+
const label = `Graphic "${id}" Canvas native geometry`;
|
|
74
|
+
requireProperties(
|
|
75
|
+
properties,
|
|
76
|
+
NATIVE_PROPERTIES[type] ?? [],
|
|
77
|
+
label,
|
|
78
|
+
scale,
|
|
79
|
+
offset
|
|
80
|
+
);
|
|
81
|
+
if (properties.rotation !== undefined) {
|
|
82
|
+
requireCanvasNativeValue(properties.rotation, label);
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(properties.strokeDash)) {
|
|
85
|
+
requireValues(properties.strokeDash, label, scale);
|
|
86
|
+
}
|
|
87
|
+
const gradient = properties.fill?.type === "linear-gradient";
|
|
88
|
+
|
|
89
|
+
let bounds;
|
|
90
|
+
if (type === "canvas" || type === "rect" || type === "circle") {
|
|
91
|
+
const circle = type === "circle";
|
|
92
|
+
const width = circle ? properties.radius : properties.width;
|
|
93
|
+
const height = circle ? properties.radius : properties.height;
|
|
94
|
+
if ([properties.x, properties.y, width, height].every(Number.isFinite) &&
|
|
95
|
+
width >= 0 && height >= 0) {
|
|
96
|
+
bounds = {
|
|
97
|
+
left: properties.x - (circle ? width : 0),
|
|
98
|
+
right: properties.x + width,
|
|
99
|
+
top: properties.y - (circle ? height : 0),
|
|
100
|
+
bottom: properties.y + height
|
|
101
|
+
};
|
|
102
|
+
requireCanvasNativeValue(bounds.left, label, scale, offset?.[0]);
|
|
103
|
+
requireCanvasNativeValue(bounds.right, label, scale, offset?.[0]);
|
|
104
|
+
requireCanvasNativeValue(bounds.top, label, scale, offset?.[1]);
|
|
105
|
+
requireCanvasNativeValue(bounds.bottom, label, scale, offset?.[1]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (type === "path" && Array.isArray(properties.commands)) {
|
|
109
|
+
for (const command of properties.commands) {
|
|
110
|
+
requireProperties(command, COMMAND_PROPERTIES, label, scale, offset);
|
|
111
|
+
}
|
|
112
|
+
if (gradient) {
|
|
113
|
+
bounds = resolvePathCommandBounds(properties.commands);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (gradient && bounds) {
|
|
117
|
+
resolveCanvasGradientCoordinates(properties.fill, bounds, id, scale);
|
|
118
|
+
}
|
|
119
|
+
if (NATIVE_PROPERTIES[type] !== undefined) {
|
|
120
|
+
validateConcreteGraphicProperties(type, properties);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function preflightCanvasGraphicSpec(target, scale = 1) {
|
|
125
|
+
const offsets = [];
|
|
126
|
+
walkGraphicTreeEvents(target.graphicSpec, {
|
|
127
|
+
enter({ id, object }) {
|
|
128
|
+
let offset = offsets.at(-1);
|
|
129
|
+
if (object.type === "canvas") {
|
|
130
|
+
const parent = offset ?? [0, 0];
|
|
131
|
+
const properties = object.properties ?? {};
|
|
132
|
+
const x = id === target.canvasId ? 0 : properties.x;
|
|
133
|
+
const y = id === target.canvasId ? 0 : properties.y;
|
|
134
|
+
const effective = [parent[0] + x, parent[1] + y];
|
|
135
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
136
|
+
requireValues(
|
|
137
|
+
effective,
|
|
138
|
+
`Graphic "${id}" Canvas nested translation`,
|
|
139
|
+
scale
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
offsets.push(effective);
|
|
143
|
+
offset = id === target.canvasId ? undefined : parent;
|
|
144
|
+
}
|
|
145
|
+
preflightGraphic(id, object, scale, object.type, offset);
|
|
146
|
+
},
|
|
147
|
+
item({ id, object, owner }) {
|
|
148
|
+
preflightGraphic(
|
|
149
|
+
id,
|
|
150
|
+
object,
|
|
151
|
+
scale,
|
|
152
|
+
object.type ?? owner.type,
|
|
153
|
+
offsets.at(-1)
|
|
154
|
+
);
|
|
155
|
+
},
|
|
156
|
+
exit({ object }) {
|
|
157
|
+
if (object.type === "canvas") offsets.pop();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
package/src/renderers/pdf.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
requireProgramGraphicSpec,
|
|
9
9
|
resolveGraphicRenderTarget
|
|
10
10
|
} from "./canvas/index.js";
|
|
11
|
+
import { preflightCanvasGraphicSpec } from "./canvas/native.js";
|
|
11
12
|
|
|
12
13
|
const PDF_OPTIONS = new Set(["output", "metadata"]);
|
|
13
14
|
const PDF_METADATA = new Set([
|
|
@@ -16,6 +17,7 @@ const PDF_METADATA = new Set([
|
|
|
16
17
|
"subject",
|
|
17
18
|
"keywords"
|
|
18
19
|
]);
|
|
20
|
+
const MAX_PDF_DIMENSION = 16_777_216;
|
|
19
21
|
|
|
20
22
|
function requirePlainObject(value, label) {
|
|
21
23
|
if (
|
|
@@ -93,10 +95,22 @@ function renderPDFBuffer(target, metadata) {
|
|
|
93
95
|
return document.close();
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
function requirePDFPageDimensions(target) {
|
|
99
|
+
if (![target.width, target.height].every(value =>
|
|
100
|
+
Number.isSafeInteger(value) && value > 0 && value <= MAX_PDF_DIMENSION
|
|
101
|
+
)) {
|
|
102
|
+
throw new RangeError(
|
|
103
|
+
`PDF page dimensions must be positive integers no larger than ${MAX_PDF_DIMENSION}.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
96
108
|
export async function renderToPDF(program, options = {}) {
|
|
97
109
|
const { output, metadata } = requirePDFOptions(options);
|
|
98
110
|
const graphicSpec = requireProgramGraphicSpec(program);
|
|
99
111
|
const target = resolveGraphicRenderTarget(graphicSpec);
|
|
112
|
+
requirePDFPageDimensions(target);
|
|
113
|
+
preflightCanvasGraphicSpec(target);
|
|
100
114
|
const buffer = renderPDFBuffer(target, metadata);
|
|
101
115
|
|
|
102
116
|
await mkdir(dirname(output), { recursive: true });
|