hoi4-agent-tools 2.8.4 → 2.8.6
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 +11 -0
- package/README.md +25 -25
- package/dist/hoi4_agent_tools/core/artifacts.js +14 -6
- package/dist/hoi4_agent_tools/gui/assets.js +9 -1
- package/dist/hoi4_agent_tools/gui/index.js +1 -0
- package/dist/hoi4_agent_tools/gui/layout.js +30 -18
- package/dist/hoi4_agent_tools/gui/renderer.js +23 -15
- package/dist/hoi4_agent_tools/gui/scenario-generator.js +556 -0
- package/dist/hoi4_agent_tools/gui/scenario.js +44 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +84 -9
- package/dist/hoi4_agent_tools/gui/studio.js +94 -16
- package/dist/hoi4_agent_tools/gui/validators.js +6 -1
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +23 -6
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/gui.md +22 -3
- package/package.json +1 -1
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
No unreleased changes.
|
|
6
6
|
|
|
7
|
+
## 2.8.6 - 2026-08-28
|
|
8
|
+
|
|
9
|
+
- Generate source-aware scripted-GUI scenarios from complete parent/child surfaces, real scripted-localisation candidates, simple threshold conditions, coherent tab states, scoped values, meter ranges, and dynamic-list rows while keeping explicit scenario values authoritative.
|
|
10
|
+
- Preserve native HOI4 bitmap-font antialiasing and shadow pixels when applying face and localisation colours, including global and per-font `textcolors` palettes.
|
|
11
|
+
- Prevent stale workspace artifacts and irrelevant language font atlases from blocking GUI calls, resolve alternate vanilla raster extensions and repeated path separators, and keep placeholder scenarios opt-in so their intentionally unresolved state does not pollute normal validation.
|
|
12
|
+
|
|
13
|
+
## 2.8.5 - 2026-08-28
|
|
14
|
+
|
|
15
|
+
- Render bitmap-font face and localisation colours through alpha masks, including black source atlases, and load exact per-font HOI4 `textcolors` palettes.
|
|
16
|
+
- Generate configurable seeded GUI scenarios with plausible dynamic text, numeric values, meters, lists, visibility, control states, and animation frames while retaining explicit values and the unresolved placeholder view.
|
|
17
|
+
|
|
7
18
|
## 2.8.4 - 2026-08-28
|
|
8
19
|
|
|
9
20
|
- Render each declared HOI4 font face with its own glyph atlas, native metrics, face colour, border colour, and inherited language-override styling.
|
package/README.md
CHANGED
|
@@ -31,31 +31,31 @@ Paste the printed Codex or generic global-install entry into your MCP client, th
|
|
|
31
31
|
|
|
32
32
|
## Tools
|
|
33
33
|
|
|
34
|
-
| Tool | Purpose
|
|
35
|
-
| --------------------------- |
|
|
36
|
-
| `hoi4.focus_inspect` | Read focus trees, continuous-focus placement, and structural or reference problems.
|
|
37
|
-
| `hoi4.focus_render` | Produce fast HTML, SVG, JSON, and source-linked layout artifacts.
|
|
38
|
-
| `hoi4.focus_raster` | Produce a high-fidelity PNG review with decoded source icons.
|
|
39
|
-
| `hoi4.focus_rewrite` | Create or update a focus tree.
|
|
40
|
-
| `hoi4.gui_inspect` | Read a scripted GUI and its linked assets and logic.
|
|
41
|
-
| `hoi4.gui_render` | Render GUI
|
|
42
|
-
| `hoi4.gui_rewrite` | Create or update a GUI source package.
|
|
43
|
-
| `hoi4.map_inspect` | Search, click, navigate, and inspect the complete rendered map and its linked data.
|
|
44
|
-
| `hoi4.map_render` | Render full-map layers, overlays, names, IDs, coordinates, and source-linked catalogs.
|
|
45
|
-
| `hoi4.map_rewrite` | Create or update states, provinces, IDs, networks, positions, and connected map data.
|
|
46
|
-
| `hoi4.event_inspect` | Scan, trace, explain, lint, or assess event chains and their state flow.
|
|
47
|
-
| `hoi4.event_render` | Render source-linked event routes, options, timing, state, scope, and unresolved edges.
|
|
48
|
-
| `hoi4.event_compare` | Compare event-chain topology and diagnostics between revisions.
|
|
49
|
-
| `hoi4.tech_inspect` | Scan, trace, explain, lint, and assess technology and doctrine systems.
|
|
50
|
-
| `hoi4.tech_render` | Render source layouts, dependency paths, unlocks, grants, metadata, and asset coverage.
|
|
51
|
-
| `hoi4.tech_compare` | Compare technology graphs, placements, references, diagnostics, and source overlays.
|
|
52
|
-
| `hoi4.probability_inspect` | Locate weighted logic and discover compatible adapters, candidates, and required inputs.
|
|
53
|
-
| `hoi4.probability_evaluate` | Evaluate exact values, probabilities, timing, bounds, and unresolved inputs.
|
|
54
|
-
| `hoi4.probability_sweep` | Find sensitivity, breakpoints, cliffs, and rank reversals across declared ranges.
|
|
55
|
-
| `hoi4.probability_simulate` | Run deterministic sampled analysis with confidence and convergence data.
|
|
56
|
-
| `hoi4.probability_sequence` | Analyze declared recovery, caps, cooldowns, resets, timers, and terminal states.
|
|
57
|
-
| `hoi4.probability_compare` | Attribute AI-weight and MTTH changes between real or proposed source.
|
|
58
|
-
| `hoi4.probability_render` | Render cached rankings, matrices, timing, sensitivity, sequence, and comparisons.
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
| --------------------------- | ----------------------------------------------------------------------------------------- |
|
|
36
|
+
| `hoi4.focus_inspect` | Read focus trees, continuous-focus placement, and structural or reference problems. |
|
|
37
|
+
| `hoi4.focus_render` | Produce fast HTML, SVG, JSON, and source-linked layout artifacts. |
|
|
38
|
+
| `hoi4.focus_raster` | Produce a high-fidelity PNG review with decoded source icons. |
|
|
39
|
+
| `hoi4.focus_rewrite` | Create or update a focus tree. |
|
|
40
|
+
| `hoi4.gui_inspect` | Read a scripted GUI and its linked assets and logic. |
|
|
41
|
+
| `hoi4.gui_render` | Render generated and explicit GUI scenarios, states, resolutions, and layout diagnostics. |
|
|
42
|
+
| `hoi4.gui_rewrite` | Create or update a GUI source package. |
|
|
43
|
+
| `hoi4.map_inspect` | Search, click, navigate, and inspect the complete rendered map and its linked data. |
|
|
44
|
+
| `hoi4.map_render` | Render full-map layers, overlays, names, IDs, coordinates, and source-linked catalogs. |
|
|
45
|
+
| `hoi4.map_rewrite` | Create or update states, provinces, IDs, networks, positions, and connected map data. |
|
|
46
|
+
| `hoi4.event_inspect` | Scan, trace, explain, lint, or assess event chains and their state flow. |
|
|
47
|
+
| `hoi4.event_render` | Render source-linked event routes, options, timing, state, scope, and unresolved edges. |
|
|
48
|
+
| `hoi4.event_compare` | Compare event-chain topology and diagnostics between revisions. |
|
|
49
|
+
| `hoi4.tech_inspect` | Scan, trace, explain, lint, and assess technology and doctrine systems. |
|
|
50
|
+
| `hoi4.tech_render` | Render source layouts, dependency paths, unlocks, grants, metadata, and asset coverage. |
|
|
51
|
+
| `hoi4.tech_compare` | Compare technology graphs, placements, references, diagnostics, and source overlays. |
|
|
52
|
+
| `hoi4.probability_inspect` | Locate weighted logic and discover compatible adapters, candidates, and required inputs. |
|
|
53
|
+
| `hoi4.probability_evaluate` | Evaluate exact values, probabilities, timing, bounds, and unresolved inputs. |
|
|
54
|
+
| `hoi4.probability_sweep` | Find sensitivity, breakpoints, cliffs, and rank reversals across declared ranges. |
|
|
55
|
+
| `hoi4.probability_simulate` | Run deterministic sampled analysis with confidence and convergence data. |
|
|
56
|
+
| `hoi4.probability_sequence` | Analyze declared recovery, caps, cooldowns, resets, timers, and terminal states. |
|
|
57
|
+
| `hoi4.probability_compare` | Attribute AI-weight and MTTH changes between real or proposed source. |
|
|
58
|
+
| `hoi4.probability_render` | Render cached rankings, matrices, timing, sensitivity, sequence, and comparisons. |
|
|
59
59
|
|
|
60
60
|
Large outputs are linked `hoi4-agent://` resources. For resources over 1 MiB, follow the `continuationUri` returned in `_meta` until it is `null`; clients may also request byte ranges with `?offset=<bytes>&length=<bytes>`.
|
|
61
61
|
|
|
@@ -131,9 +131,14 @@ export function boundedSourceHashEvidence(sourceHashes) {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
function assertManifestIntegrity(manifest, expected, workspace) {
|
|
134
|
+
assertManifestAddressIntegrity(manifest, expected);
|
|
134
135
|
if (manifest.workspaceIdentity !== workspace.workspaceIdentity ||
|
|
135
|
-
manifest.ownerIdentity !== workspace.ownerIdentity
|
|
136
|
-
manifest
|
|
136
|
+
manifest.ownerIdentity !== workspace.ownerIdentity) {
|
|
137
|
+
throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Artifact provenance manifest does not match its immutable address');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function assertManifestAddressIntegrity(manifest, expected) {
|
|
141
|
+
if (manifest.sha256 !== expected.sha256 ||
|
|
137
142
|
manifest.provenanceHash !== expected.provenanceHash ||
|
|
138
143
|
manifest.name !== expected.name ||
|
|
139
144
|
provenanceHashFor(manifest) !== expected.provenanceHash) {
|
|
@@ -712,11 +717,11 @@ export class ArtifactStore {
|
|
|
712
717
|
if (child.name !== `${manifest.name}.${manifest.provenanceHash}.manifest.json`) {
|
|
713
718
|
throw new ServiceError('ARTIFACT_MANIFEST_INTEGRITY_FAILED', 'Artifact provenance manifest filename does not match its contents');
|
|
714
719
|
}
|
|
715
|
-
|
|
720
|
+
assertManifestAddressIntegrity(manifest, {
|
|
716
721
|
sha256: path.basename(directory),
|
|
717
722
|
provenanceHash: manifest.provenanceHash,
|
|
718
723
|
name: manifest.name,
|
|
719
|
-
}
|
|
724
|
+
});
|
|
720
725
|
const targetPath = await containedGeneratedPath(workspace.artifactRoot, path.relative(workspace.artifactRoot, path.join(directory, manifest.name)));
|
|
721
726
|
let targetBytes = 0;
|
|
722
727
|
try {
|
|
@@ -728,6 +733,8 @@ export class ArtifactStore {
|
|
|
728
733
|
}
|
|
729
734
|
retained.push({
|
|
730
735
|
manifest,
|
|
736
|
+
staleIdentity: manifest.workspaceIdentity !== workspace.workspaceIdentity ||
|
|
737
|
+
manifest.ownerIdentity !== workspace.ownerIdentity,
|
|
731
738
|
manifestPath: candidate,
|
|
732
739
|
manifestBytes: metadata.size,
|
|
733
740
|
modifiedAt: metadata.mtimeMs,
|
|
@@ -789,7 +796,8 @@ export class ArtifactStore {
|
|
|
789
796
|
}
|
|
790
797
|
};
|
|
791
798
|
await reclaimDebris(await containedGeneratedPath(workspace.artifactRoot));
|
|
792
|
-
retained.sort((left, right) =>
|
|
799
|
+
retained.sort((left, right) => Number(right.staleIdentity) - Number(left.staleIdentity) ||
|
|
800
|
+
left.modifiedAt - right.modifiedAt ||
|
|
793
801
|
compareCodeUnits(left.manifestPath, right.manifestPath));
|
|
794
802
|
const current = { ...usage };
|
|
795
803
|
for (const candidate of retained) {
|
|
@@ -822,7 +830,7 @@ export class ArtifactStore {
|
|
|
822
830
|
if (error.code !== 'ENOENT')
|
|
823
831
|
throw error;
|
|
824
832
|
}
|
|
825
|
-
const verificationPrefix = `${
|
|
833
|
+
const verificationPrefix = `${candidate.manifest.workspaceIdentity}\0${candidate.manifest.ownerIdentity}\0${candidate.targetPath}\0`;
|
|
826
834
|
for (const key of this.#verifiedContent.keys()) {
|
|
827
835
|
if (key.startsWith(verificationPrefix))
|
|
828
836
|
this.#verifiedContent.delete(key);
|
|
@@ -202,6 +202,7 @@ export class GuiAssetCatalog {
|
|
|
202
202
|
...(definition.borderColour === undefined && base.borderColour !== undefined
|
|
203
203
|
? { borderColour: base.borderColour }
|
|
204
204
|
: {}),
|
|
205
|
+
textColours: { ...base.textColours, ...definition.textColours },
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
}
|
|
@@ -582,11 +583,18 @@ export class GuiAssetCatalog {
|
|
|
582
583
|
return undefined;
|
|
583
584
|
this.budget.reserveRasterOperation(`gui-bmfont-glyph:${key}`, `GUI BMFont glyph rasterization U+${codePoint.toString(16).toUpperCase()}`);
|
|
584
585
|
this.budget.reserve(metric.width, metric.height, `GUI BMFont glyph U+${codePoint.toString(16).toUpperCase()}`, { maximumPixels: RENDER_MAX_DECODED_PIXELS });
|
|
585
|
-
const
|
|
586
|
+
const extracted = await sharp(raster.data, {
|
|
586
587
|
raw: { width: raster.width, height: raster.height, channels: 4 },
|
|
587
588
|
limitInputPixels: RENDER_MAX_DECODED_PIXELS,
|
|
588
589
|
})
|
|
589
590
|
.extract({ left: metric.x, top: metric.y, width: metric.width, height: metric.height })
|
|
591
|
+
.ensureAlpha()
|
|
592
|
+
.raw()
|
|
593
|
+
.toBuffer({ resolveWithObject: true });
|
|
594
|
+
const png = await sharp(extracted.data, {
|
|
595
|
+
raw: { width: extracted.info.width, height: extracted.info.height, channels: 4 },
|
|
596
|
+
limitInputPixels: RENDER_MAX_DECODED_PIXELS,
|
|
597
|
+
})
|
|
590
598
|
.png({ compressionLevel: 9, adaptiveFiltering: false, palette: false })
|
|
591
599
|
.toBuffer();
|
|
592
600
|
return {
|
|
@@ -6,6 +6,7 @@ export * from './layout.js';
|
|
|
6
6
|
export * from './limits.js';
|
|
7
7
|
export * from './renderer.js';
|
|
8
8
|
export * from './scenario.js';
|
|
9
|
+
export * from './scenario-generator.js';
|
|
9
10
|
export * from './source-graph.js';
|
|
10
11
|
export * from './source-patch.js';
|
|
11
12
|
export * from './studio.js';
|
|
@@ -26,23 +26,32 @@ function scalarString(value) {
|
|
|
26
26
|
}
|
|
27
27
|
const defaultTextColour = '#f5f2e8';
|
|
28
28
|
const hoi4TextColours = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
C: '#23ceff',
|
|
30
|
+
L: '#c3b091',
|
|
31
|
+
W: '#ffffff',
|
|
32
|
+
B: '#0000ff',
|
|
33
|
+
G: '#009f03',
|
|
34
|
+
R: '#ff3232',
|
|
35
|
+
b: '#000000',
|
|
36
|
+
g: '#b0b0b0',
|
|
37
|
+
Y: '#ffbd00',
|
|
38
|
+
H: '#ffbd00',
|
|
39
|
+
T: '#ffffff',
|
|
40
|
+
O: '#ff7019',
|
|
41
|
+
'0': '#cb00cb',
|
|
42
|
+
'1': '#8078d3',
|
|
43
|
+
'2': '#5170f3',
|
|
44
|
+
'3': '#518fdc',
|
|
45
|
+
'4': '#5abee7',
|
|
46
|
+
'5': '#3fb5c2',
|
|
47
|
+
'6': '#77ccba',
|
|
48
|
+
'7': '#99d199',
|
|
49
|
+
'8': '#d1d175',
|
|
50
|
+
'9': '#d1a675',
|
|
42
51
|
};
|
|
43
52
|
const inlineIconMarkerStart = 0xe000;
|
|
44
53
|
const inlineIconMarkerEnd = 0xf8ff;
|
|
45
|
-
function visibleHoiText(value) {
|
|
54
|
+
function visibleHoiText(value, fontTextColours = {}) {
|
|
46
55
|
const characters = [];
|
|
47
56
|
const colours = [];
|
|
48
57
|
const inlineIcons = [];
|
|
@@ -53,7 +62,7 @@ function visibleHoiText(value) {
|
|
|
53
62
|
const next = value[index + 1];
|
|
54
63
|
if (character === '\u00a7' && next !== undefined && /^[A-Za-z0-9!]$/u.test(next)) {
|
|
55
64
|
hasColourMarkup = true;
|
|
56
|
-
colour = next === '!' ? undefined : hoi4TextColours[next];
|
|
65
|
+
colour = next === '!' ? undefined : (fontTextColours[next] ?? hoi4TextColours[next]);
|
|
57
66
|
index += 1;
|
|
58
67
|
continue;
|
|
59
68
|
}
|
|
@@ -739,11 +748,14 @@ async function layoutElement(definition, parentRect, inheritedClip, parentScale,
|
|
|
739
748
|
displayText = `${displayText} — ${displayText} — ${displayText}`;
|
|
740
749
|
if (state === 'missing-localisation')
|
|
741
750
|
displayText = `\u00a7R${rawText}_MISSING\u00a7!`;
|
|
742
|
-
const visibleText = visibleHoiText(displayText);
|
|
743
|
-
displayText = visibleText.text;
|
|
744
|
-
context.work.admitText(displayText, `GUI text for ${definition.name}`);
|
|
745
751
|
const fontName = scalarString(property(definition.attributes, 'font', 'buttonFont'));
|
|
746
752
|
const fontDefinition = fontName === undefined ? undefined : catalog.fontDefinition(fontName);
|
|
753
|
+
const visibleText = visibleHoiText(displayText, {
|
|
754
|
+
...context.graph.textColours,
|
|
755
|
+
...fontDefinition?.textColours,
|
|
756
|
+
});
|
|
757
|
+
displayText = visibleText.text;
|
|
758
|
+
context.work.admitText(displayText, `GUI text for ${definition.name}`);
|
|
747
759
|
const resolvedFontMetrics = catalog.resolvedFontMetrics(fontName);
|
|
748
760
|
const explicitFontSize = scalarNumber(property(definition.attributes, 'fontSize'), 16);
|
|
749
761
|
const fontSize = explicitFontSize ??
|
|
@@ -62,17 +62,19 @@ function sceneGlyphDefinitions(scene) {
|
|
|
62
62
|
.map(([key, glyph]) => `<image id="${bitmapDefinitionId(key)}" width="${finite(glyph.width)}" height="${finite(glyph.height)}" href="${glyph.dataUri}" preserveAspectRatio="none"/>`),
|
|
63
63
|
].join('');
|
|
64
64
|
}
|
|
65
|
-
function
|
|
65
|
+
function bitmapTintMarkup(id, colour, glyphs, rect) {
|
|
66
66
|
const match = /^#([0-9a-f]{6})([0-9a-f]{2})?$/iu.exec(colour);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
const rgb = match?.[1] ?? 'ffffff';
|
|
68
|
+
const alpha = match?.[2] ?? 'ff';
|
|
69
|
+
const channels = [
|
|
70
|
+
Number.parseInt(rgb.slice(0, 2), 16) / 255,
|
|
71
|
+
Number.parseInt(rgb.slice(2, 4), 16) / 255,
|
|
72
|
+
Number.parseInt(rgb.slice(4, 6), 16) / 255,
|
|
73
|
+
Number.parseInt(alpha, 16) / 255,
|
|
74
|
+
].map(finite);
|
|
75
|
+
const [red, green, blue, opacity] = channels;
|
|
76
|
+
const matrix = `${red} 0 0 0 0 0 ${green} 0 0 0 0 0 ${blue} 0 0 0 0 0 ${opacity} 0`;
|
|
77
|
+
return `<defs><filter id="${id}" filterUnits="userSpaceOnUse" ${rectAttributes(rect)} color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="${matrix}"/></filter></defs><g filter="url(#${id})">${glyphs}</g>`;
|
|
76
78
|
}
|
|
77
79
|
function actualGlyphMarkup(glyphLine, originX, baseline, horizontalScale) {
|
|
78
80
|
if (glyphLine.source === 'fontkit-path')
|
|
@@ -138,9 +140,14 @@ function renderText(element, toolText) {
|
|
|
138
140
|
definitions.push(`<clipPath id="${clipId}"><rect x="${finite(runX - 0.25)}" y="${finite(lineTop)}" width="${finite(run.width * horizontalScale + 0.5)}" height="${finite(text.lineHeight)}"/></clipPath>`);
|
|
139
141
|
const glyphs = actualGlyphMarkup(glyphLine, originX, baseline, horizontalScale);
|
|
140
142
|
if (glyphLine.source === 'bmfont-atlas') {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
const maskId = `gui-font-mask-${sha256Bytes(key).slice(0, 20)}`;
|
|
144
|
+
const runRect = {
|
|
145
|
+
x: runX - 0.25,
|
|
146
|
+
y: lineTop,
|
|
147
|
+
width: run.width * horizontalScale + 0.5,
|
|
148
|
+
height: text.lineHeight,
|
|
149
|
+
};
|
|
150
|
+
return `<g clip-path="url(#${clipId})" data-font-colour="${run.colour}">${bitmapTintMarkup(maskId, run.colour, glyphs, runRect)}</g>`;
|
|
144
151
|
}
|
|
145
152
|
return `<g clip-path="url(#${clipId})" fill="${run.colour}" stroke="${text.borderColour ?? '#12151a'}" data-font-colour="${run.colour}">${glyphs}</g>`;
|
|
146
153
|
});
|
|
@@ -166,8 +173,9 @@ function renderText(element, toolText) {
|
|
|
166
173
|
const glyphs = actualGlyphMarkup(glyphLine, originX, baseline, horizontalScale);
|
|
167
174
|
if (text.colour === undefined)
|
|
168
175
|
return `<g data-font-sha256="${glyphLine.sourceHash}">${glyphs}${inlineIconMarkup}</g>`;
|
|
169
|
-
const
|
|
170
|
-
|
|
176
|
+
const maskId = `gui-font-mask-${sha256Bytes(`${element.id}:${index}:${text.colour}`).slice(0, 20)}`;
|
|
177
|
+
const lineRect = { x: originX, y: lineTop, width, height: text.lineHeight };
|
|
178
|
+
return `<g data-font-sha256="${glyphLine.sourceHash}" data-font-colour="${text.colour}">${bitmapTintMarkup(maskId, text.colour, glyphs, lineRect)}${inlineIconMarkup}</g>`;
|
|
171
179
|
}
|
|
172
180
|
const visibleLine = line.replace(/[\uE000-\uF8FF]/gu, ' ');
|
|
173
181
|
return `<g>${toolText.render(visibleLine, {
|