@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.esm.js
CHANGED
|
@@ -818,12 +818,13 @@ class TextFormattingManager extends EventEmitter {
|
|
|
818
818
|
}
|
|
819
819
|
/**
|
|
820
820
|
* Get formatting at a specific character position.
|
|
821
|
-
* Returns the position-specific formatting
|
|
821
|
+
* Returns the position-specific formatting merged with defaults,
|
|
822
|
+
* ensuring all properties are consistently present.
|
|
822
823
|
*/
|
|
823
824
|
getFormattingAt(position) {
|
|
824
825
|
const override = this.formatting.get(position);
|
|
825
826
|
if (override) {
|
|
826
|
-
return { ...override };
|
|
827
|
+
return { ...this._defaultFormatting, ...override };
|
|
827
828
|
}
|
|
828
829
|
return { ...this._defaultFormatting };
|
|
829
830
|
}
|
|
@@ -910,6 +911,43 @@ class TextFormattingManager extends EventEmitter {
|
|
|
910
911
|
getAllFormatting() {
|
|
911
912
|
return new Map(this.formatting);
|
|
912
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Get formatting as compressed runs for serialization.
|
|
916
|
+
* Only outputs entries where formatting changes from the previous character.
|
|
917
|
+
* Skips leading default formatting to minimize output size.
|
|
918
|
+
* @param textLength Length of the text to serialize formatting for
|
|
919
|
+
*/
|
|
920
|
+
getCompressedRuns(textLength) {
|
|
921
|
+
const runs = [];
|
|
922
|
+
const defaultFormat = this._defaultFormatting;
|
|
923
|
+
let lastFormat = null;
|
|
924
|
+
for (let i = 0; i < textLength; i++) {
|
|
925
|
+
const currentFormat = this.getFormattingAt(i);
|
|
926
|
+
const formatChanged = lastFormat === null ||
|
|
927
|
+
currentFormat.fontFamily !== lastFormat.fontFamily ||
|
|
928
|
+
currentFormat.fontSize !== lastFormat.fontSize ||
|
|
929
|
+
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
930
|
+
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
931
|
+
currentFormat.color !== lastFormat.color ||
|
|
932
|
+
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
933
|
+
if (formatChanged) {
|
|
934
|
+
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
935
|
+
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
936
|
+
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
937
|
+
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
938
|
+
currentFormat.color === defaultFormat.color &&
|
|
939
|
+
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
940
|
+
if (!isDefault || runs.length > 0) {
|
|
941
|
+
runs.push({
|
|
942
|
+
index: i,
|
|
943
|
+
formatting: { ...currentFormat }
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
lastFormat = currentFormat;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return runs;
|
|
950
|
+
}
|
|
913
951
|
/**
|
|
914
952
|
* Restore formatting from a map (for deserialization).
|
|
915
953
|
*/
|
|
@@ -3813,6 +3851,35 @@ class ImageObject extends BaseEmbeddedObject {
|
|
|
3813
3851
|
get hasError() {
|
|
3814
3852
|
return this._error;
|
|
3815
3853
|
}
|
|
3854
|
+
/**
|
|
3855
|
+
* Get the loaded HTMLImageElement, if available.
|
|
3856
|
+
* Used by PDFGenerator to convert unsupported formats to PNG.
|
|
3857
|
+
*/
|
|
3858
|
+
get imageElement() {
|
|
3859
|
+
return this._loaded ? this._image : null;
|
|
3860
|
+
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Convert the image to a PNG data URL via canvas.
|
|
3863
|
+
* Used when the original format (e.g., SVG, WebP, GIF) is not supported by pdf-lib.
|
|
3864
|
+
* Returns null if the image is not loaded or conversion fails.
|
|
3865
|
+
*/
|
|
3866
|
+
toPngDataUrl() {
|
|
3867
|
+
if (!this._loaded || !this._image)
|
|
3868
|
+
return null;
|
|
3869
|
+
try {
|
|
3870
|
+
const canvas = document.createElement('canvas');
|
|
3871
|
+
canvas.width = this._image.naturalWidth || this._size.width;
|
|
3872
|
+
canvas.height = this._image.naturalHeight || this._size.height;
|
|
3873
|
+
const ctx = canvas.getContext('2d');
|
|
3874
|
+
if (!ctx)
|
|
3875
|
+
return null;
|
|
3876
|
+
ctx.drawImage(this._image, 0, 0, canvas.width, canvas.height);
|
|
3877
|
+
return canvas.toDataURL('image/png');
|
|
3878
|
+
}
|
|
3879
|
+
catch {
|
|
3880
|
+
return null;
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3816
3883
|
loadImage() {
|
|
3817
3884
|
if (!this._src) {
|
|
3818
3885
|
this._error = true;
|
|
@@ -4708,12 +4775,10 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4708
4775
|
this.editing = false;
|
|
4709
4776
|
}
|
|
4710
4777
|
toData() {
|
|
4711
|
-
// Serialize formatting
|
|
4712
|
-
const
|
|
4713
|
-
const
|
|
4714
|
-
|
|
4715
|
-
formattingEntries.push([key, { ...value }]);
|
|
4716
|
-
});
|
|
4778
|
+
// Serialize formatting as compressed runs (only at change boundaries)
|
|
4779
|
+
const text = this._flowingContent.getText();
|
|
4780
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
4781
|
+
const formattingEntries = compressedRuns.map(run => [run.index, { ...run.formatting }]);
|
|
4717
4782
|
// Get substitution fields as array
|
|
4718
4783
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
4719
4784
|
return {
|
|
@@ -4772,12 +4837,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4772
4837
|
this._border = { ...boxData.border };
|
|
4773
4838
|
if (boxData.padding !== undefined)
|
|
4774
4839
|
this._padding = boxData.padding;
|
|
4775
|
-
// Restore formatting runs
|
|
4776
|
-
if (boxData.formattingRuns) {
|
|
4840
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
4841
|
+
if (boxData.formattingRuns && boxData.formattingRuns.length > 0) {
|
|
4777
4842
|
const formattingManager = this._flowingContent.getFormattingManager();
|
|
4778
4843
|
formattingManager.clear();
|
|
4779
|
-
|
|
4780
|
-
|
|
4844
|
+
const textLength = this._flowingContent.getText().length;
|
|
4845
|
+
for (let i = 0; i < boxData.formattingRuns.length; i++) {
|
|
4846
|
+
const [startIndex, style] = boxData.formattingRuns[i];
|
|
4847
|
+
const nextIndex = i + 1 < boxData.formattingRuns.length
|
|
4848
|
+
? boxData.formattingRuns[i + 1][0]
|
|
4849
|
+
: textLength;
|
|
4850
|
+
if (startIndex < nextIndex) {
|
|
4851
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
4852
|
+
}
|
|
4781
4853
|
}
|
|
4782
4854
|
}
|
|
4783
4855
|
// Restore substitution fields
|
|
@@ -4927,15 +4999,19 @@ class TextBoxObject extends BaseEmbeddedObject {
|
|
|
4927
4999
|
}
|
|
4928
5000
|
/**
|
|
4929
5001
|
* Check if a point is within this text box region.
|
|
5002
|
+
* Uses the full object bounds (including padding/border) so that
|
|
5003
|
+
* double-click to enter edit mode works on the entire text box area,
|
|
5004
|
+
* not just the inner text area.
|
|
4930
5005
|
* @param point Point in canvas coordinates
|
|
4931
|
-
* @param
|
|
5006
|
+
* @param _pageIndex The page index (ignored for text boxes)
|
|
4932
5007
|
*/
|
|
4933
|
-
containsPointInRegion(point,
|
|
4934
|
-
|
|
4935
|
-
if (!bounds)
|
|
5008
|
+
containsPointInRegion(point, _pageIndex) {
|
|
5009
|
+
if (!this._renderedPosition)
|
|
4936
5010
|
return false;
|
|
4937
|
-
return point.x >=
|
|
4938
|
-
point.
|
|
5011
|
+
return point.x >= this._renderedPosition.x &&
|
|
5012
|
+
point.x <= this._renderedPosition.x + this._size.width &&
|
|
5013
|
+
point.y >= this._renderedPosition.y &&
|
|
5014
|
+
point.y <= this._renderedPosition.y + this._size.height;
|
|
4939
5015
|
}
|
|
4940
5016
|
/**
|
|
4941
5017
|
* Trigger a reflow of text in this text box.
|
|
@@ -5020,6 +5096,41 @@ function getVerticalPadding(padding) {
|
|
|
5020
5096
|
return padding.top + padding.bottom;
|
|
5021
5097
|
}
|
|
5022
5098
|
|
|
5099
|
+
/**
|
|
5100
|
+
* Logger - Centralized logging for PC Editor.
|
|
5101
|
+
*
|
|
5102
|
+
* When enabled, logs informational messages to the console.
|
|
5103
|
+
* When disabled, only errors are logged.
|
|
5104
|
+
* Controlled via EditorOptions.enableLogging or Logger.setEnabled().
|
|
5105
|
+
*/
|
|
5106
|
+
let _enabled = false;
|
|
5107
|
+
const Logger = {
|
|
5108
|
+
/** Enable or disable logging. When disabled, only errors are logged. */
|
|
5109
|
+
setEnabled(enabled) {
|
|
5110
|
+
_enabled = enabled;
|
|
5111
|
+
},
|
|
5112
|
+
/** Check if logging is enabled. */
|
|
5113
|
+
isEnabled() {
|
|
5114
|
+
return _enabled;
|
|
5115
|
+
},
|
|
5116
|
+
/** Log an informational message. Only outputs when logging is enabled. */
|
|
5117
|
+
log(...args) {
|
|
5118
|
+
if (_enabled) {
|
|
5119
|
+
console.log(...args);
|
|
5120
|
+
}
|
|
5121
|
+
},
|
|
5122
|
+
/** Log a warning. Only outputs when logging is enabled. */
|
|
5123
|
+
warn(...args) {
|
|
5124
|
+
if (_enabled) {
|
|
5125
|
+
console.warn(...args);
|
|
5126
|
+
}
|
|
5127
|
+
},
|
|
5128
|
+
/** Log an error. Always outputs regardless of logging state. */
|
|
5129
|
+
error(...args) {
|
|
5130
|
+
console.error(...args);
|
|
5131
|
+
}
|
|
5132
|
+
};
|
|
5133
|
+
|
|
5023
5134
|
/**
|
|
5024
5135
|
* TableCell - A cell within a table that contains editable text.
|
|
5025
5136
|
* Implements EditableTextRegion for unified text interaction.
|
|
@@ -5070,7 +5181,7 @@ class TableCell extends EventEmitter {
|
|
|
5070
5181
|
});
|
|
5071
5182
|
// Prevent embedded objects in table cells (only substitution fields allowed)
|
|
5072
5183
|
this._flowingContent.insertEmbeddedObject = () => {
|
|
5073
|
-
|
|
5184
|
+
Logger.warn('[pc-editor:TableCell] Embedded objects are not allowed in table cells. Use insertSubstitutionField instead.');
|
|
5074
5185
|
};
|
|
5075
5186
|
// Set initial content
|
|
5076
5187
|
if (config.content) {
|
|
@@ -5387,7 +5498,7 @@ class TableCell extends EventEmitter {
|
|
|
5387
5498
|
this._reflowDirty = false;
|
|
5388
5499
|
this._lastReflowWidth = width;
|
|
5389
5500
|
this._cachedContentHeight = null; // Clear cached height since lines changed
|
|
5390
|
-
|
|
5501
|
+
Logger.log('[pc-editor:TableCell.reflow] cellId:', this._id, 'text:', JSON.stringify(this._flowingContent.getText()), 'lines:', this._flowedLines.length);
|
|
5391
5502
|
}
|
|
5392
5503
|
/**
|
|
5393
5504
|
* Mark this cell as needing reflow.
|
|
@@ -5425,7 +5536,7 @@ class TableCell extends EventEmitter {
|
|
|
5425
5536
|
return this._editing && this._flowingContent.hasFocus();
|
|
5426
5537
|
}
|
|
5427
5538
|
handleKeyDown(e) {
|
|
5428
|
-
|
|
5539
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Key:', e.key, '_editing:', this._editing, 'flowingContent.hasFocus:', this._flowingContent.hasFocus());
|
|
5429
5540
|
if (!this._editing)
|
|
5430
5541
|
return false;
|
|
5431
5542
|
// Let parent table handle Tab navigation
|
|
@@ -5433,9 +5544,9 @@ class TableCell extends EventEmitter {
|
|
|
5433
5544
|
return false; // Not handled - parent will handle
|
|
5434
5545
|
}
|
|
5435
5546
|
// Delegate to FlowingTextContent
|
|
5436
|
-
|
|
5547
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] Delegating to FlowingTextContent.handleKeyDown');
|
|
5437
5548
|
const handled = this._flowingContent.handleKeyDown(e);
|
|
5438
|
-
|
|
5549
|
+
Logger.log('[pc-editor:TableCell.handleKeyDown] FlowingTextContent handled:', handled);
|
|
5439
5550
|
return handled;
|
|
5440
5551
|
}
|
|
5441
5552
|
onCursorBlink(handler) {
|
|
@@ -5507,28 +5618,49 @@ class TableCell extends EventEmitter {
|
|
|
5507
5618
|
// Serialization
|
|
5508
5619
|
// ============================================
|
|
5509
5620
|
toData() {
|
|
5510
|
-
const
|
|
5511
|
-
const
|
|
5512
|
-
|
|
5513
|
-
formattingRuns.push([index, { ...style }]);
|
|
5514
|
-
});
|
|
5621
|
+
const text = this._flowingContent.getText();
|
|
5622
|
+
const compressedRuns = this._flowingContent.getFormattingManager().getCompressedRuns(text.length);
|
|
5623
|
+
const formattingRuns = compressedRuns.map(run => [run.index, run.formatting]);
|
|
5515
5624
|
// Get substitution fields for serialization
|
|
5516
5625
|
const fields = this._flowingContent.getSubstitutionFieldManager().getFieldsArray();
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5626
|
+
// Only include non-default values to minimize export size
|
|
5627
|
+
const defaults = DEFAULT_TABLE_STYLE;
|
|
5628
|
+
const defaultBorder = DEFAULT_CELL_BORDER_SIDE;
|
|
5629
|
+
const isDefaultBorderSide = (side) => side.width === defaultBorder.width && side.color === defaultBorder.color && side.style === defaultBorder.style;
|
|
5630
|
+
const isDefaultBorder = isDefaultBorderSide(this._border.top) &&
|
|
5631
|
+
isDefaultBorderSide(this._border.right) &&
|
|
5632
|
+
isDefaultBorderSide(this._border.bottom) &&
|
|
5633
|
+
isDefaultBorderSide(this._border.left);
|
|
5634
|
+
const isDefaultPadding = this._padding.top === defaults.cellPadding &&
|
|
5635
|
+
this._padding.right === defaults.cellPadding &&
|
|
5636
|
+
this._padding.bottom === defaults.cellPadding &&
|
|
5637
|
+
this._padding.left === defaults.cellPadding;
|
|
5638
|
+
const data = {};
|
|
5639
|
+
if (this._rowSpan !== 1)
|
|
5640
|
+
data.rowSpan = this._rowSpan;
|
|
5641
|
+
if (this._colSpan !== 1)
|
|
5642
|
+
data.colSpan = this._colSpan;
|
|
5643
|
+
if (this._backgroundColor !== defaults.backgroundColor)
|
|
5644
|
+
data.backgroundColor = this._backgroundColor;
|
|
5645
|
+
if (!isDefaultBorder)
|
|
5646
|
+
data.border = this._border;
|
|
5647
|
+
if (!isDefaultPadding)
|
|
5648
|
+
data.padding = this._padding;
|
|
5649
|
+
if (this._verticalAlign !== 'top')
|
|
5650
|
+
data.verticalAlign = this._verticalAlign;
|
|
5651
|
+
if (text)
|
|
5652
|
+
data.content = text;
|
|
5653
|
+
if (this._fontFamily !== defaults.fontFamily)
|
|
5654
|
+
data.fontFamily = this._fontFamily;
|
|
5655
|
+
if (this._fontSize !== defaults.fontSize)
|
|
5656
|
+
data.fontSize = this._fontSize;
|
|
5657
|
+
if (this._color !== defaults.color)
|
|
5658
|
+
data.color = this._color;
|
|
5659
|
+
if (formattingRuns.length > 0)
|
|
5660
|
+
data.formattingRuns = formattingRuns;
|
|
5661
|
+
if (fields.length > 0)
|
|
5662
|
+
data.substitutionFields = fields;
|
|
5663
|
+
return data;
|
|
5532
5664
|
}
|
|
5533
5665
|
static fromData(data) {
|
|
5534
5666
|
const cell = new TableCell({
|
|
@@ -5544,14 +5676,19 @@ class TableCell extends EventEmitter {
|
|
|
5544
5676
|
fontSize: data.fontSize,
|
|
5545
5677
|
color: data.color
|
|
5546
5678
|
});
|
|
5547
|
-
// Restore formatting runs
|
|
5548
|
-
if (data.formattingRuns) {
|
|
5679
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
5680
|
+
if (data.formattingRuns && data.formattingRuns.length > 0) {
|
|
5549
5681
|
const formattingManager = cell._flowingContent.getFormattingManager();
|
|
5550
|
-
const
|
|
5551
|
-
for (
|
|
5552
|
-
|
|
5682
|
+
const textLength = (data.content || '').length;
|
|
5683
|
+
for (let i = 0; i < data.formattingRuns.length; i++) {
|
|
5684
|
+
const [startIndex, style] = data.formattingRuns[i];
|
|
5685
|
+
const nextIndex = i + 1 < data.formattingRuns.length
|
|
5686
|
+
? data.formattingRuns[i + 1][0]
|
|
5687
|
+
: textLength;
|
|
5688
|
+
if (startIndex < nextIndex) {
|
|
5689
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
5690
|
+
}
|
|
5553
5691
|
}
|
|
5554
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
5555
5692
|
}
|
|
5556
5693
|
// Restore substitution fields
|
|
5557
5694
|
if (data.substitutionFields && Array.isArray(data.substitutionFields)) {
|
|
@@ -5785,13 +5922,17 @@ class TableRow extends EventEmitter {
|
|
|
5785
5922
|
// Serialization
|
|
5786
5923
|
// ============================================
|
|
5787
5924
|
toData() {
|
|
5788
|
-
|
|
5789
|
-
id: this._id,
|
|
5790
|
-
height: this._height,
|
|
5791
|
-
minHeight: this._minHeight,
|
|
5792
|
-
isHeader: this._isHeader,
|
|
5925
|
+
const data = {
|
|
5793
5926
|
cells: this._cells.map(cell => cell.toData())
|
|
5794
5927
|
};
|
|
5928
|
+
// Only include non-default values
|
|
5929
|
+
if (this._height !== null)
|
|
5930
|
+
data.height = this._height;
|
|
5931
|
+
if (this._minHeight !== DEFAULT_TABLE_STYLE.minRowHeight)
|
|
5932
|
+
data.minHeight = this._minHeight;
|
|
5933
|
+
if (this._isHeader)
|
|
5934
|
+
data.isHeader = this._isHeader;
|
|
5935
|
+
return data;
|
|
5795
5936
|
}
|
|
5796
5937
|
static fromData(data) {
|
|
5797
5938
|
const row = new TableRow({
|
|
@@ -6125,7 +6266,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6125
6266
|
set position(value) {
|
|
6126
6267
|
// Tables only support block positioning - ignore any attempt to set other modes
|
|
6127
6268
|
if (value !== 'block') {
|
|
6128
|
-
|
|
6269
|
+
Logger.warn(`[pc-editor:TableObject] Tables only support 'block' positioning. Ignoring attempt to set '${value}'.`);
|
|
6129
6270
|
}
|
|
6130
6271
|
// Always set to block
|
|
6131
6272
|
super.position = 'block';
|
|
@@ -6664,24 +6805,24 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
6664
6805
|
createRowLoop(startRowIndex, endRowIndex, fieldPath) {
|
|
6665
6806
|
// Validate range
|
|
6666
6807
|
if (startRowIndex < 0 || endRowIndex >= this._rows.length) {
|
|
6667
|
-
|
|
6808
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Invalid row range');
|
|
6668
6809
|
return null;
|
|
6669
6810
|
}
|
|
6670
6811
|
if (startRowIndex > endRowIndex) {
|
|
6671
|
-
|
|
6812
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Start index must be <= end index');
|
|
6672
6813
|
return null;
|
|
6673
6814
|
}
|
|
6674
6815
|
// Check for overlap with existing loops
|
|
6675
6816
|
for (const existingLoop of this._rowLoops.values()) {
|
|
6676
6817
|
if (this.loopRangesOverlap(startRowIndex, endRowIndex, existingLoop.startRowIndex, existingLoop.endRowIndex)) {
|
|
6677
|
-
|
|
6818
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop range overlaps with existing loop');
|
|
6678
6819
|
return null;
|
|
6679
6820
|
}
|
|
6680
6821
|
}
|
|
6681
6822
|
// Check that loop rows are not header rows
|
|
6682
6823
|
for (let i = startRowIndex; i <= endRowIndex; i++) {
|
|
6683
6824
|
if (this._rows[i]?.isHeader) {
|
|
6684
|
-
|
|
6825
|
+
Logger.warn('[pc-editor:TableObject.createRowLoop] Loop rows cannot be header rows');
|
|
6685
6826
|
return null;
|
|
6686
6827
|
}
|
|
6687
6828
|
}
|
|
@@ -7487,7 +7628,7 @@ class TableObject extends BaseEmbeddedObject {
|
|
|
7487
7628
|
return this._editing;
|
|
7488
7629
|
}
|
|
7489
7630
|
handleKeyDown(e) {
|
|
7490
|
-
|
|
7631
|
+
Logger.log('[pc-editor:TableObject.handleKeyDown] Key:', e.key, '_editing:', this._editing, '_focusedCell:', this._focusedCell);
|
|
7491
7632
|
if (!this._editing)
|
|
7492
7633
|
return false;
|
|
7493
7634
|
// Handle Tab navigation
|
|
@@ -8275,14 +8416,20 @@ class EmbeddedObjectFactory {
|
|
|
8275
8416
|
border: data.data.border,
|
|
8276
8417
|
padding: data.data.padding
|
|
8277
8418
|
});
|
|
8278
|
-
// Restore formatting runs
|
|
8419
|
+
// Restore formatting runs (run-based: each entry applies from its index to the next)
|
|
8279
8420
|
if (data.data.formattingRuns && Array.isArray(data.data.formattingRuns)) {
|
|
8280
|
-
const
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8421
|
+
const runs = data.data.formattingRuns;
|
|
8422
|
+
if (runs.length > 0) {
|
|
8423
|
+
const formattingManager = textBox.flowingContent.getFormattingManager();
|
|
8424
|
+
const textLength = textBox.flowingContent.getText().length;
|
|
8425
|
+
for (let i = 0; i < runs.length; i++) {
|
|
8426
|
+
const [startIndex, style] = runs[i];
|
|
8427
|
+
const nextIndex = i + 1 < runs.length ? runs[i + 1][0] : textLength;
|
|
8428
|
+
if (startIndex < nextIndex) {
|
|
8429
|
+
formattingManager.applyFormatting(startIndex, nextIndex, style);
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8284
8432
|
}
|
|
8285
|
-
formattingManager.setAllFormatting(formattingMap);
|
|
8286
8433
|
}
|
|
8287
8434
|
// Restore substitution fields if present
|
|
8288
8435
|
if (data.data.substitutionFields && Array.isArray(data.data.substitutionFields)) {
|
|
@@ -9259,9 +9406,9 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9259
9406
|
* @returns true if the event was handled, false otherwise
|
|
9260
9407
|
*/
|
|
9261
9408
|
handleKeyDown(e) {
|
|
9262
|
-
|
|
9409
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] Key:', e.key, '_hasFocus:', this._hasFocus);
|
|
9263
9410
|
if (!this._hasFocus) {
|
|
9264
|
-
|
|
9411
|
+
Logger.log('[pc-editor:FlowingTextContent.handleKeyDown] No focus, returning false');
|
|
9265
9412
|
return false;
|
|
9266
9413
|
}
|
|
9267
9414
|
switch (e.key) {
|
|
@@ -9776,46 +9923,19 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9776
9923
|
toData() {
|
|
9777
9924
|
// Serialize text content
|
|
9778
9925
|
const text = this.textState.getText();
|
|
9779
|
-
// Serialize text formatting as runs
|
|
9780
|
-
|
|
9781
|
-
const formattingRuns =
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
currentFormat.fontWeight !== lastFormat.fontWeight ||
|
|
9791
|
-
currentFormat.fontStyle !== lastFormat.fontStyle ||
|
|
9792
|
-
currentFormat.color !== lastFormat.color ||
|
|
9793
|
-
currentFormat.backgroundColor !== lastFormat.backgroundColor;
|
|
9794
|
-
if (formatChanged) {
|
|
9795
|
-
// Only output if different from default (to further reduce size)
|
|
9796
|
-
const isDefault = currentFormat.fontFamily === defaultFormat.fontFamily &&
|
|
9797
|
-
currentFormat.fontSize === defaultFormat.fontSize &&
|
|
9798
|
-
currentFormat.fontWeight === defaultFormat.fontWeight &&
|
|
9799
|
-
currentFormat.fontStyle === defaultFormat.fontStyle &&
|
|
9800
|
-
currentFormat.color === defaultFormat.color &&
|
|
9801
|
-
currentFormat.backgroundColor === defaultFormat.backgroundColor;
|
|
9802
|
-
// Always output first run if it's not default, or output when format changes
|
|
9803
|
-
if (!isDefault || formattingRuns.length > 0) {
|
|
9804
|
-
formattingRuns.push({
|
|
9805
|
-
index: i,
|
|
9806
|
-
formatting: {
|
|
9807
|
-
fontFamily: currentFormat.fontFamily,
|
|
9808
|
-
fontSize: currentFormat.fontSize,
|
|
9809
|
-
fontWeight: currentFormat.fontWeight,
|
|
9810
|
-
fontStyle: currentFormat.fontStyle,
|
|
9811
|
-
color: currentFormat.color,
|
|
9812
|
-
backgroundColor: currentFormat.backgroundColor
|
|
9813
|
-
}
|
|
9814
|
-
});
|
|
9815
|
-
}
|
|
9816
|
-
lastFormat = currentFormat;
|
|
9926
|
+
// Serialize text formatting as compressed runs (only at change boundaries)
|
|
9927
|
+
const compressedRuns = this.formatting.getCompressedRuns(text.length);
|
|
9928
|
+
const formattingRuns = compressedRuns.map(run => ({
|
|
9929
|
+
index: run.index,
|
|
9930
|
+
formatting: {
|
|
9931
|
+
fontFamily: run.formatting.fontFamily,
|
|
9932
|
+
fontSize: run.formatting.fontSize,
|
|
9933
|
+
fontWeight: run.formatting.fontWeight,
|
|
9934
|
+
fontStyle: run.formatting.fontStyle,
|
|
9935
|
+
color: run.formatting.color,
|
|
9936
|
+
backgroundColor: run.formatting.backgroundColor
|
|
9817
9937
|
}
|
|
9818
|
-
}
|
|
9938
|
+
}));
|
|
9819
9939
|
// Serialize paragraph formatting
|
|
9820
9940
|
const paragraphFormatting = this.paragraphFormatting.toJSON();
|
|
9821
9941
|
// Serialize substitution fields
|
|
@@ -9911,7 +10031,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9911
10031
|
content.getEmbeddedObjectManager().insert(object, ref.textIndex);
|
|
9912
10032
|
}
|
|
9913
10033
|
else {
|
|
9914
|
-
|
|
10034
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9915
10035
|
}
|
|
9916
10036
|
}
|
|
9917
10037
|
}
|
|
@@ -9965,7 +10085,7 @@ class FlowingTextContent extends EventEmitter {
|
|
|
9965
10085
|
this.embeddedObjects.insert(object, ref.textIndex);
|
|
9966
10086
|
}
|
|
9967
10087
|
else {
|
|
9968
|
-
|
|
10088
|
+
Logger.warn(`[pc-editor:FlowingTextContent] Failed to create embedded object of type: ${ref.object.objectType}`);
|
|
9969
10089
|
}
|
|
9970
10090
|
}
|
|
9971
10091
|
}
|
|
@@ -12504,7 +12624,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12504
12624
|
updateResizeHandleTargets(selectedObjects) {
|
|
12505
12625
|
// Clear existing resize handle targets
|
|
12506
12626
|
this._hitTestManager.clearCategory('resize-handles');
|
|
12507
|
-
|
|
12627
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets selectedObjects:', selectedObjects.length);
|
|
12508
12628
|
// Register resize handles for each selected object
|
|
12509
12629
|
for (const object of selectedObjects) {
|
|
12510
12630
|
if (!object.resizable)
|
|
@@ -12517,7 +12637,7 @@ class FlowingTextRenderer extends EventEmitter {
|
|
|
12517
12637
|
// For regular objects, use renderedPosition
|
|
12518
12638
|
const pos = object.renderedPosition;
|
|
12519
12639
|
const pageIndex = object.renderedPageIndex;
|
|
12520
|
-
|
|
12640
|
+
Logger.log('[pc-editor:FlowingTextRenderer] updateResizeHandleTargets object:', object.id, 'pageIndex:', pageIndex, 'pos:', pos);
|
|
12521
12641
|
if (pos && pageIndex >= 0) {
|
|
12522
12642
|
this.registerObjectResizeHandles(object, pageIndex, pos);
|
|
12523
12643
|
}
|
|
@@ -15163,7 +15283,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15163
15283
|
this.emit('element-removed', { elementId: objectId });
|
|
15164
15284
|
}
|
|
15165
15285
|
selectElement(elementId) {
|
|
15166
|
-
|
|
15286
|
+
Logger.log('[pc-editor:CanvasManager] Selecting element:', elementId);
|
|
15167
15287
|
this.selectedElements.add(elementId);
|
|
15168
15288
|
// Update embedded object's selected state
|
|
15169
15289
|
const flowingContents = [
|
|
@@ -15175,12 +15295,12 @@ class CanvasManager extends EventEmitter {
|
|
|
15175
15295
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15176
15296
|
for (const [, obj] of embeddedObjects.entries()) {
|
|
15177
15297
|
if (obj.id === elementId) {
|
|
15178
|
-
|
|
15298
|
+
Logger.log('[pc-editor:CanvasManager] Found embedded object to select:', obj.id);
|
|
15179
15299
|
obj.selected = true;
|
|
15180
15300
|
}
|
|
15181
15301
|
}
|
|
15182
15302
|
}
|
|
15183
|
-
|
|
15303
|
+
Logger.log('[pc-editor:CanvasManager] Selected elements after selection:', Array.from(this.selectedElements));
|
|
15184
15304
|
this.render();
|
|
15185
15305
|
this.updateResizeHandleHitTargets();
|
|
15186
15306
|
this.emit('selection-change', { selectedElements: Array.from(this.selectedElements) });
|
|
@@ -15230,10 +15350,10 @@ class CanvasManager extends EventEmitter {
|
|
|
15230
15350
|
this.flowingTextRenderer.updateResizeHandleTargets(selectedObjects);
|
|
15231
15351
|
}
|
|
15232
15352
|
clearSelection() {
|
|
15233
|
-
|
|
15353
|
+
Logger.log('[pc-editor:CanvasManager] clearSelection called, current selected elements:', Array.from(this.selectedElements));
|
|
15234
15354
|
// Clear selected state on all embedded objects
|
|
15235
15355
|
this.selectedElements.forEach(elementId => {
|
|
15236
|
-
|
|
15356
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection for element:', elementId);
|
|
15237
15357
|
// Check embedded objects in all flowing content sources (body, header, footer)
|
|
15238
15358
|
const flowingContents = [
|
|
15239
15359
|
this.document.bodyFlowingContent,
|
|
@@ -15244,7 +15364,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15244
15364
|
const embeddedObjects = flowingContent.getEmbeddedObjects();
|
|
15245
15365
|
for (const [, embeddedObj] of embeddedObjects.entries()) {
|
|
15246
15366
|
if (embeddedObj.id === elementId) {
|
|
15247
|
-
|
|
15367
|
+
Logger.log('[pc-editor:CanvasManager] Clearing selection on embedded object:', elementId);
|
|
15248
15368
|
embeddedObj.selected = false;
|
|
15249
15369
|
}
|
|
15250
15370
|
}
|
|
@@ -15252,7 +15372,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15252
15372
|
});
|
|
15253
15373
|
this.selectedElements.clear();
|
|
15254
15374
|
this.selectedSectionId = null;
|
|
15255
|
-
|
|
15375
|
+
Logger.log('[pc-editor:CanvasManager] About to render after clearing selection...');
|
|
15256
15376
|
this.render();
|
|
15257
15377
|
this.updateResizeHandleHitTargets();
|
|
15258
15378
|
this.emit('selection-change', { selectedElements: [] });
|
|
@@ -15501,7 +15621,7 @@ class CanvasManager extends EventEmitter {
|
|
|
15501
15621
|
});
|
|
15502
15622
|
// Handle substitution field clicks
|
|
15503
15623
|
this.flowingTextRenderer.on('substitution-field-clicked', (data) => {
|
|
15504
|
-
|
|
15624
|
+
Logger.log('[pc-editor:CanvasManager] substitution-field-clicked Field:', data.field?.fieldName, 'Section:', data.section);
|
|
15505
15625
|
// Emit event for external handling (e.g., showing field properties panel)
|
|
15506
15626
|
this.emit('substitution-field-clicked', data);
|
|
15507
15627
|
});
|
|
@@ -15976,14 +16096,15 @@ class CanvasManager extends EventEmitter {
|
|
|
15976
16096
|
this.editingTextBox = textBox;
|
|
15977
16097
|
this._editingTextBoxPageId = pageId || null;
|
|
15978
16098
|
if (textBox) {
|
|
15979
|
-
// Use the unified focus system to handle focus/blur and cursor blink
|
|
15980
|
-
// This blurs the previous control, hiding its cursor
|
|
15981
|
-
this.setFocus(textBox);
|
|
15982
16099
|
// Clear selection in main flowing content
|
|
15983
16100
|
this.document.bodyFlowingContent.clearSelection();
|
|
15984
|
-
// Select the text box
|
|
16101
|
+
// Select the text box visually (this calls setFocus(null) internally,
|
|
16102
|
+
// so we must set focus to the text box AFTER this call)
|
|
15985
16103
|
this.clearSelection();
|
|
15986
16104
|
this.selectInlineElement({ type: 'embedded-object', object: textBox, textIndex: textBox.textIndex });
|
|
16105
|
+
// Now set focus to the text box for editing — must be AFTER selectInlineElement
|
|
16106
|
+
// because selectBaseEmbeddedObject calls setFocus(null) which would undo it
|
|
16107
|
+
this.setFocus(textBox);
|
|
15987
16108
|
this.emit('textbox-editing-started', { textBox });
|
|
15988
16109
|
}
|
|
15989
16110
|
else {
|
|
@@ -16722,7 +16843,15 @@ class PDFGenerator {
|
|
|
16722
16843
|
// Check if it's a data URL we can embed
|
|
16723
16844
|
if (src.startsWith('data:')) {
|
|
16724
16845
|
try {
|
|
16725
|
-
|
|
16846
|
+
let embeddedImage = await this.embedImageFromDataUrl(pdfDoc, src);
|
|
16847
|
+
// If the format isn't directly supported (e.g., SVG, WebP, GIF),
|
|
16848
|
+
// convert to PNG via canvas and try again
|
|
16849
|
+
if (!embeddedImage) {
|
|
16850
|
+
const pngDataUrl = image.toPngDataUrl();
|
|
16851
|
+
if (pngDataUrl) {
|
|
16852
|
+
embeddedImage = await this.embedImageFromDataUrl(pdfDoc, pngDataUrl);
|
|
16853
|
+
}
|
|
16854
|
+
}
|
|
16726
16855
|
if (embeddedImage) {
|
|
16727
16856
|
// Calculate draw position/size based on fit mode
|
|
16728
16857
|
const drawParams = this.calculateImageDrawParams(embeddedImage.width, embeddedImage.height, image.width, image.height, image.fit);
|
|
@@ -16745,7 +16874,7 @@ class PDFGenerator {
|
|
|
16745
16874
|
}
|
|
16746
16875
|
}
|
|
16747
16876
|
catch (e) {
|
|
16748
|
-
|
|
16877
|
+
Logger.warn('[pc-editor:PDFGenerator] Failed to embed image:', e);
|
|
16749
16878
|
}
|
|
16750
16879
|
}
|
|
16751
16880
|
// Fallback: draw placeholder rectangle for images we can't embed
|
|
@@ -18773,7 +18902,7 @@ class MutationUndo {
|
|
|
18773
18902
|
this.undoTableStructure(mutation);
|
|
18774
18903
|
break;
|
|
18775
18904
|
default:
|
|
18776
|
-
|
|
18905
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for undo:', mutation.type);
|
|
18777
18906
|
}
|
|
18778
18907
|
}
|
|
18779
18908
|
/**
|
|
@@ -18823,7 +18952,7 @@ class MutationUndo {
|
|
|
18823
18952
|
this.redoTableStructure(mutation);
|
|
18824
18953
|
break;
|
|
18825
18954
|
default:
|
|
18826
|
-
|
|
18955
|
+
Logger.warn('[pc-editor:MutationUndo] Unknown mutation type for redo:', mutation.type);
|
|
18827
18956
|
}
|
|
18828
18957
|
}
|
|
18829
18958
|
// --- Text Mutations ---
|
|
@@ -20155,13 +20284,13 @@ class PDFParser {
|
|
|
20155
20284
|
}
|
|
20156
20285
|
catch {
|
|
20157
20286
|
// Skip images that fail to extract
|
|
20158
|
-
|
|
20287
|
+
Logger.warn(`[pc-editor:PDFParser] Failed to extract image: ${imageName}`);
|
|
20159
20288
|
}
|
|
20160
20289
|
}
|
|
20161
20290
|
}
|
|
20162
20291
|
}
|
|
20163
20292
|
catch (error) {
|
|
20164
|
-
|
|
20293
|
+
Logger.warn('[pc-editor:PDFParser] Image extraction failed:', error);
|
|
20165
20294
|
}
|
|
20166
20295
|
return images;
|
|
20167
20296
|
}
|
|
@@ -21188,6 +21317,8 @@ class PCEditor extends EventEmitter {
|
|
|
21188
21317
|
}
|
|
21189
21318
|
this.container = container;
|
|
21190
21319
|
this.options = this.mergeOptions(options);
|
|
21320
|
+
// Initialize logging
|
|
21321
|
+
Logger.setEnabled(this.options.enableLogging ?? false);
|
|
21191
21322
|
this.document = new Document();
|
|
21192
21323
|
// Apply constructor options to document settings
|
|
21193
21324
|
this.document.updateSettings({
|
|
@@ -21212,7 +21343,8 @@ class PCEditor extends EventEmitter {
|
|
|
21212
21343
|
showControlCharacters: options?.showControlCharacters ?? false,
|
|
21213
21344
|
defaultFont: options?.defaultFont || 'Arial',
|
|
21214
21345
|
defaultFontSize: options?.defaultFontSize || 12,
|
|
21215
|
-
theme: options?.theme || 'light'
|
|
21346
|
+
theme: options?.theme || 'light',
|
|
21347
|
+
enableLogging: options?.enableLogging ?? false
|
|
21216
21348
|
};
|
|
21217
21349
|
}
|
|
21218
21350
|
initialize() {
|
|
@@ -21619,6 +21751,7 @@ class PCEditor extends EventEmitter {
|
|
|
21619
21751
|
* This changes which section receives keyboard input and cursor positioning.
|
|
21620
21752
|
*/
|
|
21621
21753
|
setActiveSection(section) {
|
|
21754
|
+
Logger.log('[pc-editor] setActiveSection', section);
|
|
21622
21755
|
if (this._activeEditingSection !== section) {
|
|
21623
21756
|
this._activeEditingSection = section;
|
|
21624
21757
|
// Delegate to canvas manager which handles the section change and emits events
|
|
@@ -21709,6 +21842,7 @@ class PCEditor extends EventEmitter {
|
|
|
21709
21842
|
}
|
|
21710
21843
|
}
|
|
21711
21844
|
loadDocument(documentData) {
|
|
21845
|
+
Logger.log('[pc-editor] loadDocument');
|
|
21712
21846
|
if (!this._isReady) {
|
|
21713
21847
|
throw new Error('Editor is not ready');
|
|
21714
21848
|
}
|
|
@@ -21735,6 +21869,7 @@ class PCEditor extends EventEmitter {
|
|
|
21735
21869
|
return this.document.toData();
|
|
21736
21870
|
}
|
|
21737
21871
|
bindData(data) {
|
|
21872
|
+
Logger.log('[pc-editor] bindData');
|
|
21738
21873
|
if (!this._isReady) {
|
|
21739
21874
|
throw new Error('Editor is not ready');
|
|
21740
21875
|
}
|
|
@@ -21743,6 +21878,7 @@ class PCEditor extends EventEmitter {
|
|
|
21743
21878
|
this.emit('data-bound', { data });
|
|
21744
21879
|
}
|
|
21745
21880
|
async exportPDF(options) {
|
|
21881
|
+
Logger.log('[pc-editor] exportPDF');
|
|
21746
21882
|
if (!this._isReady) {
|
|
21747
21883
|
throw new Error('Editor is not ready');
|
|
21748
21884
|
}
|
|
@@ -21784,6 +21920,7 @@ class PCEditor extends EventEmitter {
|
|
|
21784
21920
|
* @returns JSON string representation of the document
|
|
21785
21921
|
*/
|
|
21786
21922
|
saveDocument() {
|
|
21923
|
+
Logger.log('[pc-editor] saveDocument');
|
|
21787
21924
|
if (!this._isReady) {
|
|
21788
21925
|
throw new Error('Editor is not ready');
|
|
21789
21926
|
}
|
|
@@ -21795,6 +21932,7 @@ class PCEditor extends EventEmitter {
|
|
|
21795
21932
|
* @param filename Optional filename (defaults to 'document.pceditor.json')
|
|
21796
21933
|
*/
|
|
21797
21934
|
saveDocumentToFile(filename = 'document.pceditor.json') {
|
|
21935
|
+
Logger.log('[pc-editor] saveDocumentToFile', filename);
|
|
21798
21936
|
const jsonString = this.saveDocument();
|
|
21799
21937
|
const blob = new Blob([jsonString], { type: 'application/json' });
|
|
21800
21938
|
const url = URL.createObjectURL(blob);
|
|
@@ -21812,6 +21950,7 @@ class PCEditor extends EventEmitter {
|
|
|
21812
21950
|
* @param jsonString JSON string representation of the document
|
|
21813
21951
|
*/
|
|
21814
21952
|
loadDocumentFromJSON(jsonString) {
|
|
21953
|
+
Logger.log('[pc-editor] loadDocumentFromJSON');
|
|
21815
21954
|
if (!this._isReady) {
|
|
21816
21955
|
throw new Error('Editor is not ready');
|
|
21817
21956
|
}
|
|
@@ -21832,6 +21971,7 @@ class PCEditor extends EventEmitter {
|
|
|
21832
21971
|
* @returns Promise that resolves when loading is complete
|
|
21833
21972
|
*/
|
|
21834
21973
|
async loadDocumentFromFile(file) {
|
|
21974
|
+
Logger.log('[pc-editor] loadDocumentFromFile', file.name);
|
|
21835
21975
|
if (!this._isReady) {
|
|
21836
21976
|
throw new Error('Editor is not ready');
|
|
21837
21977
|
}
|
|
@@ -21920,22 +22060,25 @@ class PCEditor extends EventEmitter {
|
|
|
21920
22060
|
// Version compatibility check
|
|
21921
22061
|
const [major] = doc.version.split('.').map(Number);
|
|
21922
22062
|
if (major > 1) {
|
|
21923
|
-
|
|
22063
|
+
Logger.warn(`[pc-editor] Document version ${doc.version} may not be fully compatible with this editor`);
|
|
21924
22064
|
}
|
|
21925
22065
|
}
|
|
21926
22066
|
selectElement(elementId) {
|
|
22067
|
+
Logger.log('[pc-editor] selectElement', elementId);
|
|
21927
22068
|
if (!this._isReady) {
|
|
21928
22069
|
throw new Error('Editor is not ready');
|
|
21929
22070
|
}
|
|
21930
22071
|
this.canvasManager.selectElement(elementId);
|
|
21931
22072
|
}
|
|
21932
22073
|
clearSelection() {
|
|
22074
|
+
Logger.log('[pc-editor] clearSelection');
|
|
21933
22075
|
if (!this._isReady) {
|
|
21934
22076
|
throw new Error('Editor is not ready');
|
|
21935
22077
|
}
|
|
21936
22078
|
this.canvasManager.clearSelection();
|
|
21937
22079
|
}
|
|
21938
22080
|
removeEmbeddedObject(objectId) {
|
|
22081
|
+
Logger.log('[pc-editor] removeEmbeddedObject', objectId);
|
|
21939
22082
|
if (!this._isReady) {
|
|
21940
22083
|
throw new Error('Editor is not ready');
|
|
21941
22084
|
}
|
|
@@ -21945,6 +22088,7 @@ class PCEditor extends EventEmitter {
|
|
|
21945
22088
|
* Undo the last operation.
|
|
21946
22089
|
*/
|
|
21947
22090
|
undo() {
|
|
22091
|
+
Logger.log('[pc-editor] undo');
|
|
21948
22092
|
if (!this._isReady)
|
|
21949
22093
|
return;
|
|
21950
22094
|
const success = this.transactionManager.undo();
|
|
@@ -21957,6 +22101,7 @@ class PCEditor extends EventEmitter {
|
|
|
21957
22101
|
* Redo the last undone operation.
|
|
21958
22102
|
*/
|
|
21959
22103
|
redo() {
|
|
22104
|
+
Logger.log('[pc-editor] redo');
|
|
21960
22105
|
if (!this._isReady)
|
|
21961
22106
|
return;
|
|
21962
22107
|
const success = this.transactionManager.redo();
|
|
@@ -21990,16 +22135,19 @@ class PCEditor extends EventEmitter {
|
|
|
21990
22135
|
this.transactionManager.setMaxHistory(count);
|
|
21991
22136
|
}
|
|
21992
22137
|
zoomIn() {
|
|
22138
|
+
Logger.log('[pc-editor] zoomIn');
|
|
21993
22139
|
if (!this._isReady)
|
|
21994
22140
|
return;
|
|
21995
22141
|
this.canvasManager.zoomIn();
|
|
21996
22142
|
}
|
|
21997
22143
|
zoomOut() {
|
|
22144
|
+
Logger.log('[pc-editor] zoomOut');
|
|
21998
22145
|
if (!this._isReady)
|
|
21999
22146
|
return;
|
|
22000
22147
|
this.canvasManager.zoomOut();
|
|
22001
22148
|
}
|
|
22002
22149
|
setZoom(level) {
|
|
22150
|
+
Logger.log('[pc-editor] setZoom', level);
|
|
22003
22151
|
if (!this._isReady)
|
|
22004
22152
|
return;
|
|
22005
22153
|
this.canvasManager.setZoom(level);
|
|
@@ -22036,6 +22184,7 @@ class PCEditor extends EventEmitter {
|
|
|
22036
22184
|
return this.canvasManager.getContentOffset();
|
|
22037
22185
|
}
|
|
22038
22186
|
fitToWidth() {
|
|
22187
|
+
Logger.log('[pc-editor] fitToWidth');
|
|
22039
22188
|
if (!this._isReady)
|
|
22040
22189
|
return;
|
|
22041
22190
|
this.canvasManager.fitToWidth();
|
|
@@ -22044,23 +22193,27 @@ class PCEditor extends EventEmitter {
|
|
|
22044
22193
|
* Force a re-render of the canvas.
|
|
22045
22194
|
*/
|
|
22046
22195
|
render() {
|
|
22196
|
+
Logger.log('[pc-editor] render');
|
|
22047
22197
|
if (!this._isReady)
|
|
22048
22198
|
return;
|
|
22049
22199
|
this.canvasManager.render();
|
|
22050
22200
|
}
|
|
22051
22201
|
fitToPage() {
|
|
22202
|
+
Logger.log('[pc-editor] fitToPage');
|
|
22052
22203
|
if (!this._isReady)
|
|
22053
22204
|
return;
|
|
22054
22205
|
this.canvasManager.fitToPage();
|
|
22055
22206
|
}
|
|
22056
22207
|
// Layout control methods
|
|
22057
22208
|
setAutoFlow(enabled) {
|
|
22209
|
+
Logger.log('[pc-editor] setAutoFlow', enabled);
|
|
22058
22210
|
if (!this._isReady) {
|
|
22059
22211
|
throw new Error('Editor is not ready');
|
|
22060
22212
|
}
|
|
22061
22213
|
this.layoutEngine.setAutoFlow(enabled);
|
|
22062
22214
|
}
|
|
22063
22215
|
reflowDocument() {
|
|
22216
|
+
Logger.log('[pc-editor] reflowDocument');
|
|
22064
22217
|
if (!this._isReady) {
|
|
22065
22218
|
throw new Error('Editor is not ready');
|
|
22066
22219
|
}
|
|
@@ -22102,6 +22255,7 @@ class PCEditor extends EventEmitter {
|
|
|
22102
22255
|
};
|
|
22103
22256
|
}
|
|
22104
22257
|
addPage() {
|
|
22258
|
+
Logger.log('[pc-editor] addPage');
|
|
22105
22259
|
if (!this._isReady) {
|
|
22106
22260
|
throw new Error('Editor is not ready');
|
|
22107
22261
|
}
|
|
@@ -22113,6 +22267,7 @@ class PCEditor extends EventEmitter {
|
|
|
22113
22267
|
this.canvasManager.setDocument(this.document);
|
|
22114
22268
|
}
|
|
22115
22269
|
removePage(pageId) {
|
|
22270
|
+
Logger.log('[pc-editor] removePage', pageId);
|
|
22116
22271
|
if (!this._isReady) {
|
|
22117
22272
|
throw new Error('Editor is not ready');
|
|
22118
22273
|
}
|
|
@@ -22187,7 +22342,7 @@ class PCEditor extends EventEmitter {
|
|
|
22187
22342
|
}
|
|
22188
22343
|
// Use the unified focus system to get the currently focused control
|
|
22189
22344
|
const focusedControl = this.canvasManager.getFocusedControl();
|
|
22190
|
-
|
|
22345
|
+
Logger.log('[pc-editor:handleKeyDown] Key:', e.key, 'focusedControl:', focusedControl?.constructor?.name);
|
|
22191
22346
|
if (!focusedControl)
|
|
22192
22347
|
return;
|
|
22193
22348
|
// Vertical navigation needs layout context - handle specially
|
|
@@ -22211,9 +22366,9 @@ class PCEditor extends EventEmitter {
|
|
|
22211
22366
|
}
|
|
22212
22367
|
}
|
|
22213
22368
|
// Delegate to the focused control's handleKeyDown
|
|
22214
|
-
|
|
22369
|
+
Logger.log('[pc-editor:handleKeyDown] Calling focusedControl.handleKeyDown');
|
|
22215
22370
|
const handled = focusedControl.handleKeyDown(e);
|
|
22216
|
-
|
|
22371
|
+
Logger.log('[pc-editor:handleKeyDown] handled:', handled);
|
|
22217
22372
|
if (handled) {
|
|
22218
22373
|
this.canvasManager.render();
|
|
22219
22374
|
// Handle text box-specific post-processing
|
|
@@ -22577,6 +22732,7 @@ class PCEditor extends EventEmitter {
|
|
|
22577
22732
|
* This is useful when UI controls have stolen focus.
|
|
22578
22733
|
*/
|
|
22579
22734
|
applyFormattingWithFallback(start, end, formatting) {
|
|
22735
|
+
Logger.log('[pc-editor] applyFormattingWithFallback', start, end, formatting);
|
|
22580
22736
|
// Try current context first
|
|
22581
22737
|
let flowingContent = this.getEditingFlowingContent();
|
|
22582
22738
|
// Fall back to saved context
|
|
@@ -22777,6 +22933,7 @@ class PCEditor extends EventEmitter {
|
|
|
22777
22933
|
* Works for body text, text boxes, and table cells.
|
|
22778
22934
|
*/
|
|
22779
22935
|
setUnifiedAlignment(alignment) {
|
|
22936
|
+
Logger.log('[pc-editor] setUnifiedAlignment', alignment);
|
|
22780
22937
|
const flowingContent = this.getEditingFlowingContent();
|
|
22781
22938
|
if (!flowingContent) {
|
|
22782
22939
|
throw new Error('No text is being edited');
|
|
@@ -22793,6 +22950,7 @@ class PCEditor extends EventEmitter {
|
|
|
22793
22950
|
this.canvasManager.render();
|
|
22794
22951
|
}
|
|
22795
22952
|
insertText(text) {
|
|
22953
|
+
Logger.log('[pc-editor] insertText', text);
|
|
22796
22954
|
if (!this._isReady) {
|
|
22797
22955
|
throw new Error('Editor is not ready');
|
|
22798
22956
|
}
|
|
@@ -22809,6 +22967,7 @@ class PCEditor extends EventEmitter {
|
|
|
22809
22967
|
return flowingContent ? flowingContent.getText() : '';
|
|
22810
22968
|
}
|
|
22811
22969
|
setFlowingText(text) {
|
|
22970
|
+
Logger.log('[pc-editor] setFlowingText');
|
|
22812
22971
|
if (!this._isReady) {
|
|
22813
22972
|
throw new Error('Editor is not ready');
|
|
22814
22973
|
}
|
|
@@ -22822,6 +22981,7 @@ class PCEditor extends EventEmitter {
|
|
|
22822
22981
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22823
22982
|
*/
|
|
22824
22983
|
setCursorPosition(position) {
|
|
22984
|
+
Logger.log('[pc-editor] setCursorPosition', position);
|
|
22825
22985
|
if (!this._isReady) {
|
|
22826
22986
|
throw new Error('Editor is not ready');
|
|
22827
22987
|
}
|
|
@@ -22867,6 +23027,7 @@ class PCEditor extends EventEmitter {
|
|
|
22867
23027
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22868
23028
|
*/
|
|
22869
23029
|
insertEmbeddedObject(object, position = 'inline') {
|
|
23030
|
+
Logger.log('[pc-editor] insertEmbeddedObject', object.id, position);
|
|
22870
23031
|
if (!this._isReady) {
|
|
22871
23032
|
throw new Error('Editor is not ready');
|
|
22872
23033
|
}
|
|
@@ -22893,6 +23054,7 @@ class PCEditor extends EventEmitter {
|
|
|
22893
23054
|
* Works for body, header, footer, text boxes, and table cells.
|
|
22894
23055
|
*/
|
|
22895
23056
|
insertSubstitutionField(fieldName, config) {
|
|
23057
|
+
Logger.log('[pc-editor] insertSubstitutionField', fieldName);
|
|
22896
23058
|
if (!this._isReady) {
|
|
22897
23059
|
throw new Error('Editor is not ready');
|
|
22898
23060
|
}
|
|
@@ -22918,6 +23080,7 @@ class PCEditor extends EventEmitter {
|
|
|
22918
23080
|
* @param displayFormat Optional format string (e.g., "Page %d" where %d is replaced by page number)
|
|
22919
23081
|
*/
|
|
22920
23082
|
insertPageNumberField(displayFormat) {
|
|
23083
|
+
Logger.log('[pc-editor] insertPageNumberField');
|
|
22921
23084
|
if (!this._isReady) {
|
|
22922
23085
|
throw new Error('Editor is not ready');
|
|
22923
23086
|
}
|
|
@@ -22943,6 +23106,7 @@ class PCEditor extends EventEmitter {
|
|
|
22943
23106
|
* @param displayFormat Optional format string (e.g., "of %d" where %d is replaced by page count)
|
|
22944
23107
|
*/
|
|
22945
23108
|
insertPageCountField(displayFormat) {
|
|
23109
|
+
Logger.log('[pc-editor] insertPageCountField');
|
|
22946
23110
|
if (!this._isReady) {
|
|
22947
23111
|
throw new Error('Editor is not ready');
|
|
22948
23112
|
}
|
|
@@ -22968,6 +23132,7 @@ class PCEditor extends EventEmitter {
|
|
|
22968
23132
|
* or table cells are not recommended as these don't span pages.
|
|
22969
23133
|
*/
|
|
22970
23134
|
insertPageBreak() {
|
|
23135
|
+
Logger.log('[pc-editor] insertPageBreak');
|
|
22971
23136
|
if (!this._isReady) {
|
|
22972
23137
|
throw new Error('Editor is not ready');
|
|
22973
23138
|
}
|
|
@@ -23116,7 +23281,7 @@ class PCEditor extends EventEmitter {
|
|
|
23116
23281
|
// Find the text box in all flowing contents
|
|
23117
23282
|
const textBox = this.findTextBoxById(textBoxId);
|
|
23118
23283
|
if (!textBox) {
|
|
23119
|
-
|
|
23284
|
+
Logger.warn(`[pc-editor:updateTextBox] Text box not found: ${textBoxId}`);
|
|
23120
23285
|
return false;
|
|
23121
23286
|
}
|
|
23122
23287
|
// Apply updates
|
|
@@ -23179,7 +23344,7 @@ class PCEditor extends EventEmitter {
|
|
|
23179
23344
|
// Find the image in all flowing contents
|
|
23180
23345
|
const image = this.findImageById(imageId);
|
|
23181
23346
|
if (!image) {
|
|
23182
|
-
|
|
23347
|
+
Logger.warn(`[pc-editor:updateImage] Image not found: ${imageId}`);
|
|
23183
23348
|
return false;
|
|
23184
23349
|
}
|
|
23185
23350
|
// Apply updates
|
|
@@ -23213,7 +23378,7 @@ class PCEditor extends EventEmitter {
|
|
|
23213
23378
|
return false;
|
|
23214
23379
|
const image = this.findImageById(imageId);
|
|
23215
23380
|
if (!image) {
|
|
23216
|
-
|
|
23381
|
+
Logger.warn(`[pc-editor:setImageSource] Image not found: ${imageId}`);
|
|
23217
23382
|
return false;
|
|
23218
23383
|
}
|
|
23219
23384
|
image.setSource(dataUrl, options);
|
|
@@ -23340,7 +23505,7 @@ class PCEditor extends EventEmitter {
|
|
|
23340
23505
|
* @deprecated Use insertEmbeddedObject instead
|
|
23341
23506
|
*/
|
|
23342
23507
|
insertInlineElement(_elementData, _position = 'inline') {
|
|
23343
|
-
|
|
23508
|
+
Logger.warn('[pc-editor] insertInlineElement is deprecated and no longer functional. Use insertEmbeddedObject instead.');
|
|
23344
23509
|
}
|
|
23345
23510
|
/**
|
|
23346
23511
|
* Apply merge data to substitute all substitution fields with their values.
|
|
@@ -24065,18 +24230,37 @@ class PCEditor extends EventEmitter {
|
|
|
24065
24230
|
return this.document.bodyFlowingContent.getParagraphBoundaries();
|
|
24066
24231
|
}
|
|
24067
24232
|
/**
|
|
24068
|
-
* Create a repeating section
|
|
24069
|
-
*
|
|
24070
|
-
*
|
|
24071
|
-
*
|
|
24233
|
+
* Create a repeating section.
|
|
24234
|
+
*
|
|
24235
|
+
* If a table is currently being edited (focused), creates a table row loop
|
|
24236
|
+
* based on the focused cell's row. In this case, startIndex and endIndex
|
|
24237
|
+
* are ignored — the focused cell's row determines the loop range.
|
|
24238
|
+
*
|
|
24239
|
+
* Otherwise, creates a body text repeating section at the given paragraph boundaries.
|
|
24240
|
+
*
|
|
24241
|
+
* @param startIndex Text index at paragraph start (ignored for table row loops)
|
|
24242
|
+
* @param endIndex Text index at closing paragraph start (ignored for table row loops)
|
|
24072
24243
|
* @param fieldPath The field path to the array to loop over (e.g., "items")
|
|
24073
|
-
* @returns The created section, or null if
|
|
24244
|
+
* @returns The created section/loop, or null if creation failed
|
|
24074
24245
|
*/
|
|
24075
24246
|
createRepeatingSection(startIndex, endIndex, fieldPath) {
|
|
24076
24247
|
if (!this._isReady) {
|
|
24077
24248
|
throw new Error('Editor is not ready');
|
|
24078
24249
|
}
|
|
24250
|
+
// If a table is focused, create a row loop instead of a text repeating section
|
|
24251
|
+
const focusedTable = this.getFocusedTable();
|
|
24252
|
+
if (focusedTable && focusedTable.focusedCell) {
|
|
24253
|
+
Logger.log('[pc-editor] createRepeatingSection → table row loop', fieldPath);
|
|
24254
|
+
const row = focusedTable.focusedCell.row;
|
|
24255
|
+
const loop = focusedTable.createRowLoop(row, row, fieldPath);
|
|
24256
|
+
if (loop) {
|
|
24257
|
+
this.canvasManager.render();
|
|
24258
|
+
this.emit('table-row-loop-added', { table: focusedTable, loop });
|
|
24259
|
+
}
|
|
24260
|
+
return null; // Row loops are not RepeatingSections, return null
|
|
24261
|
+
}
|
|
24079
24262
|
// Repeating sections only work in body (document-level)
|
|
24263
|
+
Logger.log('[pc-editor] createRepeatingSection', startIndex, endIndex, fieldPath);
|
|
24080
24264
|
const section = this.document.bodyFlowingContent.createRepeatingSection(startIndex, endIndex, fieldPath);
|
|
24081
24265
|
if (section) {
|
|
24082
24266
|
this.canvasManager.render();
|
|
@@ -24454,7 +24638,7 @@ class PCEditor extends EventEmitter {
|
|
|
24454
24638
|
createEmbeddedObjectFromData(data) {
|
|
24455
24639
|
const object = EmbeddedObjectFactory.tryCreate(data);
|
|
24456
24640
|
if (!object) {
|
|
24457
|
-
|
|
24641
|
+
Logger.warn('[pc-editor] Unknown object type:', data.objectType);
|
|
24458
24642
|
}
|
|
24459
24643
|
return object;
|
|
24460
24644
|
}
|
|
@@ -24483,6 +24667,13 @@ class PCEditor extends EventEmitter {
|
|
|
24483
24667
|
return false;
|
|
24484
24668
|
}
|
|
24485
24669
|
}
|
|
24670
|
+
/**
|
|
24671
|
+
* Enable or disable verbose logging.
|
|
24672
|
+
* When disabled (default), only errors are logged to the console.
|
|
24673
|
+
*/
|
|
24674
|
+
setLogging(enabled) {
|
|
24675
|
+
Logger.setEnabled(enabled);
|
|
24676
|
+
}
|
|
24486
24677
|
destroy() {
|
|
24487
24678
|
this.disableTextInput();
|
|
24488
24679
|
if (this.canvasManager) {
|
|
@@ -25625,33 +25816,38 @@ class DocumentInfoPane extends BasePane {
|
|
|
25625
25816
|
}
|
|
25626
25817
|
createContent() {
|
|
25627
25818
|
const container = document.createElement('div');
|
|
25628
|
-
container.className = 'pc-pane-
|
|
25819
|
+
container.className = 'pc-pane-label-value-grid';
|
|
25629
25820
|
// Page count
|
|
25630
|
-
|
|
25631
|
-
this.pageCountEl =
|
|
25632
|
-
container.appendChild(
|
|
25821
|
+
container.appendChild(this.createLabel('Pages:'));
|
|
25822
|
+
this.pageCountEl = this.createValue('0');
|
|
25823
|
+
container.appendChild(this.pageCountEl);
|
|
25824
|
+
container.appendChild(this.createSpacer());
|
|
25633
25825
|
// Page size
|
|
25634
|
-
|
|
25635
|
-
this.pageSizeEl =
|
|
25636
|
-
container.appendChild(
|
|
25826
|
+
container.appendChild(this.createLabel('Size:'));
|
|
25827
|
+
this.pageSizeEl = this.createValue('-');
|
|
25828
|
+
container.appendChild(this.pageSizeEl);
|
|
25829
|
+
container.appendChild(this.createSpacer());
|
|
25637
25830
|
// Page orientation
|
|
25638
|
-
|
|
25639
|
-
this.pageOrientationEl =
|
|
25640
|
-
container.appendChild(
|
|
25831
|
+
container.appendChild(this.createLabel('Orientation:'));
|
|
25832
|
+
this.pageOrientationEl = this.createValue('-');
|
|
25833
|
+
container.appendChild(this.pageOrientationEl);
|
|
25834
|
+
container.appendChild(this.createSpacer());
|
|
25641
25835
|
return container;
|
|
25642
25836
|
}
|
|
25643
|
-
|
|
25644
|
-
const
|
|
25645
|
-
|
|
25646
|
-
|
|
25647
|
-
|
|
25648
|
-
|
|
25649
|
-
|
|
25650
|
-
|
|
25651
|
-
|
|
25652
|
-
|
|
25653
|
-
|
|
25654
|
-
|
|
25837
|
+
createLabel(text) {
|
|
25838
|
+
const label = document.createElement('span');
|
|
25839
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
25840
|
+
label.textContent = text;
|
|
25841
|
+
return label;
|
|
25842
|
+
}
|
|
25843
|
+
createValue(text) {
|
|
25844
|
+
const value = document.createElement('span');
|
|
25845
|
+
value.className = 'pc-pane-info-value';
|
|
25846
|
+
value.textContent = text;
|
|
25847
|
+
return value;
|
|
25848
|
+
}
|
|
25849
|
+
createSpacer() {
|
|
25850
|
+
return document.createElement('div');
|
|
25655
25851
|
}
|
|
25656
25852
|
/**
|
|
25657
25853
|
* Update the displayed information from the editor.
|
|
@@ -25831,24 +26027,48 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25831
26027
|
const container = document.createElement('div');
|
|
25832
26028
|
// Margins section
|
|
25833
26029
|
const marginsSection = this.createSection('Margins (mm)');
|
|
26030
|
+
// Five-column grid: label, edit, label, edit, stretch
|
|
25834
26031
|
const marginsGrid = document.createElement('div');
|
|
25835
|
-
marginsGrid.className = 'pc-pane-margins-grid';
|
|
26032
|
+
marginsGrid.className = 'pc-pane-margins-grid-5col';
|
|
25836
26033
|
this.marginTopInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25837
26034
|
this.marginRightInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25838
26035
|
this.marginBottomInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25839
26036
|
this.marginLeftInput = this.createNumberInput({ min: 5, max: 50, step: 0.5, value: 20 });
|
|
25840
|
-
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
26037
|
+
// Apply margins on blur
|
|
26038
|
+
const applyMargins = () => this.applyMargins();
|
|
26039
|
+
this.marginTopInput.addEventListener('blur', applyMargins);
|
|
26040
|
+
this.marginRightInput.addEventListener('blur', applyMargins);
|
|
26041
|
+
this.marginBottomInput.addEventListener('blur', applyMargins);
|
|
26042
|
+
this.marginLeftInput.addEventListener('blur', applyMargins);
|
|
26043
|
+
this.eventCleanup.push(() => {
|
|
26044
|
+
this.marginTopInput?.removeEventListener('blur', applyMargins);
|
|
26045
|
+
this.marginRightInput?.removeEventListener('blur', applyMargins);
|
|
26046
|
+
this.marginBottomInput?.removeEventListener('blur', applyMargins);
|
|
26047
|
+
this.marginLeftInput?.removeEventListener('blur', applyMargins);
|
|
26048
|
+
});
|
|
26049
|
+
// Row 1: Top / Right
|
|
26050
|
+
const topLabel = this.createMarginLabel('Top:');
|
|
26051
|
+
const rightLabel = this.createMarginLabel('Right:');
|
|
26052
|
+
marginsGrid.appendChild(topLabel);
|
|
26053
|
+
marginsGrid.appendChild(this.marginTopInput);
|
|
26054
|
+
marginsGrid.appendChild(rightLabel);
|
|
26055
|
+
marginsGrid.appendChild(this.marginRightInput);
|
|
26056
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
26057
|
+
// Row 2: Bottom / Left
|
|
26058
|
+
const bottomLabel = this.createMarginLabel('Bottom:');
|
|
26059
|
+
const leftLabel = this.createMarginLabel('Left:');
|
|
26060
|
+
marginsGrid.appendChild(bottomLabel);
|
|
26061
|
+
marginsGrid.appendChild(this.marginBottomInput);
|
|
26062
|
+
marginsGrid.appendChild(leftLabel);
|
|
26063
|
+
marginsGrid.appendChild(this.marginLeftInput);
|
|
26064
|
+
marginsGrid.appendChild(this.createSpacer());
|
|
25844
26065
|
marginsSection.appendChild(marginsGrid);
|
|
25845
|
-
// Apply margins button
|
|
25846
|
-
const applyMarginsBtn = this.createButton('Apply Margins');
|
|
25847
|
-
this.addButtonListener(applyMarginsBtn, () => this.applyMargins());
|
|
25848
|
-
marginsSection.appendChild(applyMarginsBtn);
|
|
25849
26066
|
container.appendChild(marginsSection);
|
|
25850
|
-
// Page
|
|
25851
|
-
const
|
|
26067
|
+
// Page settings section using label-value grid: label, value, stretch
|
|
26068
|
+
const pageSection = this.createSection();
|
|
26069
|
+
const pageGrid = document.createElement('div');
|
|
26070
|
+
pageGrid.className = 'pc-pane-label-value-grid';
|
|
26071
|
+
// Page Size
|
|
25852
26072
|
this.pageSizeSelect = this.createSelect([
|
|
25853
26073
|
{ value: 'A4', label: 'A4' },
|
|
25854
26074
|
{ value: 'Letter', label: 'Letter' },
|
|
@@ -25856,19 +26076,32 @@ class DocumentSettingsPane extends BasePane {
|
|
|
25856
26076
|
{ value: 'A3', label: 'A3' }
|
|
25857
26077
|
], 'A4');
|
|
25858
26078
|
this.addImmediateApplyListener(this.pageSizeSelect, () => this.applyPageSettings());
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
26079
|
+
pageGrid.appendChild(this.createMarginLabel('Page Size:'));
|
|
26080
|
+
pageGrid.appendChild(this.pageSizeSelect);
|
|
26081
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26082
|
+
// Orientation
|
|
25863
26083
|
this.orientationSelect = this.createSelect([
|
|
25864
26084
|
{ value: 'portrait', label: 'Portrait' },
|
|
25865
26085
|
{ value: 'landscape', label: 'Landscape' }
|
|
25866
26086
|
], 'portrait');
|
|
25867
26087
|
this.addImmediateApplyListener(this.orientationSelect, () => this.applyPageSettings());
|
|
25868
|
-
|
|
25869
|
-
|
|
26088
|
+
pageGrid.appendChild(this.createMarginLabel('Orientation:'));
|
|
26089
|
+
pageGrid.appendChild(this.orientationSelect);
|
|
26090
|
+
pageGrid.appendChild(this.createSpacer());
|
|
26091
|
+
pageSection.appendChild(pageGrid);
|
|
26092
|
+
container.appendChild(pageSection);
|
|
25870
26093
|
return container;
|
|
25871
26094
|
}
|
|
26095
|
+
createMarginLabel(text) {
|
|
26096
|
+
const label = document.createElement('label');
|
|
26097
|
+
label.className = 'pc-pane-label pc-pane-margin-label';
|
|
26098
|
+
label.textContent = text;
|
|
26099
|
+
return label;
|
|
26100
|
+
}
|
|
26101
|
+
createSpacer() {
|
|
26102
|
+
const spacer = document.createElement('div');
|
|
26103
|
+
return spacer;
|
|
26104
|
+
}
|
|
25872
26105
|
loadSettings() {
|
|
25873
26106
|
if (!this.editor)
|
|
25874
26107
|
return;
|
|
@@ -26408,6 +26641,7 @@ class HyperlinkPane extends BasePane {
|
|
|
26408
26641
|
this.titleInput = null;
|
|
26409
26642
|
this.rangeHint = null;
|
|
26410
26643
|
this.currentHyperlink = null;
|
|
26644
|
+
this._isUpdating = false;
|
|
26411
26645
|
this.onApply = options.onApply;
|
|
26412
26646
|
this.onRemove = options.onRemove;
|
|
26413
26647
|
}
|
|
@@ -26449,15 +26683,21 @@ class HyperlinkPane extends BasePane {
|
|
|
26449
26683
|
return container;
|
|
26450
26684
|
}
|
|
26451
26685
|
updateFromCursor() {
|
|
26452
|
-
if (!this.editor)
|
|
26686
|
+
if (!this.editor || this._isUpdating)
|
|
26453
26687
|
return;
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
this.
|
|
26688
|
+
this._isUpdating = true;
|
|
26689
|
+
try {
|
|
26690
|
+
const cursorPos = this.editor.getCursorPosition();
|
|
26691
|
+
const hyperlink = this.editor.getHyperlinkAt(cursorPos);
|
|
26692
|
+
if (hyperlink) {
|
|
26693
|
+
this.showHyperlink(hyperlink);
|
|
26694
|
+
}
|
|
26695
|
+
else {
|
|
26696
|
+
this.hideHyperlink();
|
|
26697
|
+
}
|
|
26458
26698
|
}
|
|
26459
|
-
|
|
26460
|
-
this.
|
|
26699
|
+
finally {
|
|
26700
|
+
this._isUpdating = false;
|
|
26461
26701
|
}
|
|
26462
26702
|
}
|
|
26463
26703
|
showHyperlink(hyperlink) {
|
|
@@ -27076,6 +27316,7 @@ class TextBoxPane extends BasePane {
|
|
|
27076
27316
|
this.borderColorInput = null;
|
|
27077
27317
|
this.borderStyleSelect = null;
|
|
27078
27318
|
this.paddingInput = null;
|
|
27319
|
+
this._isUpdating = false;
|
|
27079
27320
|
this.currentTextBox = null;
|
|
27080
27321
|
this.onApplyCallback = options.onApply;
|
|
27081
27322
|
}
|
|
@@ -27149,14 +27390,20 @@ class TextBoxPane extends BasePane {
|
|
|
27149
27390
|
return container;
|
|
27150
27391
|
}
|
|
27151
27392
|
updateFromSelection() {
|
|
27152
|
-
if (!this.editor)
|
|
27393
|
+
if (!this.editor || this._isUpdating)
|
|
27153
27394
|
return;
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
this.
|
|
27395
|
+
this._isUpdating = true;
|
|
27396
|
+
try {
|
|
27397
|
+
const textBox = this.editor.getSelectedTextBox?.();
|
|
27398
|
+
if (textBox && !textBox.editing) {
|
|
27399
|
+
this.showTextBox(textBox);
|
|
27400
|
+
}
|
|
27401
|
+
else {
|
|
27402
|
+
this.hideTextBox();
|
|
27403
|
+
}
|
|
27157
27404
|
}
|
|
27158
|
-
|
|
27159
|
-
this.
|
|
27405
|
+
finally {
|
|
27406
|
+
this._isUpdating = false;
|
|
27160
27407
|
}
|
|
27161
27408
|
}
|
|
27162
27409
|
/**
|
|
@@ -27310,6 +27557,7 @@ class ImagePane extends BasePane {
|
|
|
27310
27557
|
this.altTextInput = null;
|
|
27311
27558
|
this.fileInput = null;
|
|
27312
27559
|
this.currentImage = null;
|
|
27560
|
+
this._isUpdating = false;
|
|
27313
27561
|
this.maxImageWidth = options.maxImageWidth ?? 400;
|
|
27314
27562
|
this.maxImageHeight = options.maxImageHeight ?? 400;
|
|
27315
27563
|
this.onApplyCallback = options.onApply;
|
|
@@ -27391,14 +27639,20 @@ class ImagePane extends BasePane {
|
|
|
27391
27639
|
return container;
|
|
27392
27640
|
}
|
|
27393
27641
|
updateFromSelection() {
|
|
27394
|
-
if (!this.editor)
|
|
27642
|
+
if (!this.editor || this._isUpdating)
|
|
27395
27643
|
return;
|
|
27396
|
-
|
|
27397
|
-
|
|
27398
|
-
this.
|
|
27644
|
+
this._isUpdating = true;
|
|
27645
|
+
try {
|
|
27646
|
+
const image = this.editor.getSelectedImage?.();
|
|
27647
|
+
if (image) {
|
|
27648
|
+
this.showImage(image);
|
|
27649
|
+
}
|
|
27650
|
+
else {
|
|
27651
|
+
this.hideImage();
|
|
27652
|
+
}
|
|
27399
27653
|
}
|
|
27400
|
-
|
|
27401
|
-
this.
|
|
27654
|
+
finally {
|
|
27655
|
+
this._isUpdating = false;
|
|
27402
27656
|
}
|
|
27403
27657
|
}
|
|
27404
27658
|
/**
|
|
@@ -27563,6 +27817,8 @@ class TablePane extends BasePane {
|
|
|
27563
27817
|
// Default controls
|
|
27564
27818
|
this.defaultPaddingInput = null;
|
|
27565
27819
|
this.defaultBorderColorInput = null;
|
|
27820
|
+
// Row loop controls
|
|
27821
|
+
this.loopFieldInput = null;
|
|
27566
27822
|
// Cell formatting controls
|
|
27567
27823
|
this.cellBgColorInput = null;
|
|
27568
27824
|
this.borderTopCheck = null;
|
|
@@ -27573,6 +27829,7 @@ class TablePane extends BasePane {
|
|
|
27573
27829
|
this.borderColorInput = null;
|
|
27574
27830
|
this.borderStyleSelect = null;
|
|
27575
27831
|
this.currentTable = null;
|
|
27832
|
+
this._isUpdating = false;
|
|
27576
27833
|
this.onApplyCallback = options.onApply;
|
|
27577
27834
|
}
|
|
27578
27835
|
attach(options) {
|
|
@@ -27639,6 +27896,16 @@ class TablePane extends BasePane {
|
|
|
27639
27896
|
this.addButtonListener(applyHeadersBtn, () => this.applyHeaders());
|
|
27640
27897
|
headersSection.appendChild(applyHeadersBtn);
|
|
27641
27898
|
container.appendChild(headersSection);
|
|
27899
|
+
// Row Loop section
|
|
27900
|
+
const loopSection = this.createSection('Row Loop');
|
|
27901
|
+
this.loopFieldInput = this.createTextInput({ placeholder: 'items' });
|
|
27902
|
+
loopSection.appendChild(this.createFormGroup('Array Field', this.loopFieldInput, {
|
|
27903
|
+
hint: 'Creates a loop on the currently focused row'
|
|
27904
|
+
}));
|
|
27905
|
+
const createLoopBtn = this.createButton('Create Row Loop');
|
|
27906
|
+
this.addButtonListener(createLoopBtn, () => this.createRowLoop());
|
|
27907
|
+
loopSection.appendChild(createLoopBtn);
|
|
27908
|
+
container.appendChild(loopSection);
|
|
27642
27909
|
// Defaults section
|
|
27643
27910
|
const defaultsSection = this.createSection('Defaults');
|
|
27644
27911
|
const defaultsRow = this.createRow();
|
|
@@ -27711,14 +27978,20 @@ class TablePane extends BasePane {
|
|
|
27711
27978
|
return container;
|
|
27712
27979
|
}
|
|
27713
27980
|
updateFromFocusedTable() {
|
|
27714
|
-
if (!this.editor)
|
|
27981
|
+
if (!this.editor || this._isUpdating)
|
|
27715
27982
|
return;
|
|
27716
|
-
|
|
27717
|
-
|
|
27718
|
-
this.
|
|
27983
|
+
this._isUpdating = true;
|
|
27984
|
+
try {
|
|
27985
|
+
const table = this.editor.getFocusedTable();
|
|
27986
|
+
if (table) {
|
|
27987
|
+
this.showTable(table);
|
|
27988
|
+
}
|
|
27989
|
+
else {
|
|
27990
|
+
this.hideTable();
|
|
27991
|
+
}
|
|
27719
27992
|
}
|
|
27720
|
-
|
|
27721
|
-
this.
|
|
27993
|
+
finally {
|
|
27994
|
+
this._isUpdating = false;
|
|
27722
27995
|
}
|
|
27723
27996
|
}
|
|
27724
27997
|
/**
|
|
@@ -27922,6 +28195,21 @@ class TablePane extends BasePane {
|
|
|
27922
28195
|
hasTable() {
|
|
27923
28196
|
return this.currentTable !== null;
|
|
27924
28197
|
}
|
|
28198
|
+
createRowLoop() {
|
|
28199
|
+
if (!this.editor || !this.currentTable) {
|
|
28200
|
+
this.onApplyCallback?.(false, new Error('No table focused'));
|
|
28201
|
+
return;
|
|
28202
|
+
}
|
|
28203
|
+
const fieldPath = this.loopFieldInput?.value.trim() || '';
|
|
28204
|
+
if (!fieldPath) {
|
|
28205
|
+
this.onApplyCallback?.(false, new Error('Array field path is required'));
|
|
28206
|
+
return;
|
|
28207
|
+
}
|
|
28208
|
+
// Uses the unified createRepeatingSection API which detects
|
|
28209
|
+
// that a table is focused and creates a row loop on the focused row
|
|
28210
|
+
this.editor.createRepeatingSection(0, 0, fieldPath);
|
|
28211
|
+
this.onApplyCallback?.(true);
|
|
28212
|
+
}
|
|
27925
28213
|
/**
|
|
27926
28214
|
* Update the pane from current editor state.
|
|
27927
28215
|
*/
|
|
@@ -27930,5 +28218,5 @@ class TablePane extends BasePane {
|
|
|
27930
28218
|
}
|
|
27931
28219
|
}
|
|
27932
28220
|
|
|
27933
|
-
export { BaseControl, BaseEmbeddedObject, BasePane, BaseTextRegion, BodyTextRegion, ClipboardManager, ContentAnalyzer, DEFAULT_IMPORT_OPTIONS, Document, DocumentBuilder, DocumentInfoPane, DocumentSettingsPane, EmbeddedObjectFactory, EmbeddedObjectManager, EventEmitter, FlowingTextContent, FooterTextRegion, FormattingPane, HeaderTextRegion, HorizontalRuler, HtmlConverter, HyperlinkPane, ImageObject, ImagePane, MergeDataPane, PCEditor, PDFImportError, PDFImportErrorCode, PDFImporter, PDFParser, Page, RegionManager, RepeatingSectionManager, RepeatingSectionPane, RulerControl, SubstitutionFieldManager, SubstitutionFieldPane, TableCell, TableObject, TablePane, TableRow, TableRowLoopPane, TextBoxObject, TextBoxPane, TextFormattingManager, TextLayout, TextMeasurer, TextPositionCalculator, TextState, VerticalRuler, ViewSettingsPane };
|
|
28221
|
+
export { BaseControl, BaseEmbeddedObject, BasePane, BaseTextRegion, BodyTextRegion, ClipboardManager, ContentAnalyzer, DEFAULT_IMPORT_OPTIONS, Document, DocumentBuilder, DocumentInfoPane, DocumentSettingsPane, EmbeddedObjectFactory, EmbeddedObjectManager, EventEmitter, FlowingTextContent, FooterTextRegion, FormattingPane, HeaderTextRegion, HorizontalRuler, HtmlConverter, HyperlinkPane, ImageObject, ImagePane, Logger, MergeDataPane, PCEditor, PDFImportError, PDFImportErrorCode, PDFImporter, PDFParser, Page, RegionManager, RepeatingSectionManager, RepeatingSectionPane, RulerControl, SubstitutionFieldManager, SubstitutionFieldPane, TableCell, TableObject, TablePane, TableRow, TableRowLoopPane, TextBoxObject, TextBoxPane, TextFormattingManager, TextLayout, TextMeasurer, TextPositionCalculator, TextState, VerticalRuler, ViewSettingsPane };
|
|
27934
28222
|
//# sourceMappingURL=pc-editor.esm.js.map
|