@myrmidon/gve-snapshot-rendition 2.0.5 → 2.0.7
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/dist/adapter/data-feature-adapter.d.ts +2 -3
- package/dist/animation/animation-engine.d.ts +0 -13
- package/dist/core/gve-snapshot-rendition.d.ts +72 -30
- package/dist/hint-designer/gve-hint-designer.d.ts +11 -1
- package/dist/index.cjs.min.js +7 -7
- package/dist/index.cjs.min.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +173 -81
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +21 -50
- package/dist/rendering/feature-resolver.d.ts +10 -10
- package/dist/rendering/hint-renderer.d.ts +9 -12
- package/dist/rendering/svg-utils.d.ts +38 -1
- package/dist/rendering/text-layout.d.ts +29 -4
- package/dist/rendering/text-renderer.d.ts +25 -14
- package/dist/settings/hint-models.d.ts +0 -5
- package/dist/settings/settings.d.ts +0 -4
- package/dist/ui/details-area.d.ts +31 -0
- package/dist/ui/hilites.d.ts +3 -9
- package/dist/ui/operation-summary-service.d.ts +4 -7
- package/dist/ui/toolbar.d.ts +10 -0
- package/dist/utils/feature-utils.d.ts +7 -3
- package/package.json +82 -73
- package/dist/rendering/spreading-engine.d.ts +0 -98
- package/dist/src/adapter/adapter-models.d.ts +0 -171
- package/dist/src/adapter/data-feature-adapter.d.ts +0 -30
- package/dist/src/adapter/feature-adapter.d.ts +0 -34
- package/dist/src/adapter/index.d.ts +0 -6
- package/dist/src/adapter/matcher.d.ts +0 -38
- package/dist/src/adapter/parser.d.ts +0 -58
- package/dist/src/adapter/tokenizer.d.ts +0 -55
- package/dist/src/animation/animation-engine.d.ts +0 -105
- package/dist/src/animation/animation-factory.d.ts +0 -49
- package/dist/src/core/color-palette.d.ts +0 -39
- package/dist/src/core/gve-snapshot-rendition.d.ts +0 -359
- package/dist/src/core/logger.d.ts +0 -37
- package/dist/src/hint-designer/gve-hint-designer.d.ts +0 -356
- package/dist/src/hint-designer/hint-designer-models.d.ts +0 -32
- package/dist/src/index.d.ts +0 -17
- package/dist/src/models.d.ts +0 -171
- package/dist/src/rendering/bounds-cache.d.ts +0 -62
- package/dist/src/rendering/feature-resolver.d.ts +0 -98
- package/dist/src/rendering/hint-renderer.d.ts +0 -103
- package/dist/src/rendering/svg-utils.d.ts +0 -133
- package/dist/src/rendering/text-layout.d.ts +0 -100
- package/dist/src/rendering/text-renderer.d.ts +0 -81
- package/dist/src/settings/hint-models.d.ts +0 -61
- package/dist/src/settings/settings.d.ts +0 -118
- package/dist/src/ui/details-area.d.ts +0 -112
- package/dist/src/ui/hilites.d.ts +0 -63
- package/dist/src/ui/operation-summary-service.d.ts +0 -73
- package/dist/src/ui/toolbar.d.ts +0 -141
- package/dist/src/ui/version-text-area.d.ts +0 -131
- package/dist/src/ui/versions-list-area.d.ts +0 -88
- package/dist/src/utils/color-palette.d.ts +0 -36
- package/dist/src/utils/feature-utils.d.ts +0 -80
- package/dist/src/utils/node-utils.d.ts +0 -47
- package/dist/src/utils/text-utils.d.ts +0 -35
- /package/dist/{src/layout → layout}/golden-layout-styles.d.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -12,5 +12,6 @@ export type { OperationHint } from "./settings/hint-models";
|
|
|
12
12
|
export { DEFAULT_SETTINGS } from "./settings/settings";
|
|
13
13
|
export type { HintDesignerData, HintDesignerSettings, StringPair, } from "./hint-designer/hint-designer-models";
|
|
14
14
|
export { DEFAULT_HINT_DESIGNER_SETTINGS } from "./hint-designer/hint-designer-models";
|
|
15
|
+
export { OperationSummaryService } from "./ui/operation-summary-service";
|
|
15
16
|
export type { Feature, OperationFeature, OperationSource, OperationDiplomatics, OperationMetadata, CharChainOperation, CharNode, ChainOperationContextStep, CharChainResult, ChainOperationTags, CharChainNode, CharChainLink, CharChain, } from "./models";
|
|
16
17
|
export { FeatureSetPolicy, OperationType } from "./models";
|
package/dist/index.js
CHANGED
|
@@ -863,6 +863,24 @@ function getTextWidth(text, fontFamily, fontSize, bold = false, italic = false,
|
|
|
863
863
|
}
|
|
864
864
|
return width;
|
|
865
865
|
}
|
|
866
|
+
/**
|
|
867
|
+
* Reference character used to compute the "average character width/height"
|
|
868
|
+
* that the "tw"/"th" offset units resolve against (see getAverageCharWidth).
|
|
869
|
+
* This is the same reference character used to size the space character.
|
|
870
|
+
*/
|
|
871
|
+
const REFERENCE_CHAR = "m";
|
|
872
|
+
/**
|
|
873
|
+
* Get the average character width for the given font settings, using
|
|
874
|
+
* REFERENCE_CHAR ("m") as the reference glyph. This is the basis for the
|
|
875
|
+
* "tw" (text width) unit used by r_char-offsets, r_t-offset-x/y, and
|
|
876
|
+
* r_h-offset-x/y — NOT the bounding box of a whole reference text span.
|
|
877
|
+
*
|
|
878
|
+
* @param measurementRoot - Optional SVG element to use as measurement context
|
|
879
|
+
* (see getTextWidth for why this matters).
|
|
880
|
+
*/
|
|
881
|
+
function getAverageCharWidth(fontFamily, fontSize, bold = false, italic = false, measurementRoot) {
|
|
882
|
+
return getTextWidth(REFERENCE_CHAR, fontFamily, fontSize, bold, italic, measurementRoot);
|
|
883
|
+
}
|
|
866
884
|
|
|
867
885
|
/**
|
|
868
886
|
* Text processing utilities.
|
|
@@ -1021,11 +1039,6 @@ function parseHintVars(value) {
|
|
|
1021
1039
|
return result;
|
|
1022
1040
|
}
|
|
1023
1041
|
|
|
1024
|
-
/**
|
|
1025
|
-
* Reference character for calculating space width.
|
|
1026
|
-
* This can be changed if needed.
|
|
1027
|
-
*/
|
|
1028
|
-
const REFERENCE_CHAR = "m";
|
|
1029
1042
|
/**
|
|
1030
1043
|
* Text layout calculator.
|
|
1031
1044
|
* Handles positioning of base text characters, respecting line breaks,
|
|
@@ -1061,7 +1074,7 @@ class TextLayout {
|
|
|
1061
1074
|
* Calculate the width of a space character based on reference character.
|
|
1062
1075
|
*/
|
|
1063
1076
|
calculateSpaceWidth() {
|
|
1064
|
-
const refCharWidth =
|
|
1077
|
+
const refCharWidth = getAverageCharWidth(this._settings.fontFamily, this._settings.fontSize, this._settings.bold, this._settings.italic, this._measurementRoot // Use measurement root for consistent font metrics
|
|
1065
1078
|
);
|
|
1066
1079
|
return refCharWidth * this._settings.spaceWidthFraction;
|
|
1067
1080
|
}
|
|
@@ -1079,7 +1092,7 @@ class TextLayout {
|
|
|
1079
1092
|
// Compute the average character dimensions used to resolve "tw" and "th" units.
|
|
1080
1093
|
// tw = average character width (width of the reference character 'm' in the current font)
|
|
1081
1094
|
// th = average character height (≈ font size, which is the em-square in CSS/SVG pixels)
|
|
1082
|
-
const avgCharWidth =
|
|
1095
|
+
const avgCharWidth = getAverageCharWidth(this._settings.fontFamily, this._settings.fontSize, this._settings.bold, this._settings.italic, this._measurementRoot);
|
|
1083
1096
|
const avgCharHeight = this._settings.fontSize;
|
|
1084
1097
|
const positions = [];
|
|
1085
1098
|
let currentX = this._settings.marginLeft;
|
|
@@ -1405,10 +1418,10 @@ class TextRenderer {
|
|
|
1405
1418
|
let rbrs;
|
|
1406
1419
|
if (config.textDisplacedSpan) {
|
|
1407
1420
|
const nodes = getNodeSpan(allBaseNodes, config.textDisplacedSpan.nodeId, config.textDisplacedSpan.count);
|
|
1408
|
-
rbrs = this.calculateRBRs(nodes);
|
|
1421
|
+
rbrs = this.calculateRBRs(nodes, rootSvg);
|
|
1409
1422
|
}
|
|
1410
1423
|
else {
|
|
1411
|
-
rbrs = this.calculateRBRs(referenceNodes);
|
|
1424
|
+
rbrs = this.calculateRBRs(referenceNodes, rootSvg);
|
|
1412
1425
|
}
|
|
1413
1426
|
if (rbrs.length === 0) {
|
|
1414
1427
|
this._logger.warn("No RBRs calculated for additional text, skipping");
|
|
@@ -1417,10 +1430,14 @@ class TextRenderer {
|
|
|
1417
1430
|
// Use first RBR for positioning (additional text doesn't repeat per RBR)
|
|
1418
1431
|
const rbr = rbrs[0];
|
|
1419
1432
|
this._logger.debug("TextRenderer", `Using RBR for positioning`, rbr);
|
|
1420
|
-
// 2. Calculate position and offsets
|
|
1433
|
+
// 2. Calculate position and offsets.
|
|
1434
|
+
// "tw"/"th" resolve against the average character width/height in the
|
|
1435
|
+
// current (possibly r_font-size/r_font-family-overridden) font, not the RBR.
|
|
1421
1436
|
const position = config.textPosition || "o";
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
1437
|
+
const avgCharWidth = getAverageCharWidth(config.fontFamily, config.fontSize, config.bold, config.italic, rootSvg);
|
|
1438
|
+
const avgCharHeight = config.fontSize;
|
|
1439
|
+
const offsetX = parseOffset(config.textOffsetX || 0, avgCharHeight, avgCharWidth);
|
|
1440
|
+
const offsetY = parseOffset(config.textOffsetY || 0, avgCharHeight, avgCharWidth);
|
|
1424
1441
|
// 3. Render characters at preliminary positions (baseline y=0) into a hidden
|
|
1425
1442
|
// temporary group so we can measure the actual visual EBR via getBBox().
|
|
1426
1443
|
// This mirrors the hint renderer's approach: render → measure → reposition.
|
|
@@ -1600,31 +1617,43 @@ class TextRenderer {
|
|
|
1600
1617
|
/**
|
|
1601
1618
|
* Calculate RBRs from reference nodes.
|
|
1602
1619
|
* Nodes on different lines create separate RBRs.
|
|
1620
|
+
*
|
|
1621
|
+
* @param rootSvg - Root SVG element, used to read each character's laid-out
|
|
1622
|
+
* baseline `y` attribute for line-break detection (see below).
|
|
1603
1623
|
*/
|
|
1604
|
-
calculateRBRs(nodes) {
|
|
1624
|
+
calculateRBRs(nodes, rootSvg) {
|
|
1605
1625
|
if (nodes.length === 0)
|
|
1606
1626
|
return [];
|
|
1607
1627
|
const rbrs = [];
|
|
1608
1628
|
const nodeElementIds = nodes.map((n) => `n_${n.id}`);
|
|
1609
|
-
// Group nodes by line by checking their Y coordinates
|
|
1629
|
+
// Group nodes by line by checking their Y coordinates.
|
|
1630
|
+
// IMPORTANT: this compares the character's laid-out baseline Y (the `y`
|
|
1631
|
+
// attribute TextLayout assigned when positioning it), NOT its cached
|
|
1632
|
+
// getBBox().y. The tight ink bbox top varies per glyph shape (ascenders,
|
|
1633
|
+
// descenders, x-height-only glyphs), easily by more than the 5px
|
|
1634
|
+
// tolerance below, even on the very same line — using bbox.y here would
|
|
1635
|
+
// spuriously split one line into multiple RBRs.
|
|
1610
1636
|
const lineGroups = [];
|
|
1611
1637
|
let currentLineGroup = [];
|
|
1612
|
-
let
|
|
1638
|
+
let lastBaselineY = null;
|
|
1613
1639
|
for (const elementId of nodeElementIds) {
|
|
1614
1640
|
const bounds = this._boundsCache.get(elementId);
|
|
1615
1641
|
if (!bounds) {
|
|
1616
1642
|
this._logger.warn(`Bounds not found for node element ${elementId}`);
|
|
1617
1643
|
continue;
|
|
1618
1644
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1645
|
+
const el = rootSvg?.querySelector(`#${elementId}`);
|
|
1646
|
+
const attrY = el?.getAttribute("y");
|
|
1647
|
+
const baselineY = attrY !== null && attrY !== undefined ? parseFloat(attrY) : bounds.y;
|
|
1648
|
+
// If baseline Y coordinate significantly different, start new line group
|
|
1649
|
+
if (lastBaselineY !== null && Math.abs(baselineY - lastBaselineY) > 5) {
|
|
1621
1650
|
if (currentLineGroup.length > 0) {
|
|
1622
1651
|
lineGroups.push([...currentLineGroup]);
|
|
1623
1652
|
currentLineGroup = [];
|
|
1624
1653
|
}
|
|
1625
1654
|
}
|
|
1626
1655
|
currentLineGroup.push(elementId);
|
|
1627
|
-
|
|
1656
|
+
lastBaselineY = baselineY;
|
|
1628
1657
|
}
|
|
1629
1658
|
// Add last group
|
|
1630
1659
|
if (currentLineGroup.length > 0) {
|
|
@@ -1704,15 +1733,20 @@ class HintRenderer {
|
|
|
1704
1733
|
let rbrs;
|
|
1705
1734
|
if (effectiveHint.displacedRefSpan) {
|
|
1706
1735
|
// Use displaced span instead of reference nodes
|
|
1707
|
-
rbrs = this.calculateDisplacedRBRs(effectiveHint.displacedRefSpan, allBaseNodes);
|
|
1736
|
+
rbrs = this.calculateDisplacedRBRs(effectiveHint.displacedRefSpan, allBaseNodes, rootSvg);
|
|
1708
1737
|
}
|
|
1709
1738
|
else {
|
|
1710
|
-
rbrs = this.calculateRBRs(referenceNodes);
|
|
1739
|
+
rbrs = this.calculateRBRs(referenceNodes, rootSvg);
|
|
1711
1740
|
}
|
|
1712
1741
|
if (rbrs.length === 0) {
|
|
1713
1742
|
this._logger.warn(`No RBRs calculated for hint ${hintId}, skipping`);
|
|
1714
1743
|
return;
|
|
1715
1744
|
}
|
|
1745
|
+
// Average character width/height in the default text settings, used to
|
|
1746
|
+
// resolve "tw"/"th" units in offsetX/offsetY (r_h-offset-x/y). These are
|
|
1747
|
+
// NOT the RBR (reference text bounds) — see docs "Hints" properties.
|
|
1748
|
+
const avgCharWidth = getAverageCharWidth(this._settings.fontFamily, this._settings.fontSize, this._settings.bold, this._settings.italic, rootSvg);
|
|
1749
|
+
const avgCharHeight = this._settings.fontSize;
|
|
1716
1750
|
// 5. Check if this is a placeholder hint
|
|
1717
1751
|
const hasPlaceholder = hintGroup.querySelector("#placeholder") !== null;
|
|
1718
1752
|
// Default-mode placeholder: a placeholder element WITHOUT class="fit".
|
|
@@ -1754,7 +1788,7 @@ class HintRenderer {
|
|
|
1754
1788
|
// This is either the handle's bbox or the whole hint's bbox
|
|
1755
1789
|
const ebrBounds = this.getEBRBounds(currentHintGroup);
|
|
1756
1790
|
// 11. Apply positioning transform - align EBR with RBR based on position
|
|
1757
|
-
await this.applyHintPositionTransform(currentHintGroup, effectiveHint, rbr, ebrBounds, scalingInfo);
|
|
1791
|
+
await this.applyHintPositionTransform(currentHintGroup, effectiveHint, rbr, ebrBounds, scalingInfo, avgCharWidth, avgCharHeight);
|
|
1758
1792
|
// 10. Check if prolog panning is needed (element visibility check)
|
|
1759
1793
|
// This must happen AFTER positioning and spreading, but BEFORE making visible
|
|
1760
1794
|
if (panZoomInstance && viewportWidth && viewportHeight && this._settings.prologDuration > 0) {
|
|
@@ -1802,16 +1836,28 @@ class HintRenderer {
|
|
|
1802
1836
|
/**
|
|
1803
1837
|
* Calculate Reference Bounding Rectangles (RBRs) from reference nodes.
|
|
1804
1838
|
* Nodes on different lines create separate RBRs.
|
|
1839
|
+
*
|
|
1840
|
+
* @param rootSvg - Root SVG element, used to read each character's laid-out
|
|
1841
|
+
* baseline `y` attribute for line-break detection (see below).
|
|
1805
1842
|
*/
|
|
1806
|
-
calculateRBRs(nodes) {
|
|
1843
|
+
calculateRBRs(nodes, rootSvg) {
|
|
1807
1844
|
if (nodes.length === 0)
|
|
1808
1845
|
return [];
|
|
1809
1846
|
const rbrs = [];
|
|
1810
1847
|
this._logger.debug("HintRenderer", `Calculating RBRs for ${nodes.length} nodes`);
|
|
1811
|
-
// Group nodes by line by checking their Y coordinates
|
|
1848
|
+
// Group nodes by line by checking their Y coordinates.
|
|
1849
|
+
// IMPORTANT: this compares the character's laid-out baseline Y (the `y`
|
|
1850
|
+
// attribute TextLayout assigned when positioning it), NOT its cached
|
|
1851
|
+
// getBBox().y. The tight ink bbox top varies per glyph shape (e.g. an
|
|
1852
|
+
// ascender like "t" vs. a descender like "g" vs. an x-height-only glyph
|
|
1853
|
+
// like "s"), easily by more than the 5px tolerance below, even though
|
|
1854
|
+
// the characters sit on the very same line. Using bbox.y here would
|
|
1855
|
+
// spuriously split a single line into multiple "line groups" — and thus
|
|
1856
|
+
// multiple RBRs, causing hints to be rendered (and animated) more than
|
|
1857
|
+
// once for what is really a single reference span.
|
|
1812
1858
|
const lineGroups = [];
|
|
1813
1859
|
let currentLineGroup = [];
|
|
1814
|
-
let
|
|
1860
|
+
let lastBaselineY = null;
|
|
1815
1861
|
const missingBounds = [];
|
|
1816
1862
|
const skippedInvisible = [];
|
|
1817
1863
|
for (const node of nodes) {
|
|
@@ -1830,15 +1876,21 @@ class HintRenderer {
|
|
|
1830
1876
|
missingBounds.push(elementId);
|
|
1831
1877
|
continue;
|
|
1832
1878
|
}
|
|
1833
|
-
//
|
|
1834
|
-
|
|
1879
|
+
// Prefer the laid-out baseline `y` attribute (deterministic, identical
|
|
1880
|
+
// for every character on the same line); fall back to the cached ink
|
|
1881
|
+
// bbox top only if the element can't be found (defensive).
|
|
1882
|
+
const el = rootSvg?.querySelector(`#${elementId}`);
|
|
1883
|
+
const attrY = el?.getAttribute("y");
|
|
1884
|
+
const baselineY = attrY !== null && attrY !== undefined ? parseFloat(attrY) : bounds.y;
|
|
1885
|
+
// If baseline Y coordinate significantly different, start new line group
|
|
1886
|
+
if (lastBaselineY !== null && Math.abs(baselineY - lastBaselineY) > 5) {
|
|
1835
1887
|
if (currentLineGroup.length > 0) {
|
|
1836
1888
|
lineGroups.push([...currentLineGroup]);
|
|
1837
1889
|
currentLineGroup = [];
|
|
1838
1890
|
}
|
|
1839
1891
|
}
|
|
1840
1892
|
currentLineGroup.push(elementId);
|
|
1841
|
-
|
|
1893
|
+
lastBaselineY = baselineY;
|
|
1842
1894
|
}
|
|
1843
1895
|
// Add last group
|
|
1844
1896
|
if (currentLineGroup.length > 0) {
|
|
@@ -1863,7 +1915,7 @@ class HintRenderer {
|
|
|
1863
1915
|
/**
|
|
1864
1916
|
* Calculate RBRs from a displaced span reference (IDxN format).
|
|
1865
1917
|
*/
|
|
1866
|
-
calculateDisplacedRBRs(displacedRefSpan, allBaseNodes) {
|
|
1918
|
+
calculateDisplacedRBRs(displacedRefSpan, allBaseNodes, rootSvg) {
|
|
1867
1919
|
const parsed = parseDisplacedSpan(displacedRefSpan);
|
|
1868
1920
|
if (!parsed) {
|
|
1869
1921
|
this._logger.warn(`Invalid displaced span format: ${displacedRefSpan}`);
|
|
@@ -1874,7 +1926,7 @@ class HintRenderer {
|
|
|
1874
1926
|
this._logger.warn(`No nodes found for displaced span ${displacedRefSpan}`);
|
|
1875
1927
|
return [];
|
|
1876
1928
|
}
|
|
1877
|
-
return this.calculateRBRs(nodes);
|
|
1929
|
+
return this.calculateRBRs(nodes, rootSvg);
|
|
1878
1930
|
}
|
|
1879
1931
|
/**
|
|
1880
1932
|
* Calculate scaling information for the hint.
|
|
@@ -1928,14 +1980,15 @@ class HintRenderer {
|
|
|
1928
1980
|
* Per documentation: hints use the handle's bounding box as EBR if a handle element exists,
|
|
1929
1981
|
* otherwise they use the whole hint's bounding box as EBR.
|
|
1930
1982
|
*/
|
|
1931
|
-
async applyHintPositionTransform(hintGroup, hint, rbr, ebrBounds, scalingInfo) {
|
|
1983
|
+
async applyHintPositionTransform(hintGroup, hint, rbr, ebrBounds, scalingInfo, avgCharWidth, avgCharHeight) {
|
|
1932
1984
|
// Calculate the alignment point on the RBR
|
|
1933
1985
|
const rbrAlignPoint = this.calculateRBRAlignmentPoint(hint.position, rbr);
|
|
1934
1986
|
// Calculate the alignment point on the EBR (in local/untransformed coordinates)
|
|
1935
1987
|
const ebrAlignPoint = this.calculateEBRAlignmentPoint(hint.position, ebrBounds);
|
|
1936
|
-
// Parse offsets (they can be px or "th"/"tw"
|
|
1937
|
-
|
|
1938
|
-
const
|
|
1988
|
+
// Parse offsets (they can be px or "th"/"tw", resolved against the average
|
|
1989
|
+
// character height/width, not the RBR — see docs "Hints" properties).
|
|
1990
|
+
const offsetX = parseOffset(hint.offsetX, avgCharHeight, avgCharWidth);
|
|
1991
|
+
const offsetY = parseOffset(hint.offsetY, avgCharHeight, avgCharWidth);
|
|
1939
1992
|
// Apply offsets to the RBR alignment point
|
|
1940
1993
|
const finalRbrX = rbrAlignPoint.x + offsetX;
|
|
1941
1994
|
const finalRbrY = rbrAlignPoint.y + offsetY;
|
|
@@ -1980,6 +2033,7 @@ class HintRenderer {
|
|
|
1980
2033
|
ebrAlignPoint_LOCAL: ebrAlignPoint,
|
|
1981
2034
|
scalingInfo,
|
|
1982
2035
|
rotation: hint.rotation,
|
|
2036
|
+
offsetInputs: { x: hint.offsetX, y: hint.offsetY, avgCharWidth, avgCharHeight },
|
|
1983
2037
|
offsets: { x: offsetX, y: offsetY },
|
|
1984
2038
|
finalRbrPoint: { x: finalRbrX, y: finalRbrY },
|
|
1985
2039
|
transformString,
|
|
@@ -25917,7 +25971,7 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
25917
25971
|
* of the web component is loaded.
|
|
25918
25972
|
*/
|
|
25919
25973
|
static get version() {
|
|
25920
|
-
return "2.0.
|
|
25974
|
+
return "2.0.7";
|
|
25921
25975
|
}
|
|
25922
25976
|
constructor() {
|
|
25923
25977
|
super();
|
|
@@ -25930,6 +25984,12 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
25930
25984
|
this._autoForwardEnabled = false;
|
|
25931
25985
|
this._autoForwardTimerId = null;
|
|
25932
25986
|
this._renderScheduled = false;
|
|
25987
|
+
// Guards against re-entrant navigation: a slideshow tick (or a rapid
|
|
25988
|
+
// double-click/keypress) firing while a previous goToVersionIndex() call
|
|
25989
|
+
// is still awaiting its hint/text animations would otherwise re-run the
|
|
25990
|
+
// whole render pipeline for the same transition, duplicating every
|
|
25991
|
+
// element and animation involved (see goToVersionIndex()).
|
|
25992
|
+
this._isNavigating = false;
|
|
25933
25993
|
// Initialize with default settings
|
|
25934
25994
|
this._settings = { ...DEFAULT_SETTINGS };
|
|
25935
25995
|
this._autoForwardEnabled = this._settings.autoForwardOnGroup;
|
|
@@ -26634,55 +26694,72 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
26634
26694
|
async goToVersionIndex(targetIndex) {
|
|
26635
26695
|
if (!this._data || !this._textRenderer || !this._rootSvg)
|
|
26636
26696
|
return;
|
|
26637
|
-
this._logger.info(`Navigating from v${this._currentVersionIndex} to v${targetIndex}`);
|
|
26638
26697
|
// Determine direction
|
|
26639
26698
|
if (targetIndex === this._currentVersionIndex) {
|
|
26640
26699
|
// Already at target
|
|
26641
26700
|
return;
|
|
26642
26701
|
}
|
|
26643
|
-
//
|
|
26644
|
-
|
|
26645
|
-
//
|
|
26646
|
-
|
|
26647
|
-
|
|
26648
|
-
|
|
26649
|
-
|
|
26650
|
-
|
|
26651
|
-
|
|
26652
|
-
|
|
26653
|
-
// FORWARD navigation: add elements
|
|
26654
|
-
await this.navigateForward(targetIndex);
|
|
26655
|
-
}
|
|
26656
|
-
else {
|
|
26657
|
-
// BACKWARD navigation: remove elements
|
|
26658
|
-
await this.navigateBackward(targetIndex);
|
|
26659
|
-
}
|
|
26660
|
-
// Update versions list area
|
|
26661
|
-
const versionTag = `v${targetIndex}`;
|
|
26662
|
-
if (this._versionsListArea) {
|
|
26663
|
-
this._versionsListArea.updateCurrentVersion(versionTag);
|
|
26702
|
+
// Re-entrancy guard: a previous call may still be awaiting its hint/text
|
|
26703
|
+
// animations (which can take arbitrarily long, since hints and their
|
|
26704
|
+
// animations are user-defined). _currentVersionIndex is only updated
|
|
26705
|
+
// once that call finishes, so without this guard a caller that fires
|
|
26706
|
+
// again in the meantime (e.g. a slideshow tick, or a fast double-click)
|
|
26707
|
+
// would re-run the whole render pipeline for the same transition,
|
|
26708
|
+
// duplicating every hint/text element and animation it touches.
|
|
26709
|
+
if (this._isNavigating) {
|
|
26710
|
+
this._logger.warn(`Ignoring navigation to v${targetIndex} - a navigation is already in progress`);
|
|
26711
|
+
return;
|
|
26664
26712
|
}
|
|
26665
|
-
|
|
26666
|
-
|
|
26667
|
-
this.
|
|
26713
|
+
this._isNavigating = true;
|
|
26714
|
+
try {
|
|
26715
|
+
this._logger.info(`Navigating from v${this._currentVersionIndex} to v${targetIndex}`);
|
|
26716
|
+
// Stop any pending auto-forward when user manually navigates
|
|
26717
|
+
this.stopAutoForward();
|
|
26718
|
+
// Clear hilites before navigating
|
|
26719
|
+
if (this._hilites && this._hilites.hasActiveHilites()) {
|
|
26720
|
+
await this._hilites.clearHilites();
|
|
26721
|
+
}
|
|
26722
|
+
if (this._versionsListArea) {
|
|
26723
|
+
this._versionsListArea.clearHilites();
|
|
26724
|
+
}
|
|
26725
|
+
if (targetIndex > this._currentVersionIndex) {
|
|
26726
|
+
// FORWARD navigation: add elements
|
|
26727
|
+
await this.navigateForward(targetIndex);
|
|
26728
|
+
}
|
|
26729
|
+
else {
|
|
26730
|
+
// BACKWARD navigation: remove elements
|
|
26731
|
+
await this.navigateBackward(targetIndex);
|
|
26732
|
+
}
|
|
26733
|
+
// Update versions list area
|
|
26734
|
+
const versionTag = `v${targetIndex}`;
|
|
26735
|
+
if (this._versionsListArea) {
|
|
26736
|
+
this._versionsListArea.updateCurrentVersion(versionTag);
|
|
26737
|
+
}
|
|
26738
|
+
// Update toolbar
|
|
26739
|
+
if (this._toolbar) {
|
|
26740
|
+
this._toolbar.setCurrentIndex(targetIndex);
|
|
26741
|
+
}
|
|
26742
|
+
// Update version text area
|
|
26743
|
+
this.updateVersionText(targetIndex);
|
|
26744
|
+
// Update details area
|
|
26745
|
+
this.updateDetailsArea(targetIndex);
|
|
26746
|
+
// Re-setup hover listeners (new text elements may have been added)
|
|
26747
|
+
this.setupTextElementHoverListeners();
|
|
26748
|
+
// Check if we should auto-forward through grouped operations
|
|
26749
|
+
// Only auto-forward if:
|
|
26750
|
+
// 1. Feature is enabled
|
|
26751
|
+
// 2. We moved forward (targetIndex > previous index)
|
|
26752
|
+
// 3. Current operation has a groupId
|
|
26753
|
+
// 4. Not already in slideshow mode
|
|
26754
|
+
if (this._autoForwardEnabled &&
|
|
26755
|
+
targetIndex > 0 &&
|
|
26756
|
+
targetIndex <= (this._data?.steps?.length || 0) &&
|
|
26757
|
+
this._slideshowTimerId === null) {
|
|
26758
|
+
this.checkAndTriggerAutoForward(targetIndex);
|
|
26759
|
+
}
|
|
26668
26760
|
}
|
|
26669
|
-
|
|
26670
|
-
|
|
26671
|
-
// Update details area
|
|
26672
|
-
this.updateDetailsArea(targetIndex);
|
|
26673
|
-
// Re-setup hover listeners (new text elements may have been added)
|
|
26674
|
-
this.setupTextElementHoverListeners();
|
|
26675
|
-
// Check if we should auto-forward through grouped operations
|
|
26676
|
-
// Only auto-forward if:
|
|
26677
|
-
// 1. Feature is enabled
|
|
26678
|
-
// 2. We moved forward (targetIndex > previous index)
|
|
26679
|
-
// 3. Current operation has a groupId
|
|
26680
|
-
// 4. Not already in slideshow mode
|
|
26681
|
-
if (this._autoForwardEnabled &&
|
|
26682
|
-
targetIndex > 0 &&
|
|
26683
|
-
targetIndex <= (this._data?.steps?.length || 0) &&
|
|
26684
|
-
this._slideshowTimerId === null) {
|
|
26685
|
-
this.checkAndTriggerAutoForward(targetIndex);
|
|
26761
|
+
finally {
|
|
26762
|
+
this._isNavigating = false;
|
|
26686
26763
|
}
|
|
26687
26764
|
}
|
|
26688
26765
|
/**
|
|
@@ -27154,9 +27231,24 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
27154
27231
|
this._toolbar.setSlideshowActive(true);
|
|
27155
27232
|
this._toolbar.setNavigationDisabled(true);
|
|
27156
27233
|
}
|
|
27157
|
-
|
|
27158
|
-
|
|
27159
|
-
|
|
27234
|
+
// Use a self-rescheduling setTimeout rather than setInterval: each tick
|
|
27235
|
+
// is scheduled only after the previous slideshowAdvance() (and the
|
|
27236
|
+
// navigation/animations it awaits) has fully completed. A fixed-tick
|
|
27237
|
+
// setInterval would keep firing regardless of how long a transition's
|
|
27238
|
+
// (user-defined) hint animations actually take, and once a tick landed
|
|
27239
|
+
// mid-transition it would re-enter goToVersionIndex() for the very same
|
|
27240
|
+
// transition, duplicating every hint/text element it renders.
|
|
27241
|
+
const scheduleTick = () => {
|
|
27242
|
+
this._slideshowTimerId = window.setTimeout(async () => {
|
|
27243
|
+
await this.slideshowAdvance();
|
|
27244
|
+
// Only reschedule if still active - slideshowAdvance() may have
|
|
27245
|
+
// called stopSlideshow() (reached the end/start with no loop).
|
|
27246
|
+
if (this._slideshowTimerId !== null) {
|
|
27247
|
+
scheduleTick();
|
|
27248
|
+
}
|
|
27249
|
+
}, this._settings.slideshowInterval);
|
|
27250
|
+
};
|
|
27251
|
+
scheduleTick();
|
|
27160
27252
|
}
|
|
27161
27253
|
/**
|
|
27162
27254
|
* Stop slideshow mode.
|
|
@@ -27167,7 +27259,7 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
27167
27259
|
return;
|
|
27168
27260
|
}
|
|
27169
27261
|
this._logger.info("Stopping slideshow");
|
|
27170
|
-
window.
|
|
27262
|
+
window.clearTimeout(this._slideshowTimerId);
|
|
27171
27263
|
this._slideshowTimerId = null;
|
|
27172
27264
|
if (this._toolbar) {
|
|
27173
27265
|
this._toolbar.setSlideshowActive(false);
|
|
@@ -40860,7 +40952,7 @@ function requireD () {
|
|
|
40860
40952
|
+ 'pragma private protected public pure ref return scope shared static struct '
|
|
40861
40953
|
+ 'super switch synchronized template this throw try typedef typeid typeof union '
|
|
40862
40954
|
+ 'unittest version void volatile while with __FILE__ __LINE__ __gshared|10 '
|
|
40863
|
-
+ '__thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ 2.0.
|
|
40955
|
+
+ '__thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ 2.0.7',
|
|
40864
40956
|
built_in:
|
|
40865
40957
|
'bool cdouble cent cfloat char creal dchar delegate double dstring float function '
|
|
40866
40958
|
+ 'idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar '
|