@pixldocs/canvas-renderer 0.5.165 → 0.5.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -1
- package/dist/{index-B8sm4VZ8.cjs → index-DgX2Y94P.cjs} +221 -24
- package/dist/index-DgX2Y94P.cjs.map +1 -0
- package/dist/{index-CQGSjfcH.js → index-oFnROAcT.js} +235 -38
- package/dist/index-oFnROAcT.js.map +1 -0
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +56 -0
- package/dist/index.js +16 -14
- package/dist/{vectorPdfExport-aWseupbk.cjs → vectorPdfExport-Bw2WQNoU.cjs} +4 -4
- package/dist/{vectorPdfExport-aWseupbk.cjs.map → vectorPdfExport-Bw2WQNoU.cjs.map} +1 -1
- package/dist/{vectorPdfExport-C0lD4Sum.js → vectorPdfExport-DCy1uYUF.js} +4 -4
- package/dist/{vectorPdfExport-C0lD4Sum.js.map → vectorPdfExport-DCy1uYUF.js.map} +1 -1
- package/package.json +4 -1
- package/dist/index-B8sm4VZ8.cjs.map +0 -1
- package/dist/index-CQGSjfcH.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,7 +8,85 @@ Client-side template renderer for Pixldocs — render templates in any web app w
|
|
|
8
8
|
npm install @pixldocs/canvas-renderer fabric react react-dom
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
>
|
|
11
|
+
> Public package — no auth token or `.npmrc` needed.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Quick Start: Build an App From a Workspace
|
|
16
|
+
|
|
17
|
+
Got a Pixldocs workspace full of published templates (e.g. social-media posts,
|
|
18
|
+
invitations, certificates)? You can spin up a standalone app that lists and
|
|
19
|
+
renders them with just the **workspace ID** + Pixldocs anon key — no per-template
|
|
20
|
+
wiring needed.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import {
|
|
24
|
+
listPublishedTemplates,
|
|
25
|
+
PixldocsRenderer,
|
|
26
|
+
} from '@pixldocs/canvas-renderer';
|
|
27
|
+
|
|
28
|
+
const SUPABASE_URL = 'https://ttvtjhxjxuxdeybcnjkd.supabase.co';
|
|
29
|
+
const SUPABASE_ANON_KEY = 'eyJ...'; // Pixldocs publishable key (safe in browser)
|
|
30
|
+
const WORKSPACE_ID = 'your-workspace-uuid';
|
|
31
|
+
|
|
32
|
+
// 1. List all published templates owned by a workspace
|
|
33
|
+
const templates = await listPublishedTemplates({
|
|
34
|
+
workspaceId: WORKSPACE_ID,
|
|
35
|
+
supabaseUrl: SUPABASE_URL,
|
|
36
|
+
supabaseAnonKey: SUPABASE_ANON_KEY,
|
|
37
|
+
// category: 'social-media', // optional filter
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// templates: [{ id, name, thumbnail_url, category, price, ... }, ...]
|
|
41
|
+
|
|
42
|
+
// 2. Render a chosen template (form-bound or static)
|
|
43
|
+
const renderer = new PixldocsRenderer({
|
|
44
|
+
supabaseUrl: SUPABASE_URL,
|
|
45
|
+
supabaseAnonKey: SUPABASE_ANON_KEY,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const pages = await renderer.renderFromForm({
|
|
49
|
+
templateId: templates[0].id,
|
|
50
|
+
sectionState: { /* user inputs (optional for static templates) */ },
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Two ways to source templates
|
|
55
|
+
|
|
56
|
+
| Approach | Best for | API |
|
|
57
|
+
|----------|----------|-----|
|
|
58
|
+
| **By workspace** | Multi-template apps (template gallery, social-media post maker, certificate maker) — pick from a curated workspace | `listPublishedTemplates({ workspaceId })` |
|
|
59
|
+
| **By form schema** | Single-purpose apps where one form drives many template variants (e.g. BioMaker — one form, many biodata designs) | Resolve directly via `renderFromForm({ templateId, formSchemaId, sectionState })` |
|
|
60
|
+
|
|
61
|
+
Both work with the same anon key and the same renderer — choose whichever
|
|
62
|
+
matches your product shape.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Templates Catalog API
|
|
67
|
+
|
|
68
|
+
### `listPublishedTemplates(options)`
|
|
69
|
+
|
|
70
|
+
Returns every published template belonging to a workspace. Uses public RLS
|
|
71
|
+
(no auth needed beyond the anon key).
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const templates = await listPublishedTemplates({
|
|
75
|
+
workspaceId: 'uuid',
|
|
76
|
+
supabaseUrl: '...',
|
|
77
|
+
supabaseAnonKey: '...',
|
|
78
|
+
category: 'social-media', // optional
|
|
79
|
+
limit: 200, // optional (default 200)
|
|
80
|
+
offset: 0, // optional pagination
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Each item: `{ id, name, description, category, thumbnail_url, preview_images, price, download_count, workspace_id, sort_order, created_at, updated_at }`.
|
|
85
|
+
|
|
86
|
+
### `getPublishedTemplate({ templateId, supabaseUrl, supabaseAnonKey })`
|
|
87
|
+
|
|
88
|
+
Fetch a single published template's catalog row (without the heavy `config`
|
|
89
|
+
JSON — use the renderer for that).
|
|
12
90
|
|
|
13
91
|
---
|
|
14
92
|
|
|
@@ -348,11 +348,12 @@ function getCacheKey(element) {
|
|
|
348
348
|
scaleY: element.scaleY,
|
|
349
349
|
splitByGrapheme: element.splitByGrapheme,
|
|
350
350
|
overflowPolicy: element.overflowPolicy,
|
|
351
|
-
height: element.overflowPolicy === "auto-shrink" ? element.height : void 0
|
|
351
|
+
height: element.overflowPolicy === "auto-shrink" ? element.height : void 0,
|
|
352
|
+
minBoxHeight: element.minBoxHeight,
|
|
353
|
+
verticalAlign: element.verticalAlign
|
|
352
354
|
});
|
|
353
355
|
}
|
|
354
356
|
function measureTextHeight(element) {
|
|
355
|
-
var _a;
|
|
356
357
|
if (element.type !== "text") {
|
|
357
358
|
return element.height || 20;
|
|
358
359
|
}
|
|
@@ -368,8 +369,8 @@ function measureTextHeight(element) {
|
|
|
368
369
|
let fontSize = element.fontSize || 16;
|
|
369
370
|
const overflowPolicy = element.overflowPolicy || "grow-and-push";
|
|
370
371
|
if (overflowPolicy === "auto-shrink") {
|
|
371
|
-
const
|
|
372
|
-
const
|
|
372
|
+
const minBoxH = Math.max(0, Number(element.minBoxHeight) || 0);
|
|
373
|
+
const baseHeight = typeof element.height === "number" ? Math.max(element.height, minBoxH) : minBoxH > 0 ? minBoxH : element.height;
|
|
373
374
|
while (fontSize > 1) {
|
|
374
375
|
const testTb = new fabric__namespace.Textbox(textToMeasure, {
|
|
375
376
|
width: measureWidth,
|
|
@@ -383,11 +384,9 @@ function measureTextHeight(element) {
|
|
|
383
384
|
});
|
|
384
385
|
testTb.initDimensions();
|
|
385
386
|
const textHeight = testTb.height || 0;
|
|
386
|
-
const renderedLineCount = ((_a = testTb.textLines) == null ? void 0 : _a.length) || 1;
|
|
387
|
-
const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
|
|
388
387
|
const fitsHeight = !baseHeight || textHeight <= baseHeight;
|
|
389
388
|
const { fitsWidth } = getTextboxWidthFitMetrics(testTb, measureWidth);
|
|
390
|
-
if (
|
|
389
|
+
if (fitsHeight && fitsWidth) break;
|
|
391
390
|
fontSize--;
|
|
392
391
|
}
|
|
393
392
|
const finalTb = new fabric__namespace.Textbox(textToMeasure, {
|
|
@@ -402,7 +401,7 @@ function measureTextHeight(element) {
|
|
|
402
401
|
});
|
|
403
402
|
finalTb.initDimensions();
|
|
404
403
|
const measuredH = (finalTb.height || element.height || 20) * (element.scaleY || 1);
|
|
405
|
-
const cappedH = typeof baseHeight === "number" ? Math.
|
|
404
|
+
const cappedH = typeof baseHeight === "number" ? Math.max(baseHeight * (element.scaleY || 1), measuredH) : measuredH;
|
|
406
405
|
heightCache.set(cacheKey, { height: cappedH, timestamp: Date.now() });
|
|
407
406
|
return cappedH;
|
|
408
407
|
}
|
|
@@ -5550,6 +5549,54 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
|
|
|
5550
5549
|
return true;
|
|
5551
5550
|
});
|
|
5552
5551
|
}
|
|
5552
|
+
const TextboxProto = fabric__namespace.Textbox.prototype;
|
|
5553
|
+
if (!TextboxProto.__pixldocsOrigCalcTextHeight) {
|
|
5554
|
+
TextboxProto.__pixldocsOrigCalcTextHeight = TextboxProto.calcTextHeight;
|
|
5555
|
+
TextboxProto.calcTextHeight = function() {
|
|
5556
|
+
const orig = TextboxProto.__pixldocsOrigCalcTextHeight.call(this);
|
|
5557
|
+
this._contentHeight = orig;
|
|
5558
|
+
const min = this.minBoxHeight || 0;
|
|
5559
|
+
return min > orig ? min : orig;
|
|
5560
|
+
};
|
|
5561
|
+
}
|
|
5562
|
+
if (!TextboxProto.__pixldocsOrigGetTopOffset) {
|
|
5563
|
+
TextboxProto.__pixldocsOrigGetTopOffset = TextboxProto._getTopOffset;
|
|
5564
|
+
TextboxProto._getTopOffset = function() {
|
|
5565
|
+
const baseOffset = TextboxProto.__pixldocsOrigGetTopOffset.call(this);
|
|
5566
|
+
const valign = this.verticalAlign || "top";
|
|
5567
|
+
if (valign === "top") return baseOffset;
|
|
5568
|
+
const content = typeof this._contentHeight === "number" ? this._contentHeight : TextboxProto.__pixldocsOrigCalcTextHeight.call(this);
|
|
5569
|
+
const padding = (this.height || 0) - content;
|
|
5570
|
+
if (padding <= 0) return baseOffset;
|
|
5571
|
+
if (valign === "middle") return baseOffset + padding / 2;
|
|
5572
|
+
if (valign === "bottom") return baseOffset + padding;
|
|
5573
|
+
return baseOffset;
|
|
5574
|
+
};
|
|
5575
|
+
}
|
|
5576
|
+
if (TextboxProto._getSVGLeftTopOffsets && !TextboxProto.__pixldocsOrigGetSVGLeftTopOffsets) {
|
|
5577
|
+
TextboxProto.__pixldocsOrigGetSVGLeftTopOffsets = TextboxProto._getSVGLeftTopOffsets;
|
|
5578
|
+
TextboxProto._getSVGLeftTopOffsets = function() {
|
|
5579
|
+
const base = TextboxProto.__pixldocsOrigGetSVGLeftTopOffsets.call(this);
|
|
5580
|
+
const valign = this.verticalAlign || "top";
|
|
5581
|
+
if (valign === "top") return base;
|
|
5582
|
+
const content = typeof this._contentHeight === "number" ? this._contentHeight : TextboxProto.__pixldocsOrigCalcTextHeight.call(this);
|
|
5583
|
+
const padding = (this.height || 0) - content;
|
|
5584
|
+
if (padding <= 0) return base;
|
|
5585
|
+
const extra = valign === "middle" ? padding / 2 : padding;
|
|
5586
|
+
return { ...base, textTop: base.textTop + extra };
|
|
5587
|
+
};
|
|
5588
|
+
}
|
|
5589
|
+
const stateProps = fabric__namespace.Textbox.prototype.stateProperties;
|
|
5590
|
+
if (Array.isArray(stateProps)) {
|
|
5591
|
+
if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
|
|
5592
|
+
if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
|
|
5593
|
+
}
|
|
5594
|
+
const cacheProps = fabric__namespace.Textbox.prototype.cacheProperties;
|
|
5595
|
+
if (Array.isArray(cacheProps)) {
|
|
5596
|
+
if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
|
|
5597
|
+
if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
|
|
5598
|
+
}
|
|
5599
|
+
TextboxProto.__pixldocsTextboxExtended = true;
|
|
5553
5600
|
const PD_BG_KEY = "__pdBg";
|
|
5554
5601
|
const PATCHED_KEY = "__pdBgPatched";
|
|
5555
5602
|
function extractTextBgConfig(element) {
|
|
@@ -6369,7 +6416,7 @@ function createText(element) {
|
|
|
6369
6416
|
iterationSamples.push(lastIter);
|
|
6370
6417
|
}
|
|
6371
6418
|
}
|
|
6372
|
-
if (
|
|
6419
|
+
if (fitsHeight && fitsWidth) {
|
|
6373
6420
|
breakReason = "fits";
|
|
6374
6421
|
break;
|
|
6375
6422
|
}
|
|
@@ -6462,7 +6509,15 @@ function createText(element) {
|
|
|
6462
6509
|
// formatting tokens (**, __, [c=...], etc). Disable inline edit and steer
|
|
6463
6510
|
// users to the right-panel text field which exposes the raw markdown.
|
|
6464
6511
|
editable: !formattingEnabled,
|
|
6465
|
-
...formattingEnabled ? { styles: parsedStyles } : element.styles ? { styles: element.styles } : {}
|
|
6512
|
+
...formattingEnabled ? { styles: parsedStyles } : element.styles ? { styles: element.styles } : {},
|
|
6513
|
+
// Vertical sizing extensions (see fabricTextboxExtensions.ts). Apply for
|
|
6514
|
+
// every overflow policy — in auto-shrink mode `minBoxHeight` acts as a
|
|
6515
|
+
// visual floor so the box renders at the user's chosen height even after
|
|
6516
|
+
// the font shrinks to fit. PageCanvas's auto-shrink loop already uses the
|
|
6517
|
+
// same value as a fit-target, so the rendered box and the shrink target
|
|
6518
|
+
// stay in sync (parity with the Use page / EC2 renderer).
|
|
6519
|
+
...(element.minBoxHeight ?? 0) > 0 ? { minBoxHeight: element.minBoxHeight } : {},
|
|
6520
|
+
verticalAlign: element.verticalAlign || "top"
|
|
6466
6521
|
});
|
|
6467
6522
|
textbox.__formattingEnabled = formattingEnabled;
|
|
6468
6523
|
textbox.initDimensions();
|
|
@@ -8057,6 +8112,22 @@ const PageCanvas = react.forwardRef(
|
|
|
8057
8112
|
obj.dirty = true;
|
|
8058
8113
|
}
|
|
8059
8114
|
}
|
|
8115
|
+
if (obj instanceof fabric__namespace.Textbox) {
|
|
8116
|
+
const sy = obj.scaleY ?? 1;
|
|
8117
|
+
if (Math.abs(sy - 1) > 1e-3) {
|
|
8118
|
+
const center = obj.getCenterPoint();
|
|
8119
|
+
const newMinH = Math.max(0, (obj.height ?? 0) * Math.abs(sy));
|
|
8120
|
+
obj.minBoxHeight = newMinH;
|
|
8121
|
+
obj.scaleY = 1;
|
|
8122
|
+
try {
|
|
8123
|
+
obj.initDimensions();
|
|
8124
|
+
} catch {
|
|
8125
|
+
}
|
|
8126
|
+
obj.setPositionByOrigin(center, "center", "center");
|
|
8127
|
+
obj.setCoords();
|
|
8128
|
+
obj.dirty = true;
|
|
8129
|
+
}
|
|
8130
|
+
}
|
|
8060
8131
|
if (obj.__lockScaleDuringCrop || obj.__cropDrag) {
|
|
8061
8132
|
obj.set({ scaleX: 1, scaleY: 1 });
|
|
8062
8133
|
obj.setCoords();
|
|
@@ -8633,6 +8704,12 @@ const PageCanvas = react.forwardRef(
|
|
|
8633
8704
|
scaleY: finalScaleY,
|
|
8634
8705
|
transformMatrix: finalAbsoluteMatrix
|
|
8635
8706
|
};
|
|
8707
|
+
if (obj instanceof fabric__namespace.Textbox) {
|
|
8708
|
+
const baked = obj.minBoxHeight;
|
|
8709
|
+
if (typeof baked === "number" && baked > 0) {
|
|
8710
|
+
elementUpdate.minBoxHeight = baked;
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8636
8713
|
if (sourceElement && sourceElement.opacity !== void 0) {
|
|
8637
8714
|
elementUpdate.opacity = sourceElement.opacity;
|
|
8638
8715
|
}
|
|
@@ -9344,7 +9421,9 @@ const PageCanvas = react.forwardRef(
|
|
|
9344
9421
|
const resolvedSizeForCompare = (pageChildren == null ? void 0 : pageChildren.length) ? getNodeBounds(element, pageChildren) : { width: typeof element.width === "number" ? element.width : 0, height: typeof element.height === "number" ? element.height : 0 };
|
|
9345
9422
|
const fabricText = existingObj.text ?? "";
|
|
9346
9423
|
const storeText = element.text ?? "";
|
|
9347
|
-
const otherPropsChanged = Math.abs((existingObj.width ?? 0) - resolvedSizeForCompare.width) > 0.1 || Math.abs((existingObj.height ?? 0) - resolvedSizeForCompare.height) > 0.1 || Math.abs((existingObj.angle ?? 0) - (element.angle ?? 0)) > 0.1 || Math.abs((existingObj.scaleX ?? 1) - (element.scaleX ?? 1)) > 0.01 || Math.abs((existingObj.scaleY ?? 1) - (element.scaleY ?? 1)) > 0.01 || (existingObj.flipX ?? false) !== (element.flipX ?? false) || (existingObj.flipY ?? false) !== (element.flipY ?? false) || fabricText !== storeText || existingObj.fill !== (element.fill ?? "") || existingObj.stroke !== (element.stroke ?? "") || Math.abs((existingObj.strokeWidth ?? 0) - (element.strokeWidth ?? 0)) > 0.01 || Math.abs((existingObj.opacity ?? 1) - (element.opacity ?? 1)) > 0.01 || (existingObj.fontSize ?? 0) !== (element.fontSize ?? 0) || (existingObj.fontFamily ?? "") !== (element.fontFamily ?? "") || //
|
|
9424
|
+
const otherPropsChanged = Math.abs((existingObj.width ?? 0) - resolvedSizeForCompare.width) > 0.1 || Math.abs((existingObj.height ?? 0) - resolvedSizeForCompare.height) > 0.1 || Math.abs((existingObj.angle ?? 0) - (element.angle ?? 0)) > 0.1 || Math.abs((existingObj.scaleX ?? 1) - (element.scaleX ?? 1)) > 0.01 || Math.abs((existingObj.scaleY ?? 1) - (element.scaleY ?? 1)) > 0.01 || (existingObj.flipX ?? false) !== (element.flipX ?? false) || (existingObj.flipY ?? false) !== (element.flipY ?? false) || fabricText !== storeText || existingObj.fill !== (element.fill ?? "") || existingObj.stroke !== (element.stroke ?? "") || Math.abs((existingObj.strokeWidth ?? 0) - (element.strokeWidth ?? 0)) > 0.01 || Math.abs((existingObj.opacity ?? 1) - (element.opacity ?? 1)) > 0.01 || (existingObj.fontSize ?? 0) !== (element.fontSize ?? 0) || (existingObj.fontFamily ?? "") !== (element.fontFamily ?? "") || // Vertical alignment & min box height: panel-driven changes must trigger a re-apply
|
|
9425
|
+
// so _getTopOffset (verticalAlign) and calcTextHeight (minBoxHeight) repaint correctly.
|
|
9426
|
+
(existingObj.verticalAlign ?? "top") !== (element.verticalAlign ?? "top") || Math.abs((existingObj.minBoxHeight ?? 0) - (element.minBoxHeight ?? 0)) > 0.1 || // Detect text background + shadow changes so panel edits flow into Fabric.
|
|
9348
9427
|
JSON.stringify({
|
|
9349
9428
|
c: element.textBgColor ?? null,
|
|
9350
9429
|
p: element.textBgPadding ?? 0,
|
|
@@ -9799,7 +9878,7 @@ const PageCanvas = react.forwardRef(
|
|
|
9799
9878
|
});
|
|
9800
9879
|
}, [selectedIds, isActive, ready, elements]);
|
|
9801
9880
|
const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
|
|
9802
|
-
var _a, _b
|
|
9881
|
+
var _a, _b;
|
|
9803
9882
|
const fc = fabricRef.current;
|
|
9804
9883
|
if (fc && isTransforming(fc)) {
|
|
9805
9884
|
return;
|
|
@@ -10168,7 +10247,8 @@ const PageCanvas = react.forwardRef(
|
|
|
10168
10247
|
const fixedWidth = Math.max(storedWidth, 1);
|
|
10169
10248
|
const splitByGrapheme = overflowPolicy === "auto-shrink" ? false : element.splitByGrapheme ?? element.wordWrap === "break-word";
|
|
10170
10249
|
if (overflowPolicy === "auto-shrink") {
|
|
10171
|
-
const
|
|
10250
|
+
const minBoxHForShrink = Math.max(0, Number(element.minBoxHeight) || 0);
|
|
10251
|
+
const heightBound = Math.max(rH || 0, minBoxHForShrink);
|
|
10172
10252
|
while (fontSize > 1) {
|
|
10173
10253
|
const testTextbox = new fabric__namespace.Textbox(text, {
|
|
10174
10254
|
width: fixedWidth,
|
|
@@ -10178,15 +10258,13 @@ const PageCanvas = react.forwardRef(
|
|
|
10178
10258
|
fontStyle: element.fontStyle || "normal",
|
|
10179
10259
|
lineHeight: element.lineHeight || 1.2,
|
|
10180
10260
|
charSpacing: element.charSpacing || 0,
|
|
10181
|
-
splitByGrapheme: false
|
|
10261
|
+
splitByGrapheme: element.splitByGrapheme ?? false
|
|
10182
10262
|
});
|
|
10183
10263
|
testTextbox.initDimensions();
|
|
10184
10264
|
const textHeight = testTextbox.height || 0;
|
|
10185
|
-
const
|
|
10186
|
-
const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;
|
|
10187
|
-
const fitsHeight = rH <= 0 || textHeight <= rH;
|
|
10265
|
+
const fitsHeight = heightBound <= 0 || textHeight <= heightBound;
|
|
10188
10266
|
const { fitsWidth } = getTextboxWidthFitMetrics(testTextbox, fixedWidth);
|
|
10189
|
-
if (
|
|
10267
|
+
if (fitsHeight && fitsWidth) {
|
|
10190
10268
|
break;
|
|
10191
10269
|
}
|
|
10192
10270
|
fontSize--;
|
|
@@ -10257,6 +10335,10 @@ const PageCanvas = react.forwardRef(
|
|
|
10257
10335
|
splitByGrapheme,
|
|
10258
10336
|
text
|
|
10259
10337
|
});
|
|
10338
|
+
const valign = element.verticalAlign || "top";
|
|
10339
|
+
const minBoxH = Math.max(0, Number(element.minBoxHeight) || 0);
|
|
10340
|
+
obj.verticalAlign = valign;
|
|
10341
|
+
obj.minBoxHeight = minBoxH;
|
|
10260
10342
|
if (element.formattingEnabled === true) {
|
|
10261
10343
|
obj.styles = parsedStyles || {};
|
|
10262
10344
|
} else {
|
|
@@ -10279,7 +10361,7 @@ const PageCanvas = react.forwardRef(
|
|
|
10279
10361
|
} catch {
|
|
10280
10362
|
}
|
|
10281
10363
|
obj.dirty = true;
|
|
10282
|
-
(
|
|
10364
|
+
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
10283
10365
|
obj.__lastTextBgShadowJson = JSON.stringify({
|
|
10284
10366
|
c: element.textBgColor ?? null,
|
|
10285
10367
|
p: element.textBgPadding ?? 0,
|
|
@@ -16036,9 +16118,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16036
16118
|
}
|
|
16037
16119
|
return svgString;
|
|
16038
16120
|
}
|
|
16039
|
-
const resolvedPackageVersion = "0.5.
|
|
16121
|
+
const resolvedPackageVersion = "0.5.168";
|
|
16040
16122
|
const PACKAGE_VERSION = resolvedPackageVersion;
|
|
16041
|
-
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.
|
|
16123
|
+
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.168";
|
|
16042
16124
|
const roundParityValue = (value) => {
|
|
16043
16125
|
if (typeof value !== "number") return value;
|
|
16044
16126
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -16164,6 +16246,68 @@ function installUnderlineFix(fab) {
|
|
|
16164
16246
|
__underlineFixInstalled = true;
|
|
16165
16247
|
console.log(`[canvas-renderer] underline-fix monkey patch installed (v${PACKAGE_VERSION})`);
|
|
16166
16248
|
}
|
|
16249
|
+
let __textboxBoxExtensionsInstalled = false;
|
|
16250
|
+
function installTextboxBoxExtensions(fab) {
|
|
16251
|
+
var _a;
|
|
16252
|
+
if (__textboxBoxExtensionsInstalled) return;
|
|
16253
|
+
const TextboxProto2 = (_a = fab.Textbox) == null ? void 0 : _a.prototype;
|
|
16254
|
+
if (!TextboxProto2) return;
|
|
16255
|
+
if (TextboxProto2.__pixldocsTextboxExtended) {
|
|
16256
|
+
__textboxBoxExtensionsInstalled = true;
|
|
16257
|
+
return;
|
|
16258
|
+
}
|
|
16259
|
+
if (typeof TextboxProto2.calcTextHeight === "function") {
|
|
16260
|
+
const origCalc = TextboxProto2.calcTextHeight;
|
|
16261
|
+
TextboxProto2.__pixldocsOrigCalcTextHeight = origCalc;
|
|
16262
|
+
TextboxProto2.calcTextHeight = function() {
|
|
16263
|
+
const orig = origCalc.call(this);
|
|
16264
|
+
this._contentHeight = orig;
|
|
16265
|
+
const min = this.minBoxHeight || 0;
|
|
16266
|
+
return min > orig ? min : orig;
|
|
16267
|
+
};
|
|
16268
|
+
}
|
|
16269
|
+
if (typeof TextboxProto2._getTopOffset === "function") {
|
|
16270
|
+
const origTop = TextboxProto2._getTopOffset;
|
|
16271
|
+
TextboxProto2.__pixldocsOrigGetTopOffset = origTop;
|
|
16272
|
+
TextboxProto2._getTopOffset = function() {
|
|
16273
|
+
const baseOffset = origTop.call(this);
|
|
16274
|
+
const valign = this.verticalAlign || "top";
|
|
16275
|
+
if (valign === "top") return baseOffset;
|
|
16276
|
+
const content = typeof this._contentHeight === "number" ? this._contentHeight : TextboxProto2.__pixldocsOrigCalcTextHeight ? TextboxProto2.__pixldocsOrigCalcTextHeight.call(this) : 0;
|
|
16277
|
+
const padding = (this.height || 0) - content;
|
|
16278
|
+
if (padding <= 0) return baseOffset;
|
|
16279
|
+
if (valign === "middle") return baseOffset + padding / 2;
|
|
16280
|
+
if (valign === "bottom") return baseOffset + padding;
|
|
16281
|
+
return baseOffset;
|
|
16282
|
+
};
|
|
16283
|
+
}
|
|
16284
|
+
if (typeof TextboxProto2._getSVGLeftTopOffsets === "function") {
|
|
16285
|
+
const origSvgOffsets = TextboxProto2._getSVGLeftTopOffsets;
|
|
16286
|
+
TextboxProto2.__pixldocsOrigGetSVGLeftTopOffsets = origSvgOffsets;
|
|
16287
|
+
TextboxProto2._getSVGLeftTopOffsets = function() {
|
|
16288
|
+
const base = origSvgOffsets.call(this);
|
|
16289
|
+
const valign = this.verticalAlign || "top";
|
|
16290
|
+
if (valign === "top") return base;
|
|
16291
|
+
const content = typeof this._contentHeight === "number" ? this._contentHeight : TextboxProto2.__pixldocsOrigCalcTextHeight ? TextboxProto2.__pixldocsOrigCalcTextHeight.call(this) : 0;
|
|
16292
|
+
const padding = (this.height || 0) - content;
|
|
16293
|
+
if (padding <= 0) return base;
|
|
16294
|
+
const extra = valign === "middle" ? padding / 2 : padding;
|
|
16295
|
+
return { ...base, textTop: base.textTop + extra };
|
|
16296
|
+
};
|
|
16297
|
+
}
|
|
16298
|
+
const stateProps2 = TextboxProto2.stateProperties;
|
|
16299
|
+
if (Array.isArray(stateProps2)) {
|
|
16300
|
+
if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
|
|
16301
|
+
if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
|
|
16302
|
+
}
|
|
16303
|
+
const cacheProps2 = TextboxProto2.cacheProperties;
|
|
16304
|
+
if (Array.isArray(cacheProps2)) {
|
|
16305
|
+
if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
|
|
16306
|
+
if (!cacheProps2.includes("verticalAlign")) cacheProps2.push("verticalAlign");
|
|
16307
|
+
}
|
|
16308
|
+
TextboxProto2.__pixldocsTextboxExtended = true;
|
|
16309
|
+
__textboxBoxExtensionsInstalled = true;
|
|
16310
|
+
}
|
|
16167
16311
|
function configHasAutoShrinkText(config) {
|
|
16168
16312
|
var _a;
|
|
16169
16313
|
if (!((_a = config == null ? void 0 : config.pages) == null ? void 0 : _a.length)) return false;
|
|
@@ -16186,6 +16330,7 @@ class PixldocsRenderer {
|
|
|
16186
16330
|
this.config = config;
|
|
16187
16331
|
this.installRuntimeGlobals();
|
|
16188
16332
|
installUnderlineFix(fabric__namespace);
|
|
16333
|
+
installTextboxBoxExtensions(fabric__namespace);
|
|
16189
16334
|
try {
|
|
16190
16335
|
console.log(`[canvas-renderer] PixldocsRenderer v${PACKAGE_VERSION} initialized`);
|
|
16191
16336
|
} catch {
|
|
@@ -16473,7 +16618,7 @@ class PixldocsRenderer {
|
|
|
16473
16618
|
await this.waitForCanvasScene(container, cloned, i);
|
|
16474
16619
|
}
|
|
16475
16620
|
console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
|
|
16476
|
-
const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-
|
|
16621
|
+
const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Bw2WQNoU.cjs"));
|
|
16477
16622
|
const prepared = preparePagesForExport(
|
|
16478
16623
|
cloned.pages,
|
|
16479
16624
|
canvasWidth,
|
|
@@ -18575,7 +18720,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
18575
18720
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
18576
18721
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
18577
18722
|
try {
|
|
18578
|
-
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-
|
|
18723
|
+
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Bw2WQNoU.cjs"));
|
|
18579
18724
|
try {
|
|
18580
18725
|
await logTextMeasurementDiagnostic(svgToDraw);
|
|
18581
18726
|
} catch {
|
|
@@ -18781,6 +18926,56 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
|
|
|
18781
18926
|
pages: svgResults.map((p) => ({ width: p.width, height: p.height }))
|
|
18782
18927
|
};
|
|
18783
18928
|
}
|
|
18929
|
+
const SELECT_COLUMNS = "id,name,description,category,thumbnail_url,preview_images,price,download_count,workspace_id,sort_order,created_at,updated_at";
|
|
18930
|
+
async function listPublishedTemplates(options) {
|
|
18931
|
+
const { workspaceId, supabaseUrl, supabaseAnonKey, category, limit = 200, offset = 0 } = options;
|
|
18932
|
+
if (!workspaceId) throw new Error("listPublishedTemplates: workspaceId is required");
|
|
18933
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
18934
|
+
throw new Error("listPublishedTemplates: supabaseUrl and supabaseAnonKey are required");
|
|
18935
|
+
}
|
|
18936
|
+
const params = new URLSearchParams({
|
|
18937
|
+
select: SELECT_COLUMNS,
|
|
18938
|
+
workspace_id: `eq.${workspaceId}`,
|
|
18939
|
+
status: "eq.published",
|
|
18940
|
+
order: "sort_order.asc,updated_at.desc",
|
|
18941
|
+
limit: String(limit),
|
|
18942
|
+
offset: String(offset)
|
|
18943
|
+
});
|
|
18944
|
+
if (category) params.set("category", `eq.${category}`);
|
|
18945
|
+
const url = `${supabaseUrl.replace(/\/$/, "")}/rest/v1/templates?${params.toString()}`;
|
|
18946
|
+
const res = await fetch(url, {
|
|
18947
|
+
headers: {
|
|
18948
|
+
apikey: supabaseAnonKey,
|
|
18949
|
+
Authorization: `Bearer ${supabaseAnonKey}`,
|
|
18950
|
+
Accept: "application/json"
|
|
18951
|
+
}
|
|
18952
|
+
});
|
|
18953
|
+
if (!res.ok) {
|
|
18954
|
+
const text = await res.text().catch(() => "");
|
|
18955
|
+
throw new Error(`listPublishedTemplates failed: ${res.status} ${text}`);
|
|
18956
|
+
}
|
|
18957
|
+
return await res.json();
|
|
18958
|
+
}
|
|
18959
|
+
async function getPublishedTemplate(options) {
|
|
18960
|
+
const { templateId, supabaseUrl, supabaseAnonKey } = options;
|
|
18961
|
+
const params = new URLSearchParams({
|
|
18962
|
+
select: SELECT_COLUMNS,
|
|
18963
|
+
id: `eq.${templateId}`,
|
|
18964
|
+
status: "eq.published",
|
|
18965
|
+
limit: "1"
|
|
18966
|
+
});
|
|
18967
|
+
const url = `${supabaseUrl.replace(/\/$/, "")}/rest/v1/templates?${params.toString()}`;
|
|
18968
|
+
const res = await fetch(url, {
|
|
18969
|
+
headers: {
|
|
18970
|
+
apikey: supabaseAnonKey,
|
|
18971
|
+
Authorization: `Bearer ${supabaseAnonKey}`,
|
|
18972
|
+
Accept: "application/json"
|
|
18973
|
+
}
|
|
18974
|
+
});
|
|
18975
|
+
if (!res.ok) return null;
|
|
18976
|
+
const rows = await res.json();
|
|
18977
|
+
return rows[0] ?? null;
|
|
18978
|
+
}
|
|
18784
18979
|
function collectImageUrls(config) {
|
|
18785
18980
|
const urls = [];
|
|
18786
18981
|
const walk = (nodes) => {
|
|
@@ -18901,6 +19096,7 @@ exports.getCanvasForPage = getCanvasForPage;
|
|
|
18901
19096
|
exports.getEmbeddedJsPDFFontName = getEmbeddedJsPDFFontName;
|
|
18902
19097
|
exports.getImageProxyFetchOptions = getImageProxyFetchOptions;
|
|
18903
19098
|
exports.getProxiedImageUrl = getProxiedImageUrl;
|
|
19099
|
+
exports.getPublishedTemplate = getPublishedTemplate;
|
|
18904
19100
|
exports.getRoundedRectRadii = getRoundedRectRadii;
|
|
18905
19101
|
exports.getTrianglePoints = getTrianglePoints;
|
|
18906
19102
|
exports.hasEdgeFade = hasEdgeFade;
|
|
@@ -18909,6 +19105,7 @@ exports.isElement = isElement;
|
|
|
18909
19105
|
exports.isFontAvailable = isFontAvailable;
|
|
18910
19106
|
exports.isGroup = isGroup;
|
|
18911
19107
|
exports.isPrivateUrl = isPrivateUrl;
|
|
19108
|
+
exports.listPublishedTemplates = listPublishedTemplates;
|
|
18912
19109
|
exports.loadGoogleFontCSS = loadGoogleFontCSS;
|
|
18913
19110
|
exports.normalizeFontFamily = normalizeFontFamily;
|
|
18914
19111
|
exports.normalizeShapeType = normalizeShapeType;
|
|
@@ -18922,4 +19119,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
|
|
|
18922
19119
|
exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
|
|
18923
19120
|
exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
|
|
18924
19121
|
exports.warmTemplateFromForm = warmTemplateFromForm;
|
|
18925
|
-
//# sourceMappingURL=index-
|
|
19122
|
+
//# sourceMappingURL=index-DgX2Y94P.cjs.map
|