@productcloudos/editor 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pc-editor.esm.js +497 -209
- package/dist/pc-editor.esm.js.map +1 -1
- package/dist/pc-editor.js +497 -208
- package/dist/pc-editor.js.map +1 -1
- package/dist/pc-editor.min.js +1 -1
- package/dist/pc-editor.min.js.map +1 -1
- package/dist/types/lib/core/PCEditor.d.ts +16 -5
- package/dist/types/lib/core/PCEditor.d.ts.map +1 -1
- package/dist/types/lib/import/PDFParser.d.ts.map +1 -1
- package/dist/types/lib/index.d.ts +1 -0
- package/dist/types/lib/index.d.ts.map +1 -1
- package/dist/types/lib/objects/EmbeddedObjectFactory.d.ts.map +1 -1
- package/dist/types/lib/objects/ImageObject.d.ts +11 -0
- package/dist/types/lib/objects/ImageObject.d.ts.map +1 -1
- package/dist/types/lib/objects/TextBoxObject.d.ts +5 -2
- package/dist/types/lib/objects/TextBoxObject.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableCell.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableObject.d.ts.map +1 -1
- package/dist/types/lib/objects/table/TableRow.d.ts.map +1 -1
- package/dist/types/lib/objects/table/types.d.ts +15 -15
- package/dist/types/lib/objects/table/types.d.ts.map +1 -1
- package/dist/types/lib/panes/DocumentInfoPane.d.ts +3 -1
- package/dist/types/lib/panes/DocumentInfoPane.d.ts.map +1 -1
- package/dist/types/lib/panes/DocumentSettingsPane.d.ts +2 -0
- package/dist/types/lib/panes/DocumentSettingsPane.d.ts.map +1 -1
- package/dist/types/lib/panes/HyperlinkPane.d.ts +1 -0
- package/dist/types/lib/panes/HyperlinkPane.d.ts.map +1 -1
- package/dist/types/lib/panes/ImagePane.d.ts +1 -0
- package/dist/types/lib/panes/ImagePane.d.ts.map +1 -1
- package/dist/types/lib/panes/TablePane.d.ts +3 -0
- package/dist/types/lib/panes/TablePane.d.ts.map +1 -1
- package/dist/types/lib/panes/TextBoxPane.d.ts +1 -0
- package/dist/types/lib/panes/TextBoxPane.d.ts.map +1 -1
- package/dist/types/lib/rendering/CanvasManager.d.ts.map +1 -1
- package/dist/types/lib/rendering/FlowingTextRenderer.d.ts.map +1 -1
- package/dist/types/lib/rendering/PDFGenerator.d.ts.map +1 -1
- package/dist/types/lib/text/FlowingTextContent.d.ts.map +1 -1
- package/dist/types/lib/text/TextFormatting.d.ts +12 -1
- package/dist/types/lib/text/TextFormatting.d.ts.map +1 -1
- package/dist/types/lib/types/index.d.ts +2 -0
- package/dist/types/lib/types/index.d.ts.map +1 -1
- package/dist/types/lib/undo/transaction/MutationUndo.d.ts.map +1 -1
- package/dist/types/lib/utils/logger.d.ts +20 -0
- package/dist/types/lib/utils/logger.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/pc-editor.js
CHANGED
|
@@ -839,12 +839,13 @@ class TextFormattingManager extends EventEmitter {
|
|
|
839
839
|
}
|
|
840
840
|
/**
|
|
841
841
|
* Get formatting at a specific character position.
|
|
842
|
-
* Returns the position-specific formatting
|
|
842
|
+
* Returns the position-specific formatting merged with defaults,
|
|
843
|
+
* ensuring all properties are consistently present.
|
|
843
844
|
*/
|
|
844
845
|
getFormattingAt(position) {
|
|
845
846
|
const override = this.formatting.get(position);
|
|
846
847
|
if (override) {
|
|
847
|
-
return { ...override };
|
|
848
|
+
return { ...this._defaultFormatting, ...override };
|
|
848
849
|
}
|
|
849
850
|
return { ...this._defaultFormatting };
|
|
850
851
|
}
|
|
@@ -931,6 +932,43 @@ class TextFormattingManager extends EventEmitter {
|
|
|
931
932
|
getAllFormatting() {
|
|
932
933
|
return new Map(this.formatting);
|
|
933
934
|
}
|
|
935
|
+
/**
|
|
936
|
+
* Get formatting as compressed runs for serialization.
|
|
937
|
+
* Only outputs entries where formatting changes from the previous character.
|
|
938
|
+
* Skips leading default formatting to minimize output size.
|
|
939
|
+
* @param textLength Length of the text to serialize formatting for
|
|
940
|
+
*/
|
|
941
|
+
getCompressedRuns(textLength) {
|
|
942
|
+
const runs = [];
|
|
943
|
+
const defaultFormat = this._defaultFormatting;
|
|
944
|
+
let lastFormat = null;
|
|
945
|
+
for (let i = 0; i < textLength; i++) {
|
|
946
|
+
const currentFormat = this.getFormattingAt(i);
|
|
947
|
+
const formatChanged = lastFormat === null ||
|
|
948
|
+
currentFormat.fontFamily !== lastFormat.fontFamily ||
|
|
949
|
+
currentFormat.fontSize !== lastFormat.fontSize ||
|
|
950
|
+
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
951
|
+
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
952
|
+
currentFormat.color !== lastFormat.color ||
|
|
953
|
+
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
954
|
+
if (formatChanged) {
|
|
955
|
+
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
956
|
+
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
957
|
+
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
958
|
+
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
959
|
+
currentFormat.color === defaultFormat.color &&
|
|
960
|
+
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
961
|
+
if (!isDefault || runs.length > 0) {
|
|
962
|
+
runs.push({
|
|
963
|
+
index: i,
|
|
964
|
+
formatting: { ...currentFormat }
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
lastFormat = currentFormat;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return runs;
|
|
971
|
+
}
|
|
934
972
|
/**
|
|
935
973
|
* Restore formatting from a map (for deserialization).
|
|
936
974
|
*/
|
|
@@ -3834,6 +3872,35 @@ class ImageObject extends BaseEmbeddedObject {
|
|
|
3834
3872
|
get hasError() {
|
|
3835
3873
|
return this._error;
|
|
3836
3874
|
}
|
|
3875
|
+
/**
|
|
3876
|
+
* Get the loaded HTMLImageElement, if available.
|
|
3877
|
+
* Used by PDFGenerator to convert unsupported formats to PNG.
|
|
3878
|
+
*/
|
|
3879
|
+
get imageElement() {
|
|
3880
|
+
return this._loaded ? this._image : null;
|
|
3881
|
+
}
|
|
3882
|
+
/**
|
|
3883
|
+
* Convert the image to a PNG data URL via canvas.
|
|
3884
|
+
* Used when the original format (e.g., SVG, WebP, GIF) is not supported by pdf-lib.
|
|
3885
|
+
* Returns null if the image is not loaded or conversion fails.
|
|
3886
|
+
*/
|
|
3887
|
+
toPngDataUrl() {
|
|
3888
|
+
if (!this._loaded || !this._image)
|
|
3889
|
+
return null;
|
|
3890
|
+
try {
|
|
3891
|
+
const canvas = document.createElement('canvas');
|
|
3892
|
+
canvas.width = this._image.naturalWidth || this._size.width;
|
|
3893
|
+
canvas.height = this._image.naturalHeight || this._size.height;
|
|
3894
|
+
const ctx = canvas.getContext('2d');
|
|
3895
|
+
if (!ctx)
|
|
3896
|
+
return null;
|
|
3897
|
+
ctx.drawImage(this._image, 0, 0, canvas.width, canvas.height);
|
|
3898
|
+
return canvas.toDataURL('image/png');
|
|
3899
|
+
}
|
|
3900
|
+
catch {
|
|
3901
|
+
return null;
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3837
3904
|
loadImage() {
|
|
3838
3905
|
if (!this._src) {
|
|
3839
3906
|
this._error = true;
|
|
@@ -4729,12 +4796,10 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4729
4796
|
this.editing = false;
|
|
4730
4797
|
}
|
|
4731
4798
|
toData() {
|
|
4732
|
-
// Serialize formatting
|
|
4733
|
-
const
|
|
4734
|
-
const
|
|
4735
|
-
|
|
4736
|
-
formattingEntries.push([key, { ...value }]);
|
|
4737
|
-
});
|
|
4799
|
+
// Serialize formatting as compressed runs (only at change boundaries)
|
|
4800
|
+
const text = this._flowingContent.getText();
|
|
4801
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
4802
|
+
const formattingEntries = compressedRuns.map(run => [run.index, { ...run.formatting }]);
|
|
4738
4803
|
// Get substitution fields as array
|
|
4739
4804
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
4740
4805
|
return {
|
|
@@ -4793,12 +4858,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4793
4858
|
this._border = { ...boxData.border };
|
|
4794
4859
|
if (boxData.padding !== undefined)
|
|
4795
4860
|
this._padding = boxData.padding;
|
|
4796
|
-
// Restore formatting runs
|
|
4797
|
-
if (boxData.formattingRuns) {
|
|
4861
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
4862
|
+
if (boxData.formattingRuns && boxData.formattingRuns.length > 0) {
|
|
4798
4863
|
const formattingManager = this._flowingContent.getFormattingManager();
|
|
4799
4864
|
formattingManager.clear();
|
|
4800
|
-
|
|
4801
|
-
|
|
4865
|
+
const textLength = this._flowingContent.getText().length;
|
|
4866
|
+
for (let i = 0; i < boxData.formattingRuns.length; i++) {
|
|
4867
|
+
const [startIndex, style] = boxData.formattingRuns[i];
|
|
4868
|
+
const nextIndex = i + 1 < boxData.formattingRuns.length
|
|
4869
|
+
? boxData.formattingRuns[i + 1][0]
|
|
4870
|
+
: textLength;
|
|
4871
|
+
if (startIndex < nextIndex) {
|
|
4872
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
4873
|
+
}
|
|
4802
4874
|
}
|
|
4803
4875
|
}
|
|
4804
4876
|
// Restore substitution fields
|
|
@@ -4948,15 +5020,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4948
5020
|
}
|
|
4949
5021
|
/**
|
|
4950
5022
|
* Check if a point is within this text box region.
|
|
5023
|
+
* Uses the full object bounds (including padding/border) so that
|
|
5024
|
+
* double-click to enter edit mode works on the entire text box area,
|
|
5025
|
+
* not just the inner text area.
|
|
4951
5026
|
* @param point Point in canvas coordinates
|
|
4952
|
-
* @param
|
|
5027
|
+
* @param _pageIndex The page index (ignored for text boxes)
|
|
4953
5028
|
*/
|
|
4954
|
-
containsPointInRegion(point,
|
|
4955
|
-
|
|
4956
|
-
if (!bounds)
|
|
5029
|
+
containsPointInRegion(point, _pageIndex) {
|
|
5030
|
+
if (!this._renderedPosition)
|
|
4957
5031
|
return false;
|
|
4958
|
-
return point.x >=
|
|
4959
|
-
point.
|
|
5032
|
+
return point.x >= this._renderedPosition.x &&
|
|
5033
|
+
point.x <= this._renderedPosition.x + this._size.width &&
|
|
5034
|
+
point.y >= this._renderedPosition.y &&
|
|
5035
|
+
point.y <= this._renderedPosition.y + this._size.height;
|
|
4960
5036
|
}
|
|
4961
5037
|
/**
|
|
4962
5038
|
* Trigger a reflow of text in this text box.
|
|
@@ -5041,6 +5117,41 @@ function getVerticalPadding(padding) {
|
|
|
5041
5117
|
return padding.top + padding.bottom;
|
|
5042
5118
|
}
|
|
5043
5119
|
|
|
5120
|
+
/**
|
|
5121
|
+
* Logger - Centralized logging for PC Editor.
|
|
5122
|
+
*
|
|
5123
|
+
* When enabled, logs informational messages to the console.
|
|
5124
|
+
* When disabled, only errors are logged.
|
|
5125
|
+
* Controlled via EditorOptions.enableLogging or Logger.setEnabled().
|
|
5126
|
+
*/
|
|
5127
|
+
let _enabled = false;
|
|
5128
|
+
const Logger = {
|
|
5129
|
+
/** Enable or disable logging. When disabled, only errors are logged. */
|
|
5130
|
+
setEnabled(enabled) {
|
|
5131
|
+
_enabled = enabled;
|
|
5132
|
+
},
|
|
5133
|
+
/** Check if logging is enabled. */
|
|
5134
|
+
isEnabled() {
|
|
5135
|
+
return _enabled;
|
|
5136
|
+
},
|
|
5137
|
+
/** Log an informational message. Only outputs when logging is enabled. */
|
|
5138
|
+
log(...args) {
|
|
5139
|
+
if (_enabled) {
|
|
5140
|
+
console.log(...args);
|
|
5141
|
+
}
|
|
5142
|
+
},
|
|
5143
|
+
/** Log a warning. Only outputs when logging is enabled. */
|
|
5144
|
+
warn(...args) {
|
|
5145
|
+
if (_enabled) {
|
|
5146
|
+
console.warn(...args);
|
|
5147
|
+
}
|
|
5148
|
+
},
|
|
5149
|
+
/** Log an error. Always outputs regardless of logging state. */
|
|
5150
|
+
error(...args) {
|
|
5151
|
+
console.error(...args);
|
|
5152
|
+
}
|
|
5153
|
+
};
|
|
5154
|
+
|
|
5044
5155
|
/**
|
|
5045
5156
|
* TableCell - A cell within a table that contains editable text.
|
|
5046
5157
|
* Implements EditableTextRegion for unified text interaction.
|
|
@@ -5091,7 +5202,7 @@ class TableCell extends EventEmitter {
|
|
|
5091
5202
|
});
|
|
5092
5203
|
// Prevent embedded objects in table cells (only substitution fields allowed)
|
|
5093
5204
|
this._flowingContent.insertEmbeddedObject = () => {
|
|
5094
|
-
|
|
5205
|
+
Logger.warn('[pc-editor:TableCell] Embedded objects are not allowed in table cells. Use insertSubstitutionField instead.');
|
|
5095
5206
|
};
|
|
5096
5207
|
// Set initial content
|
|
5097
5208
|
if (config.content) {
|
|
@@ -5408,7 +5519,7 @@ class TableCell extends EventEmitter {
|
|
|
5408
5519
|
this._reflowDirty = false;
|
|
5409
5520
|
this._lastReflowWidth = width;
|
|
5410
5521
|
this._cachedContentHeight = null; // Clear cached height since lines changed
|
|
5411
|
-
|
|
5522
|
+
Logger.log('[pc-editor:TableCell.reflow] cellId:', this._id, 'text:', JSON.stringify(this._flowingContent.getText()), 'lines:', this._flowedLines.length);
|
|
5412
5523
|
}
|
|
5413
5524
|
/**
|
|
5414
5525
|
* Mark this cell as needing reflow.
|
|
@@ -5446,7 +5557,7 @@ class TableCell extends EventEmitter {
|
|
|
5446
5557
|
return this._editing && this._flowingContent.hasFocus();
|
|
5447
5558
|
}
|
|
5448
5559
|
handleKeyDown(e) {
|
|
5449
|
-
|
|
5560
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Key:', e.key, '_editing:', this._editing, 'flowingContent.hasFocus:', this._flowingContent.hasFocus());
|
|
5450
5561
|
if (!this._editing)
|
|
5451
5562
|
return false;
|
|
5452
5563
|
// Let parent table handle Tab navigation
|
|
@@ -5454,9 +5565,9 @@ class TableCell extends EventEmitter {
|
|
|
5454
5565
|
return false; // Not handled - parent will handle
|
|
5455
5566
|
}
|
|
5456
5567
|
// Delegate to FlowingTextContent
|
|
5457
|
-
|
|
5568
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Delegating to FlowingTextContent.handleKeyDown');
|
|
5458
5569
|
const handled = this._flowingContent.handleKeyDown(e);
|
|
5459
|
-
|
|
5570
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] FlowingTextContent handled:', handled);
|
|
5460
5571
|
return handled;
|
|
5461
5572
|
}
|
|
5462
5573
|
onCursorBlink(handler) {
|
|
@@ -5528,28 +5639,49 @@ class TableCell extends EventEmitter {
|
|
|
5528
5639
|
// Serialization
|
|
5529
5640
|
// ============================================
|
|
5530
5641
|
toData() {
|
|
5531
|
-
const
|
|
5532
|
-
const
|
|
5533
|
-
|
|
5534
|
-
formattingRuns.push([index, { ...style }]);
|
|
5535
|
-
});
|
|
5642
|
+
const text = this._flowingContent.getText();
|
|
5643
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
5644
|
+
const formattingRuns = compressedRuns.map(run => [run.index, run.formatting]);
|
|
5536
5645
|
// Get substitution fields for serialization
|
|
5537
5646
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5647
|
+
// Only include non-default values to minimize export size
|
|
5648
|
+
const defaults = DEFAULT_TABLE_STYLE;
|
|
5649
|
+
const defaultBorder = DEFAULT_CELL_BORDER_SIDE;
|
|
5650
|
+
const isDefaultBorderSide = (side) => side.width === defaultBorder.width && side.color === defaultBorder.color && side.style === defaultBorder.style;
|
|
5651
|
+
const isDefaultBorder = isDefaultBorderSide(this._border.top) &&
|
|
5652
|
+
isDefaultBorderSide(this._border.right) &&
|
|
5653
|
+
isDefaultBorderSide(this._border.bottom) &&
|
|
5654
|
+
isDefaultBorderSide(this._border.left);
|
|
5655
|
+
const isDefaultPadding = this._padding.top === defaults.cellPadding &&
|
|
5656
|
+
this._padding.right === defaults.cellPadding &&
|
|
5657
|
+
this._padding.bottom === defaults.cellPadding &&
|
|
5658
|
+
this._padding.left === defaults.cellPadding;
|
|
5659
|
+
const data = {};
|
|
5660
|
+
if (this._rowSpan !== 1)
|
|
5661
|
+
data.rowSpan = this._rowSpan;
|
|
5662
|
+
if (this._colSpan !== 1)
|
|
5663
|
+
data.colSpan = this._colSpan;
|
|
5664
|
+
if (this._backgroundColor !== defaults.backgroundColor)
|
|
5665
|
+
data.backgroundColor = this._backgroundColor;
|
|
5666
|
+
if (!isDefaultBorder)
|
|
5667
|
+
data.border = this._border;
|
|
5668
|
+
if (!isDefaultPadding)
|
|
5669
|
+
data.padding = this._padding;
|
|
5670
|
+
if (this._verticalAlign !== 'top')
|
|
5671
|
+
data.verticalAlign = this._verticalAlign;
|
|
5672
|
+
if (text)
|
|
5673
|
+
data.content = text;
|
|
5674
|
+
if (this._fontFamily !== defaults.fontFamily)
|
|
5675
|
+
data.fontFamily = this._fontFamily;
|
|
5676
|
+
if (this._fontSize !== defaults.fontSize)
|
|
5677
|
+
data.fontSize = this._fontSize;
|
|
5678
|
+
if (this._color !== defaults.color)
|
|
5679
|
+
data.color = this._color;
|
|
5680
|
+
if (formattingRuns.length > 0)
|
|
5681
|
+
data.formattingRuns = formattingRuns;
|
|
5682
|
+
if (fields.length > 0)
|
|
5683
|
+
data.substitutionFields = fields;
|
|
5684
|
+
return data;
|
|
5553
5685
|
}
|
|
5554
5686
|
static fromData(data) {
|
|
5555
5687
|
const cell = new TableCell({
|
|
@@ -5565,14 +5697,19 @@ class TableCell extends EventEmitter {
|
|
|
5565
5697
|
fontSize: data.fontSize,
|
|
5566
5698
|
color: data.color
|
|
5567
5699
|
});
|
|
5568
|
-
// Restore formatting runs
|
|
5569
|
-
if (data.formattingRuns) {
|
|
5700
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
5701
|
+
if (data.formattingRuns && data.formattingRuns.length > 0) {
|
|
5570
5702
|
const formattingManager = cell._flowingContent.getFormattingManager();
|
|
5571
|
-
const
|
|
5572
|
-
for (
|
|
5573
|
-
|
|
5703
|
+
const textLength = (data.content || '').length;
|
|
5704
|
+
for (let i = 0; i < data.formattingRuns.length; i++) {
|
|
5705
|
+
const [startIndex, style] = data.formattingRuns[i];
|
|
5706
|
+
const nextIndex = i + 1 < data.formattingRuns.length
|
|
5707
|
+
? data.formattingRuns[i + 1][0]
|
|
5708
|
+
: textLength;
|
|
5709
|
+
if (startIndex < nextIndex) {
|
|
5710
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
5711
|
+
}
|
|
5574
5712
|
}
|
|
5575
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
5576
5713
|
}
|
|
5577
5714
|
// Restore substitution fields
|
|
5578
5715
|
if (data.substitutionFields && Array.isArray(data.substitutionFields)) {
|
|
@@ -5806,13 +5943,17 @@ class TableRow extends EventEmitter {
|
|
|
5806
5943
|
// Serialization
|
|
5807
5944
|
// ============================================
|
|
5808
5945
|
toData() {
|
|
5809
|
-
|
|
5810
|
-
id: this._id,
|
|
5811
|
-
height: this._height,
|
|
5812
|
-
minHeight: this._minHeight,
|
|
5813
|
-
isHeader: this._isHeader,
|
|
5946
|
+
const data = {
|
|
5814
5947
|
cells: this._cells.map(cell => cell.toData())
|
|
5815
5948
|
};
|
|
5949
|
+
// Only include non-default values
|
|
5950
|
+
if (this._height !== null)
|
|
5951
|
+
data.height = this._height;
|
|
5952
|
+
if (this._minHeight !== DEFAULT_TABLE_STYLE.minRowHeight)
|
|
5953
|
+
data.minHeight = this._minHeight;
|
|
5954
|
+
if (this._isHeader)
|
|
5955
|
+
data.isHeader = this._isHeader;
|
|
5956
|
+
return data;
|
|
5816
5957
|
}
|
|
5817
5958
|
static fromData(data) {
|
|
5818
5959
|
const row = new TableRow({
|
|
@@ -6146,7 +6287,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6146
6287
|
set position(value) {
|
|
6147
6288
|
// Tables only support block positioning - ignore any attempt to set other modes
|
|
6148
6289
|
if (value !== 'block') {
|
|
6149
|
-
|
|
6290
|
+
Logger.warn(`[pc-editor:TableObject] Tables only support 'block' positioning. Ignoring attempt to set '${value}'.`);
|
|
6150
6291
|
}
|
|
6151
6292
|
// Always set to block
|
|
6152
6293
|
super.position = 'block';
|
|
@@ -6685,24 +6826,24 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6685
6826
|
createRowLoop(startRowIndex, endRowIndex, fieldPath) {
|
|
6686
6827
|
// Validate range
|
|
6687
6828
|
if (startRowIndex < 0 || endRowIndex >= this._rows.length) {
|
|
6688
|
-
|
|
6829
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Invalid row range');
|
|
6689
6830
|
return null;
|
|
6690
6831
|
}
|
|
6691
6832
|
if (startRowIndex > endRowIndex) {
|
|
6692
|
-
|
|
6833
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Start index must be <= end index');
|
|
6693
6834
|
return null;
|
|
6694
6835
|
}
|
|
6695
6836
|
// Check for overlap with existing loops
|
|
6696
6837
|
for (const existingLoop of this._rowLoops.values()) {
|
|
6697
6838
|
if (this.loopRangesOverlap(startRowIndex, endRowIndex, existingLoop.startRowIndex, existingLoop.endRowIndex)) {
|
|
6698
|
-
|
|
6839
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop range overlaps with existing loop');
|
|
6699
6840
|
return null;
|
|
6700
6841
|
}
|
|
6701
6842
|
}
|
|
6702
6843
|
// Check that loop rows are not header rows
|
|
6703
6844
|
for (let i = startRowIndex; i <= endRowIndex; i++) {
|
|
6704
6845
|
if (this._rows[i]?.isHeader) {
|
|
6705
|
-
|
|
6846
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop rows cannot be header rows');
|
|
6706
6847
|
return null;
|
|
6707
6848
|
}
|
|
6708
6849
|
}
|
|
@@ -7508,7 +7649,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
7508
7649
|
return this._editing;
|
|
7509
7650
|
}
|
|
7510
7651
|
handleKeyDown(e) {
|
|
7511
|
-
|
|
7652
|
+
Logger.log('[pc-editor:TableObject.handleKeyDown] Key:', e.key, '_editing:', this._editing, '_focusedCell:', this._focusedCell);
|
|
7512
7653
|
if (!this._editing)
|
|
7513
7654
|
return false;
|
|
7514
7655
|
// Handle Tab navigation
|
|
@@ -8296,14 +8437,20 @@ class EmbeddedObjectFactory {
|
|
|
8296
8437
|
border: data.data.border,
|
|
8297
8438
|
padding: data.data.padding
|
|
8298
8439
|
});
|
|
8299
|
-
// Restore formatting runs
|
|
8440
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
8300
8441
|
if (data.data.formattingRuns && Array.isArray(data.data.formattingRuns)) {
|
|
8301
|
-
const
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8442
|
+
const runs = data.data.formattingRuns;
|
|
8443
|
+
if (runs.length > 0) {
|
|
8444
|
+
const formattingManager = textBox.flowingContent.getFormattingManager();
|
|
8445
|
+
const textLength = textBox.flowingContent.getText().length;
|
|
8446
|
+
for (let i = 0; i < runs.length; i++) {
|
|
8447
|
+
const [startIndex, style] = runs[i];
|
|
8448
|
+
const nextIndex = i + 1 < runs.length ? runs[i + 1][0] : textLength;
|
|
8449
|
+
if (startIndex < nextIndex) {
|
|
8450
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
8451
|
+
}
|
|
8452
|
+
}
|
|
8305
8453
|
}
|
|
8306
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
8307
8454
|
}
|
|
8308
8455
|
// Restore substitution fields if present
|
|
8309
8456
|
if (data.data.substitutionFields && Array.isArray(data.data.substitutionFields)) {
|
|
@@ -9280,9 +9427,9 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9280
9427
|
* @returns true if the event was handled, false otherwise
|
|
9281
9428
|
*/
|
|
9282
9429
|
handleKeyDown(e) {
|
|
9283
|
-
|
|
9430
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] Key:', e.key, '_hasFocus:', this._hasFocus);
|
|
9284
9431
|
if (!this._hasFocus) {
|
|
9285
|
-
|
|
9432
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] No focus, returning false');
|
|
9286
9433
|
return false;
|
|
9287
9434
|
}
|
|
9288
9435
|
switch (e.key) {
|
|
@@ -9797,46 +9944,19 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9797
9944
|
toData() {
|
|
9798
9945
|
// Serialize text content
|
|
9799
9946
|
const text = this.textState.getText();
|
|
9800
|
-
// Serialize text formatting as runs
|
|
9801
|
-
|
|
9802
|
-
const formattingRuns =
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
9812
|
-
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
9813
|
-
currentFormat.color !== lastFormat.color ||
|
|
9814
|
-
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
9815
|
-
if (formatChanged) {
|
|
9816
|
-
// Only output if different from default (to further reduce size)
|
|
9817
|
-
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
9818
|
-
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
9819
|
-
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
9820
|
-
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
9821
|
-
currentFormat.color === defaultFormat.color &&
|
|
9822
|
-
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
9823
|
-
// Always output first run if it's not default, or output when format changes
|
|
9824
|
-
if (!isDefault || formattingRuns.length > 0) {
|
|
9825
|
-
formattingRuns.push({
|
|
9826
|
-
index: i,
|
|
9827
|
-
formatting: {
|
|
9828
|
-
fontFamily: currentFormat.fontFamily,
|
|
9829
|
-
fontSize: currentFormat.fontSize,
|
|
9830
|
-
fontWeight: currentFormat.fontWeight,
|
|
9831
|
-
fontStyle: currentFormat.fontStyle,
|
|
9832
|
-
color: currentFormat.color,
|
|
9833
|
-
backgroundColor: currentFormat.backgroundColor
|
|
9834
|
-
}
|
|
9835
|
-
});
|
|
9836
|
-
}
|
|
9837
|
-
lastFormat = currentFormat;
|
|
9947
|
+
// Serialize text formatting as compressed runs (only at change boundaries)
|
|
9948
|
+
const compressedRuns = this.formatting.getCompressedRuns(text.length);
|
|
9949
|
+
const formattingRuns = compressedRuns.map(run => ({
|
|
9950
|
+
index: run.index,
|
|
9951
|
+
formatting: {
|
|
9952
|
+
fontFamily: run.formatting.fontFamily,
|
|
9953
|
+
fontSize: run.formatting.fontSize,
|
|
9954
|
+
fontWeight: run.formatting.fontWeight,
|
|
9955
|
+
fontStyle: run.formatting.fontStyle,
|
|
9956
|
+
color: run.formatting.color,
|
|
9957
|
+
backgroundColor: run.formatting.backgroundColor
|
|
9838
9958
|
}
|
|
9839
|
-
}
|
|
9959
|
+
}));
|
|
9840
9960
|
// Serialize paragraph formatting
|
|
9841
9961
|
const paragraphFormatting = this.paragraphFormatting.toJSON();
|
|
9842
9962
|
// Serialize substitution fields
|
|
@@ -9932,7 +10052,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9932
10052
|
content.getEmbeddedObjectManager().insert(object, ref.textIndex);
|
|
9933
10053
|
}
|
|
9934
10054
|
else {
|
|
9935
|
-
|
|
10055
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9936
10056
|
}
|
|
9937
10057
|
}
|
|
9938
10058
|
}
|
|
@@ -9986,7 +10106,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9986
10106
|
this.embeddedObjects.insert(object, ref.textIndex);
|
|
9987
10107
|
}
|
|
9988
10108
|
else {
|
|
9989
|
-
|
|
10109
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9990
10110
|
}
|
|
9991
10111
|
}
|
|
9992
10112
|
}
|
|
@@ -12525,7 +12645,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12525
12645
|
updateResizeHandleTargets(selectedObjects) {
|
|
12526
12646
|
// Clear existing resize handle targets
|
|
12527
12647
|
this._hitTestManager.clearCategory('resize-handles');
|
|
12528
|
-
|
|
12648
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets selectedObjects:', selectedObjects.length);
|
|
12529
12649
|
// Register resize handles for each selected object
|
|
12530
12650
|
for (const object of selectedObjects) {
|
|
12531
12651
|
if (!object.resizable)
|
|
@@ -12538,7 +12658,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12538
12658
|
// For regular objects, use renderedPosition
|
|
12539
12659
|
const pos = object.renderedPosition;
|
|
12540
12660
|
const pageIndex = object.renderedPageIndex;
|
|
12541
|
-
|
|
12661
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets object:', object.id, 'pageIndex:', pageIndex, 'pos:', pos);
|
|
12542
12662
|
if (pos && pageIndex >= 0) {
|
|
12543
12663
|
this.registerObjectResizeHandles(object, pageIndex, pos);
|
|
12544
12664
|
}
|
|
@@ -15184,7 +15304,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15184
15304
|
this.emit('element-removed', { elementId: objectId });
|
|
15185
15305
|
}
|
|
15186
15306
|
selectElement(elementId) {
|
|
15187
|
-
|
|
15307
|
+
Logger.log('[pc-editor:CanvasManager] Selecting element:', elementId);
|
|
15188
15308
|
this.selectedElements.add(elementId);
|
|
15189
15309
|
// Update embedded object's selected state
|
|
15190
15310
|
const flowingContents = [
|
|
@@ -15196,12 +15316,12 @@ class CanvasManager extends EventEmitter {
|
|
|
15196
15316
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15197
15317
|
for (const [, obj] of embeddedObjects.entries()) {
|
|
15198
15318
|
if (obj.id === elementId) {
|
|
15199
|
-
|
|
15319
|
+
Logger.log('[pc-editor:CanvasManager] Found embedded object to select:', obj.id);
|
|
15200
15320
|
obj.selected = true;
|
|
15201
15321
|
}
|
|
15202
15322
|
}
|
|
15203
15323
|
}
|
|
15204
|
-
|
|
15324
|
+
Logger.log('[pc-editor:CanvasManager] Selected elements after selection:', Array.from(this.selectedElements));
|
|
15205
15325
|
this.render();
|
|
15206
15326
|
this.updateResizeHandleHitTargets();
|
|
15207
15327
|
this.emit('selection-change', { selectedElements: Array.from(this.selectedElements) });
|
|
@@ -15251,10 +15371,10 @@ class CanvasManager extends EventEmitter {
|
|
|
15251
15371
|
this.flowingTextRenderer.updateResizeHandleTargets(selectedObjects);
|
|
15252
15372
|
}
|
|
15253
15373
|
clearSelection() {
|
|
15254
|
-
|
|
15374
|
+
Logger.log('[pc-editor:CanvasManager] clearSelection called, current selected elements:', Array.from(this.selectedElements));
|
|
15255
15375
|
// Clear selected state on all embedded objects
|
|
15256
15376
|
this.selectedElements.forEach(elementId => {
|
|
15257
|
-
|
|
15377
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection for element:', elementId);
|
|
15258
15378
|
// Check embedded objects in all flowing content sources (body, header, footer)
|
|
15259
15379
|
const flowingContents = [
|
|
15260
15380
|
this.document.bodyFlowingContent,
|
|
@@ -15265,7 +15385,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15265
15385
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15266
15386
|
for (const [, embeddedObj] of embeddedObjects.entries()) {
|
|
15267
15387
|
if (embeddedObj.id === elementId) {
|
|
15268
|
-
|
|
15388
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection on embedded object:', elementId);
|
|
15269
15389
|
embeddedObj.selected = false;
|
|
15270
15390
|
}
|
|
15271
15391
|
}
|
|
@@ -15273,7 +15393,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15273
15393
|
});
|
|
15274
15394
|
this.selectedElements.clear();
|
|
15275
15395
|
this.selectedSectionId = null;
|
|
15276
|
-
|
|
15396
|
+
Logger.log('[pc-editor:CanvasManager] About to render after clearing selection...');
|
|
15277
15397
|
this.render();
|
|
15278
15398
|
this.updateResizeHandleHitTargets();
|
|
15279
15399
|
this.emit('selection-change', { selectedElements: [] });
|
|
@@ -15522,7 +15642,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15522
15642
|
});
|
|
15523
15643
|
// Handle substitution field clicks
|
|
15524
15644
|
this.flowingTextRenderer.on('substitution-field-clicked', (data) => {
|
|
15525
|
-
|
|
15645
|
+
Logger.log('[pc-editor:CanvasManager] substitution-field-clicked Field:', data.field?.fieldName, 'Section:', data.section);
|
|
15526
15646
|
// Emit event for external handling (e.g., showing field properties panel)
|
|
15527
15647
|
this.emit('substitution-field-clicked', data);
|
|
15528
15648
|
});
|
|
@@ -15997,14 +16117,15 @@ class CanvasManager extends EventEmitter {
|
|
|
15997
16117
|
this.editingTextBox = textBox;
|
|
15998
16118
|
this._editingTextBoxPageId = pageId || null;
|
|
15999
16119
|
if (textBox) {
|
|
16000
|
-
// Use the unified focus system to handle focus/blur and cursor blink
|
|
16001
|
-
// This blurs the previous control, hiding its cursor
|
|
16002
|
-
this.setFocus(textBox);
|
|
16003
16120
|
// Clear selection in main flowing content
|
|
16004
16121
|
this.document.bodyFlowingContent.clearSelection();
|
|
16005
|
-
// Select the text box
|
|
16122
|
+
// Select the text box visually (this calls setFocus(null) internally,
|
|
16123
|
+
// so we must set focus to the text box AFTER this call)
|
|
16006
16124
|
this.clearSelection();
|
|
16007
16125
|
this.selectInlineElement({ type: 'embedded-object', object: textBox, textIndex: textBox.textIndex });
|
|
16126
|
+
// Now set focus to the text box for editing — must be AFTER selectInlineElement
|
|
16127
|
+
// because selectBaseEmbeddedObject calls setFocus(null) which would undo it
|
|
16128
|
+
this.setFocus(textBox);
|
|
16008
16129
|
this.emit('textbox-editing-started', { textBox });
|
|
16009
16130
|
}
|
|
16010
16131
|
else {
|
|
@@ -16743,7 +16864,15 @@ class PDFGenerator {
|
|
|
16743
16864
|
// Check if it's a data URL we can embed
|
|
16744
16865
|
if (src.startsWith('data:')) {
|
|
16745
16866
|
try {
|
|
16746
|
-
|
|
16867
|
+
let embeddedImage = await this.embedImageFromDataUrl(pdfDoc, src);
|
|
16868
|
+
// If the format isn't directly supported (e.g., SVG, WebP, GIF),
|
|
16869
|
+
// convert to PNG via canvas and try again
|
|
16870
|
+
if (!embeddedImage) {
|
|
16871
|
+
const pngDataUrl = image.toPngDataUrl();
|
|
16872
|
+
if (pngDataUrl) {
|
|
16873
|
+
embeddedImage = await this.embedImageFromDataUrl(pdfDoc, pngDataUrl);
|
|
16874
|
+
}
|
|
16875
|
+
}
|
|
16747
16876
|
if (embeddedImage) {
|
|
16748
16877
|
// Calculate draw position/size based on fit mode
|
|
16749
16878
|
const drawParams = this.calculateImageDrawParams(embeddedImage.width, embeddedImage.height, image.width, image.height, image.fit);
|
|
@@ -16766,7 +16895,7 @@ class PDFGenerator {
|
|
|
16766
16895
|
}
|
|
16767
16896
|
}
|
|
16768
16897
|
catch (e) {
|
|
16769
|
-
|
|
16898
|
+
Logger.warn('[pc-editor:PDFGenerator] Failed to embed image:', e);
|
|
16770
16899
|
}
|
|
16771
16900
|
}
|
|
16772
16901
|
// Fallback: draw placeholder rectangle for images we can't embed
|
|
@@ -18794,7 +18923,7 @@ class MutationUndo {
|
|
|
18794
18923
|
this.undoTableStructure(mutation);
|
|
18795
18924
|
break;
|
|
18796
18925
|
default:
|
|
18797
|
-
|
|
18926
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for undo:', mutation.type);
|
|
18798
18927
|
}
|
|
18799
18928
|
}
|
|
18800
18929
|
/**
|
|
@@ -18844,7 +18973,7 @@ class MutationUndo {
|
|
|
18844
18973
|
this.redoTableStructure(mutation);
|
|
18845
18974
|
break;
|
|
18846
18975
|
default:
|
|
18847
|
-
|
|
18976
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for redo:', mutation.type);
|
|
18848
18977
|
}
|
|
18849
18978
|
}
|
|
18850
18979
|
// --- Text Mutations ---
|
|
@@ -20176,13 +20305,13 @@ class PDFParser {
|
|
|
20176
20305
|
}
|
|
20177
20306
|
catch {
|
|
20178
20307
|
// Skip images that fail to extract
|
|
20179
|
-
|
|
20308
|
+
Logger.warn(`[pc-editor:PDFParser] Failed to extract image: ${imageName}`);
|
|
20180
20309
|
}
|
|
20181
20310
|
}
|
|
20182
20311
|
}
|
|
20183
20312
|
}
|
|
20184
20313
|
catch (error) {
|
|
20185
|
-
|
|
20314
|
+
Logger.warn('[pc-editor:PDFParser] Image extraction failed:', error);
|
|
20186
20315
|
}
|
|
20187
20316
|
return images;
|
|
20188
20317
|
}
|
|
@@ -21209,6 +21338,8 @@ class PCEditor extends EventEmitter {
|
|
|
21209
21338
|
}
|
|
21210
21339
|
this.container = container;
|
|
21211
21340
|
this.options = this.mergeOptions(options);
|
|
21341
|
+
// Initialize logging
|
|
21342
|
+
Logger.setEnabled(this.options.enableLogging ?? false);
|
|
21212
21343
|
this.document = new Document();
|
|
21213
21344
|
// Apply constructor options to document settings
|
|
21214
21345
|
this.document.updateSettings({
|
|
@@ -21233,7 +21364,8 @@ class PCEditor extends EventEmitter {
|
|
|
21233
21364
|
showControlCharacters: options?.showControlCharacters ?? false,
|
|
21234
21365
|
defaultFont: options?.defaultFont || 'Arial',
|
|
21235
21366
|
defaultFontSize: options?.defaultFontSize || 12,
|
|
21236
|
-
theme: options?.theme || 'light'
|
|
21367
|
+
theme: options?.theme || 'light',
|
|
21368
|
+
enableLogging: options?.enableLogging ?? false
|
|
21237
21369
|
};
|
|
21238
21370
|
}
|
|
21239
21371
|
initialize() {
|
|
@@ -21640,6 +21772,7 @@ class PCEditor extends EventEmitter {
|
|
|
21640
21772
|
* This changes which section receives keyboard input and cursor positioning.
|
|
21641
21773
|
*/
|
|
21642
21774
|
setActiveSection(section) {
|
|
21775
|
+
Logger.log('[pc-editor] setActiveSection', section);
|
|
21643
21776
|
if (this._activeEditingSection !== section) {
|
|
21644
21777
|
this._activeEditingSection = section;
|
|
21645
21778
|
// Delegate to canvas manager which handles the section change and emits events
|
|
@@ -21730,6 +21863,7 @@ class PCEditor extends EventEmitter {
|
|
|
21730
21863
|
}
|
|
21731
21864
|
}
|
|
21732
21865
|
loadDocument(documentData) {
|
|
21866
|
+
Logger.log('[pc-editor] loadDocument');
|
|
21733
21867
|
if (!this._isReady) {
|
|
21734
21868
|
throw new Error('Editor is not ready');
|
|
21735
21869
|
}
|
|
@@ -21756,6 +21890,7 @@ class PCEditor extends EventEmitter {
|
|
|
21756
21890
|
return this.document.toData();
|
|
21757
21891
|
}
|
|
21758
21892
|
bindData(data) {
|
|
21893
|
+
Logger.log('[pc-editor] bindData');
|
|
21759
21894
|
if (!this._isReady) {
|
|
21760
21895
|
throw new Error('Editor is not ready');
|
|
21761
21896
|
}
|
|
@@ -21764,6 +21899,7 @@ class PCEditor extends EventEmitter {
|
|
|
21764
21899
|
this.emit('data-bound', { data });
|
|
21765
21900
|
}
|
|
21766
21901
|
async exportPDF(options) {
|
|
21902
|
+
Logger.log('[pc-editor] exportPDF');
|
|
21767
21903
|
if (!this._isReady) {
|
|
21768
21904
|
throw new Error('Editor is not ready');
|
|
21769
21905
|
}
|
|
@@ -21805,6 +21941,7 @@ class PCEditor extends EventEmitter {
|
|
|
21805
21941
|
* @returns JSON string representation of the document
|
|
21806
21942
|
*/
|
|
21807
21943
|
saveDocument() {
|
|
21944
|
+
Logger.log('[pc-editor] saveDocument');
|
|
21808
21945
|
if (!this._isReady) {
|
|
21809
21946
|
throw new Error('Editor is not ready');
|
|
21810
21947
|
}
|
|
@@ -21816,6 +21953,7 @@ class PCEditor extends EventEmitter {
|
|
|
21816
21953
|
* @param filename Optional filename (defaults to 'document.pceditor.json')
|
|
21817
21954
|
*/
|
|
21818
21955
|
saveDocumentToFile(filename = 'document.pceditor.json') {
|
|
21956
|
+
Logger.log('[pc-editor] saveDocumentToFile', filename);
|
|
21819
21957
|
const jsonString = this.saveDocument();
|
|
21820
21958
|
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
21821
21959
|
const url = URL.createObjectURL(blob);
|
|
@@ -21833,6 +21971,7 @@ class PCEditor extends EventEmitter {
|
|
|
21833
21971
|
* @param jsonString JSON string representation of the document
|
|
21834
21972
|
*/
|
|
21835
21973
|
loadDocumentFromJSON(jsonString) {
|
|
21974
|
+
Logger.log('[pc-editor] loadDocumentFromJSON');
|
|
21836
21975
|
if (!this._isReady) {
|
|
21837
21976
|
throw new Error('Editor is not ready');
|
|
21838
21977
|
}
|
|
@@ -21853,6 +21992,7 @@ class PCEditor extends EventEmitter {
|
|
|
21853
21992
|
* @returns Promise that resolves when loading is complete
|
|
21854
21993
|
*/
|
|
21855
21994
|
async loadDocumentFromFile(file) {
|
|
21995
|
+
Logger.log('[pc-editor] loadDocumentFromFile', file.name);
|
|
21856
21996
|
if (!this._isReady) {
|
|
21857
21997
|
throw new Error('Editor is not ready');
|
|
21858
21998
|
}
|
|
@@ -21941,22 +22081,25 @@ class PCEditor extends EventEmitter {
|
|
|
21941
22081
|
// Version compatibility check
|
|
21942
22082
|
const [major] = doc.version.split('.').map(Number);
|
|
21943
22083
|
if (major > 1) {
|
|
21944
|
-
|
|
22084
|
+
Logger.warn(`[pc-editor] Document version ${doc.version} may not be fully compatible with this editor`);
|
|
21945
22085
|
}
|
|
21946
22086
|
}
|
|
21947
22087
|
selectElement(elementId) {
|
|
22088
|
+
Logger.log('[pc-editor] selectElement', elementId);
|
|
21948
22089
|
if (!this._isReady) {
|
|
21949
22090
|
throw new Error('Editor is not ready');
|
|
21950
22091
|
}
|
|
21951
22092
|
this.canvasManager.selectElement(elementId);
|
|
21952
22093
|
}
|
|
21953
22094
|
clearSelection() {
|
|
22095
|
+
Logger.log('[pc-editor] clearSelection');
|
|
21954
22096
|
if (!this._isReady) {
|
|
21955
22097
|
throw new Error('Editor is not ready');
|
|
21956
22098
|
}
|
|
21957
22099
|
this.canvasManager.clearSelection();
|
|
21958
22100
|
}
|
|
21959
22101
|
removeEmbeddedObject(objectId) {
|
|
22102
|
+
Logger.log('[pc-editor] removeEmbeddedObject', objectId);
|
|
21960
22103
|
if (!this._isReady) {
|
|
21961
22104
|
throw new Error('Editor is not ready');
|
|
21962
22105
|
}
|
|
@@ -21966,6 +22109,7 @@ class PCEditor extends EventEmitter {
|
|
|
21966
22109
|
* Undo the last operation.
|
|
21967
22110
|
*/
|
|
21968
22111
|
undo() {
|
|
22112
|
+
Logger.log('[pc-editor] undo');
|
|
21969
22113
|
if (!this._isReady)
|
|
21970
22114
|
return;
|
|
21971
22115
|
const success = this.transactionManager.undo();
|
|
@@ -21978,6 +22122,7 @@ class PCEditor extends EventEmitter {
|
|
|
21978
22122
|
* Redo the last undone operation.
|
|
21979
22123
|
*/
|
|
21980
22124
|
redo() {
|
|
22125
|
+
Logger.log('[pc-editor] redo');
|
|
21981
22126
|
if (!this._isReady)
|
|
21982
22127
|
return;
|
|
21983
22128
|
const success = this.transactionManager.redo();
|
|
@@ -22011,16 +22156,19 @@ class PCEditor extends EventEmitter {
|
|
|
22011
22156
|
this.transactionManager.setMaxHistory(count);
|
|
22012
22157
|
}
|
|
22013
22158
|
zoomIn() {
|
|
22159
|
+
Logger.log('[pc-editor] zoomIn');
|
|
22014
22160
|
if (!this._isReady)
|
|
22015
22161
|
return;
|
|
22016
22162
|
this.canvasManager.zoomIn();
|
|
22017
22163
|
}
|
|
22018
22164
|
zoomOut() {
|
|
22165
|
+
Logger.log('[pc-editor] zoomOut');
|
|
22019
22166
|
if (!this._isReady)
|
|
22020
22167
|
return;
|
|
22021
22168
|
this.canvasManager.zoomOut();
|
|
22022
22169
|
}
|
|
22023
22170
|
setZoom(level) {
|
|
22171
|
+
Logger.log('[pc-editor] setZoom', level);
|
|
22024
22172
|
if (!this._isReady)
|
|
22025
22173
|
return;
|
|
22026
22174
|
this.canvasManager.setZoom(level);
|
|
@@ -22057,6 +22205,7 @@ class PCEditor extends EventEmitter {
|
|
|
22057
22205
|
return this.canvasManager.getContentOffset();
|
|
22058
22206
|
}
|
|
22059
22207
|
fitToWidth() {
|
|
22208
|
+
Logger.log('[pc-editor] fitToWidth');
|
|
22060
22209
|
if (!this._isReady)
|
|
22061
22210
|
return;
|
|
22062
22211
|
this.canvasManager.fitToWidth();
|
|
@@ -22065,23 +22214,27 @@ class PCEditor extends EventEmitter {
|
|
|
22065
22214
|
* Force a re-render of the canvas.
|
|
22066
22215
|
*/
|
|
22067
22216
|
render() {
|
|
22217
|
+
Logger.log('[pc-editor] render');
|
|
22068
22218
|
if (!this._isReady)
|
|
22069
22219
|
return;
|
|
22070
22220
|
this.canvasManager.render();
|
|
22071
22221
|
}
|
|
22072
22222
|
fitToPage() {
|
|
22223
|
+
Logger.log('[pc-editor] fitToPage');
|
|
22073
22224
|
if (!this._isReady)
|
|
22074
22225
|
return;
|
|
22075
22226
|
this.canvasManager.fitToPage();
|
|
22076
22227
|
}
|
|
22077
22228
|
// Layout control methods
|
|
22078
22229
|
setAutoFlow(enabled) {
|
|
22230
|
+
Logger.log('[pc-editor] setAutoFlow', enabled);
|
|
22079
22231
|
if (!this._isReady) {
|
|
22080
22232
|
throw new Error('Editor is not ready');
|
|
22081
22233
|
}
|
|
22082
22234
|
this.layoutEngine.setAutoFlow(enabled);
|
|
22083
22235
|
}
|
|
22084
22236
|
reflowDocument() {
|
|
22237
|
+
Logger.log('[pc-editor] reflowDocument');
|
|
22085
22238
|
if (!this._isReady) {
|
|
22086
22239
|
throw new Error('Editor is not ready');
|
|
22087
22240
|
}
|
|
@@ -22123,6 +22276,7 @@ class PCEditor extends EventEmitter {
|
|
|
22123
22276
|
};
|
|
22124
22277
|
}
|
|
22125
22278
|
addPage() {
|
|
22279
|
+
Logger.log('[pc-editor] addPage');
|
|
22126
22280
|
if (!this._isReady) {
|
|
22127
22281
|
throw new Error('Editor is not ready');
|
|
22128
22282
|
}
|
|
@@ -22134,6 +22288,7 @@ class PCEditor extends EventEmitter {
|
|
|
22134
22288
|
this.canvasManager.setDocument(this.document);
|
|
22135
22289
|
}
|
|
22136
22290
|
removePage(pageId) {
|
|
22291
|
+
Logger.log('[pc-editor] removePage', pageId);
|
|
22137
22292
|
if (!this._isReady) {
|
|
22138
22293
|
throw new Error('Editor is not ready');
|
|
22139
22294
|
}
|
|
@@ -22208,7 +22363,7 @@ class PCEditor extends EventEmitter {
|
|
|
22208
22363
|
}
|
|
22209
22364
|
// Use the unified focus system to get the currently focused control
|
|
22210
22365
|
const focusedControl = this.canvasManager.getFocusedControl();
|
|
22211
|
-
|
|
22366
|
+
Logger.log('[pc-editor:handleKeyDown] Key:', e.key, 'focusedControl:', focusedControl?.constructor?.name);
|
|
22212
22367
|
if (!focusedControl)
|
|
22213
22368
|
return;
|
|
22214
22369
|
// Vertical navigation needs layout context - handle specially
|
|
@@ -22232,9 +22387,9 @@ class PCEditor extends EventEmitter {
|
|
|
22232
22387
|
}
|
|
22233
22388
|
}
|
|
22234
22389
|
// Delegate to the focused control's handleKeyDown
|
|
22235
|
-
|
|
22390
|
+
Logger.log('[pc-editor:handleKeyDown] Calling focusedControl.handleKeyDown');
|
|
22236
22391
|
const handled = focusedControl.handleKeyDown(e);
|
|
22237
|
-
|
|
22392
|
+
Logger.log('[pc-editor:handleKeyDown] handled:', handled);
|
|
22238
22393
|
if (handled) {
|
|
22239
22394
|
this.canvasManager.render();
|
|
22240
22395
|
// Handle text box-specific post-processing
|
|
@@ -22598,6 +22753,7 @@ class PCEditor extends EventEmitter {
|
|
|
22598
22753
|
* This is useful when UI controls have stolen focus.
|
|
22599
22754
|
*/
|
|
22600
22755
|
applyFormattingWithFallback(start, end, formatting) {
|
|
22756
|
+
Logger.log('[pc-editor] applyFormattingWithFallback', start, end, formatting);
|
|
22601
22757
|
// Try current context first
|
|
22602
22758
|
let flowingContent = this.getEditingFlowingContent();
|
|
22603
22759
|
// Fall back to saved context
|
|
@@ -22798,6 +22954,7 @@ class PCEditor extends EventEmitter {
|
|
|
22798
22954
|
* Works for body text, text boxes, and table cells.
|
|
22799
22955
|
*/
|
|
22800
22956
|
setUnifiedAlignment(alignment) {
|
|
22957
|
+
Logger.log('[pc-editor] setUnifiedAlignment', alignment);
|
|
22801
22958
|
const flowingContent = this.getEditingFlowingContent();
|
|
22802
22959
|
if (!flowingContent) {
|
|
22803
22960
|
throw new Error('No text is being edited');
|
|
@@ -22814,6 +22971,7 @@ class PCEditor extends EventEmitter {
|
|
|
22814
22971
|
this.canvasManager.render();
|
|
22815
22972
|
}
|
|
22816
22973
|
insertText(text) {
|
|
22974
|
+
Logger.log('[pc-editor] insertText', text);
|
|
22817
22975
|
if (!this._isReady) {
|
|
22818
22976
|
throw new Error('Editor is not ready');
|
|
22819
22977
|
}
|
|
@@ -22830,6 +22988,7 @@ class PCEditor extends EventEmitter {
|
|
|
22830
22988
|
return flowingContent ? flowingContent.getText() : '';
|
|
22831
22989
|
}
|
|
22832
22990
|
setFlowingText(text) {
|
|
22991
|
+
Logger.log('[pc-editor] setFlowingText');
|
|
22833
22992
|
if (!this._isReady) {
|
|
22834
22993
|
throw new Error('Editor is not ready');
|
|
22835
22994
|
}
|
|
@@ -22843,6 +23002,7 @@ class PCEditor extends EventEmitter {
|
|
|
22843
23002
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22844
23003
|
*/
|
|
22845
23004
|
setCursorPosition(position) {
|
|
23005
|
+
Logger.log('[pc-editor] setCursorPosition', position);
|
|
22846
23006
|
if (!this._isReady) {
|
|
22847
23007
|
throw new Error('Editor is not ready');
|
|
22848
23008
|
}
|
|
@@ -22888,6 +23048,7 @@ class PCEditor extends EventEmitter {
|
|
|
22888
23048
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22889
23049
|
*/
|
|
22890
23050
|
insertEmbeddedObject(object, position = 'inline') {
|
|
23051
|
+
Logger.log('[pc-editor] insertEmbeddedObject', object.id, position);
|
|
22891
23052
|
if (!this._isReady) {
|
|
22892
23053
|
throw new Error('Editor is not ready');
|
|
22893
23054
|
}
|
|
@@ -22914,6 +23075,7 @@ class PCEditor extends EventEmitter {
|
|
|
22914
23075
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22915
23076
|
*/
|
|
22916
23077
|
insertSubstitutionField(fieldName, config) {
|
|
23078
|
+
Logger.log('[pc-editor] insertSubstitutionField', fieldName);
|
|
22917
23079
|
if (!this._isReady) {
|
|
22918
23080
|
throw new Error('Editor is not ready');
|
|
22919
23081
|
}
|
|
@@ -22939,6 +23101,7 @@ class PCEditor extends EventEmitter {
|
|
|
22939
23101
|
* @param displayFormat Optional format string (e.g., "Page %d" where %d is replaced by page number)
|
|
22940
23102
|
*/
|
|
22941
23103
|
insertPageNumberField(displayFormat) {
|
|
23104
|
+
Logger.log('[pc-editor] insertPageNumberField');
|
|
22942
23105
|
if (!this._isReady) {
|
|
22943
23106
|
throw new Error('Editor is not ready');
|
|
22944
23107
|
}
|
|
@@ -22964,6 +23127,7 @@ class PCEditor extends EventEmitter {
|
|
|
22964
23127
|
* @param displayFormat Optional format string (e.g., "of %d" where %d is replaced by page count)
|
|
22965
23128
|
*/
|
|
22966
23129
|
insertPageCountField(displayFormat) {
|
|
23130
|
+
Logger.log('[pc-editor] insertPageCountField');
|
|
22967
23131
|
if (!this._isReady) {
|
|
22968
23132
|
throw new Error('Editor is not ready');
|
|
22969
23133
|
}
|
|
@@ -22989,6 +23153,7 @@ class PCEditor extends EventEmitter {
|
|
|
22989
23153
|
* or table cells are not recommended as these don't span pages.
|
|
22990
23154
|
*/
|
|
22991
23155
|
insertPageBreak() {
|
|
23156
|
+
Logger.log('[pc-editor] insertPageBreak');
|
|
22992
23157
|
if (!this._isReady) {
|
|
22993
23158
|
throw new Error('Editor is not ready');
|
|
22994
23159
|
}
|
|
@@ -23137,7 +23302,7 @@ class PCEditor extends EventEmitter {
|
|
|
23137
23302
|
// Find the text box in all flowing contents
|
|
23138
23303
|
const textBox = this.findTextBoxById(textBoxId);
|
|
23139
23304
|
if (!textBox) {
|
|
23140
|
-
|
|
23305
|
+
Logger.warn(`[pc-editor:updateTextBox] Text box not found: ${textBoxId}`);
|
|
23141
23306
|
return false;
|
|
23142
23307
|
}
|
|
23143
23308
|
// Apply updates
|
|
@@ -23200,7 +23365,7 @@ class PCEditor extends EventEmitter {
|
|
|
23200
23365
|
// Find the image in all flowing contents
|
|
23201
23366
|
const image = this.findImageById(imageId);
|
|
23202
23367
|
if (!image) {
|
|
23203
|
-
|
|
23368
|
+
Logger.warn(`[pc-editor:updateImage] Image not found: ${imageId}`);
|
|
23204
23369
|
return false;
|
|
23205
23370
|
}
|
|
23206
23371
|
// Apply updates
|
|
@@ -23234,7 +23399,7 @@ class PCEditor extends EventEmitter {
|
|
|
23234
23399
|
return false;
|
|
23235
23400
|
const image = this.findImageById(imageId);
|
|
23236
23401
|
if (!image) {
|
|
23237
|
-
|
|
23402
|
+
Logger.warn(`[pc-editor:setImageSource] Image not found: ${imageId}`);
|
|
23238
23403
|
return false;
|
|
23239
23404
|
}
|
|
23240
23405
|
image.setSource(dataUrl, options);
|
|
@@ -23361,7 +23526,7 @@ class PCEditor extends EventEmitter {
|
|
|
23361
23526
|
* @deprecated Use insertEmbeddedObject instead
|
|
23362
23527
|
*/
|
|
23363
23528
|
insertInlineElement(_elementData, _position = 'inline') {
|
|
23364
|
-
|
|
23529
|
+
Logger.warn('[pc-editor] insertInlineElement is deprecated and no longer functional. Use insertEmbeddedObject instead.');
|
|
23365
23530
|
}
|
|
23366
23531
|
/**
|
|
23367
23532
|
* Apply merge data to substitute all substitution fields with their values.
|
|
@@ -24086,18 +24251,37 @@ class PCEditor extends EventEmitter {
|
|
|
24086
24251
|
return this.document.bodyFlowingContent.getParagraphBoundaries();
|
|
24087
24252
|
}
|
|
24088
24253
|
/**
|
|
24089
|
-
* Create a repeating section
|
|
24090
|
-
*
|
|
24091
|
-
*
|
|
24092
|
-
*
|
|
24254
|
+
* Create a repeating section.
|
|
24255
|
+
*
|
|
24256
|
+
* If a table is currently being edited (focused), creates a table row loop
|
|
24257
|
+
* based on the focused cell's row. In this case, startIndex and endIndex
|
|
24258
|
+
* are ignored — the focused cell's row determines the loop range.
|
|
24259
|
+
*
|
|
24260
|
+
* Otherwise, creates a body text repeating section at the given paragraph boundaries.
|
|
24261
|
+
*
|
|
24262
|
+
* @param startIndex Text index at paragraph start (ignored for table row loops)
|
|
24263
|
+
* @param endIndex Text index at closing paragraph start (ignored for table row loops)
|
|
24093
24264
|
* @param fieldPath The field path to the array to loop over (e.g., "items")
|
|
24094
|
-
* @returns The created section, or null if
|
|
24265
|
+
* @returns The created section/loop, or null if creation failed
|
|
24095
24266
|
*/
|
|
24096
24267
|
createRepeatingSection(startIndex, endIndex, fieldPath) {
|
|
24097
24268
|
if (!this._isReady) {
|
|
24098
24269
|
throw new Error('Editor is not ready');
|
|
24099
24270
|
}
|
|
24271
|
+
// If a table is focused, create a row loop instead of a text repeating section
|
|
24272
|
+
const focusedTable = this.getFocusedTable();
|
|
24273
|
+
if (focusedTable && focusedTable.focusedCell) {
|
|
24274
|
+
Logger.log('[pc-editor] createRepeatingSection → table row loop', fieldPath);
|
|
24275
|
+
const row = focusedTable.focusedCell.row;
|
|
24276
|
+
const loop = focusedTable.createRowLoop(row, row, fieldPath);
|
|
24277
|
+
if (loop) {
|
|
24278
|
+
this.canvasManager.render();
|
|
24279
|
+
this.emit('table-row-loop-added', { table: focusedTable, loop });
|
|
24280
|
+
}
|
|
24281
|
+
return null; // Row loops are not RepeatingSections, return null
|
|
24282
|
+
}
|
|
24100
24283
|
// Repeating sections only work in body (document-level)
|
|
24284
|
+
Logger.log('[pc-editor] createRepeatingSection', startIndex, endIndex, fieldPath);
|
|
24101
24285
|
const section = this.document.bodyFlowingContent.createRepeatingSection(startIndex, endIndex, fieldPath);
|
|
24102
24286
|
if (section) {
|
|
24103
24287
|
this.canvasManager.render();
|
|
@@ -24475,7 +24659,7 @@ class PCEditor extends EventEmitter {
|
|
|
24475
24659
|
createEmbeddedObjectFromData(data) {
|
|
24476
24660
|
const object = EmbeddedObjectFactory.tryCreate(data);
|
|
24477
24661
|
if (!object) {
|
|
24478
|
-
|
|
24662
|
+
Logger.warn('[pc-editor] Unknown object type:', data.objectType);
|
|
24479
24663
|
}
|
|
24480
24664
|
return object;
|
|
24481
24665
|
}
|
|
@@ -24504,6 +24688,13 @@ class PCEditor extends EventEmitter {
|
|
|
24504
24688
|
return false;
|
|
24505
24689
|
}
|
|
24506
24690
|
}
|
|
24691
|
+
/**
|
|
24692
|
+
* Enable or disable verbose logging.
|
|
24693
|
+
* When disabled (default), only errors are logged to the console.
|
|
24694
|
+
*/
|
|
24695
|
+
setLogging(enabled) {
|
|
24696
|
+
Logger.setEnabled(enabled);
|
|
24697
|
+
}
|
|
24507
24698
|
destroy() {
|
|
24508
24699
|
this.disableTextInput();
|
|
24509
24700
|
if (this.canvasManager) {
|
|
@@ -25646,33 +25837,38 @@ class DocumentInfoPane extends BasePane {
|
|
|
25646
25837
|
}
|
|
25647
25838
|
createContent() {
|
|
25648
25839
|
const container = document.createElement('div');
|
|
25649
|
-
container.className = 'pc-pane-
|
|
25840
|
+
container.className = 'pc-pane-label-value-grid';
|
|
25650
25841
|
// Page count
|
|
25651
|
-
|
|
25652
|
-
this.pageCountEl =
|
|
25653
|
-
container.appendChild(
|
|
25842
|
+
container.appendChild(this.createLabel('Pages:'));
|
|
25843
|
+
this.pageCountEl = this.createValue('0');
|
|
25844
|
+
container.appendChild(this.pageCountEl);
|
|
25845
|
+
container.appendChild(this.createSpacer());
|
|
25654
25846
|
// Page size
|
|
25655
|
-
|
|
25656
|
-
this.pageSizeEl =
|
|
25657
|
-
container.appendChild(
|
|
25847
|
+
container.appendChild(this.createLabel('Size:'));
|
|
25848
|
+
this.pageSizeEl = this.createValue('-');
|
|
25849
|
+
container.appendChild(this.pageSizeEl);
|
|
25850
|
+
container.appendChild(this.createSpacer());
|
|
25658
25851
|
// Page orientation
|
|
25659
|
-
|
|
25660
|
-
this.pageOrientationEl =
|
|
25661
|
-
container.appendChild(
|
|
25852
|
+
container.appendChild(this.createLabel('Orientation:'));
|
|
25853
|
+
this.pageOrientationEl = this.createValue('-');
|
|
25854
|
+
container.appendChild(this.pageOrientationEl);
|
|
25855
|
+
container.appendChild(this.createSpacer());
|
|
25662
25856
|
return container;
|
|
25663
25857
|
}
|
|
25664
|
-
|
|
25665
|
-
const
|
|
25666
|
-
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
25673
|
-
|
|
25674
|
-
|
|
25675
|
-
|
|
25858
|
+
createLabel(text) {
|
|
25859
|
+
const label = document.createElement('span');
|
|
25860
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
25861
|
+
label.textContent = text;
|
|
25862
|
+
return label;
|
|
25863
|
+
}
|
|
25864
|
+
createValue(text) {
|
|
25865
|
+
const value = document.createElement('span');
|
|
25866
|
+
value.className = 'pc-pane-info-value';
|
|
25867
|
+
value.textContent = text;
|
|
25868
|
+
return value;
|
|
25869
|
+
}
|
|
25870
|
+
createSpacer() {
|
|
25871
|
+
return document.createElement('div');
|
|
25676
25872
|
}
|
|
25677
25873
|
/**
|
|
25678
25874
|
* Update the displayed information from the editor.
|
|
@@ -25852,24 +26048,48 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25852
26048
|
const container = document.createElement('div');
|
|
25853
26049
|
// Margins section
|
|
25854
26050
|
const marginsSection = this.createSection('Margins (mm)');
|
|
26051
|
+
// Five-column grid: label, edit, label, edit, stretch
|
|
25855
26052
|
const marginsGrid = document.createElement('div');
|
|
25856
|
-
marginsGrid.className = 'pc-pane-margins-grid';
|
|
26053
|
+
marginsGrid.className = 'pc-pane-margins-grid-5col';
|
|
25857
26054
|
this.marginTopInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25858
26055
|
this.marginRightInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25859
26056
|
this.marginBottomInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25860
26057
|
this.marginLeftInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
|
|
25864
|
-
|
|
26058
|
+
// Apply margins on blur
|
|
26059
|
+
const applyMargins = () => this.applyMargins();
|
|
26060
|
+
this.marginTopInput.addEventListener('blur', applyMargins);
|
|
26061
|
+
this.marginRightInput.addEventListener('blur', applyMargins);
|
|
26062
|
+
this.marginBottomInput.addEventListener('blur', applyMargins);
|
|
26063
|
+
this.marginLeftInput.addEventListener('blur', applyMargins);
|
|
26064
|
+
this.eventCleanup.push(() => {
|
|
26065
|
+
this.marginTopInput?.removeEventListener('blur', applyMargins);
|
|
26066
|
+
this.marginRightInput?.removeEventListener('blur', applyMargins);
|
|
26067
|
+
this.marginBottomInput?.removeEventListener('blur', applyMargins);
|
|
26068
|
+
this.marginLeftInput?.removeEventListener('blur', applyMargins);
|
|
26069
|
+
});
|
|
26070
|
+
// Row 1: Top / Right
|
|
26071
|
+
const topLabel = this.createMarginLabel('Top:');
|
|
26072
|
+
const rightLabel = this.createMarginLabel('Right:');
|
|
26073
|
+
marginsGrid.appendChild(topLabel);
|
|
26074
|
+
marginsGrid.appendChild(this.marginTopInput);
|
|
26075
|
+
marginsGrid.appendChild(rightLabel);
|
|
26076
|
+
marginsGrid.appendChild(this.marginRightInput);
|
|
26077
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
26078
|
+
// Row 2: Bottom / Left
|
|
26079
|
+
const bottomLabel = this.createMarginLabel('Bottom:');
|
|
26080
|
+
const leftLabel = this.createMarginLabel('Left:');
|
|
26081
|
+
marginsGrid.appendChild(bottomLabel);
|
|
26082
|
+
marginsGrid.appendChild(this.marginBottomInput);
|
|
26083
|
+
marginsGrid.appendChild(leftLabel);
|
|
26084
|
+
marginsGrid.appendChild(this.marginLeftInput);
|
|
26085
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
25865
26086
|
marginsSection.appendChild(marginsGrid);
|
|
25866
|
-
// Apply margins button
|
|
25867
|
-
const applyMarginsBtn = this.createButton('Apply Margins');
|
|
25868
|
-
this.addButtonListener(applyMarginsBtn, () => this.applyMargins());
|
|
25869
|
-
marginsSection.appendChild(applyMarginsBtn);
|
|
25870
26087
|
container.appendChild(marginsSection);
|
|
25871
|
-
// Page
|
|
25872
|
-
const
|
|
26088
|
+
// Page settings section using label-value grid: label, value, stretch
|
|
26089
|
+
const pageSection = this.createSection();
|
|
26090
|
+
const pageGrid = document.createElement('div');
|
|
26091
|
+
pageGrid.className = 'pc-pane-label-value-grid';
|
|
26092
|
+
// Page Size
|
|
25873
26093
|
this.pageSizeSelect = this.createSelect([
|
|
25874
26094
|
{ value: 'A4', label: 'A4' },
|
|
25875
26095
|
{ value: 'Letter', label: 'Letter' },
|
|
@@ -25877,19 +26097,32 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25877
26097
|
{ value: 'A3', label: 'A3' }
|
|
25878
26098
|
], 'A4');
|
|
25879
26099
|
this.addImmediateApplyListener(this.pageSizeSelect, () => this.applyPageSettings());
|
|
25880
|
-
|
|
25881
|
-
|
|
25882
|
-
|
|
25883
|
-
|
|
26100
|
+
pageGrid.appendChild(this.createMarginLabel('Page Size:'));
|
|
26101
|
+
pageGrid.appendChild(this.pageSizeSelect);
|
|
26102
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26103
|
+
// Orientation
|
|
25884
26104
|
this.orientationSelect = this.createSelect([
|
|
25885
26105
|
{ value: 'portrait', label: 'Portrait' },
|
|
25886
26106
|
{ value: 'landscape', label: 'Landscape' }
|
|
25887
26107
|
], 'portrait');
|
|
25888
26108
|
this.addImmediateApplyListener(this.orientationSelect, () => this.applyPageSettings());
|
|
25889
|
-
|
|
25890
|
-
|
|
26109
|
+
pageGrid.appendChild(this.createMarginLabel('Orientation:'));
|
|
26110
|
+
pageGrid.appendChild(this.orientationSelect);
|
|
26111
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26112
|
+
pageSection.appendChild(pageGrid);
|
|
26113
|
+
container.appendChild(pageSection);
|
|
25891
26114
|
return container;
|
|
25892
26115
|
}
|
|
26116
|
+
createMarginLabel(text) {
|
|
26117
|
+
const label = document.createElement('label');
|
|
26118
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
26119
|
+
label.textContent = text;
|
|
26120
|
+
return label;
|
|
26121
|
+
}
|
|
26122
|
+
createSpacer() {
|
|
26123
|
+
const spacer = document.createElement('div');
|
|
26124
|
+
return spacer;
|
|
26125
|
+
}
|
|
25893
26126
|
loadSettings() {
|
|
25894
26127
|
if (!this.editor)
|
|
25895
26128
|
return;
|
|
@@ -26429,6 +26662,7 @@ class HyperlinkPane extends BasePane {
|
|
|
26429
26662
|
this.titleInput = null;
|
|
26430
26663
|
this.rangeHint = null;
|
|
26431
26664
|
this.currentHyperlink = null;
|
|
26665
|
+
this._isUpdating = false;
|
|
26432
26666
|
this.onApply = options.onApply;
|
|
26433
26667
|
this.onRemove = options.onRemove;
|
|
26434
26668
|
}
|
|
@@ -26470,15 +26704,21 @@ class HyperlinkPane extends BasePane {
|
|
|
26470
26704
|
return container;
|
|
26471
26705
|
}
|
|
26472
26706
|
updateFromCursor() {
|
|
26473
|
-
if (!this.editor)
|
|
26707
|
+
if (!this.editor || this._isUpdating)
|
|
26474
26708
|
return;
|
|
26475
|
-
|
|
26476
|
-
|
|
26477
|
-
|
|
26478
|
-
this.
|
|
26709
|
+
this._isUpdating = true;
|
|
26710
|
+
try {
|
|
26711
|
+
const cursorPos = this.editor.getCursorPosition();
|
|
26712
|
+
const hyperlink = this.editor.getHyperlinkAt(cursorPos);
|
|
26713
|
+
if (hyperlink) {
|
|
26714
|
+
this.showHyperlink(hyperlink);
|
|
26715
|
+
}
|
|
26716
|
+
else {
|
|
26717
|
+
this.hideHyperlink();
|
|
26718
|
+
}
|
|
26479
26719
|
}
|
|
26480
|
-
|
|
26481
|
-
this.
|
|
26720
|
+
finally {
|
|
26721
|
+
this._isUpdating = false;
|
|
26482
26722
|
}
|
|
26483
26723
|
}
|
|
26484
26724
|
showHyperlink(hyperlink) {
|
|
@@ -27097,6 +27337,7 @@ class TextBoxPane extends BasePane {
|
|
|
27097
27337
|
this.borderColorInput = null;
|
|
27098
27338
|
this.borderStyleSelect = null;
|
|
27099
27339
|
this.paddingInput = null;
|
|
27340
|
+
this._isUpdating = false;
|
|
27100
27341
|
this.currentTextBox = null;
|
|
27101
27342
|
this.onApplyCallback = options.onApply;
|
|
27102
27343
|
}
|
|
@@ -27170,14 +27411,20 @@ class TextBoxPane extends BasePane {
|
|
|
27170
27411
|
return container;
|
|
27171
27412
|
}
|
|
27172
27413
|
updateFromSelection() {
|
|
27173
|
-
if (!this.editor)
|
|
27414
|
+
if (!this.editor || this._isUpdating)
|
|
27174
27415
|
return;
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
this.
|
|
27416
|
+
this._isUpdating = true;
|
|
27417
|
+
try {
|
|
27418
|
+
const textBox = this.editor.getSelectedTextBox?.();
|
|
27419
|
+
if (textBox && !textBox.editing) {
|
|
27420
|
+
this.showTextBox(textBox);
|
|
27421
|
+
}
|
|
27422
|
+
else {
|
|
27423
|
+
this.hideTextBox();
|
|
27424
|
+
}
|
|
27178
27425
|
}
|
|
27179
|
-
|
|
27180
|
-
this.
|
|
27426
|
+
finally {
|
|
27427
|
+
this._isUpdating = false;
|
|
27181
27428
|
}
|
|
27182
27429
|
}
|
|
27183
27430
|
/**
|
|
@@ -27331,6 +27578,7 @@ class ImagePane extends BasePane {
|
|
|
27331
27578
|
this.altTextInput = null;
|
|
27332
27579
|
this.fileInput = null;
|
|
27333
27580
|
this.currentImage = null;
|
|
27581
|
+
this._isUpdating = false;
|
|
27334
27582
|
this.maxImageWidth = options.maxImageWidth ?? 400;
|
|
27335
27583
|
this.maxImageHeight = options.maxImageHeight ?? 400;
|
|
27336
27584
|
this.onApplyCallback = options.onApply;
|
|
@@ -27412,14 +27660,20 @@ class ImagePane extends BasePane {
|
|
|
27412
27660
|
return container;
|
|
27413
27661
|
}
|
|
27414
27662
|
updateFromSelection() {
|
|
27415
|
-
if (!this.editor)
|
|
27663
|
+
if (!this.editor || this._isUpdating)
|
|
27416
27664
|
return;
|
|
27417
|
-
|
|
27418
|
-
|
|
27419
|
-
this.
|
|
27665
|
+
this._isUpdating = true;
|
|
27666
|
+
try {
|
|
27667
|
+
const image = this.editor.getSelectedImage?.();
|
|
27668
|
+
if (image) {
|
|
27669
|
+
this.showImage(image);
|
|
27670
|
+
}
|
|
27671
|
+
else {
|
|
27672
|
+
this.hideImage();
|
|
27673
|
+
}
|
|
27420
27674
|
}
|
|
27421
|
-
|
|
27422
|
-
this.
|
|
27675
|
+
finally {
|
|
27676
|
+
this._isUpdating = false;
|
|
27423
27677
|
}
|
|
27424
27678
|
}
|
|
27425
27679
|
/**
|
|
@@ -27584,6 +27838,8 @@ class TablePane extends BasePane {
|
|
|
27584
27838
|
// Default controls
|
|
27585
27839
|
this.defaultPaddingInput = null;
|
|
27586
27840
|
this.defaultBorderColorInput = null;
|
|
27841
|
+
// Row loop controls
|
|
27842
|
+
this.loopFieldInput = null;
|
|
27587
27843
|
// Cell formatting controls
|
|
27588
27844
|
this.cellBgColorInput = null;
|
|
27589
27845
|
this.borderTopCheck = null;
|
|
@@ -27594,6 +27850,7 @@ class TablePane extends BasePane {
|
|
|
27594
27850
|
this.borderColorInput = null;
|
|
27595
27851
|
this.borderStyleSelect = null;
|
|
27596
27852
|
this.currentTable = null;
|
|
27853
|
+
this._isUpdating = false;
|
|
27597
27854
|
this.onApplyCallback = options.onApply;
|
|
27598
27855
|
}
|
|
27599
27856
|
attach(options) {
|
|
@@ -27660,6 +27917,16 @@ class TablePane extends BasePane {
|
|
|
27660
27917
|
this.addButtonListener(applyHeadersBtn, () => this.applyHeaders());
|
|
27661
27918
|
headersSection.appendChild(applyHeadersBtn);
|
|
27662
27919
|
container.appendChild(headersSection);
|
|
27920
|
+
// Row Loop section
|
|
27921
|
+
const loopSection = this.createSection('Row Loop');
|
|
27922
|
+
this.loopFieldInput = this.createTextInput({ placeholder: 'items' });
|
|
27923
|
+
loopSection.appendChild(this.createFormGroup('Array Field', this.loopFieldInput, {
|
|
27924
|
+
hint: 'Creates a loop on the currently focused row'
|
|
27925
|
+
}));
|
|
27926
|
+
const createLoopBtn = this.createButton('Create Row Loop');
|
|
27927
|
+
this.addButtonListener(createLoopBtn, () => this.createRowLoop());
|
|
27928
|
+
loopSection.appendChild(createLoopBtn);
|
|
27929
|
+
container.appendChild(loopSection);
|
|
27663
27930
|
// Defaults section
|
|
27664
27931
|
const defaultsSection = this.createSection('Defaults');
|
|
27665
27932
|
const defaultsRow = this.createRow();
|
|
@@ -27732,14 +27999,20 @@ class TablePane extends BasePane {
|
|
|
27732
27999
|
return container;
|
|
27733
28000
|
}
|
|
27734
28001
|
updateFromFocusedTable() {
|
|
27735
|
-
if (!this.editor)
|
|
28002
|
+
if (!this.editor || this._isUpdating)
|
|
27736
28003
|
return;
|
|
27737
|
-
|
|
27738
|
-
|
|
27739
|
-
this.
|
|
28004
|
+
this._isUpdating = true;
|
|
28005
|
+
try {
|
|
28006
|
+
const table = this.editor.getFocusedTable();
|
|
28007
|
+
if (table) {
|
|
28008
|
+
this.showTable(table);
|
|
28009
|
+
}
|
|
28010
|
+
else {
|
|
28011
|
+
this.hideTable();
|
|
28012
|
+
}
|
|
27740
28013
|
}
|
|
27741
|
-
|
|
27742
|
-
this.
|
|
28014
|
+
finally {
|
|
28015
|
+
this._isUpdating = false;
|
|
27743
28016
|
}
|
|
27744
28017
|
}
|
|
27745
28018
|
/**
|
|
@@ -27943,6 +28216,21 @@ class TablePane extends BasePane {
|
|
|
27943
28216
|
hasTable() {
|
|
27944
28217
|
return this.currentTable !== null;
|
|
27945
28218
|
}
|
|
28219
|
+
createRowLoop() {
|
|
28220
|
+
if (!this.editor || !this.currentTable) {
|
|
28221
|
+
this.onApplyCallback?.(false, new Error('No table focused'));
|
|
28222
|
+
return;
|
|
28223
|
+
}
|
|
28224
|
+
const fieldPath = this.loopFieldInput?.value.trim() || '';
|
|
28225
|
+
if (!fieldPath) {
|
|
28226
|
+
this.onApplyCallback?.(false, new Error('Array field path is required'));
|
|
28227
|
+
return;
|
|
28228
|
+
}
|
|
28229
|
+
// Uses the unified createRepeatingSection API which detects
|
|
28230
|
+
// that a table is focused and creates a row loop on the focused row
|
|
28231
|
+
this.editor.createRepeatingSection(0, 0, fieldPath);
|
|
28232
|
+
this.onApplyCallback?.(true);
|
|
28233
|
+
}
|
|
27946
28234
|
/**
|
|
27947
28235
|
* Update the pane from current editor state.
|
|
27948
28236
|
*/
|
|
@@ -27975,6 +28263,7 @@ exports.HtmlConverter = HtmlConverter;
|
|
|
27975
28263
|
exports.HyperlinkPane = HyperlinkPane;
|
|
27976
28264
|
exports.ImageObject = ImageObject;
|
|
27977
28265
|
exports.ImagePane = ImagePane;
|
|
28266
|
+
exports.Logger = Logger;
|
|
27978
28267
|
exports.MergeDataPane = MergeDataPane;
|
|
27979
28268
|
exports.PCEditor = PCEditor;
|
|
27980
28269
|
exports.PDFImportError = PDFImportError;
|