@myrmidon/gve-snapshot-rendition 2.0.2 → 2.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 +2 -2
- package/dist/index.cjs.min.js +1 -1
- package/dist/index.cjs.min.js.map +1 -1
- package/dist/index.js +174 -6
- package/dist/index.js.map +1 -1
- package/dist/src/core/gve-snapshot-rendition.d.ts +9 -0
- package/dist/src/hint-designer/gve-hint-designer.d.ts +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25917,7 +25917,7 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
25917
25917
|
* of the web component is loaded.
|
|
25918
25918
|
*/
|
|
25919
25919
|
static get version() {
|
|
25920
|
-
return "2.0.
|
|
25920
|
+
return "2.0.4";
|
|
25921
25921
|
}
|
|
25922
25922
|
constructor() {
|
|
25923
25923
|
super();
|
|
@@ -25929,6 +25929,7 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
25929
25929
|
this._currentVersionIndex = 0;
|
|
25930
25930
|
this._autoForwardEnabled = false;
|
|
25931
25931
|
this._autoForwardTimerId = null;
|
|
25932
|
+
this._renderScheduled = false;
|
|
25932
25933
|
// Initialize with default settings
|
|
25933
25934
|
this._settings = { ...DEFAULT_SETTINGS };
|
|
25934
25935
|
this._autoForwardEnabled = this._settings.autoForwardOnGroup;
|
|
@@ -26001,14 +26002,20 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
26001
26002
|
this._settings = { ...DEFAULT_SETTINGS, ...value };
|
|
26002
26003
|
this._logger.setEnabled(this._settings.debug || false);
|
|
26003
26004
|
this._logger.info("Settings updated", this._settings);
|
|
26004
|
-
// When settings change, update renderers
|
|
26005
26005
|
if (this._textRenderer) {
|
|
26006
26006
|
this._textRenderer.updateSettings(this._settings);
|
|
26007
26007
|
}
|
|
26008
26008
|
if (this._hintRenderer) {
|
|
26009
26009
|
this._hintRenderer.updateSettings(this._settings);
|
|
26010
26010
|
}
|
|
26011
|
-
|
|
26011
|
+
// Build the UI shell on first call; after that just schedule a content
|
|
26012
|
+
// re-render so rapid settings+data assignments coalesce into one render.
|
|
26013
|
+
if (!this._container) {
|
|
26014
|
+
this.render();
|
|
26015
|
+
}
|
|
26016
|
+
else {
|
|
26017
|
+
this.scheduleRenderContent();
|
|
26018
|
+
}
|
|
26012
26019
|
}
|
|
26013
26020
|
/**
|
|
26014
26021
|
* Data property (bindable).
|
|
@@ -26110,20 +26117,40 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
26110
26117
|
});
|
|
26111
26118
|
}
|
|
26112
26119
|
}
|
|
26113
|
-
//
|
|
26120
|
+
// Schedule content rendering. Using scheduleRenderContent() rather than
|
|
26121
|
+
// a direct call ensures that when settings and data are both set in the
|
|
26122
|
+
// same synchronous turn (the common Angular pattern), only one render
|
|
26123
|
+
// fires after both assignments have completed.
|
|
26114
26124
|
if (this._textRenderer && this._rootSvg && this._baseNodes.length > 0) {
|
|
26115
|
-
this.
|
|
26125
|
+
this.scheduleRenderContent();
|
|
26116
26126
|
}
|
|
26117
26127
|
}
|
|
26118
26128
|
/**
|
|
26119
26129
|
* Render the component UI.
|
|
26120
26130
|
*/
|
|
26121
26131
|
render() {
|
|
26132
|
+
// Cancel any pending coalesced render — initializeGoldenLayout() will
|
|
26133
|
+
// trigger renderContent() once the new DOM is actually ready.
|
|
26134
|
+
this._renderScheduled = false;
|
|
26122
26135
|
// Destroy existing GL instance before clearing the DOM
|
|
26123
26136
|
if (this._goldenLayout) {
|
|
26124
26137
|
this._goldenLayout.destroy();
|
|
26125
26138
|
this._goldenLayout = undefined;
|
|
26126
26139
|
}
|
|
26140
|
+
// Destroy pan/zoom — its internal state is tied to the old SVG element.
|
|
26141
|
+
// initializePanZoom() guards against double-init with a null-check,
|
|
26142
|
+
// so it must be reset here or it will silently skip re-initialization.
|
|
26143
|
+
if (this._panZoomInstance) {
|
|
26144
|
+
this._panZoomInstance.destroy();
|
|
26145
|
+
this._panZoomInstance = undefined;
|
|
26146
|
+
}
|
|
26147
|
+
// Kill in-flight GSAP animations on the old SVG tree.
|
|
26148
|
+
if (this._animationEngine) {
|
|
26149
|
+
this._animationEngine.killAll();
|
|
26150
|
+
}
|
|
26151
|
+
// Reset toolbar — processData() only creates it when this is null,
|
|
26152
|
+
// so leaving a stale reference would prevent toolbar rebuild.
|
|
26153
|
+
this._toolbar = undefined;
|
|
26127
26154
|
this._shadow.innerHTML = "";
|
|
26128
26155
|
// Add styles (includes inlined GL CSS for Shadow DOM compatibility)
|
|
26129
26156
|
const style = document.createElement("style");
|
|
@@ -26334,6 +26361,22 @@ class GveSnapshotRendition extends HTMLElement {
|
|
|
26334
26361
|
this._logger.error("GL", "Failed to reset layout", error);
|
|
26335
26362
|
}
|
|
26336
26363
|
}
|
|
26364
|
+
/**
|
|
26365
|
+
* Coalesce concurrent render requests into a single call.
|
|
26366
|
+
* Multiple same-turn invocations (e.g. settings setter + data setter both
|
|
26367
|
+
* firing in the same Angular expression) collapse into one microtask render,
|
|
26368
|
+
* always running after all synchronous assignments have completed so the
|
|
26369
|
+
* latest data is used.
|
|
26370
|
+
*/
|
|
26371
|
+
scheduleRenderContent() {
|
|
26372
|
+
if (this._renderScheduled)
|
|
26373
|
+
return;
|
|
26374
|
+
this._renderScheduled = true;
|
|
26375
|
+
Promise.resolve().then(() => {
|
|
26376
|
+
this._renderScheduled = false;
|
|
26377
|
+
this.renderContent();
|
|
26378
|
+
});
|
|
26379
|
+
}
|
|
26337
26380
|
/**
|
|
26338
26381
|
* Render content (base text only - starts at v0).
|
|
26339
26382
|
* Use goToVersionIndex() to navigate to specific versions.
|
|
@@ -40804,7 +40847,7 @@ function requireD () {
|
|
|
40804
40847
|
+ 'pragma private protected public pure ref return scope shared static struct '
|
|
40805
40848
|
+ 'super switch synchronized template this throw try typedef typeid typeof union '
|
|
40806
40849
|
+ 'unittest version void volatile while with __FILE__ __LINE__ __gshared|10 '
|
|
40807
|
-
+ '__thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ 2.0.
|
|
40850
|
+
+ '__thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ 2.0.4',
|
|
40808
40851
|
built_in:
|
|
40809
40852
|
'bool cdouble cent cfloat char creal dchar delegate double dstring float function '
|
|
40810
40853
|
+ 'idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar '
|
|
@@ -90587,9 +90630,11 @@ class GveHintDesigner extends HTMLElement {
|
|
|
90587
90630
|
const loadDataBtn = this.createButton("load-data", "upload", "Load data from file");
|
|
90588
90631
|
const clearDataBtn = this.createButton("clear-data", "x-circle", "Clear all data");
|
|
90589
90632
|
clearDataBtn.classList.add("btn-danger");
|
|
90633
|
+
const exportInkscapeBtn = this.createButton("export-inkscape", "external-link", "Export hint to InkScape SVG");
|
|
90590
90634
|
dataGroup.appendChild(saveDataBtn);
|
|
90591
90635
|
dataGroup.appendChild(loadDataBtn);
|
|
90592
90636
|
dataGroup.appendChild(clearDataBtn);
|
|
90637
|
+
dataGroup.appendChild(exportInkscapeBtn);
|
|
90593
90638
|
toolbar.appendChild(dataGroup);
|
|
90594
90639
|
return toolbar;
|
|
90595
90640
|
}
|
|
@@ -91037,6 +91082,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
91037
91082
|
this.addClickListener("save-data", () => this.saveData());
|
|
91038
91083
|
this.addClickListener("load-data", () => this.loadData());
|
|
91039
91084
|
this.addClickListener("clear-data", () => this.clearData());
|
|
91085
|
+
this.addClickListener("export-inkscape", () => this.exportToInkscape());
|
|
91040
91086
|
// Timeline player
|
|
91041
91087
|
this.addClickListener("play", () => this.playAnimation());
|
|
91042
91088
|
this.addClickListener("pause", () => this.pauseAnimation());
|
|
@@ -91066,6 +91112,21 @@ class GveHintDesigner extends HTMLElement {
|
|
|
91066
91112
|
// Word wrap toggles
|
|
91067
91113
|
this.addClickListener("toggle-svg-wrap", () => this.toggleSvgWordWrap());
|
|
91068
91114
|
this.addClickListener("toggle-js-wrap", () => this.toggleJsWordWrap());
|
|
91115
|
+
// Form field highlighting (non-default values)
|
|
91116
|
+
[
|
|
91117
|
+
this._positionSelect,
|
|
91118
|
+
this._offsetXInput,
|
|
91119
|
+
this._offsetYInput,
|
|
91120
|
+
this._scaleXInput,
|
|
91121
|
+
this._scaleYInput,
|
|
91122
|
+
this._rotationInput,
|
|
91123
|
+
this._displacedRefSpanInput,
|
|
91124
|
+
].forEach((el) => {
|
|
91125
|
+
if (el) {
|
|
91126
|
+
el.addEventListener("input", () => this.updateFormFieldHighlights());
|
|
91127
|
+
el.addEventListener("change", () => this.updateFormFieldHighlights());
|
|
91128
|
+
}
|
|
91129
|
+
});
|
|
91069
91130
|
// Syntax highlighting on textareas
|
|
91070
91131
|
if (this._svgTextarea) {
|
|
91071
91132
|
this._svgTextarea.addEventListener("input", () => this.highlightSvg());
|
|
@@ -91300,6 +91361,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
91300
91361
|
}
|
|
91301
91362
|
// Auto-refresh SVG display when hint is loaded
|
|
91302
91363
|
this.refreshSvgDisplay();
|
|
91364
|
+
this.updateFormFieldHighlights();
|
|
91303
91365
|
this._logger.debug("Component", "Loaded hint for editing", hintId);
|
|
91304
91366
|
}
|
|
91305
91367
|
/**
|
|
@@ -91326,6 +91388,7 @@ class GveHintDesigner extends HTMLElement {
|
|
|
91326
91388
|
this._jsTextarea.value = "";
|
|
91327
91389
|
if (this._embeddedJsCheckbox)
|
|
91328
91390
|
this._embeddedJsCheckbox.checked = false;
|
|
91391
|
+
this.updateFormFieldHighlights();
|
|
91329
91392
|
}
|
|
91330
91393
|
/**
|
|
91331
91394
|
* Validate SVG code for well-formedness and structure.
|
|
@@ -92311,6 +92374,76 @@ class GveHintDesigner extends HTMLElement {
|
|
|
92311
92374
|
this.showMessage("All data cleared", "success");
|
|
92312
92375
|
this._logger.info("Data cleared");
|
|
92313
92376
|
}
|
|
92377
|
+
/**
|
|
92378
|
+
* Export the current hint as an InkScape-compatible SVG file.
|
|
92379
|
+
*/
|
|
92380
|
+
exportToInkscape() {
|
|
92381
|
+
if (!this._hintId) {
|
|
92382
|
+
this.showMessage("No hint selected!", "error");
|
|
92383
|
+
return;
|
|
92384
|
+
}
|
|
92385
|
+
const hint = this._data.hints[this._hintId];
|
|
92386
|
+
if (!hint?.svg) {
|
|
92387
|
+
this.showMessage("No SVG to export!", "error");
|
|
92388
|
+
return;
|
|
92389
|
+
}
|
|
92390
|
+
const w = this._settings.hintDesignWidth;
|
|
92391
|
+
const h = this._settings.hintDesignHeight;
|
|
92392
|
+
const hw = w / 2;
|
|
92393
|
+
const hh = h / 2;
|
|
92394
|
+
const resolvedSvg = this.resolveVariables(hint.svg);
|
|
92395
|
+
const unresolvedMatches = resolvedSvg.match(/\{\{[^}]+\}\}/g);
|
|
92396
|
+
const svgContent = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n` +
|
|
92397
|
+
`<svg\n` +
|
|
92398
|
+
` width="${w}"\n` +
|
|
92399
|
+
` height="${h}"\n` +
|
|
92400
|
+
` viewBox="0 0 ${w} ${h}"\n` +
|
|
92401
|
+
` version="1.1"\n` +
|
|
92402
|
+
` id="svg1"\n` +
|
|
92403
|
+
` sodipodi:docname="${this._hintId}.svg"\n` +
|
|
92404
|
+
` xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"\n` +
|
|
92405
|
+
` xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"\n` +
|
|
92406
|
+
` xmlns="http://www.w3.org/2000/svg"\n` +
|
|
92407
|
+
` xmlns:svg="http://www.w3.org/2000/svg">\n` +
|
|
92408
|
+
` <sodipodi:namedview\n` +
|
|
92409
|
+
` id="namedview1"\n` +
|
|
92410
|
+
` pagecolor="#ffffff"\n` +
|
|
92411
|
+
` bordercolor="#000000"\n` +
|
|
92412
|
+
` borderopacity="0.25"\n` +
|
|
92413
|
+
` inkscape:showpageshadow="2"\n` +
|
|
92414
|
+
` inkscape:pageopacity="0.0"\n` +
|
|
92415
|
+
` inkscape:pagecheckerboard="false"\n` +
|
|
92416
|
+
` inkscape:deskcolor="#d1d1d1"\n` +
|
|
92417
|
+
` inkscape:document-units="px"\n` +
|
|
92418
|
+
` inkscape:zoom="3"\n` +
|
|
92419
|
+
` inkscape:cx="${hw}"\n` +
|
|
92420
|
+
` inkscape:cy="${hh}"\n` +
|
|
92421
|
+
` inkscape:window-maximized="0"\n` +
|
|
92422
|
+
` inkscape:current-layer="g1"\n` +
|
|
92423
|
+
` showgrid="false" />\n` +
|
|
92424
|
+
` <defs id="defs1" />\n` +
|
|
92425
|
+
` <g\n` +
|
|
92426
|
+
` inkscape:label="Layer 1"\n` +
|
|
92427
|
+
` inkscape:groupmode="layer"\n` +
|
|
92428
|
+
` id="layer1" />\n` +
|
|
92429
|
+
` ${resolvedSvg}\n` +
|
|
92430
|
+
`</svg>`;
|
|
92431
|
+
const blob = new Blob([svgContent], { type: "image/svg+xml" });
|
|
92432
|
+
const url = URL.createObjectURL(blob);
|
|
92433
|
+
const link = document.createElement("a");
|
|
92434
|
+
link.href = url;
|
|
92435
|
+
link.download = `${this._hintId}.svg`;
|
|
92436
|
+
link.click();
|
|
92437
|
+
URL.revokeObjectURL(url);
|
|
92438
|
+
if (unresolvedMatches) {
|
|
92439
|
+
const names = [...new Set(unresolvedMatches)].join(", ");
|
|
92440
|
+
this.showMessage(`Exported "${this._hintId}.svg" — warning: unresolved variables: ${names}`, "info");
|
|
92441
|
+
}
|
|
92442
|
+
else {
|
|
92443
|
+
this.showMessage(`Exported "${this._hintId}.svg" for InkScape`, "success");
|
|
92444
|
+
}
|
|
92445
|
+
this._logger.info("Exported hint to InkScape", this._hintId);
|
|
92446
|
+
}
|
|
92314
92447
|
/**
|
|
92315
92448
|
* Load animation code from catalog.
|
|
92316
92449
|
*/
|
|
@@ -92488,6 +92621,30 @@ class GveHintDesigner extends HTMLElement {
|
|
|
92488
92621
|
this.validateSvgVariables(svgCode);
|
|
92489
92622
|
}
|
|
92490
92623
|
}
|
|
92624
|
+
/**
|
|
92625
|
+
* Update the highlighted state of all Hint Properties form rows.
|
|
92626
|
+
* Rows whose value differs from the default get the form-row--modified class.
|
|
92627
|
+
*/
|
|
92628
|
+
updateFormFieldHighlights() {
|
|
92629
|
+
this.setFormRowModified(this._positionSelect, this._positionSelect?.value !== "o");
|
|
92630
|
+
this.setFormRowModified(this._offsetXInput, this.isOffsetModified(this._offsetXInput?.value));
|
|
92631
|
+
this.setFormRowModified(this._offsetYInput, this.isOffsetModified(this._offsetYInput?.value));
|
|
92632
|
+
this.setFormRowModified(this._scaleXInput, parseFloat(this._scaleXInput?.value ?? "1") !== 1);
|
|
92633
|
+
this.setFormRowModified(this._scaleYInput, parseFloat(this._scaleYInput?.value ?? "1") !== 1);
|
|
92634
|
+
this.setFormRowModified(this._rotationInput, parseFloat(this._rotationInput?.value ?? "0") !== 0);
|
|
92635
|
+
this.setFormRowModified(this._displacedRefSpanInput, (this._displacedRefSpanInput?.value ?? "").trim() !== "");
|
|
92636
|
+
}
|
|
92637
|
+
isOffsetModified(value) {
|
|
92638
|
+
if (!value)
|
|
92639
|
+
return false;
|
|
92640
|
+
const parsed = this.parseOffset(value);
|
|
92641
|
+
return typeof parsed === "string" || parsed !== 0;
|
|
92642
|
+
}
|
|
92643
|
+
setFormRowModified(input, modified) {
|
|
92644
|
+
if (!input?.parentElement)
|
|
92645
|
+
return;
|
|
92646
|
+
input.parentElement.classList.toggle("form-row--modified", modified);
|
|
92647
|
+
}
|
|
92491
92648
|
/**
|
|
92492
92649
|
* Show a message in the message panel.
|
|
92493
92650
|
*/
|
|
@@ -92928,6 +93085,17 @@ class GveHintDesigner extends HTMLElement {
|
|
|
92928
93085
|
min-width: auto;
|
|
92929
93086
|
}
|
|
92930
93087
|
|
|
93088
|
+
.form-row--modified label {
|
|
93089
|
+
color: var(--hint-designer-primary, #1565c0);
|
|
93090
|
+
font-weight: 700;
|
|
93091
|
+
}
|
|
93092
|
+
|
|
93093
|
+
.form-row--modified input,
|
|
93094
|
+
.form-row--modified select {
|
|
93095
|
+
border-color: var(--hint-designer-primary-border, #2196f3);
|
|
93096
|
+
background-color: #e3f2fd;
|
|
93097
|
+
}
|
|
93098
|
+
|
|
92931
93099
|
.textarea-header {
|
|
92932
93100
|
display: flex;
|
|
92933
93101
|
justify-content: space-between;
|