@myrmidon/gve-snapshot-rendition 0.0.2 → 0.0.4
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/README.md +31 -4
- package/dist/hint-designer/gve-hint-designer.d.ts +31 -0
- package/dist/index.cjs.min.js +6 -6
- package/dist/index.cjs.min.js.map +1 -1
- package/dist/index.js +638 -182
- package/dist/index.js.map +1 -1
- package/dist/rendering/feature-resolver.d.ts +1 -0
- package/dist/ui/details-area.d.ts +16 -0
- package/dist/ui/toolbar.d.ts +5 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -77,7 +77,7 @@ const DEFAULT_SETTINGS = {
|
|
|
77
77
|
// Hints - empty by default, to be filled by consumer code
|
|
78
78
|
hints: {},
|
|
79
79
|
hintMargin: 0,
|
|
80
|
-
hintDesignWidth:
|
|
80
|
+
hintDesignWidth: 300,
|
|
81
81
|
hintDesignHeight: 100,
|
|
82
82
|
showHintHandles: false,
|
|
83
83
|
// Hilites
|
|
@@ -1545,6 +1545,9 @@ class FeatureResolver {
|
|
|
1545
1545
|
this._logger.warn(`Invalid r_t-displaced-span value: ${value}`);
|
|
1546
1546
|
}
|
|
1547
1547
|
break;
|
|
1548
|
+
case "r_t-value":
|
|
1549
|
+
config.textValue = value;
|
|
1550
|
+
break;
|
|
1548
1551
|
case "r_hints":
|
|
1549
1552
|
config.hints = value.trim().split(/\s+/);
|
|
1550
1553
|
break;
|
|
@@ -1854,6 +1857,24 @@ class TextRenderer {
|
|
|
1854
1857
|
this._logger.warn("No nodes to render");
|
|
1855
1858
|
return;
|
|
1856
1859
|
}
|
|
1860
|
+
// Apply r_t-value override if specified
|
|
1861
|
+
// This overrides the operation's value (the text being added) for display only
|
|
1862
|
+
if (config.textValue) {
|
|
1863
|
+
this._logger.debug("TextRenderer", `Applying r_t-value override: "${config.textValue}" (original text had ${nodes.length} characters)`);
|
|
1864
|
+
// Replace node data with characters from r_t-value
|
|
1865
|
+
// If r_t-value has fewer characters than nodes, we only override the first N nodes
|
|
1866
|
+
// If r_t-value has more characters, we only use the first N characters
|
|
1867
|
+
const overrideChars = config.textValue.split('');
|
|
1868
|
+
const charsToOverride = Math.min(nodes.length, overrideChars.length);
|
|
1869
|
+
for (let i = 0; i < charsToOverride; i++) {
|
|
1870
|
+
nodes[i] = { ...nodes[i], data: overrideChars[i] };
|
|
1871
|
+
}
|
|
1872
|
+
// If r_t-value has fewer characters, we need to only render those nodes
|
|
1873
|
+
if (overrideChars.length < nodes.length) {
|
|
1874
|
+
nodes = nodes.slice(0, overrideChars.length);
|
|
1875
|
+
this._logger.debug("TextRenderer", `r_t-value has fewer characters (${overrideChars.length}) than nodes (${nodes.length}), only rendering first ${overrideChars.length} characters`);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1857
1878
|
// 1. Calculate RBR (or use displaced span if specified)
|
|
1858
1879
|
let rbrs;
|
|
1859
1880
|
if (config.textDisplacedSpan) {
|
|
@@ -5109,7 +5130,6 @@ class Toolbar {
|
|
|
5109
5130
|
<button id="prev-btn" class="toolbar-btn" title="Previous Version (←)">
|
|
5110
5131
|
<i data-feather="chevron-left"></i>
|
|
5111
5132
|
</button>
|
|
5112
|
-
<div id="version-label" class="version-label">v0 (1/1)</div>
|
|
5113
5133
|
<button id="next-btn" class="toolbar-btn" title="Next Version (→)">
|
|
5114
5134
|
<i data-feather="chevron-right"></i>
|
|
5115
5135
|
</button>
|
|
@@ -5157,8 +5177,14 @@ class Toolbar {
|
|
|
5157
5177
|
<div id="custom-buttons-group" class="toolbar-group custom-buttons-group">
|
|
5158
5178
|
</div>
|
|
5159
5179
|
|
|
5160
|
-
<!--
|
|
5161
|
-
<div id="
|
|
5180
|
+
<!-- Log Display (flexible space) -->
|
|
5181
|
+
<div id="log-display" class="toolbar-log-display" style="flex: 1; display: none;"></div>
|
|
5182
|
+
|
|
5183
|
+
<!-- Version Label and Group Badge (at the right edge) -->
|
|
5184
|
+
<div class="toolbar-group version-info-group" style="margin-left: auto;">
|
|
5185
|
+
<div id="version-label" class="version-label">v0 (1/1)</div>
|
|
5186
|
+
<div id="group-badge" class="version-group-badge" style="display: none;"></div>
|
|
5187
|
+
</div>
|
|
5162
5188
|
</div>
|
|
5163
5189
|
`;
|
|
5164
5190
|
// Get element references
|
|
@@ -5170,6 +5196,7 @@ class Toolbar {
|
|
|
5170
5196
|
this.lastBtn = this.container.querySelector("#last-btn");
|
|
5171
5197
|
this.versionLabel = this.container.querySelector("#version-label");
|
|
5172
5198
|
this.groupBadge = this.container.querySelector("#group-badge");
|
|
5199
|
+
this.logDisplay = this.container.querySelector("#log-display");
|
|
5173
5200
|
this.slideshowStartBtn = this.container.querySelector("#slideshow-start-btn");
|
|
5174
5201
|
this.slideshowStopBtn = this.container.querySelector("#slideshow-stop-btn");
|
|
5175
5202
|
this.slideshowReverseBtn = this.container.querySelector("#slideshow-reverse-btn");
|
|
@@ -5294,6 +5321,7 @@ class Toolbar {
|
|
|
5294
5321
|
if (!info) {
|
|
5295
5322
|
this.versionLabel.innerHTML = "v0 (1/1)";
|
|
5296
5323
|
this.groupBadge.style.display = "none";
|
|
5324
|
+
this.logDisplay.style.display = "none";
|
|
5297
5325
|
return;
|
|
5298
5326
|
}
|
|
5299
5327
|
let labelHtml = `<span class="version-tag">${info.tag}</span>`;
|
|
@@ -5310,6 +5338,40 @@ class Toolbar {
|
|
|
5310
5338
|
else {
|
|
5311
5339
|
this.groupBadge.style.display = "none";
|
|
5312
5340
|
}
|
|
5341
|
+
// Update log display
|
|
5342
|
+
this.updateLogDisplay();
|
|
5343
|
+
}
|
|
5344
|
+
/**
|
|
5345
|
+
* Update the log display with the current operation's log feature.
|
|
5346
|
+
*/
|
|
5347
|
+
updateLogDisplay() {
|
|
5348
|
+
if (!this._data || this._currentVersionIndex === 0) {
|
|
5349
|
+
// v0 has no operation, hide log
|
|
5350
|
+
this.logDisplay.style.display = "none";
|
|
5351
|
+
this.logDisplay.textContent = "";
|
|
5352
|
+
this.logDisplay.title = "";
|
|
5353
|
+
return;
|
|
5354
|
+
}
|
|
5355
|
+
const stepIndex = this._currentVersionIndex - 1;
|
|
5356
|
+
if (stepIndex < 0 || stepIndex >= (this._data.steps?.length || 0)) {
|
|
5357
|
+
this.logDisplay.style.display = "none";
|
|
5358
|
+
this.logDisplay.textContent = "";
|
|
5359
|
+
this.logDisplay.title = "";
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
5362
|
+
const step = this._data.steps[stepIndex];
|
|
5363
|
+
// Look for a global feature named "log"
|
|
5364
|
+
const logFeature = step.operation?.features?.find((f) => f.name === "log" && f.isGlobal);
|
|
5365
|
+
if (logFeature && logFeature.value) {
|
|
5366
|
+
this.logDisplay.textContent = logFeature.value;
|
|
5367
|
+
this.logDisplay.title = logFeature.value; // Add tooltip with full text
|
|
5368
|
+
this.logDisplay.style.display = "block";
|
|
5369
|
+
}
|
|
5370
|
+
else {
|
|
5371
|
+
this.logDisplay.style.display = "none";
|
|
5372
|
+
this.logDisplay.textContent = "";
|
|
5373
|
+
this.logDisplay.title = "";
|
|
5374
|
+
}
|
|
5313
5375
|
}
|
|
5314
5376
|
/**
|
|
5315
5377
|
* Get version information for current step.
|
|
@@ -7440,6 +7502,9 @@ class DetailsArea {
|
|
|
7440
7502
|
this._showHiddenFeatures = false;
|
|
7441
7503
|
this._data = null;
|
|
7442
7504
|
this._currentVersionTag = "v0";
|
|
7505
|
+
this._isResizing = false;
|
|
7506
|
+
this._initialHeight = 0;
|
|
7507
|
+
this._initialMouseY = 0;
|
|
7443
7508
|
this._settings = settings;
|
|
7444
7509
|
this._logger = logger;
|
|
7445
7510
|
this._summaryService = new OperationSummaryService(logger);
|
|
@@ -7462,9 +7527,10 @@ class DetailsArea {
|
|
|
7462
7527
|
</div>
|
|
7463
7528
|
<div class="details-content">
|
|
7464
7529
|
<div id="operation-section" class="details-operation-section"></div>
|
|
7465
|
-
<div id="version-features-section" class="details-version-section"></div>
|
|
7466
7530
|
<div id="hovered-elements-section" class="details-hovered-section"></div>
|
|
7531
|
+
<div id="version-features-section" class="details-version-section"></div>
|
|
7467
7532
|
</div>
|
|
7533
|
+
<div class="details-resize-handle" id="details-resize-handle"></div>
|
|
7468
7534
|
`;
|
|
7469
7535
|
// Get element references
|
|
7470
7536
|
this.operationSection = this.container.querySelector("#operation-section");
|
|
@@ -7472,6 +7538,7 @@ class DetailsArea {
|
|
|
7472
7538
|
this.hoveredElementsSection = this.container.querySelector("#hovered-elements-section");
|
|
7473
7539
|
this.hiddenFeaturesCheckbox = this.container.querySelector("#hidden-features-checkbox");
|
|
7474
7540
|
this.collapseBtn = this.container.querySelector("#collapse-details-btn");
|
|
7541
|
+
this.resizeHandle = this.container.querySelector("#details-resize-handle");
|
|
7475
7542
|
this.attachEventListeners();
|
|
7476
7543
|
// Replace feather icons - must be done after elements are in DOM
|
|
7477
7544
|
this.replaceFeatherIcons();
|
|
@@ -7509,6 +7576,51 @@ class DetailsArea {
|
|
|
7509
7576
|
this.collapseBtn.addEventListener("click", () => {
|
|
7510
7577
|
this.toggleCollapse();
|
|
7511
7578
|
});
|
|
7579
|
+
// Resize handle
|
|
7580
|
+
this.resizeHandle.addEventListener("mousedown", (e) => {
|
|
7581
|
+
this.startResize(e);
|
|
7582
|
+
});
|
|
7583
|
+
}
|
|
7584
|
+
/**
|
|
7585
|
+
* Start resizing the details area.
|
|
7586
|
+
*/
|
|
7587
|
+
startResize(e) {
|
|
7588
|
+
e.preventDefault();
|
|
7589
|
+
this._isResizing = true;
|
|
7590
|
+
this._initialHeight = this.container.offsetHeight;
|
|
7591
|
+
this._initialMouseY = e.clientY;
|
|
7592
|
+
// Attach document-level listeners for smooth resizing
|
|
7593
|
+
const onMouseMove = (e) => this.handleResize(e);
|
|
7594
|
+
const onMouseUp = () => this.stopResize(onMouseMove, onMouseUp);
|
|
7595
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
7596
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
7597
|
+
this._logger.debug("DetailsArea", "Started resizing");
|
|
7598
|
+
}
|
|
7599
|
+
/**
|
|
7600
|
+
* Handle resize drag.
|
|
7601
|
+
*/
|
|
7602
|
+
handleResize(e) {
|
|
7603
|
+
if (!this._isResizing)
|
|
7604
|
+
return;
|
|
7605
|
+
// Calculate delta: dragging down increases height, dragging up decreases height
|
|
7606
|
+
const deltaY = e.clientY - this._initialMouseY;
|
|
7607
|
+
const newHeight = this._initialHeight + deltaY;
|
|
7608
|
+
// Enforce minimum and maximum height
|
|
7609
|
+
const minHeight = 100;
|
|
7610
|
+
const maxHeight = 1200;
|
|
7611
|
+
const clampedHeight = Math.max(minHeight, Math.min(maxHeight, newHeight));
|
|
7612
|
+
this.container.style.height = `${clampedHeight}px`;
|
|
7613
|
+
this.container.style.maxHeight = `${clampedHeight}px`;
|
|
7614
|
+
}
|
|
7615
|
+
/**
|
|
7616
|
+
* Stop resizing the details area.
|
|
7617
|
+
*/
|
|
7618
|
+
stopResize(onMouseMove, onMouseUp) {
|
|
7619
|
+
this._isResizing = false;
|
|
7620
|
+
// Remove document-level listeners
|
|
7621
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
7622
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
7623
|
+
this._logger.debug("DetailsArea", `Stopped resizing (height: ${this.container.offsetHeight}px)`);
|
|
7512
7624
|
}
|
|
7513
7625
|
/**
|
|
7514
7626
|
* Toggle collapse/expand state.
|
|
@@ -10725,7 +10837,7 @@ function _assertThisInitialized(self) { if (self === void 0) { throw new Referen
|
|
|
10725
10837
|
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
|
|
10726
10838
|
|
|
10727
10839
|
/*!
|
|
10728
|
-
* GSAP 3.
|
|
10840
|
+
* GSAP 3.14.2
|
|
10729
10841
|
* https://gsap.com
|
|
10730
10842
|
*
|
|
10731
10843
|
* @license Copyright 2008-2025, GreenSock. All rights reserved.
|
|
@@ -10785,6 +10897,8 @@ var _config = {
|
|
|
10785
10897
|
_isTypedArray = typeof ArrayBuffer === "function" && ArrayBuffer.isView || function () {},
|
|
10786
10898
|
// note: IE10 has ArrayBuffer, but NOT ArrayBuffer.isView().
|
|
10787
10899
|
_isArray = Array.isArray,
|
|
10900
|
+
_randomExp = /random\([^)]+\)/g,
|
|
10901
|
+
_commaDelimExp = /,\s*/g,
|
|
10788
10902
|
_strictNumExp = /(?:-?\.?\d|\.)+/gi,
|
|
10789
10903
|
//only numbers (including negatives and decimals) but NOT relative values.
|
|
10790
10904
|
_numExp$1 = /[-+=.]*\d+[.e\-+]*\d*[e\-+]*\d*/g,
|
|
@@ -11379,7 +11493,7 @@ clamp = function clamp(min, max, value) {
|
|
|
11379
11493
|
return _isString(value) && !leaveStrings || _isArrayLike(value, 1) ? (_accumulator = accumulator).push.apply(_accumulator, toArray(value)) : accumulator.push(value);
|
|
11380
11494
|
}) || accumulator;
|
|
11381
11495
|
},
|
|
11382
|
-
//takes any value and returns an
|
|
11496
|
+
// takes any value and returns an Array. If it's a string (and leaveStrings isn't true), it'll use document.querySelectorAll() and convert that to an array. It'll also accept iterables like jQuery objects.
|
|
11383
11497
|
toArray = function toArray(value, scope, leaveStrings) {
|
|
11384
11498
|
return _context && !scope && _context.selector ? _context.selector(value) : _isString(value) && !leaveStrings && (_coreInitted$1 || !_wake()) ? _slice.call((scope || _doc$1).querySelectorAll(value), 0) : _isArray(value) ? _flatten(value, leaveStrings) : _isArrayLike(value) ? _slice.call(value, 0) : value ? [value] : [];
|
|
11385
11499
|
},
|
|
@@ -11396,7 +11510,7 @@ toArray = function toArray(value, scope, leaveStrings) {
|
|
|
11396
11510
|
});
|
|
11397
11511
|
},
|
|
11398
11512
|
// alternative that's a bit faster and more reliably diverse but bigger: for (let j, v, i = a.length; i; j = (Math.random() * i) | 0, v = a[--i], a[i] = a[j], a[j] = v); return a;
|
|
11399
|
-
//for distributing values across an
|
|
11513
|
+
// for distributing values across an Array. Can accept a number, a function or (most commonly) an object which can contain the following properties: {base, amount, from, ease, grid, axis, length, each}. Returns a function that expects the following parameters: index, target, array.
|
|
11400
11514
|
distribute = function distribute(v) {
|
|
11401
11515
|
if (_isFunction(v)) {
|
|
11402
11516
|
return v;
|
|
@@ -11583,24 +11697,13 @@ distribute = function distribute(v) {
|
|
|
11583
11697
|
return min + (value > range ? total - value : value);
|
|
11584
11698
|
});
|
|
11585
11699
|
},
|
|
11586
|
-
_replaceRandom = function _replaceRandom(
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
|
|
11592
|
-
|
|
11593
|
-
isArray;
|
|
11594
|
-
|
|
11595
|
-
while (~(i = value.indexOf("random(", prev))) {
|
|
11596
|
-
end = value.indexOf(")", i);
|
|
11597
|
-
isArray = value.charAt(i + 7) === "[";
|
|
11598
|
-
nums = value.substr(i + 7, end - i - 7).match(isArray ? _delimitedValueExp : _strictNumExp);
|
|
11599
|
-
s += value.substr(prev, i - prev) + random(isArray ? nums : +nums[0], isArray ? 0 : +nums[1], +nums[2] || 1e-5);
|
|
11600
|
-
prev = end + 1;
|
|
11601
|
-
}
|
|
11602
|
-
|
|
11603
|
-
return s + value.substr(prev, value.length - prev);
|
|
11700
|
+
_replaceRandom = function _replaceRandom(s) {
|
|
11701
|
+
return s.replace(_randomExp, function (match) {
|
|
11702
|
+
//replaces all occurrences of random(...) in a string with the calculated random value. can be a range like random(-100, 100, 5) or an array like random([0, 100, 500])
|
|
11703
|
+
var arIndex = match.indexOf("[") + 1,
|
|
11704
|
+
values = match.substring(arIndex || 7, arIndex ? match.indexOf("]") : match.length - 1).split(_commaDelimExp);
|
|
11705
|
+
return random(arIndex ? values : +values[0], arIndex ? 0 : +values[1], +values[2] || 1e-5);
|
|
11706
|
+
});
|
|
11604
11707
|
},
|
|
11605
11708
|
mapRange = function mapRange(inMin, inMax, outMin, outMax, value) {
|
|
11606
11709
|
var inRange = inMax - inMin,
|
|
@@ -12436,7 +12539,7 @@ var Animation = /*#__PURE__*/function () {
|
|
|
12436
12539
|
}
|
|
12437
12540
|
}
|
|
12438
12541
|
|
|
12439
|
-
if (this._tTime !== _totalTime || !this._dur && !suppressEvents || this._initted && Math.abs(this._zTime) === _tinyNum || !_totalTime && !this._initted && (this.add || this._ptLookup)) {
|
|
12542
|
+
if (this._tTime !== _totalTime || !this._dur && !suppressEvents || this._initted && Math.abs(this._zTime) === _tinyNum || !this._initted && this._dur && _totalTime || !_totalTime && !this._initted && (this.add || this._ptLookup)) {
|
|
12440
12543
|
// check for _ptLookup on a Tween instance to ensure it has actually finished being instantiated, otherwise if this.reverse() gets called in the Animation constructor, it could trigger a render() here even though the _targets weren't populated, thus when _init() is called there won't be any PropTweens (it'll act like the tween is non-functional)
|
|
12441
12544
|
this._ts || (this._pTime = _totalTime); // otherwise, if an animation is paused, then the playhead is moved back to zero, then resumed, it'd revert back to the original time at the pause
|
|
12442
12545
|
//if (!this._lock) { // avoid endless recursion (not sure we need this yet or if it's worth the performance hit)
|
|
@@ -12531,9 +12634,9 @@ var Animation = /*#__PURE__*/function () {
|
|
|
12531
12634
|
|
|
12532
12635
|
_proto.startTime = function startTime(value) {
|
|
12533
12636
|
if (arguments.length) {
|
|
12534
|
-
this._start = value;
|
|
12637
|
+
this._start = _roundPrecise(value);
|
|
12535
12638
|
var parent = this.parent || this._dp;
|
|
12536
|
-
parent && (parent._sort || !this.parent) && _addToTimeline(parent, this,
|
|
12639
|
+
parent && (parent._sort || !this.parent) && _addToTimeline(parent, this, this._start - this._delay);
|
|
12537
12640
|
return this;
|
|
12538
12641
|
}
|
|
12539
12642
|
|
|
@@ -12683,13 +12786,15 @@ var Animation = /*#__PURE__*/function () {
|
|
|
12683
12786
|
};
|
|
12684
12787
|
|
|
12685
12788
|
_proto.then = function then(onFulfilled) {
|
|
12686
|
-
var self = this
|
|
12789
|
+
var self = this,
|
|
12790
|
+
prevProm = self._prom;
|
|
12687
12791
|
return new Promise(function (resolve) {
|
|
12688
12792
|
var f = _isFunction(onFulfilled) ? onFulfilled : _passThrough,
|
|
12689
12793
|
_resolve = function _resolve() {
|
|
12690
12794
|
var _then = self.then;
|
|
12691
12795
|
self.then = null; // temporarily null the then() method to avoid an infinite loop (see https://github.com/greensock/GSAP/issues/322)
|
|
12692
12796
|
|
|
12797
|
+
prevProm && prevProm();
|
|
12693
12798
|
_isFunction(f) && (f = f(self)) && (f.then || f === self) && (self.then = _then);
|
|
12694
12799
|
resolve(f);
|
|
12695
12800
|
self.then = _then;
|
|
@@ -12910,7 +13015,11 @@ var Timeline = /*#__PURE__*/function (_Animation) {
|
|
|
12910
13015
|
this._tTime = tTime; // if a user gets the iteration() inside the onRepeat, for example, it should be accurate.
|
|
12911
13016
|
|
|
12912
13017
|
!suppressEvents && this.parent && _callback(this, "onRepeat");
|
|
12913
|
-
|
|
13018
|
+
|
|
13019
|
+
if (this.vars.repeatRefresh && !isYoyo) {
|
|
13020
|
+
this.invalidate()._lock = 1;
|
|
13021
|
+
prevIteration = iteration; // otherwise, the onStart() may fire on the 2nd iteration.
|
|
13022
|
+
}
|
|
12914
13023
|
|
|
12915
13024
|
if (prevTime && prevTime !== this._time || prevPaused !== !this._ts || this.vars.onRepeat && !this.parent && !this._act) {
|
|
12916
13025
|
// if prevTime is 0 and we render at the very end, _time will be the end, thus won't match. So in this edge case, prevTime won't match _time but that's okay. If it gets killed in the onRepeat, eject as well.
|
|
@@ -12958,7 +13067,7 @@ var Timeline = /*#__PURE__*/function (_Animation) {
|
|
|
12958
13067
|
prevTime = 0; // upon init, the playhead should always go forward; someone could invalidate() a completed timeline and then if they restart(), that would make child tweens render in reverse order which could lock in the wrong starting values if they build on each other, like tl.to(obj, {x: 100}).to(obj, {x: 0}).
|
|
12959
13068
|
}
|
|
12960
13069
|
|
|
12961
|
-
if (!prevTime && tTime && !suppressEvents && !prevIteration) {
|
|
13070
|
+
if (!prevTime && tTime && dur && !suppressEvents && !prevIteration) {
|
|
12962
13071
|
_callback(this, "onStart");
|
|
12963
13072
|
|
|
12964
13073
|
if (this._tTime !== tTime) {
|
|
@@ -13305,6 +13414,7 @@ var Timeline = /*#__PURE__*/function (_Animation) {
|
|
|
13305
13414
|
var child = this._first,
|
|
13306
13415
|
labels = this.labels,
|
|
13307
13416
|
p;
|
|
13417
|
+
amount = _roundPrecise(amount);
|
|
13308
13418
|
|
|
13309
13419
|
while (child) {
|
|
13310
13420
|
if (child._start >= ignoreBeforeTime) {
|
|
@@ -13394,7 +13504,7 @@ var Timeline = /*#__PURE__*/function (_Animation) {
|
|
|
13394
13504
|
max -= start;
|
|
13395
13505
|
|
|
13396
13506
|
if (!parent && !self._dp || parent && parent.smoothChildTiming) {
|
|
13397
|
-
self._start += start / self._ts;
|
|
13507
|
+
self._start += _roundPrecise(start / self._ts);
|
|
13398
13508
|
self._time -= start;
|
|
13399
13509
|
self._tTime -= start;
|
|
13400
13510
|
}
|
|
@@ -15194,7 +15304,7 @@ var gsap$1 = _gsap.registerPlugin({
|
|
|
15194
15304
|
}
|
|
15195
15305
|
}, _buildModifierPlugin("roundProps", _roundModifier), _buildModifierPlugin("modifiers"), _buildModifierPlugin("snap", snap)) || _gsap; //to prevent the core plugins from being dropped via aggressive tree shaking, we must include them in the variable declaration in this way.
|
|
15196
15306
|
|
|
15197
|
-
Tween.version = Timeline.version = gsap$1.version = "3.
|
|
15307
|
+
Tween.version = Timeline.version = gsap$1.version = "3.14.2";
|
|
15198
15308
|
_coreReady = 1;
|
|
15199
15309
|
_windowExists$2() && _wake();
|
|
15200
15310
|
_easeMap.Power0;
|
|
@@ -15217,7 +15327,7 @@ _easeMap.Power0;
|
|
|
15217
15327
|
_easeMap.Circ;
|
|
15218
15328
|
|
|
15219
15329
|
/*!
|
|
15220
|
-
* CSSPlugin 3.
|
|
15330
|
+
* CSSPlugin 3.14.2
|
|
15221
15331
|
* https://gsap.com
|
|
15222
15332
|
*
|
|
15223
15333
|
* Copyright 2008-2025, GreenSock. All rights reserved.
|
|
@@ -15259,6 +15369,10 @@ var _win$1,
|
|
|
15259
15369
|
return data.set(data.t, data.p, ratio ? Math.round((data.s + data.c * ratio) * 10000) / 10000 + data.u : data.b, data);
|
|
15260
15370
|
},
|
|
15261
15371
|
//if units change, we need a way to render the original unit/value when the tween goes all the way back to the beginning (ratio:0)
|
|
15372
|
+
_renderCSSPropWithBeginningAndEnd = function _renderCSSPropWithBeginningAndEnd(ratio, data) {
|
|
15373
|
+
return data.set(data.t, data.p, ratio === 1 ? data.e : ratio ? Math.round((data.s + data.c * ratio) * 10000) / 10000 + data.u : data.b, data);
|
|
15374
|
+
},
|
|
15375
|
+
//if units change, we need a way to render the original unit/value when the tween goes all the way back to the beginning (ratio:0)
|
|
15262
15376
|
_renderRoundedCSSProp = function _renderRoundedCSSProp(ratio, data) {
|
|
15263
15377
|
var value = data.s + data.c * ratio;
|
|
15264
15378
|
data.set(data.t, data.p, ~~(value + (value < 0 ? -0.5 : .5)) + data.u, data);
|
|
@@ -16558,7 +16672,8 @@ var CSSPlugin = {
|
|
|
16558
16672
|
cache,
|
|
16559
16673
|
smooth,
|
|
16560
16674
|
hasPriority,
|
|
16561
|
-
inlineProps
|
|
16675
|
+
inlineProps,
|
|
16676
|
+
finalTransformValue;
|
|
16562
16677
|
_pluginInitted || _initCore$1(); // we may call init() multiple times on the same plugin instance, like when adding special properties, so make sure we don't overwrite the revert data or inlineProps
|
|
16563
16678
|
|
|
16564
16679
|
this.styles = this.styles || _getStyleSaver$1(target);
|
|
@@ -16601,9 +16716,9 @@ var CSSPlugin = {
|
|
|
16601
16716
|
// colors don't have units
|
|
16602
16717
|
startUnit = getUnit(startValue);
|
|
16603
16718
|
endUnit = getUnit(endValue);
|
|
16719
|
+
endUnit ? startUnit !== endUnit && (startValue = _convertToUnit(target, p, startValue, endUnit) + endUnit) : startUnit && (endValue += startUnit);
|
|
16604
16720
|
}
|
|
16605
16721
|
|
|
16606
|
-
endUnit ? startUnit !== endUnit && (startValue = _convertToUnit(target, p, startValue, endUnit) + endUnit) : startUnit && (endValue += startUnit);
|
|
16607
16722
|
this.add(style, "setProperty", startValue, endValue, index, targets, 0, 0, p);
|
|
16608
16723
|
props.push(p);
|
|
16609
16724
|
inlineProps.push(p, 0, style[p]);
|
|
@@ -16647,9 +16762,18 @@ var CSSPlugin = {
|
|
|
16647
16762
|
|
|
16648
16763
|
if (isTransformRelated) {
|
|
16649
16764
|
this.styles.save(p);
|
|
16765
|
+
finalTransformValue = endValue; // this is always the same as endValue except when it's a var(--) value, in which case we need to calculate the end value.
|
|
16650
16766
|
|
|
16651
16767
|
if (type === "string" && endValue.substring(0, 6) === "var(--") {
|
|
16652
16768
|
endValue = _getComputedProperty(target, endValue.substring(4, endValue.indexOf(")")));
|
|
16769
|
+
|
|
16770
|
+
if (endValue.substring(0, 5) === "calc(") {
|
|
16771
|
+
var origPerspective = target.style.perspective;
|
|
16772
|
+
target.style.perspective = endValue;
|
|
16773
|
+
endValue = _getComputedProperty(target, "perspective");
|
|
16774
|
+
origPerspective ? target.style.perspective = origPerspective : _removeProperty(target, "perspective");
|
|
16775
|
+
}
|
|
16776
|
+
|
|
16653
16777
|
endNum = parseFloat(endValue);
|
|
16654
16778
|
}
|
|
16655
16779
|
|
|
@@ -16716,7 +16840,11 @@ var CSSPlugin = {
|
|
|
16716
16840
|
this._pt = new PropTween(this._pt, isTransformRelated ? cache : style, p, startNum, (relative ? _parseRelative(startNum, relative + endNum) : endNum) - startNum, !isTransformRelated && (endUnit === "px" || p === "zIndex") && vars.autoRound !== false ? _renderRoundedCSSProp : _renderCSSProp);
|
|
16717
16841
|
this._pt.u = endUnit || 0;
|
|
16718
16842
|
|
|
16719
|
-
if (
|
|
16843
|
+
if (isTransformRelated && finalTransformValue !== endValue) {
|
|
16844
|
+
this._pt.b = startValue;
|
|
16845
|
+
this._pt.e = finalTransformValue;
|
|
16846
|
+
this._pt.r = _renderCSSPropWithBeginningAndEnd;
|
|
16847
|
+
} else if (startUnit !== endUnit && endUnit !== "%") {
|
|
16720
16848
|
//when the tween goes all the way back to the beginning, we need to revert it to the OLD/ORIGINAL value (with those units). We record that as a "b" (beginning) property and point to a render method that handles that. (performance optimization)
|
|
16721
16849
|
this._pt.b = startValue;
|
|
16722
16850
|
this._pt.r = _renderCSSPropWithBeginning;
|
|
@@ -18255,6 +18383,23 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
18255
18383
|
background: #f5f5f5;
|
|
18256
18384
|
}
|
|
18257
18385
|
|
|
18386
|
+
.toolbar-log-display {
|
|
18387
|
+
padding: 0.5rem 1rem;
|
|
18388
|
+
font-size: 13px;
|
|
18389
|
+
color: var(--gve-text-color, #333);
|
|
18390
|
+
font-style: italic;
|
|
18391
|
+
overflow: hidden;
|
|
18392
|
+
text-overflow: ellipsis;
|
|
18393
|
+
text-align: center;
|
|
18394
|
+
max-width: 800px;
|
|
18395
|
+
display: -webkit-box;
|
|
18396
|
+
-webkit-line-clamp: 2;
|
|
18397
|
+
-webkit-box-orient: vertical;
|
|
18398
|
+
line-height: 1.4;
|
|
18399
|
+
max-height: calc(1.4em * 2);
|
|
18400
|
+
cursor: help;
|
|
18401
|
+
}
|
|
18402
|
+
|
|
18258
18403
|
.version-label {
|
|
18259
18404
|
font-family: monospace;
|
|
18260
18405
|
font-size: 16px;
|
|
@@ -18646,6 +18791,26 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
18646
18791
|
overflow: hidden;
|
|
18647
18792
|
}
|
|
18648
18793
|
|
|
18794
|
+
.details-area.collapsed .details-resize-handle {
|
|
18795
|
+
display: none;
|
|
18796
|
+
}
|
|
18797
|
+
|
|
18798
|
+
.details-resize-handle {
|
|
18799
|
+
position: absolute;
|
|
18800
|
+
bottom: 0;
|
|
18801
|
+
left: 0;
|
|
18802
|
+
right: 0;
|
|
18803
|
+
height: 6px;
|
|
18804
|
+
cursor: ns-resize;
|
|
18805
|
+
background: transparent;
|
|
18806
|
+
transition: background-color 0.2s ease;
|
|
18807
|
+
z-index: 10;
|
|
18808
|
+
}
|
|
18809
|
+
|
|
18810
|
+
.details-resize-handle:hover {
|
|
18811
|
+
background-color: var(--gve-border-color, #ddd);
|
|
18812
|
+
}
|
|
18813
|
+
|
|
18649
18814
|
.details-header {
|
|
18650
18815
|
display: flex;
|
|
18651
18816
|
justify-content: space-between;
|
|
@@ -80731,7 +80896,7 @@ var libExports = /*@__PURE__*/ requireLib();
|
|
|
80731
80896
|
var HighlightJS = /*@__PURE__*/getDefaultExportFromCjs(libExports);
|
|
80732
80897
|
|
|
80733
80898
|
/*!
|
|
80734
|
-
* DrawSVGPlugin 3.
|
|
80899
|
+
* DrawSVGPlugin 3.14.2
|
|
80735
80900
|
* https://gsap.com
|
|
80736
80901
|
*
|
|
80737
80902
|
* @license Copyright 2008-2025, GreenSock. All rights reserved.
|
|
@@ -80930,7 +81095,7 @@ _parse = function _parse(value, length, defaultStart) {
|
|
|
80930
81095
|
};
|
|
80931
81096
|
|
|
80932
81097
|
var DrawSVGPlugin = {
|
|
80933
|
-
version: "3.
|
|
81098
|
+
version: "3.14.2",
|
|
80934
81099
|
name: "drawSVG",
|
|
80935
81100
|
register: function register(core) {
|
|
80936
81101
|
gsap = core;
|
|
@@ -81058,7 +81223,9 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81058
81223
|
super();
|
|
81059
81224
|
// Properties
|
|
81060
81225
|
this._data = { hints: {}, animations: {} };
|
|
81061
|
-
this._settings = {
|
|
81226
|
+
this._settings = {
|
|
81227
|
+
...DEFAULT_HINT_DESIGNER_SETTINGS,
|
|
81228
|
+
};
|
|
81062
81229
|
this._hintVariables = [];
|
|
81063
81230
|
// Custom zoom/pan state
|
|
81064
81231
|
this._zoomLevel = 1;
|
|
@@ -81141,9 +81308,18 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81141
81308
|
return this._hintVariables;
|
|
81142
81309
|
}
|
|
81143
81310
|
set hintVariables(value) {
|
|
81144
|
-
|
|
81311
|
+
// Ensure value is an array
|
|
81312
|
+
if (!Array.isArray(value)) {
|
|
81313
|
+
this._logger.warn("Component", "hintVariables setter received non-array value, converting to array", value);
|
|
81314
|
+
this._hintVariables = [];
|
|
81315
|
+
}
|
|
81316
|
+
else {
|
|
81317
|
+
this._hintVariables = value || [];
|
|
81318
|
+
}
|
|
81145
81319
|
this.refreshVariablesTable();
|
|
81146
|
-
this.fireEvent("hintVariablesChange", {
|
|
81320
|
+
this.fireEvent("hintVariablesChange", {
|
|
81321
|
+
hintVariables: this._hintVariables,
|
|
81322
|
+
});
|
|
81147
81323
|
}
|
|
81148
81324
|
// ==================== RENDERING ====================
|
|
81149
81325
|
/**
|
|
@@ -81202,7 +81378,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81202
81378
|
// Hints dropdown
|
|
81203
81379
|
this._hintsDropdown = document.createElement("select");
|
|
81204
81380
|
this._hintsDropdown.className = "hints-dropdown";
|
|
81205
|
-
this._hintsDropdown.innerHTML =
|
|
81381
|
+
this._hintsDropdown.innerHTML =
|
|
81382
|
+
'<option value="">-- Select Hint --</option>';
|
|
81206
81383
|
toolbar.appendChild(this._hintsDropdown);
|
|
81207
81384
|
// Save button (accent)
|
|
81208
81385
|
const saveBtn = this.createButton("save-hint", "save", "Save hint");
|
|
@@ -81260,7 +81437,6 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81260
81437
|
const width = this._settings.hintDesignWidth;
|
|
81261
81438
|
const height = this._settings.hintDesignHeight;
|
|
81262
81439
|
const gridSize = 10; // Grid cell size in pixels
|
|
81263
|
-
const rulerSize = 20; // Ruler width/height in pixels
|
|
81264
81440
|
// Create defs section for the grid pattern
|
|
81265
81441
|
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
81266
81442
|
// Define grid pattern
|
|
@@ -81273,7 +81449,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81273
81449
|
const gridPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
81274
81450
|
gridPath.setAttribute("d", `M ${gridSize} 0 L 0 0 0 ${gridSize}`);
|
|
81275
81451
|
gridPath.setAttribute("fill", "none");
|
|
81276
|
-
gridPath.setAttribute("stroke", "#
|
|
81452
|
+
gridPath.setAttribute("stroke", "#999999");
|
|
81277
81453
|
gridPath.setAttribute("stroke-width", "0.5");
|
|
81278
81454
|
pattern.appendChild(gridPath);
|
|
81279
81455
|
defs.appendChild(pattern);
|
|
@@ -81281,62 +81457,32 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81281
81457
|
// Create main group for grid and rulers (will be behind hint content)
|
|
81282
81458
|
const backgroundGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
81283
81459
|
backgroundGroup.setAttribute("id", "grid-and-rulers");
|
|
81284
|
-
// Add grid background rectangle
|
|
81460
|
+
// Add grid background rectangle - now starting at (0,0)
|
|
81285
81461
|
const gridRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
81286
|
-
gridRect.setAttribute("x",
|
|
81287
|
-
gridRect.setAttribute("y",
|
|
81288
|
-
gridRect.setAttribute("width",
|
|
81289
|
-
gridRect.setAttribute("height",
|
|
81462
|
+
gridRect.setAttribute("x", "0");
|
|
81463
|
+
gridRect.setAttribute("y", "0");
|
|
81464
|
+
gridRect.setAttribute("width", width.toString());
|
|
81465
|
+
gridRect.setAttribute("height", height.toString());
|
|
81290
81466
|
gridRect.setAttribute("fill", "url(#grid)");
|
|
81291
81467
|
backgroundGroup.appendChild(gridRect);
|
|
81292
|
-
// Add horizontal ruler (top)
|
|
81293
|
-
const hRuler = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
81294
|
-
hRuler.setAttribute("x", rulerSize.toString());
|
|
81295
|
-
hRuler.setAttribute("y", "0");
|
|
81296
|
-
hRuler.setAttribute("width", (width - rulerSize).toString());
|
|
81297
|
-
hRuler.setAttribute("height", rulerSize.toString());
|
|
81298
|
-
hRuler.setAttribute("fill", "#f5f5f5");
|
|
81299
|
-
hRuler.setAttribute("stroke", "#ccc");
|
|
81300
|
-
hRuler.setAttribute("stroke-width", "1");
|
|
81301
|
-
backgroundGroup.appendChild(hRuler);
|
|
81302
|
-
// Add vertical ruler (left)
|
|
81303
|
-
const vRuler = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
81304
|
-
vRuler.setAttribute("x", "0");
|
|
81305
|
-
vRuler.setAttribute("y", rulerSize.toString());
|
|
81306
|
-
vRuler.setAttribute("width", rulerSize.toString());
|
|
81307
|
-
vRuler.setAttribute("height", (height - rulerSize).toString());
|
|
81308
|
-
vRuler.setAttribute("fill", "#f5f5f5");
|
|
81309
|
-
vRuler.setAttribute("stroke", "#ccc");
|
|
81310
|
-
vRuler.setAttribute("stroke-width", "1");
|
|
81311
|
-
backgroundGroup.appendChild(vRuler);
|
|
81312
|
-
// Add corner square
|
|
81313
|
-
const corner = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
81314
|
-
corner.setAttribute("x", "0");
|
|
81315
|
-
corner.setAttribute("y", "0");
|
|
81316
|
-
corner.setAttribute("width", rulerSize.toString());
|
|
81317
|
-
corner.setAttribute("height", rulerSize.toString());
|
|
81318
|
-
corner.setAttribute("fill", "#e8e8e8");
|
|
81319
|
-
corner.setAttribute("stroke", "#ccc");
|
|
81320
|
-
corner.setAttribute("stroke-width", "1");
|
|
81321
|
-
backgroundGroup.appendChild(corner);
|
|
81322
81468
|
// Add ruler tick marks and labels (horizontal)
|
|
81323
81469
|
const tickInterval = 50; // Major tick every 50 pixels
|
|
81324
81470
|
const minorTickInterval = 10; // Minor tick every 10 pixels
|
|
81325
|
-
for (let x = 0; x <= width
|
|
81471
|
+
for (let x = 0; x <= width; x += minorTickInterval) {
|
|
81326
81472
|
const isMajorTick = x % tickInterval === 0;
|
|
81327
81473
|
const tickHeight = isMajorTick ? 8 : 4;
|
|
81328
81474
|
const tick = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
81329
|
-
tick.setAttribute("x1",
|
|
81330
|
-
tick.setAttribute("y1",
|
|
81331
|
-
tick.setAttribute("x2",
|
|
81332
|
-
tick.setAttribute("y2",
|
|
81475
|
+
tick.setAttribute("x1", x.toString());
|
|
81476
|
+
tick.setAttribute("y1", "0");
|
|
81477
|
+
tick.setAttribute("x2", x.toString());
|
|
81478
|
+
tick.setAttribute("y2", tickHeight.toString());
|
|
81333
81479
|
tick.setAttribute("stroke", "#666");
|
|
81334
81480
|
tick.setAttribute("stroke-width", "1");
|
|
81335
81481
|
backgroundGroup.appendChild(tick);
|
|
81336
|
-
// Add label for major ticks
|
|
81482
|
+
// Add label for major ticks - positioned inside the grid, below the top edge
|
|
81337
81483
|
if (isMajorTick && x > 0) {
|
|
81338
81484
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
81339
|
-
label.setAttribute("x",
|
|
81485
|
+
label.setAttribute("x", x.toString());
|
|
81340
81486
|
label.setAttribute("y", "12");
|
|
81341
81487
|
label.setAttribute("font-size", "9");
|
|
81342
81488
|
label.setAttribute("font-family", "Arial, sans-serif");
|
|
@@ -81347,26 +81493,26 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81347
81493
|
}
|
|
81348
81494
|
}
|
|
81349
81495
|
// Add ruler tick marks and labels (vertical)
|
|
81350
|
-
for (let y = 0; y <= height
|
|
81496
|
+
for (let y = 0; y <= height; y += minorTickInterval) {
|
|
81351
81497
|
const isMajorTick = y % tickInterval === 0;
|
|
81352
81498
|
const tickWidth = isMajorTick ? 8 : 4;
|
|
81353
81499
|
const tick = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
81354
|
-
tick.setAttribute("x1",
|
|
81355
|
-
tick.setAttribute("y1",
|
|
81356
|
-
tick.setAttribute("x2",
|
|
81357
|
-
tick.setAttribute("y2",
|
|
81500
|
+
tick.setAttribute("x1", "0");
|
|
81501
|
+
tick.setAttribute("y1", y.toString());
|
|
81502
|
+
tick.setAttribute("x2", tickWidth.toString());
|
|
81503
|
+
tick.setAttribute("y2", y.toString());
|
|
81358
81504
|
tick.setAttribute("stroke", "#666");
|
|
81359
81505
|
tick.setAttribute("stroke-width", "1");
|
|
81360
81506
|
backgroundGroup.appendChild(tick);
|
|
81361
|
-
// Add label for major ticks
|
|
81507
|
+
// Add label for major ticks - positioned inside the grid, to the right of the left edge
|
|
81362
81508
|
if (isMajorTick && y > 0) {
|
|
81363
81509
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
81364
81510
|
label.setAttribute("x", "10");
|
|
81365
|
-
label.setAttribute("y", (
|
|
81511
|
+
label.setAttribute("y", (y + 3).toString());
|
|
81366
81512
|
label.setAttribute("font-size", "9");
|
|
81367
81513
|
label.setAttribute("font-family", "Arial, sans-serif");
|
|
81368
81514
|
label.setAttribute("fill", "#333");
|
|
81369
|
-
label.setAttribute("text-anchor", "
|
|
81515
|
+
label.setAttribute("text-anchor", "start");
|
|
81370
81516
|
label.textContent = y.toString();
|
|
81371
81517
|
backgroundGroup.appendChild(label);
|
|
81372
81518
|
}
|
|
@@ -81514,7 +81660,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81514
81660
|
// Animation dropdown
|
|
81515
81661
|
this._animationsDropdown = document.createElement("select");
|
|
81516
81662
|
this._animationsDropdown.className = "animations-dropdown";
|
|
81517
|
-
this._animationsDropdown.innerHTML =
|
|
81663
|
+
this._animationsDropdown.innerHTML =
|
|
81664
|
+
'<option value="">-- Select Animation --</option>';
|
|
81518
81665
|
animControls.appendChild(this._animationsDropdown);
|
|
81519
81666
|
// Load button
|
|
81520
81667
|
const loadAnimBtn = this.createButton("load-animation", "download-cloud", "Load animation code");
|
|
@@ -81542,10 +81689,14 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81542
81689
|
createHintVariablesList() {
|
|
81543
81690
|
const panel = document.createElement("div");
|
|
81544
81691
|
panel.className = "variables-panel resizable-panel";
|
|
81545
|
-
// Header
|
|
81692
|
+
// Header with warning icon placeholder
|
|
81546
81693
|
const header = document.createElement("div");
|
|
81547
81694
|
header.className = "panel-header";
|
|
81548
|
-
|
|
81695
|
+
const headerText = document.createElement("span");
|
|
81696
|
+
headerText.textContent = "Hint Variables";
|
|
81697
|
+
header.appendChild(headerText);
|
|
81698
|
+
// Store reference to header for adding warning icon
|
|
81699
|
+
this._variablesPanelHeader = header;
|
|
81549
81700
|
panel.appendChild(header);
|
|
81550
81701
|
// Toolbar
|
|
81551
81702
|
const toolbar = document.createElement("div");
|
|
@@ -81553,10 +81704,18 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81553
81704
|
const addVarBtn = this.createButton("add-variable", "plus-circle", "Add new variable");
|
|
81554
81705
|
addVarBtn.classList.add("btn-primary");
|
|
81555
81706
|
toolbar.appendChild(addVarBtn);
|
|
81707
|
+
const populateBtn = this.createButton("populate-from-hints", "download-cloud", "Populate from all hints");
|
|
81708
|
+
populateBtn.classList.add("btn-accent");
|
|
81709
|
+
toolbar.appendChild(populateBtn);
|
|
81556
81710
|
const deleteAllBtn = this.createButton("delete-all-vars", "trash-2", "Delete all variables");
|
|
81557
81711
|
deleteAllBtn.classList.add("btn-danger");
|
|
81558
81712
|
toolbar.appendChild(deleteAllBtn);
|
|
81559
81713
|
panel.appendChild(toolbar);
|
|
81714
|
+
// Validation message (initially hidden)
|
|
81715
|
+
this._variablesValidationMsg = document.createElement("div");
|
|
81716
|
+
this._variablesValidationMsg.className = "validation-message";
|
|
81717
|
+
this._variablesValidationMsg.style.display = "none";
|
|
81718
|
+
panel.appendChild(this._variablesValidationMsg);
|
|
81560
81719
|
// Content
|
|
81561
81720
|
const content = document.createElement("div");
|
|
81562
81721
|
content.className = "panel-content";
|
|
@@ -81579,7 +81738,12 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81579
81738
|
// Use feather.icons to get the SVG directly (for Shadow DOM compatibility)
|
|
81580
81739
|
const icon = featherExports.icons[iconName];
|
|
81581
81740
|
if (icon) {
|
|
81582
|
-
btn.innerHTML = icon.toSvg({
|
|
81741
|
+
btn.innerHTML = icon.toSvg({
|
|
81742
|
+
class: "feather-icon",
|
|
81743
|
+
width: 16,
|
|
81744
|
+
height: 16,
|
|
81745
|
+
"stroke-width": 2,
|
|
81746
|
+
});
|
|
81583
81747
|
}
|
|
81584
81748
|
else {
|
|
81585
81749
|
this._logger.warn("Feather icon not found:", iconName);
|
|
@@ -81649,14 +81813,14 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81649
81813
|
splitter.addEventListener("pointerdown", (e) => {
|
|
81650
81814
|
const startY = e.clientY;
|
|
81651
81815
|
const startHeightAbove = panelAbove.offsetHeight;
|
|
81652
|
-
const container = this._shadow.querySelector(
|
|
81816
|
+
const container = this._shadow.querySelector(".hint-designer-container");
|
|
81653
81817
|
if (!container)
|
|
81654
81818
|
return;
|
|
81655
81819
|
const onMove = (moveEvent) => {
|
|
81656
81820
|
const delta = moveEvent.clientY - startY;
|
|
81657
81821
|
const newHeightAbove = Math.max(300, startHeightAbove + delta);
|
|
81658
81822
|
// Determine which splitter this is based on the panel above
|
|
81659
|
-
const isTopSplitter = panelAbove.classList.contains(
|
|
81823
|
+
const isTopSplitter = panelAbove.classList.contains("top-panels-wrapper");
|
|
81660
81824
|
if (isTopSplitter) {
|
|
81661
81825
|
// Splitter 1: between top and properties
|
|
81662
81826
|
// Set top height, let properties and variables share remaining space
|
|
@@ -81665,7 +81829,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81665
81829
|
else {
|
|
81666
81830
|
// Splitter 2: between properties and variables
|
|
81667
81831
|
// Get top panel height and keep it fixed
|
|
81668
|
-
const topPanel = this._shadow.querySelector(
|
|
81832
|
+
const topPanel = this._shadow.querySelector(".top-panels-wrapper");
|
|
81669
81833
|
const topHeight = topPanel ? topPanel.offsetHeight : 300;
|
|
81670
81834
|
// Set top and properties heights, let variables fill remaining
|
|
81671
81835
|
container.style.gridTemplateRows = `${topHeight}px 6px ${newHeightAbove}px 6px minmax(200px, 1fr)`;
|
|
@@ -81723,6 +81887,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81723
81887
|
// Variables panel
|
|
81724
81888
|
this.addClickListener("delete-all-vars", () => this.deleteAllVariables());
|
|
81725
81889
|
this.addClickListener("add-variable", () => this.addVariable());
|
|
81890
|
+
this.addClickListener("populate-from-hints", () => this.populateVariablesFromHints());
|
|
81726
81891
|
// Word wrap toggles
|
|
81727
81892
|
this.addClickListener("toggle-svg-wrap", () => this.toggleSvgWordWrap());
|
|
81728
81893
|
this.addClickListener("toggle-js-wrap", () => this.toggleJsWordWrap());
|
|
@@ -81745,17 +81910,17 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81745
81910
|
* Initialize panning with mouse drag.
|
|
81746
81911
|
*/
|
|
81747
81912
|
initializePanning() {
|
|
81748
|
-
const panel = this._shadow.querySelector(
|
|
81913
|
+
const panel = this._shadow.querySelector(".svg-display-panel");
|
|
81749
81914
|
if (!panel)
|
|
81750
81915
|
return;
|
|
81751
|
-
panel.addEventListener(
|
|
81916
|
+
panel.addEventListener("mousedown", (e) => {
|
|
81752
81917
|
const mouseEvent = e;
|
|
81753
81918
|
this._isPanning = true;
|
|
81754
81919
|
this._lastPanX = mouseEvent.clientX;
|
|
81755
81920
|
this._lastPanY = mouseEvent.clientY;
|
|
81756
|
-
panel.style.cursor =
|
|
81921
|
+
panel.style.cursor = "grabbing";
|
|
81757
81922
|
});
|
|
81758
|
-
document.addEventListener(
|
|
81923
|
+
document.addEventListener("mousemove", (e) => {
|
|
81759
81924
|
if (!this._isPanning)
|
|
81760
81925
|
return;
|
|
81761
81926
|
const deltaX = e.clientX - this._lastPanX;
|
|
@@ -81766,24 +81931,24 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81766
81931
|
this._lastPanY = e.clientY;
|
|
81767
81932
|
this.updateSvgTransform();
|
|
81768
81933
|
});
|
|
81769
|
-
document.addEventListener(
|
|
81934
|
+
document.addEventListener("mouseup", () => {
|
|
81770
81935
|
if (this._isPanning) {
|
|
81771
81936
|
this._isPanning = false;
|
|
81772
81937
|
const panelEl = panel;
|
|
81773
|
-
panelEl.style.cursor =
|
|
81938
|
+
panelEl.style.cursor = "grab";
|
|
81774
81939
|
}
|
|
81775
81940
|
});
|
|
81776
81941
|
// Set initial cursor
|
|
81777
|
-
panel.style.cursor =
|
|
81942
|
+
panel.style.cursor = "grab";
|
|
81778
81943
|
}
|
|
81779
81944
|
/**
|
|
81780
81945
|
* Initialize zooming with mouse wheel.
|
|
81781
81946
|
*/
|
|
81782
81947
|
initializeWheelZoom() {
|
|
81783
|
-
const panel = this._shadow.querySelector(
|
|
81948
|
+
const panel = this._shadow.querySelector(".svg-display-panel");
|
|
81784
81949
|
if (!panel)
|
|
81785
81950
|
return;
|
|
81786
|
-
panel.addEventListener(
|
|
81951
|
+
panel.addEventListener("wheel", (e) => {
|
|
81787
81952
|
const wheelEvent = e;
|
|
81788
81953
|
wheelEvent.preventDefault();
|
|
81789
81954
|
// Determine zoom direction based on wheel delta
|
|
@@ -81838,7 +82003,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81838
82003
|
if (!this._hintsDropdown)
|
|
81839
82004
|
return;
|
|
81840
82005
|
const currentValue = this._hintsDropdown.value;
|
|
81841
|
-
this._hintsDropdown.innerHTML =
|
|
82006
|
+
this._hintsDropdown.innerHTML =
|
|
82007
|
+
'<option value="">-- Select Hint --</option>';
|
|
81842
82008
|
const hintIds = Object.keys(this._data.hints);
|
|
81843
82009
|
hintIds.forEach((hintId) => {
|
|
81844
82010
|
const option = document.createElement("option");
|
|
@@ -81860,7 +82026,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81860
82026
|
if (!this._animationsDropdown)
|
|
81861
82027
|
return;
|
|
81862
82028
|
const currentValue = this._animationsDropdown.value;
|
|
81863
|
-
this._animationsDropdown.innerHTML =
|
|
82029
|
+
this._animationsDropdown.innerHTML =
|
|
82030
|
+
'<option value="">-- Select Animation --</option>';
|
|
81864
82031
|
Object.keys(this._data.animations).forEach((animId) => {
|
|
81865
82032
|
const option = document.createElement("option");
|
|
81866
82033
|
option.value = animId;
|
|
@@ -81931,7 +82098,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81931
82098
|
if (this._jsTextarea) {
|
|
81932
82099
|
// If the ID is not found, use the template; otherwise use empty string
|
|
81933
82100
|
const animCode = this._data.animations[animId];
|
|
81934
|
-
this._jsTextarea.value =
|
|
82101
|
+
this._jsTextarea.value =
|
|
82102
|
+
animCode !== undefined ? animCode : NEW_ANIMATION_TEMPLATE;
|
|
81935
82103
|
this.highlightJs();
|
|
81936
82104
|
}
|
|
81937
82105
|
}
|
|
@@ -81988,6 +82156,49 @@ class GveHintDesigner extends HTMLElement {
|
|
|
81988
82156
|
if (this._embeddedJsCheckbox)
|
|
81989
82157
|
this._embeddedJsCheckbox.checked = false;
|
|
81990
82158
|
}
|
|
82159
|
+
/**
|
|
82160
|
+
* Validate SVG code for well-formedness and structure.
|
|
82161
|
+
* Returns an error message if validation fails, or null if valid.
|
|
82162
|
+
*/
|
|
82163
|
+
validateSvgCode(svgCode) {
|
|
82164
|
+
// Check if SVG code is empty
|
|
82165
|
+
if (!svgCode || !svgCode.trim()) {
|
|
82166
|
+
return "SVG code cannot be empty";
|
|
82167
|
+
}
|
|
82168
|
+
const trimmedCode = svgCode.trim();
|
|
82169
|
+
// Check that it starts with <g and ends with </g>
|
|
82170
|
+
if (!trimmedCode.startsWith("<g")) {
|
|
82171
|
+
return "SVG must start with a <g> element (e.g., <g> or <g ...>)";
|
|
82172
|
+
}
|
|
82173
|
+
if (!trimmedCode.endsWith("</g>")) {
|
|
82174
|
+
return "SVG must end with </g> (closing tag for the root <g> element)";
|
|
82175
|
+
}
|
|
82176
|
+
// Use DOMParser to validate XML well-formedness
|
|
82177
|
+
const parser = new DOMParser();
|
|
82178
|
+
const wrappedSvg = `<svg xmlns="http://www.w3.org/2000/svg">${svgCode}</svg>`;
|
|
82179
|
+
const doc = parser.parseFromString(wrappedSvg, "image/svg+xml");
|
|
82180
|
+
// Check for parsing errors
|
|
82181
|
+
const errorNode = doc.querySelector("parsererror");
|
|
82182
|
+
if (errorNode) {
|
|
82183
|
+
const errorText = errorNode.textContent || "Unknown XML parsing error";
|
|
82184
|
+
return `XML is not well-formed: ${errorText}`;
|
|
82185
|
+
}
|
|
82186
|
+
// Verify there is exactly one root <g> element
|
|
82187
|
+
const svgElement = doc.documentElement;
|
|
82188
|
+
const rootChildren = Array.from(svgElement.children);
|
|
82189
|
+
if (rootChildren.length === 0) {
|
|
82190
|
+
return "SVG must contain a <g> element";
|
|
82191
|
+
}
|
|
82192
|
+
if (rootChildren.length > 1) {
|
|
82193
|
+
return `SVG must have exactly one root <g> element, but found ${rootChildren.length} root elements. Wrap all content in a single <g> element.`;
|
|
82194
|
+
}
|
|
82195
|
+
const rootElement = rootChildren[0];
|
|
82196
|
+
if (rootElement.tagName.toLowerCase() !== "g") {
|
|
82197
|
+
return `Root element must be <g>, but found <${rootElement.tagName}>`;
|
|
82198
|
+
}
|
|
82199
|
+
// All validations passed
|
|
82200
|
+
return null;
|
|
82201
|
+
}
|
|
81991
82202
|
/**
|
|
81992
82203
|
* Add a new hint.
|
|
81993
82204
|
*/
|
|
@@ -82027,19 +82238,21 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82027
82238
|
// Validate SVG and JS
|
|
82028
82239
|
const svgCode = this._svgTextarea?.value || "";
|
|
82029
82240
|
const animationValue = this._jsTextarea?.value || "";
|
|
82241
|
+
// Validate SVG code for well-formedness and structure
|
|
82242
|
+
const svgValidationError = this.validateSvgCode(svgCode);
|
|
82243
|
+
if (svgValidationError) {
|
|
82244
|
+
this.showMessage(`SVG validation error: ${svgValidationError}`, "error");
|
|
82245
|
+
return; // Prevent saving on error
|
|
82246
|
+
}
|
|
82247
|
+
// Validate JS syntax if present
|
|
82030
82248
|
try {
|
|
82031
|
-
// Validate SVG (basic check)
|
|
82032
|
-
if (svgCode && !svgCode.trim().startsWith("<g")) {
|
|
82033
|
-
throw new Error("SVG must start with <g> element");
|
|
82034
|
-
}
|
|
82035
|
-
// Validate JS syntax if present (we'll do more specific validation below)
|
|
82036
82249
|
if (animationValue && !animationValue.trim().startsWith("#")) {
|
|
82037
82250
|
// Try to create a function to validate syntax
|
|
82038
82251
|
new Function("gsap", "targetEl", "rootEl", animationValue);
|
|
82039
82252
|
}
|
|
82040
82253
|
}
|
|
82041
82254
|
catch (err) {
|
|
82042
|
-
this.showMessage(`
|
|
82255
|
+
this.showMessage(`JS validation error: ${err}`, "error");
|
|
82043
82256
|
return; // Prevent saving on error
|
|
82044
82257
|
}
|
|
82045
82258
|
// Build hint object
|
|
@@ -82178,23 +82391,33 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82178
82391
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
82179
82392
|
const element = node;
|
|
82180
82393
|
const tagName = element.tagName.toLowerCase();
|
|
82181
|
-
const id = element.getAttribute(
|
|
82394
|
+
const id = element.getAttribute("id");
|
|
82182
82395
|
// Keep defs and grid-and-rulers, remove everything else
|
|
82183
|
-
if (tagName !==
|
|
82396
|
+
if (tagName !== "defs" && id !== "grid-and-rulers") {
|
|
82184
82397
|
nodesToRemove.push(node);
|
|
82185
82398
|
}
|
|
82186
82399
|
}
|
|
82187
82400
|
}
|
|
82188
|
-
nodesToRemove.forEach(node => this._svgDisplay.removeChild(node));
|
|
82401
|
+
nodesToRemove.forEach((node) => this._svgDisplay.removeChild(node));
|
|
82189
82402
|
// If grid and rulers don't exist yet, add them
|
|
82190
|
-
if (!this._svgDisplay.querySelector(
|
|
82403
|
+
if (!this._svgDisplay.querySelector("#grid-and-rulers")) {
|
|
82191
82404
|
this.addGridAndRulers(this._svgDisplay);
|
|
82192
82405
|
}
|
|
82193
|
-
if (!this._hintId
|
|
82406
|
+
if (!this._hintId) {
|
|
82194
82407
|
return;
|
|
82195
82408
|
}
|
|
82196
|
-
|
|
82197
|
-
let svgCode =
|
|
82409
|
+
// Get SVG code from textarea if it exists, otherwise from saved data
|
|
82410
|
+
let svgCode = this._svgTextarea?.value || "";
|
|
82411
|
+
// Fallback to saved data if textarea is empty and we have saved data
|
|
82412
|
+
if (!svgCode && this._data.hints[this._hintId]) {
|
|
82413
|
+
svgCode = this._data.hints[this._hintId].svg;
|
|
82414
|
+
}
|
|
82415
|
+
// If still no SVG code, return early
|
|
82416
|
+
if (!svgCode) {
|
|
82417
|
+
return;
|
|
82418
|
+
}
|
|
82419
|
+
// Validate variables before resolving them
|
|
82420
|
+
this.validateSvgVariables(svgCode);
|
|
82198
82421
|
// Resolve variables
|
|
82199
82422
|
svgCode = this.resolveVariables(svgCode);
|
|
82200
82423
|
try {
|
|
@@ -82244,13 +82467,103 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82244
82467
|
});
|
|
82245
82468
|
return resolved;
|
|
82246
82469
|
}
|
|
82470
|
+
/**
|
|
82471
|
+
* Extract variable names from SVG code (variables in {{...}} placeholders).
|
|
82472
|
+
*/
|
|
82473
|
+
extractVariablesFromSvg(svgCode) {
|
|
82474
|
+
const variablePattern = /\{\{([^}]+)\}\}/g;
|
|
82475
|
+
const variables = [];
|
|
82476
|
+
let match;
|
|
82477
|
+
while ((match = variablePattern.exec(svgCode)) !== null) {
|
|
82478
|
+
const varName = match[1].trim();
|
|
82479
|
+
if (varName && !variables.includes(varName)) {
|
|
82480
|
+
variables.push(varName);
|
|
82481
|
+
}
|
|
82482
|
+
}
|
|
82483
|
+
return variables;
|
|
82484
|
+
}
|
|
82485
|
+
/**
|
|
82486
|
+
* Validate SVG variables and update validation message.
|
|
82487
|
+
* Shows a warning if any variables used in SVG are not defined in hint variables.
|
|
82488
|
+
*/
|
|
82489
|
+
validateSvgVariables(svgCode) {
|
|
82490
|
+
if (!this._variablesValidationMsg || !this._variablesPanelHeader) {
|
|
82491
|
+
return;
|
|
82492
|
+
}
|
|
82493
|
+
// Extract variables from SVG
|
|
82494
|
+
const usedVariables = this.extractVariablesFromSvg(svgCode);
|
|
82495
|
+
if (usedVariables.length === 0) {
|
|
82496
|
+
// No variables used - hide validation message and warning icon
|
|
82497
|
+
this._variablesValidationMsg.style.display = "none";
|
|
82498
|
+
this.updateVariablesHeaderWarning(false);
|
|
82499
|
+
return;
|
|
82500
|
+
}
|
|
82501
|
+
// Get defined variable names
|
|
82502
|
+
const definedVariables = this._hintVariables.map((v) => v.name);
|
|
82503
|
+
// Find undefined variables
|
|
82504
|
+
const undefinedVariables = usedVariables.filter((v) => !definedVariables.includes(v));
|
|
82505
|
+
if (undefinedVariables.length === 0) {
|
|
82506
|
+
// All variables are defined - hide validation message and warning icon
|
|
82507
|
+
this._variablesValidationMsg.style.display = "none";
|
|
82508
|
+
this.updateVariablesHeaderWarning(false);
|
|
82509
|
+
return;
|
|
82510
|
+
}
|
|
82511
|
+
// Show validation message with list of undefined variables
|
|
82512
|
+
this._variablesValidationMsg.style.display = "block";
|
|
82513
|
+
this._variablesValidationMsg.innerHTML = `
|
|
82514
|
+
<strong>⚠️ Undefined Variables:</strong>
|
|
82515
|
+
<div style="margin-top: 0.5rem;">
|
|
82516
|
+
The following variables are used in the SVG code but not defined in Hint Variables:
|
|
82517
|
+
<ul style="margin: 0.5rem 0; padding-left: 1.5rem;">
|
|
82518
|
+
${undefinedVariables
|
|
82519
|
+
.map((v) => `<li><code>{{${v}}}</code></li>`)
|
|
82520
|
+
.join("")}
|
|
82521
|
+
</ul>
|
|
82522
|
+
<em>Add these variables to the list below to ensure the hint displays correctly.</em>
|
|
82523
|
+
</div>
|
|
82524
|
+
`;
|
|
82525
|
+
// Show warning icon in header
|
|
82526
|
+
this.updateVariablesHeaderWarning(true);
|
|
82527
|
+
}
|
|
82528
|
+
/**
|
|
82529
|
+
* Update warning icon in variables panel header.
|
|
82530
|
+
*/
|
|
82531
|
+
updateVariablesHeaderWarning(showWarning) {
|
|
82532
|
+
if (!this._variablesPanelHeader)
|
|
82533
|
+
return;
|
|
82534
|
+
// Remove existing warning icon if present
|
|
82535
|
+
const existingWarning = this._variablesPanelHeader.querySelector(".warning-icon");
|
|
82536
|
+
if (existingWarning) {
|
|
82537
|
+
existingWarning.remove();
|
|
82538
|
+
}
|
|
82539
|
+
if (showWarning) {
|
|
82540
|
+
// Add warning icon
|
|
82541
|
+
const warningIcon = document.createElement("span");
|
|
82542
|
+
warningIcon.className = "warning-icon";
|
|
82543
|
+
warningIcon.title = "Some variables used in SVG are not defined";
|
|
82544
|
+
const icon = featherExports.icons["alert-triangle"];
|
|
82545
|
+
if (icon) {
|
|
82546
|
+
warningIcon.innerHTML = icon.toSvg({
|
|
82547
|
+
class: "feather-icon",
|
|
82548
|
+
width: 16,
|
|
82549
|
+
height: 16,
|
|
82550
|
+
"stroke-width": 2,
|
|
82551
|
+
color: "#ff9800",
|
|
82552
|
+
});
|
|
82553
|
+
}
|
|
82554
|
+
else {
|
|
82555
|
+
warningIcon.textContent = "⚠️";
|
|
82556
|
+
}
|
|
82557
|
+
this._variablesPanelHeader.appendChild(warningIcon);
|
|
82558
|
+
}
|
|
82559
|
+
}
|
|
82247
82560
|
/**
|
|
82248
82561
|
* Play animation.
|
|
82249
82562
|
*/
|
|
82250
82563
|
playAnimation() {
|
|
82251
82564
|
this._logger.debug("Animation", "playAnimation called", {
|
|
82252
82565
|
hasTimeline: !!this._currentTimeline,
|
|
82253
|
-
isPlaying: this._isPlaying
|
|
82566
|
+
isPlaying: this._isPlaying,
|
|
82254
82567
|
});
|
|
82255
82568
|
if (this._currentTimeline) {
|
|
82256
82569
|
// If timeline exists, just play it
|
|
@@ -82288,7 +82601,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82288
82601
|
createTimeline() {
|
|
82289
82602
|
this._logger.debug("Animation", "createTimeline called", {
|
|
82290
82603
|
hasSvgDisplay: !!this._svgDisplay,
|
|
82291
|
-
hintId: this._hintId
|
|
82604
|
+
hintId: this._hintId,
|
|
82292
82605
|
});
|
|
82293
82606
|
if (!this._svgDisplay || !this._hintId || !this._data.hints[this._hintId]) {
|
|
82294
82607
|
this.showMessage("No hint to animate!", "error");
|
|
@@ -82302,11 +82615,16 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82302
82615
|
if (hint.animation.startsWith("#")) {
|
|
82303
82616
|
const animId = hint.animation.substring(1);
|
|
82304
82617
|
jsCode = this._data.animations[animId] || "";
|
|
82305
|
-
this._logger.debug("Animation", "Using referenced animation", {
|
|
82618
|
+
this._logger.debug("Animation", "Using referenced animation", {
|
|
82619
|
+
animId,
|
|
82620
|
+
hasCode: !!jsCode,
|
|
82621
|
+
});
|
|
82306
82622
|
}
|
|
82307
82623
|
else {
|
|
82308
82624
|
jsCode = hint.animation;
|
|
82309
|
-
this._logger.debug("Animation", "Using embedded animation", {
|
|
82625
|
+
this._logger.debug("Animation", "Using embedded animation", {
|
|
82626
|
+
codeLength: jsCode.length,
|
|
82627
|
+
});
|
|
82310
82628
|
}
|
|
82311
82629
|
}
|
|
82312
82630
|
if (!jsCode) {
|
|
@@ -82323,7 +82641,10 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82323
82641
|
if (!hintEl) {
|
|
82324
82642
|
throw new Error("No hint <g> element found in SVG");
|
|
82325
82643
|
}
|
|
82326
|
-
this._logger.debug("Animation", "Found hint element", {
|
|
82644
|
+
this._logger.debug("Animation", "Found hint element", {
|
|
82645
|
+
tagName: hintEl.tagName,
|
|
82646
|
+
id: hintEl.getAttribute("id"),
|
|
82647
|
+
});
|
|
82327
82648
|
// Set initial opacity to 0 (on style attribute)
|
|
82328
82649
|
hintEl.style.opacity = "0";
|
|
82329
82650
|
// Check if GSAP is available
|
|
@@ -82369,12 +82690,12 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82369
82690
|
}
|
|
82370
82691
|
this._logger.debug("Animation", "Animation function executed", {
|
|
82371
82692
|
resultType: typeof result,
|
|
82372
|
-
isPromise: result && typeof result.then ===
|
|
82373
|
-
isTimeline: result && typeof result.progress ===
|
|
82374
|
-
capturedTweensCount: capturedTweens.length
|
|
82693
|
+
isPromise: result && typeof result.then === "function",
|
|
82694
|
+
isTimeline: result && typeof result.progress === "function",
|
|
82695
|
+
capturedTweensCount: capturedTweens.length,
|
|
82375
82696
|
});
|
|
82376
82697
|
// Check what was returned
|
|
82377
|
-
if (result && typeof result.progress ===
|
|
82698
|
+
if (result && typeof result.progress === "function") {
|
|
82378
82699
|
// Direct timeline/tween return
|
|
82379
82700
|
this._currentTimeline = result;
|
|
82380
82701
|
this._currentTimeline.pause();
|
|
@@ -82384,7 +82705,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82384
82705
|
// Animation called gsap methods - create timeline with captured tweens
|
|
82385
82706
|
this._logger.debug("Animation", "Creating timeline from captured tweens", {
|
|
82386
82707
|
tweenCount: capturedTweens.length,
|
|
82387
|
-
tweenDurations: capturedTweens.map(t => t.duration())
|
|
82708
|
+
tweenDurations: capturedTweens.map((t) => t.duration()),
|
|
82388
82709
|
});
|
|
82389
82710
|
// The tweens have already started playing, so we need to:
|
|
82390
82711
|
// 1. Kill them to stop auto-playing
|
|
@@ -82399,14 +82720,14 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82399
82720
|
duration: duration,
|
|
82400
82721
|
totalDuration: totalDuration,
|
|
82401
82722
|
targets: tween.targets(),
|
|
82402
|
-
vars: tween.vars
|
|
82723
|
+
vars: tween.vars,
|
|
82403
82724
|
});
|
|
82404
82725
|
// Get the animation method that was used (to, from, fromTo)
|
|
82405
82726
|
const targets = tween.targets();
|
|
82406
82727
|
const vars = { ...tween.vars };
|
|
82407
82728
|
// Get the initial values if this was a fromTo or from tween
|
|
82408
82729
|
const isFromTo = tween._from !== undefined;
|
|
82409
|
-
const isFrom = !isFromTo && tween.vars.hasOwnProperty(
|
|
82730
|
+
const isFrom = !isFromTo && tween.vars.hasOwnProperty("startAt");
|
|
82410
82731
|
// Kill the original tween to stop it from playing
|
|
82411
82732
|
tween.kill();
|
|
82412
82733
|
// Recreate the tween in our timeline using the appropriate method
|
|
@@ -82425,7 +82746,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82425
82746
|
}
|
|
82426
82747
|
});
|
|
82427
82748
|
this._logger.debug("Animation", "Timeline built from tweens", {
|
|
82428
|
-
timelineDuration: this._currentTimeline.duration()
|
|
82749
|
+
timelineDuration: this._currentTimeline.duration(),
|
|
82429
82750
|
});
|
|
82430
82751
|
}
|
|
82431
82752
|
else {
|
|
@@ -82436,7 +82757,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82436
82757
|
this._logger.debug("Animation", "Timeline created", {
|
|
82437
82758
|
duration: this._currentTimeline.duration(),
|
|
82438
82759
|
progress: this._currentTimeline.progress(),
|
|
82439
|
-
paused: this._currentTimeline.paused()
|
|
82760
|
+
paused: this._currentTimeline.paused(),
|
|
82440
82761
|
});
|
|
82441
82762
|
// Check if timeline has duration
|
|
82442
82763
|
if (this._currentTimeline.duration() === 0) {
|
|
@@ -82448,7 +82769,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82448
82769
|
this._logger.debug("Animation", "Animation completed callback", {
|
|
82449
82770
|
finalProgress: this._currentTimeline.progress(),
|
|
82450
82771
|
finalTime: this._currentTimeline.time(),
|
|
82451
|
-
duration: this._currentTimeline.duration()
|
|
82772
|
+
duration: this._currentTimeline.duration(),
|
|
82452
82773
|
});
|
|
82453
82774
|
this._isPlaying = false;
|
|
82454
82775
|
if (this._playButton)
|
|
@@ -82464,7 +82785,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82464
82785
|
});
|
|
82465
82786
|
this.showMessage("", "");
|
|
82466
82787
|
this._logger.info("Animation", "Timeline created successfully", {
|
|
82467
|
-
duration: this._currentTimeline.duration()
|
|
82788
|
+
duration: this._currentTimeline.duration(),
|
|
82468
82789
|
});
|
|
82469
82790
|
}
|
|
82470
82791
|
catch (err) {
|
|
@@ -82483,7 +82804,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82483
82804
|
pauseAnimation() {
|
|
82484
82805
|
this._logger.debug("Animation", "pauseAnimation called", {
|
|
82485
82806
|
hasTimeline: !!this._currentTimeline,
|
|
82486
|
-
isPlaying: this._isPlaying
|
|
82807
|
+
isPlaying: this._isPlaying,
|
|
82487
82808
|
});
|
|
82488
82809
|
if (this._currentTimeline) {
|
|
82489
82810
|
this._currentTimeline.pause();
|
|
@@ -82494,7 +82815,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82494
82815
|
this._pauseButton.disabled = true;
|
|
82495
82816
|
this.stopProgressUpdate();
|
|
82496
82817
|
this._logger.debug("Animation", "Animation paused", {
|
|
82497
|
-
progress: this._currentTimeline.progress()
|
|
82818
|
+
progress: this._currentTimeline.progress(),
|
|
82498
82819
|
});
|
|
82499
82820
|
}
|
|
82500
82821
|
}
|
|
@@ -82504,7 +82825,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82504
82825
|
restartAnimation() {
|
|
82505
82826
|
this._logger.debug("Animation", "restartAnimation called", {
|
|
82506
82827
|
hasTimeline: !!this._currentTimeline,
|
|
82507
|
-
isPlaying: this._isPlaying
|
|
82828
|
+
isPlaying: this._isPlaying,
|
|
82508
82829
|
});
|
|
82509
82830
|
if (this._currentTimeline) {
|
|
82510
82831
|
this._logger.debug("Animation", "Restarting existing timeline");
|
|
@@ -82529,7 +82850,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82529
82850
|
this._logger.debug("Animation", "seekAnimation called", {
|
|
82530
82851
|
hasProgressBar: !!this._progressBar,
|
|
82531
82852
|
hasTimeline: !!this._currentTimeline,
|
|
82532
|
-
progressBarValue: this._progressBar?.value
|
|
82853
|
+
progressBarValue: this._progressBar?.value,
|
|
82533
82854
|
});
|
|
82534
82855
|
if (!this._progressBar)
|
|
82535
82856
|
return;
|
|
@@ -82547,7 +82868,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82547
82868
|
progressBarValue: this._progressBar.value,
|
|
82548
82869
|
calculatedProgress: progress,
|
|
82549
82870
|
wasPlaying: this._isPlaying,
|
|
82550
|
-
timelineDuration: this._currentTimeline.duration()
|
|
82871
|
+
timelineDuration: this._currentTimeline.duration(),
|
|
82551
82872
|
});
|
|
82552
82873
|
// Pause the timeline to prevent it from continuing to play
|
|
82553
82874
|
this._isPlaying;
|
|
@@ -82556,7 +82877,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82556
82877
|
this._currentTimeline.progress(progress);
|
|
82557
82878
|
this._logger.debug("Animation", "Seek completed", {
|
|
82558
82879
|
newProgress: this._currentTimeline.progress(),
|
|
82559
|
-
timelinePaused: this._currentTimeline.paused()
|
|
82880
|
+
timelinePaused: this._currentTimeline.paused(),
|
|
82560
82881
|
});
|
|
82561
82882
|
// Update playing state
|
|
82562
82883
|
this._isPlaying = false;
|
|
@@ -82580,7 +82901,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82580
82901
|
}
|
|
82581
82902
|
}, 50); // Update every 50ms
|
|
82582
82903
|
this._logger.debug("Animation", "Progress update started", {
|
|
82583
|
-
intervalId: this._progressUpdateInterval
|
|
82904
|
+
intervalId: this._progressUpdateInterval,
|
|
82584
82905
|
});
|
|
82585
82906
|
}
|
|
82586
82907
|
/**
|
|
@@ -82589,7 +82910,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82589
82910
|
stopProgressUpdate() {
|
|
82590
82911
|
if (this._progressUpdateInterval) {
|
|
82591
82912
|
this._logger.debug("Animation", "Stopping progress update", {
|
|
82592
|
-
intervalId: this._progressUpdateInterval
|
|
82913
|
+
intervalId: this._progressUpdateInterval,
|
|
82593
82914
|
});
|
|
82594
82915
|
clearInterval(this._progressUpdateInterval);
|
|
82595
82916
|
this._progressUpdateInterval = undefined;
|
|
@@ -82616,7 +82937,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82616
82937
|
if (!this._svgDisplay)
|
|
82617
82938
|
return;
|
|
82618
82939
|
// Get panel size to center the SVG
|
|
82619
|
-
const panel = this._shadow.querySelector(
|
|
82940
|
+
const panel = this._shadow.querySelector(".svg-display-panel");
|
|
82620
82941
|
if (panel) {
|
|
82621
82942
|
const panelWidth = panel.clientWidth;
|
|
82622
82943
|
const panelHeight = panel.clientHeight;
|
|
@@ -82647,8 +82968,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82647
82968
|
panY: this._panY,
|
|
82648
82969
|
zoomLevel: this._zoomLevel,
|
|
82649
82970
|
transformString,
|
|
82650
|
-
svgWidth: this._svgDisplay.getAttribute(
|
|
82651
|
-
svgHeight: this._svgDisplay.getAttribute(
|
|
82971
|
+
svgWidth: this._svgDisplay.getAttribute("width"),
|
|
82972
|
+
svgHeight: this._svgDisplay.getAttribute("height"),
|
|
82652
82973
|
svgBBox: this._svgDisplay.getBBox(),
|
|
82653
82974
|
svgBoundingClientRect: {
|
|
82654
82975
|
x: svgBoundingRect.x,
|
|
@@ -82658,18 +82979,18 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82658
82979
|
top: svgBoundingRect.top,
|
|
82659
82980
|
left: svgBoundingRect.left,
|
|
82660
82981
|
right: svgBoundingRect.right,
|
|
82661
|
-
bottom: svgBoundingRect.bottom
|
|
82662
|
-
}
|
|
82982
|
+
bottom: svgBoundingRect.bottom,
|
|
82983
|
+
},
|
|
82663
82984
|
});
|
|
82664
82985
|
// Apply transform: translate first, then scale from the SVG's own center
|
|
82665
82986
|
this._svgDisplay.style.transform = transformString;
|
|
82666
|
-
this._svgDisplay.style.transformOrigin =
|
|
82987
|
+
this._svgDisplay.style.transformOrigin = "0 0";
|
|
82667
82988
|
// Only apply transition during zoom, not during pan (to avoid sluggishness)
|
|
82668
82989
|
if (!this._isPanning) {
|
|
82669
|
-
this._svgDisplay.style.transition =
|
|
82990
|
+
this._svgDisplay.style.transition = "transform 0.15s ease-out";
|
|
82670
82991
|
}
|
|
82671
82992
|
else {
|
|
82672
|
-
this._svgDisplay.style.transition =
|
|
82993
|
+
this._svgDisplay.style.transition = "none";
|
|
82673
82994
|
}
|
|
82674
82995
|
}
|
|
82675
82996
|
/**
|
|
@@ -82689,7 +83010,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82689
83010
|
// Get the bounding box of the hint content
|
|
82690
83011
|
const bbox = hintContent.getBBox();
|
|
82691
83012
|
// Get the SVG display panel size
|
|
82692
|
-
const panel = this._shadow.querySelector(
|
|
83013
|
+
const panel = this._shadow.querySelector(".svg-display-panel");
|
|
82693
83014
|
if (!panel)
|
|
82694
83015
|
return;
|
|
82695
83016
|
const panelWidth = panel.clientWidth;
|
|
@@ -82710,8 +83031,8 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82710
83031
|
// We want this to be at the center of the panel: (panelWidth/2, panelHeight/2)
|
|
82711
83032
|
// So: panX + (bboxCenterX * scale) = panelWidth / 2
|
|
82712
83033
|
// panY + (bboxCenterY * scale) = panelHeight / 2
|
|
82713
|
-
const panX =
|
|
82714
|
-
const panY =
|
|
83034
|
+
const panX = panelWidth / 2 - bboxCenterX * scale;
|
|
83035
|
+
const panY = panelHeight / 2 - bboxCenterY * scale;
|
|
82715
83036
|
this._zoomLevel = scale;
|
|
82716
83037
|
this._panX = panX;
|
|
82717
83038
|
this._panY = panY;
|
|
@@ -82724,7 +83045,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82724
83045
|
bboxCenterX,
|
|
82725
83046
|
bboxCenterY,
|
|
82726
83047
|
panelWidth,
|
|
82727
|
-
panelHeight
|
|
83048
|
+
panelHeight,
|
|
82728
83049
|
});
|
|
82729
83050
|
}
|
|
82730
83051
|
catch (err) {
|
|
@@ -82737,13 +83058,13 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82737
83058
|
*/
|
|
82738
83059
|
toggleSvgWordWrap() {
|
|
82739
83060
|
this._svgWordWrapEnabled = !this._svgWordWrapEnabled;
|
|
82740
|
-
const wrapper = this._shadow.getElementById(
|
|
83061
|
+
const wrapper = this._shadow.getElementById("svg-wrapper");
|
|
82741
83062
|
if (wrapper) {
|
|
82742
83063
|
if (this._svgWordWrapEnabled) {
|
|
82743
|
-
wrapper.classList.add(
|
|
83064
|
+
wrapper.classList.add("word-wrap-enabled");
|
|
82744
83065
|
}
|
|
82745
83066
|
else {
|
|
82746
|
-
wrapper.classList.remove(
|
|
83067
|
+
wrapper.classList.remove("word-wrap-enabled");
|
|
82747
83068
|
}
|
|
82748
83069
|
}
|
|
82749
83070
|
this._logger.debug("Component", "SVG word wrap toggled", this._svgWordWrapEnabled);
|
|
@@ -82753,13 +83074,13 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82753
83074
|
*/
|
|
82754
83075
|
toggleJsWordWrap() {
|
|
82755
83076
|
this._jsWordWrapEnabled = !this._jsWordWrapEnabled;
|
|
82756
|
-
const wrapper = this._shadow.getElementById(
|
|
83077
|
+
const wrapper = this._shadow.getElementById("js-wrapper");
|
|
82757
83078
|
if (wrapper) {
|
|
82758
83079
|
if (this._jsWordWrapEnabled) {
|
|
82759
|
-
wrapper.classList.add(
|
|
83080
|
+
wrapper.classList.add("word-wrap-enabled");
|
|
82760
83081
|
}
|
|
82761
83082
|
else {
|
|
82762
|
-
wrapper.classList.remove(
|
|
83083
|
+
wrapper.classList.remove("word-wrap-enabled");
|
|
82763
83084
|
}
|
|
82764
83085
|
}
|
|
82765
83086
|
this._logger.debug("Component", "JS word wrap toggled", this._jsWordWrapEnabled);
|
|
@@ -82855,7 +83176,9 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82855
83176
|
}
|
|
82856
83177
|
this._hintVariables.push({ name, value: "" });
|
|
82857
83178
|
this.refreshVariablesTable();
|
|
82858
|
-
this.fireEvent("hintVariablesChange", {
|
|
83179
|
+
this.fireEvent("hintVariablesChange", {
|
|
83180
|
+
hintVariables: this._hintVariables,
|
|
83181
|
+
});
|
|
82859
83182
|
}
|
|
82860
83183
|
/**
|
|
82861
83184
|
* Delete a variable.
|
|
@@ -82863,7 +83186,9 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82863
83186
|
deleteVariable(index) {
|
|
82864
83187
|
this._hintVariables.splice(index, 1);
|
|
82865
83188
|
this.refreshVariablesTable();
|
|
82866
|
-
this.fireEvent("hintVariablesChange", {
|
|
83189
|
+
this.fireEvent("hintVariablesChange", {
|
|
83190
|
+
hintVariables: this._hintVariables,
|
|
83191
|
+
});
|
|
82867
83192
|
}
|
|
82868
83193
|
/**
|
|
82869
83194
|
* Edit a variable.
|
|
@@ -82874,7 +83199,9 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82874
83199
|
if (newValue !== null) {
|
|
82875
83200
|
variable.value = newValue;
|
|
82876
83201
|
this.refreshVariablesTable();
|
|
82877
|
-
this.fireEvent("hintVariablesChange", {
|
|
83202
|
+
this.fireEvent("hintVariablesChange", {
|
|
83203
|
+
hintVariables: this._hintVariables,
|
|
83204
|
+
});
|
|
82878
83205
|
}
|
|
82879
83206
|
}
|
|
82880
83207
|
/**
|
|
@@ -82886,7 +83213,61 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82886
83213
|
return;
|
|
82887
83214
|
this._hintVariables = [];
|
|
82888
83215
|
this.refreshVariablesTable();
|
|
82889
|
-
this.fireEvent("hintVariablesChange", {
|
|
83216
|
+
this.fireEvent("hintVariablesChange", {
|
|
83217
|
+
hintVariables: this._hintVariables,
|
|
83218
|
+
});
|
|
83219
|
+
}
|
|
83220
|
+
/**
|
|
83221
|
+
* Extract all variable names from all hints in data.
|
|
83222
|
+
* Returns a sorted array of unique variable names.
|
|
83223
|
+
*/
|
|
83224
|
+
extractAllVariablesFromHints() {
|
|
83225
|
+
const allVariables = new Set();
|
|
83226
|
+
// Iterate through all hints in data
|
|
83227
|
+
Object.values(this._data.hints).forEach((hint) => {
|
|
83228
|
+
if (hint.svg) {
|
|
83229
|
+
// Extract variables from this hint's SVG
|
|
83230
|
+
const variables = this.extractVariablesFromSvg(hint.svg);
|
|
83231
|
+
variables.forEach((varName) => allVariables.add(varName));
|
|
83232
|
+
}
|
|
83233
|
+
});
|
|
83234
|
+
// Convert to array and sort alphabetically
|
|
83235
|
+
return Array.from(allVariables).sort();
|
|
83236
|
+
}
|
|
83237
|
+
/**
|
|
83238
|
+
* Populate hint variables from all hints.
|
|
83239
|
+
* Extracts all variables from all hints' SVG code and merges them into hintVariables.
|
|
83240
|
+
* Only adds variables that don't already exist.
|
|
83241
|
+
*/
|
|
83242
|
+
populateVariablesFromHints() {
|
|
83243
|
+
// Extract all variables from hints
|
|
83244
|
+
const extractedVariables = this.extractAllVariablesFromHints();
|
|
83245
|
+
if (extractedVariables.length === 0) {
|
|
83246
|
+
this.showMessage("No variables found in hints", "info");
|
|
83247
|
+
return;
|
|
83248
|
+
}
|
|
83249
|
+
// Get existing variable names for quick lookup
|
|
83250
|
+
const existingNames = new Set(this._hintVariables.map((v) => v.name));
|
|
83251
|
+
// Add new variables (those not already in hintVariables)
|
|
83252
|
+
let addedCount = 0;
|
|
83253
|
+
extractedVariables.forEach((varName) => {
|
|
83254
|
+
if (!existingNames.has(varName)) {
|
|
83255
|
+
this._hintVariables.push({ name: varName, value: "" });
|
|
83256
|
+
addedCount++;
|
|
83257
|
+
}
|
|
83258
|
+
});
|
|
83259
|
+
// Refresh UI and notify
|
|
83260
|
+
this.refreshVariablesTable();
|
|
83261
|
+
this.fireEvent("hintVariablesChange", {
|
|
83262
|
+
hintVariables: this._hintVariables,
|
|
83263
|
+
});
|
|
83264
|
+
// Show feedback message
|
|
83265
|
+
if (addedCount === 0) {
|
|
83266
|
+
this.showMessage(`All ${extractedVariables.length} variable(s) already defined`, "info");
|
|
83267
|
+
}
|
|
83268
|
+
else {
|
|
83269
|
+
this.showMessage(`Added ${addedCount} new variable(s) from hints (${extractedVariables.length} total found)`, "success");
|
|
83270
|
+
}
|
|
82890
83271
|
}
|
|
82891
83272
|
/**
|
|
82892
83273
|
* Refresh the variables table.
|
|
@@ -82895,6 +83276,11 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82895
83276
|
if (!this._variablesTable)
|
|
82896
83277
|
return;
|
|
82897
83278
|
this._variablesTable.innerHTML = "";
|
|
83279
|
+
// Ensure _hintVariables is an array
|
|
83280
|
+
if (!Array.isArray(this._hintVariables)) {
|
|
83281
|
+
this._logger.warn("Component", "hintVariables is not an array, resetting to empty array", this._hintVariables);
|
|
83282
|
+
this._hintVariables = [];
|
|
83283
|
+
}
|
|
82898
83284
|
if (this._hintVariables.length === 0) {
|
|
82899
83285
|
const row = this._variablesTable.insertRow();
|
|
82900
83286
|
const cell = row.insertCell();
|
|
@@ -82902,6 +83288,11 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82902
83288
|
cell.textContent = "No variables defined";
|
|
82903
83289
|
cell.style.fontStyle = "italic";
|
|
82904
83290
|
cell.style.color = "#999";
|
|
83291
|
+
// Re-validate SVG variables after clearing variables
|
|
83292
|
+
const svgCode = this._svgTextarea?.value || "";
|
|
83293
|
+
if (svgCode) {
|
|
83294
|
+
this.validateSvgVariables(svgCode);
|
|
83295
|
+
}
|
|
82905
83296
|
return;
|
|
82906
83297
|
}
|
|
82907
83298
|
this._hintVariables.forEach((variable, index) => {
|
|
@@ -82922,6 +83313,11 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82922
83313
|
const valueCell = row.insertCell();
|
|
82923
83314
|
valueCell.textContent = variable.value;
|
|
82924
83315
|
});
|
|
83316
|
+
// Re-validate SVG variables after updating the variables table
|
|
83317
|
+
const svgCode = this._svgTextarea?.value || "";
|
|
83318
|
+
if (svgCode) {
|
|
83319
|
+
this.validateSvgVariables(svgCode);
|
|
83320
|
+
}
|
|
82925
83321
|
}
|
|
82926
83322
|
/**
|
|
82927
83323
|
* Show a message in the message panel.
|
|
@@ -82942,16 +83338,18 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82942
83338
|
if (!this._svgTextarea || !this._svgHighlightContainer) {
|
|
82943
83339
|
this._logger.debug("Component", "Cannot highlight SVG - missing elements", {
|
|
82944
83340
|
hasTextarea: !!this._svgTextarea,
|
|
82945
|
-
hasContainer: !!this._svgHighlightContainer
|
|
83341
|
+
hasContainer: !!this._svgHighlightContainer,
|
|
82946
83342
|
});
|
|
82947
83343
|
return;
|
|
82948
83344
|
}
|
|
82949
83345
|
const code = this._svgTextarea.value;
|
|
82950
|
-
this._logger.debug("Component", "Highlighting SVG", {
|
|
83346
|
+
this._logger.debug("Component", "Highlighting SVG", {
|
|
83347
|
+
codeLength: code.length,
|
|
83348
|
+
});
|
|
82951
83349
|
try {
|
|
82952
83350
|
// Highlight as XML
|
|
82953
|
-
const highlighted = HighlightJS.highlight(code, { language:
|
|
82954
|
-
this._svgHighlightContainer.innerHTML = highlighted +
|
|
83351
|
+
const highlighted = HighlightJS.highlight(code, { language: "xml" }).value;
|
|
83352
|
+
this._svgHighlightContainer.innerHTML = highlighted + "\n";
|
|
82955
83353
|
this._logger.debug("Component", "SVG highlighted successfully");
|
|
82956
83354
|
}
|
|
82957
83355
|
catch (err) {
|
|
@@ -82969,16 +83367,20 @@ class GveHintDesigner extends HTMLElement {
|
|
|
82969
83367
|
if (!this._jsTextarea || !this._jsHighlightContainer) {
|
|
82970
83368
|
this._logger.debug("Component", "Cannot highlight JS - missing elements", {
|
|
82971
83369
|
hasTextarea: !!this._jsTextarea,
|
|
82972
|
-
hasContainer: !!this._jsHighlightContainer
|
|
83370
|
+
hasContainer: !!this._jsHighlightContainer,
|
|
82973
83371
|
});
|
|
82974
83372
|
return;
|
|
82975
83373
|
}
|
|
82976
83374
|
const code = this._jsTextarea.value;
|
|
82977
|
-
this._logger.debug("Component", "Highlighting JS", {
|
|
83375
|
+
this._logger.debug("Component", "Highlighting JS", {
|
|
83376
|
+
codeLength: code.length,
|
|
83377
|
+
});
|
|
82978
83378
|
try {
|
|
82979
83379
|
// Highlight as JavaScript
|
|
82980
|
-
const highlighted = HighlightJS.highlight(code, {
|
|
82981
|
-
|
|
83380
|
+
const highlighted = HighlightJS.highlight(code, {
|
|
83381
|
+
language: "javascript",
|
|
83382
|
+
}).value;
|
|
83383
|
+
this._jsHighlightContainer.innerHTML = highlighted + "\n";
|
|
82982
83384
|
this._logger.debug("Component", "JS highlighted successfully");
|
|
82983
83385
|
}
|
|
82984
83386
|
catch (err) {
|
|
@@ -83576,6 +83978,60 @@ class GveHintDesigner extends HTMLElement {
|
|
|
83576
83978
|
background: #ffcdd2;
|
|
83577
83979
|
}
|
|
83578
83980
|
|
|
83981
|
+
/* Validation Message */
|
|
83982
|
+
.validation-message {
|
|
83983
|
+
padding: 0.75rem;
|
|
83984
|
+
margin: 0.5rem;
|
|
83985
|
+
background: #fff3cd;
|
|
83986
|
+
border: 1px solid #ffc107;
|
|
83987
|
+
border-radius: 4px;
|
|
83988
|
+
color: #856404;
|
|
83989
|
+
font-size: 13px;
|
|
83990
|
+
line-height: 1.5;
|
|
83991
|
+
}
|
|
83992
|
+
|
|
83993
|
+
.validation-message strong {
|
|
83994
|
+
display: block;
|
|
83995
|
+
margin-bottom: 0.5rem;
|
|
83996
|
+
color: #ff9800;
|
|
83997
|
+
}
|
|
83998
|
+
|
|
83999
|
+
.validation-message code {
|
|
84000
|
+
background: #fff;
|
|
84001
|
+
padding: 0.2rem 0.4rem;
|
|
84002
|
+
border-radius: 3px;
|
|
84003
|
+
font-family: 'Consolas', 'Monaco', monospace;
|
|
84004
|
+
font-size: 12px;
|
|
84005
|
+
border: 1px solid #e0e0e0;
|
|
84006
|
+
}
|
|
84007
|
+
|
|
84008
|
+
.validation-message ul {
|
|
84009
|
+
margin: 0.5rem 0;
|
|
84010
|
+
}
|
|
84011
|
+
|
|
84012
|
+
.validation-message li {
|
|
84013
|
+
margin: 0.25rem 0;
|
|
84014
|
+
}
|
|
84015
|
+
|
|
84016
|
+
.validation-message em {
|
|
84017
|
+
display: block;
|
|
84018
|
+
margin-top: 0.5rem;
|
|
84019
|
+
font-size: 12px;
|
|
84020
|
+
color: #666;
|
|
84021
|
+
}
|
|
84022
|
+
|
|
84023
|
+
/* Warning Icon in Panel Header */
|
|
84024
|
+
.warning-icon {
|
|
84025
|
+
display: inline-flex;
|
|
84026
|
+
align-items: center;
|
|
84027
|
+
margin-left: 0.5rem;
|
|
84028
|
+
color: #ff9800;
|
|
84029
|
+
}
|
|
84030
|
+
|
|
84031
|
+
.warning-icon .feather-icon {
|
|
84032
|
+
display: block;
|
|
84033
|
+
}
|
|
84034
|
+
|
|
83579
84035
|
/* Responsive layout - use nested grids for each column */
|
|
83580
84036
|
@media (min-width: 900px) {
|
|
83581
84037
|
.properties-panel .panel-content {
|