@campxdev/pdfme 1.2.2 → 1.3.0

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.
Files changed (46) hide show
  1. package/dist/cjs/chunks/{index-C8qZMUOU.js → fontSizePxWidget-Drk8HKGH.js} +138 -9
  2. package/dist/cjs/chunks/fontSizeTransform-CQQ_O42f.js +37 -0
  3. package/dist/cjs/chunks/{helper-BfoMn47R.js → helper-DGH62Z2s.js} +4 -0
  4. package/dist/cjs/chunks/{index-COKtXyPp.js → index-CoNR0xQU.js} +6 -2
  5. package/dist/cjs/chunks/{index-CVqJfcgy.js → index-CsEKt088.js} +697 -547
  6. package/dist/cjs/chunks/{pluginRegistry-C8bMreez.js → pluginRegistry-D2vr9MUy.js} +1 -1
  7. package/dist/cjs/common.js +7 -3
  8. package/dist/cjs/converter.js +1 -1
  9. package/dist/cjs/generator.js +3 -3
  10. package/dist/cjs/index.js +23 -16
  11. package/dist/cjs/print-designer-editor.js +3320 -3296
  12. package/dist/cjs/schemas.js +500 -38
  13. package/dist/cjs/ui.js +2007 -1868
  14. package/dist/esm/chunks/{index-C4F7EwBG.js → fontSizePxWidget-CbzQrSSM.js} +130 -5
  15. package/dist/esm/chunks/fontSizeTransform-CkTVJdRF.js +34 -0
  16. package/dist/esm/chunks/{helper-D5PPN6Bv.js → helper-DSxGxZ0j.js} +4 -1
  17. package/dist/esm/chunks/{index-CDhErAtE.js → index-DJkUkUo9.js} +4 -3
  18. package/dist/esm/chunks/{index-C7jr4GIK.js → index-D_-j4c4P.js} +691 -544
  19. package/dist/esm/chunks/{pluginRegistry-B-XSNgmK.js → pluginRegistry-Bgrz5qWG.js} +1 -1
  20. package/dist/esm/common.js +4 -3
  21. package/dist/esm/converter.js +1 -1
  22. package/dist/esm/generator.js +3 -3
  23. package/dist/esm/index.js +7 -6
  24. package/dist/esm/print-designer-editor.js +3307 -3286
  25. package/dist/esm/schemas.js +472 -13
  26. package/dist/esm/ui.js +2007 -1868
  27. package/dist/types/_vendors/common/fontSizeTransform.d.ts +5 -0
  28. package/dist/types/_vendors/common/helper.d.ts +1 -0
  29. package/dist/types/_vendors/common/index.d.ts +3 -2
  30. package/dist/types/_vendors/print-designer-editor/index.d.ts +2 -1
  31. package/dist/types/_vendors/print-designer-editor/types.d.ts +1 -1
  32. package/dist/types/_vendors/print-designer-editor/useDesigner.d.ts +1 -1
  33. package/dist/types/_vendors/schemas/index.d.ts +8 -2
  34. package/dist/types/_vendors/schemas/richText/helper.d.ts +3 -0
  35. package/dist/types/_vendors/schemas/richText/index.d.ts +4 -0
  36. package/dist/types/_vendors/schemas/richText/pdfRender.d.ts +3 -0
  37. package/dist/types/_vendors/schemas/richText/propPanel.d.ts +3 -0
  38. package/dist/types/_vendors/schemas/richText/types.d.ts +7 -0
  39. package/dist/types/_vendors/schemas/richText/uiRender.d.ts +3 -0
  40. package/dist/types/_vendors/schemas/singleVariableText/index.d.ts +4 -0
  41. package/dist/types/_vendors/schemas/singleVariableText/propPanel.d.ts +3 -0
  42. package/dist/types/_vendors/schemas/singleVariableText/types.d.ts +4 -0
  43. package/dist/types/_vendors/schemas/text/fontSizePxWidget.d.ts +9 -0
  44. package/dist/types/_vendors/ui/components/CtlBar.d.ts +1 -1
  45. package/dist/types/_vendors/ui/components/Paper.d.ts +1 -0
  46. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
- import { m as propPanel$1, p as pdfRender$2, u as uiRender$2, c as createSvgStr, d as convertForPdfLayoutProps, i as isEditable, a as addAlphaToHex, k as createErrorElm, D as DEFAULT_OPACITY, H as HEX_COLOR_PATTERN, n as getBodyWithRange, o as createSingleTable, q as getBody, f as getDefaultCellStyles, t as getColumnStylesPropPanelSchema, v as getCellPropPanelSchema, w as DEFAULT_FONT_COLOR, x as DEFAULT_CHARACTER_SPACING, y as DEFAULT_FONT_SIZE, z as DEFAULT_ALIGNMENT, A as getExtraFormatterSchema, F as Formatter, B as DEFAULT_LINE_HEIGHT, V as VERTICAL_ALIGN_MIDDLE, C as mapVerticalAlignToFlex } from './chunks/index-C7jr4GIK.js';
2
- export { b as builtInPlugins, g as getDynamicHeightsForTable, s as multiVariableText } from './chunks/index-C7jr4GIK.js';
3
- import { c as validateBarcodeInput, f as createBarCode, D as DEFAULT_BARCODE_COLOR, g as DEFAULT_BARCODE_BG_COLOR, h as DEFAULT_BARCODE_INCLUDETEXT, B as BARCODE_TYPES, r as rectangle, j as cellSchema } from './chunks/index-C4F7EwBG.js';
4
- export { d as dynamicBarcode, a as dynamicQrCode, b as dynamicTable, e as ellipse, i as image, l as line, s as signature, v as variableBarcodes } from './chunks/index-C4F7EwBG.js';
5
- import { TextCursorInput, Route, QrCode, Barcode, Table, CalendarClock, Calendar, Clock, ChevronDown, CircleDot, Circle, SquareCheck, Square } from 'lucide';
6
- import { E as px2mm, Z as ZOOM, r as getFallbackFontName, D as DEFAULT_FONT_NAME } from './chunks/helper-D5PPN6Bv.js';
1
+ import { p as pdfRender$3, l as propPanel$2, u as uiRender$3, i as isEditable, m as getFontKitFont, n as buildStyledTextContainer, o as makeElementPlainTextContentEditable, c as createSvgStr, e as convertForPdfLayoutProps, d as addAlphaToHex, k as createErrorElm, D as DEFAULT_OPACITY, H as HEX_COLOR_PATTERN, q as getBodyWithRange, v as createSingleTable, w as getBody, j as getDefaultCellStyles, x as getColumnStylesPropPanelSchema, y as getCellPropPanelSchema, z as DEFAULT_FONT_COLOR, A as DEFAULT_CHARACTER_SPACING, B as DEFAULT_FONT_SIZE, C as DEFAULT_ALIGNMENT, E as getExtraFormatterSchema, F as Formatter, t as textSchema, G as DEFAULT_LINE_HEIGHT, V as VERTICAL_ALIGN_MIDDLE, I as mapVerticalAlignToFlex } from './chunks/index-D_-j4c4P.js';
2
+ export { b as builtInPlugins, g as getDynamicHeightsForTable, s as richText, a as singleVariableText } from './chunks/index-D_-j4c4P.js';
3
+ import { c as validateVariables, g as substituteVariables, h as createInsertVariableWidget, j as validateBarcodeInput, k as createBarCode, D as DEFAULT_BARCODE_COLOR, m as DEFAULT_BARCODE_BG_COLOR, n as DEFAULT_BARCODE_INCLUDETEXT, B as BARCODE_TYPES, r as rectangle, o as cellSchema } from './chunks/fontSizePxWidget-CbzQrSSM.js';
4
+ export { d as dynamicBarcode, a as dynamicQrCode, b as dynamicTable, e as ellipse, f as fontSizePxWidget, i as image, l as line, s as signature, v as variableBarcodes } from './chunks/fontSizePxWidget-CbzQrSSM.js';
5
+ import { Type, Route, QrCode, Barcode, Table, CalendarClock, Calendar, Clock, ChevronDown, CircleDot, Circle, SquareCheck, Square } from 'lucide';
6
+ import { q as getDefaultFont, E as px2mm, Z as ZOOM, r as getFallbackFontName, D as DEFAULT_FONT_NAME } from './chunks/helper-DSxGxZ0j.js';
7
7
  import AirDatepicker from 'air-datepicker';
8
8
  import localeAr from 'air-datepicker/locale/ar';
9
9
  import localeBg from 'air-datepicker/locale/bg';
@@ -52,11 +52,470 @@ import 'uuid';
52
52
  import 'bwip-js';
53
53
  import 'zod';
54
54
 
55
- const textSchema = {
55
+ const pdfRender$2 = async (arg) => {
56
+ const { value, schema, pageContext, ...rest } = arg;
57
+ // Static mode: no template text → render value directly as plain text
58
+ if (!schema.text) {
59
+ await pdfRender$3({ value, schema, ...rest });
60
+ return;
61
+ }
62
+ // readOnly: value is already resolved by generate.ts via replacePlaceholders
63
+ if (schema.readOnly) {
64
+ await pdfRender$3({ value, schema, ...rest });
65
+ return;
66
+ }
67
+ // Dynamic mode (form): substitute variables in template
68
+ if (!validateVariables(value, schema)) {
69
+ return;
70
+ }
71
+ const renderArgs = {
72
+ value: substituteVariables(schema.text, value || '{}', pageContext),
73
+ schema,
74
+ ...rest,
75
+ };
76
+ await pdfRender$3(renderArgs);
77
+ };
78
+
79
+ const insertVariableWidget = createInsertVariableWidget('text');
80
+ const mapDynamicVariables = (props) => {
81
+ const { rootElement, changeSchemas, activeSchema } = props;
82
+ const mvtSchema = activeSchema;
83
+ const text = mvtSchema.text ?? '';
84
+ if (!text) {
85
+ rootElement.style.display = 'none';
86
+ return;
87
+ }
88
+ const variables = JSON.parse(mvtSchema.content || '{}');
89
+ const variablesChanged = updateVariablesFromText(text, variables);
90
+ const varNames = Object.keys(variables);
91
+ if (variablesChanged) {
92
+ changeSchemas([
93
+ { key: 'content', value: JSON.stringify(variables), schemaId: activeSchema.id },
94
+ { key: 'variables', value: varNames, schemaId: activeSchema.id },
95
+ { key: 'readOnly', value: varNames.length === 0, schemaId: activeSchema.id },
96
+ ]);
97
+ }
98
+ // No UI needed — sample data is auto-generated from variable paths
99
+ rootElement.style.display = 'none';
100
+ };
101
+ const propPanel$1 = {
102
+ schema: (propPanelProps) => {
103
+ if (typeof propPanel$2.schema !== 'function') {
104
+ throw new Error('Oops, is text schema no longer a function?');
105
+ }
106
+ // Safely call schema function with proper type handling
107
+ const parentSchema = typeof propPanel$2.schema === 'function' ? propPanel$2.schema(propPanelProps) : {};
108
+ return {
109
+ insertVariablePicker: {
110
+ type: 'void',
111
+ widget: 'insertVariableWidget',
112
+ bind: false,
113
+ span: 24,
114
+ },
115
+ '----': { type: 'void', widget: 'Divider' },
116
+ ...parentSchema,
117
+ dynamicVariables: {
118
+ type: 'object',
119
+ widget: 'mapDynamicVariables',
120
+ bind: false,
121
+ span: 0,
122
+ },
123
+ };
124
+ },
125
+ widgets: { ...(propPanel$2.widgets || {}), mapDynamicVariables, insertVariableWidget },
126
+ defaultSchema: {
127
+ ...propPanel$2.defaultSchema,
128
+ readOnly: false,
129
+ type: 'text',
130
+ text: 'Type Something...',
131
+ width: 50,
132
+ height: 15,
133
+ content: '{}',
134
+ variables: [],
135
+ },
136
+ };
137
+ /** Known JS globals/keywords that should NOT be treated as user-defined variables */
138
+ const RESERVED_NAMES$1 = new Set([
139
+ 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'in',
140
+ 'void', 'delete', 'new', 'this', 'NaN', 'Infinity',
141
+ 'Math', 'String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'JSON',
142
+ 'isNaN', 'parseFloat', 'parseInt', 'decodeURI', 'decodeURIComponent',
143
+ 'encodeURI', 'encodeURIComponent', 'date', 'dateTime',
144
+ 'currentPage', 'totalPages',
145
+ ]);
146
+ /**
147
+ * Extract full dot-notation paths from an expression string.
148
+ * E.g. "student.marks.sem1 > 80" → ["student.marks.sem1"]
149
+ * Handles method calls: "student.name.toUpperCase()" → ["student.name"]
150
+ * Skips string literals and reserved names.
151
+ */
152
+ const extractDotPaths = (expr) => {
153
+ // Replace string literals with spaces (preserving positions for nextChar lookup)
154
+ const cleaned = expr.replace(/'[^']*'|"[^"]*"|`[^`]*`/g, (m) => ' '.repeat(m.length));
155
+ const pathRegex = /[a-zA-Z_$][a-zA-Z0-9_$]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$]*)*/g;
156
+ const paths = new Set();
157
+ let m;
158
+ while ((m = pathRegex.exec(cleaned)) !== null) {
159
+ let path = m[0];
160
+ // If followed by '(', the last segment is a method call — trim it
161
+ const nextChar = cleaned[m.index + path.length];
162
+ if (nextChar === '(') {
163
+ const lastDot = path.lastIndexOf('.');
164
+ if (lastDot !== -1) {
165
+ path = path.substring(0, lastDot);
166
+ }
167
+ else {
168
+ // Standalone function call like parseInt(...) — skip
169
+ continue;
170
+ }
171
+ }
172
+ const root = path.split('.')[0];
173
+ if (!RESERVED_NAMES$1.has(root))
174
+ paths.add(path);
175
+ }
176
+ return Array.from(paths);
177
+ };
178
+ /**
179
+ * Build a nested default object from dot-paths.
180
+ * E.g. ["student.name", "student.marks.sem1"] →
181
+ * { name: "NAME", marks: { sem1: "SEM1" } }
182
+ * Merges into an existing object, only adding missing leaves.
183
+ * Returns true if anything was added.
184
+ */
185
+ const buildNestedDefault = (obj, paths) => {
186
+ let added = false;
187
+ for (const path of paths) {
188
+ const parts = path.split('.');
189
+ if (parts.length <= 1)
190
+ continue; // no nested parts
191
+ let current = obj;
192
+ for (let i = 1; i < parts.length - 1; i++) {
193
+ if (!(parts[i] in current) || typeof current[parts[i]] !== 'object' || current[parts[i]] === null) {
194
+ current[parts[i]] = {};
195
+ added = true;
196
+ }
197
+ current = current[parts[i]];
198
+ }
199
+ const leaf = parts[parts.length - 1];
200
+ if (!(leaf in current)) {
201
+ current[leaf] = path.replace(/\./g, '_').toUpperCase();
202
+ added = true;
203
+ }
204
+ }
205
+ return added;
206
+ };
207
+ const updateVariablesFromText = (text, variables) => {
208
+ // Find all {...} blocks and extract dot-notation paths from each
209
+ const blockRegex = /\{([^{}]+)\}/g;
210
+ const allPaths = new Set();
211
+ let blockMatch;
212
+ while ((blockMatch = blockRegex.exec(text)) !== null) {
213
+ for (const path of extractDotPaths(blockMatch[1])) {
214
+ allPaths.add(path);
215
+ }
216
+ }
217
+ // Group paths by root identifier
218
+ const rootToPaths = new Map();
219
+ for (const path of allPaths) {
220
+ const root = path.split('.')[0];
221
+ if (!rootToPaths.has(root))
222
+ rootToPaths.set(root, []);
223
+ rootToPaths.get(root).push(path);
224
+ }
225
+ const allRoots = new Set(rootToPaths.keys());
226
+ let changed = false;
227
+ for (const [root, paths] of rootToPaths) {
228
+ const hasNested = paths.some((p) => p.includes('.'));
229
+ if (hasNested) {
230
+ // Parse existing value or start fresh
231
+ let obj = {};
232
+ if (root in variables) {
233
+ try {
234
+ const parsed = JSON.parse(variables[root]);
235
+ if (typeof parsed === 'object' && parsed !== null) {
236
+ obj = parsed;
237
+ }
238
+ }
239
+ catch {
240
+ /* not JSON, will rebuild */
241
+ }
242
+ }
243
+ const added = buildNestedDefault(obj, paths);
244
+ if (!(root in variables) || added) {
245
+ variables[root] = JSON.stringify(obj);
246
+ changed = true;
247
+ }
248
+ }
249
+ else {
250
+ if (!(root in variables)) {
251
+ variables[root] = root.toUpperCase();
252
+ changed = true;
253
+ }
254
+ }
255
+ }
256
+ // Remove variables whose root is no longer referenced
257
+ for (const varName of Object.keys(variables)) {
258
+ if (!allRoots.has(varName)) {
259
+ delete variables[varName];
260
+ changed = true;
261
+ }
262
+ }
263
+ return changed;
264
+ };
265
+
266
+ const uiRender$2 = async (arg) => {
267
+ const { value, schema, rootElement, mode, onChange, pageContext, ...rest } = arg;
268
+ // Static mode: no template text → delegate to plain text behavior
269
+ if (!schema.text) {
270
+ await uiRender$3(arg);
271
+ return;
272
+ }
273
+ // Dynamic mode: template with optional variables
274
+ let text = schema.text;
275
+ let numVariables = (schema.variables || []).length;
276
+ if (mode === 'form' && numVariables > 0) {
277
+ await formUiRender(arg);
278
+ return;
279
+ }
280
+ await uiRender$3({
281
+ value: isEditable(mode, schema) ? text : substituteVariables(text, value, pageContext),
282
+ schema,
283
+ mode: mode === 'form' ? 'viewer' : mode, // if no variables for form it's just a viewer
284
+ rootElement,
285
+ onChange: (arg) => {
286
+ if (!Array.isArray(arg)) {
287
+ if (onChange) {
288
+ onChange({ key: 'text', value: arg.value });
289
+ }
290
+ }
291
+ else {
292
+ throw new Error('onChange is not an array, the parent text plugin has changed...');
293
+ }
294
+ },
295
+ ...rest,
296
+ });
297
+ const textBlock = rootElement.querySelector('#text-' + String(schema.id));
298
+ if (!textBlock) {
299
+ throw new Error('Text block not found. Ensure the text block has an id of "text-" + schema.id');
300
+ }
301
+ if (mode === 'designer') {
302
+ textBlock.addEventListener('keyup', (event) => {
303
+ text = textBlock.textContent || '';
304
+ if (keyPressShouldBeChecked(event)) {
305
+ const newNumVariables = countUniqueVariableNames(text);
306
+ if (numVariables !== newNumVariables) {
307
+ if (onChange) {
308
+ onChange({ key: 'text', value: text });
309
+ }
310
+ numVariables = newNumVariables;
311
+ }
312
+ }
313
+ });
314
+ }
315
+ };
316
+ /** Re-evaluate all expression spans inside a container using current variable values */
317
+ const refreshExpressionSpans = (container, variables, pageCtx) => {
318
+ container.querySelectorAll('[data-expr]').forEach((span) => {
319
+ const expr = span.getAttribute('data-expr');
320
+ span.textContent = substituteVariables(`{${expr}}`, variables, pageCtx);
321
+ });
322
+ };
323
+ const formUiRender = async (arg) => {
324
+ const { value, schema, rootElement, onChange, stopEditing, theme, _cache, options, pageContext } = arg;
325
+ const rawText = schema.text;
326
+ if (rootElement.parentElement) {
327
+ // remove the outline for the whole schema, we'll apply outlines on each individual variable field instead
328
+ rootElement.parentElement.style.outline = '';
329
+ }
330
+ const variables = value
331
+ ? JSON.parse(value) || {}
332
+ : {};
333
+ const substitutedText = substituteVariables(rawText, variables, pageContext);
334
+ const font = options?.font || getDefaultFont();
335
+ const fontKitFont = await getFontKitFont(schema.fontName, font, _cache);
336
+ const textBlock = buildStyledTextContainer(arg, fontKitFont, substitutedText);
337
+ // Tokenize rawText into static text, simple-identifier variables (editable), and
338
+ // complex expressions (evaluated, static). This supports both {name} and {price * 1.1}.
339
+ const tokens = tokenizeTemplate(rawText, variables, pageContext);
340
+ // Find variables that have inline editable spans (standalone {var} tokens)
341
+ const inlineVarNames = new Set(tokens.filter((t) => t.type === 'simpleVar')
342
+ .map(t => t.name));
343
+ // Variables that only appear inside expressions — need separate input fields
344
+ const expressionOnlyVars = (schema.variables || []).filter(v => !inlineVarNames.has(v));
345
+ if (expressionOnlyVars.length > 0) {
346
+ const varSection = document.createElement('div');
347
+ Object.assign(varSection.style, {
348
+ width: '100%',
349
+ padding: '2px 4px',
350
+ boxSizing: 'border-box',
351
+ display: 'flex',
352
+ flexWrap: 'wrap',
353
+ gap: '4px',
354
+ marginBottom: '2px',
355
+ });
356
+ for (const varName of expressionOnlyVars) {
357
+ const row = document.createElement('div');
358
+ Object.assign(row.style, { display: 'flex', alignItems: 'center', gap: '2px' });
359
+ const label = document.createElement('label');
360
+ label.textContent = varName + ':';
361
+ Object.assign(label.style, {
362
+ fontSize: '9px', whiteSpace: 'nowrap', color: theme.colorText,
363
+ });
364
+ const input = document.createElement('input');
365
+ input.type = 'text';
366
+ input.value = variables[varName] ?? '';
367
+ Object.assign(input.style, {
368
+ width: '60px',
369
+ fontSize: '9px',
370
+ border: `1px dashed ${theme.colorPrimary}`,
371
+ borderRadius: '2px',
372
+ padding: '1px 3px',
373
+ outline: 'none',
374
+ });
375
+ input.addEventListener('input', (e) => {
376
+ variables[varName] = e.target.value;
377
+ if (onChange)
378
+ onChange({ key: 'content', value: JSON.stringify(variables) });
379
+ refreshExpressionSpans(textBlock, variables, pageContext);
380
+ });
381
+ row.appendChild(label);
382
+ row.appendChild(input);
383
+ varSection.appendChild(row);
384
+ }
385
+ rootElement.insertBefore(varSection, textBlock);
386
+ }
387
+ for (const token of tokens) {
388
+ if (token.type === 'text') {
389
+ const span = document.createElement('span');
390
+ span.style.letterSpacing = 'inherit';
391
+ span.textContent = token.text;
392
+ textBlock.appendChild(span);
393
+ }
394
+ else if (token.type === 'simpleVar') {
395
+ const varName = token.name;
396
+ const span = document.createElement('span');
397
+ span.style.outline = `${theme.colorPrimary} dashed 1px`;
398
+ makeElementPlainTextContentEditable(span);
399
+ span.textContent = variables[varName] ?? '';
400
+ span.addEventListener('blur', (e) => {
401
+ const newValue = e.target.textContent || '';
402
+ if (newValue !== variables[varName]) {
403
+ variables[varName] = newValue;
404
+ if (onChange)
405
+ onChange({ key: 'content', value: JSON.stringify(variables) });
406
+ if (stopEditing)
407
+ stopEditing();
408
+ refreshExpressionSpans(textBlock, variables, pageContext);
409
+ }
410
+ });
411
+ textBlock.appendChild(span);
412
+ }
413
+ else {
414
+ // Complex expression — show evaluated result as static span with data-expr for re-evaluation
415
+ const span = document.createElement('span');
416
+ span.style.letterSpacing = 'inherit';
417
+ span.textContent = token.evaluated;
418
+ span.setAttribute('data-expr', token.raw);
419
+ textBlock.appendChild(span);
420
+ }
421
+ }
422
+ };
423
+ // Simple identifier regex — matches {name}, {orderId}, etc. but NOT {price * 1.1}
424
+ const SIMPLE_VAR_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
425
+ /** Tokenize a template string into static text, simple-identifier vars, and complex expressions. */
426
+ const tokenizeTemplate = (rawText, variables, pageCtx) => {
427
+ const tokens = [];
428
+ let i = 0;
429
+ while (i < rawText.length) {
430
+ if (rawText[i] !== '{') {
431
+ const start = i;
432
+ while (i < rawText.length && rawText[i] !== '{')
433
+ i++;
434
+ tokens.push({ type: 'text', text: rawText.slice(start, i) });
435
+ }
436
+ else {
437
+ // Find matching closing brace (handle nested braces)
438
+ let depth = 1;
439
+ let j = i + 1;
440
+ while (j < rawText.length && depth > 0) {
441
+ if (rawText[j] === '{')
442
+ depth++;
443
+ else if (rawText[j] === '}')
444
+ depth--;
445
+ j++;
446
+ }
447
+ const inner = rawText.slice(i + 1, j - 1);
448
+ if (SIMPLE_VAR_RE.test(inner)) {
449
+ tokens.push({ type: 'simpleVar', name: inner });
450
+ }
451
+ else {
452
+ // Evaluate the expression using substituteVariables on just this segment
453
+ const evaluated = substituteVariables(`{${inner}}`, variables, pageCtx);
454
+ tokens.push({ type: 'expression', evaluated, raw: inner });
455
+ }
456
+ i = j;
457
+ }
458
+ }
459
+ return tokens;
460
+ };
461
+ /** Reserved names — JS globals and expression builtins (must match propPanel's set) */
462
+ const RESERVED_NAMES = new Set([
463
+ 'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'in',
464
+ 'void', 'delete', 'new', 'this', 'NaN', 'Infinity',
465
+ 'Math', 'String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'JSON',
466
+ 'isNaN', 'parseFloat', 'parseInt', 'decodeURI', 'decodeURIComponent',
467
+ 'encodeURI', 'encodeURIComponent', 'date', 'dateTime',
468
+ 'currentPage', 'totalPages',
469
+ ]);
470
+ /** Count unique user-defined variable identifiers in all {...} blocks */
471
+ const countUniqueVariableNames = (content) => {
472
+ const blockRegex = /\{([^{}]+)\}/g;
473
+ const allIds = new Set();
474
+ let blockMatch;
475
+ while ((blockMatch = blockRegex.exec(content)) !== null) {
476
+ const cleaned = blockMatch[1].replace(/'[^']*'|"[^"]*"|`[^`]*`/g, '');
477
+ const tokenRegex = /\.?[a-zA-Z_$][a-zA-Z0-9_$]*/g;
478
+ let m;
479
+ while ((m = tokenRegex.exec(cleaned)) !== null) {
480
+ const token = m[0];
481
+ if (!token.startsWith('.') && !RESERVED_NAMES.has(token)) {
482
+ allIds.add(token);
483
+ }
484
+ }
485
+ }
486
+ return allIds.size;
487
+ };
488
+ /**
489
+ * An optimisation to try to minimise jank while typing.
490
+ * Only check whether variables were modified based on certain key presses.
491
+ * Regex would otherwise be performed on every key press (which isn't terrible, but this code helps).
492
+ */
493
+ const keyPressShouldBeChecked = (event) => {
494
+ if (event.key === 'ArrowUp' ||
495
+ event.key === 'ArrowDown' ||
496
+ event.key === 'ArrowLeft' ||
497
+ event.key === 'ArrowRight') {
498
+ return false;
499
+ }
500
+ const selection = window.getSelection();
501
+ const contenteditable = event.target;
502
+ const isCursorAtEnd = selection?.focusOffset === contenteditable?.textContent?.length;
503
+ if (isCursorAtEnd) {
504
+ return event.key === '}' || event.key === 'Backspace' || event.key === 'Delete';
505
+ }
506
+ const isCursorAtStart = selection?.anchorOffset === 0;
507
+ if (isCursorAtStart) {
508
+ return event.key === '{' || event.key === 'Backspace' || event.key === 'Delete';
509
+ }
510
+ return true;
511
+ };
512
+
513
+ const schema$3 = {
56
514
  pdf: pdfRender$2,
57
515
  ui: uiRender$2,
58
516
  propPanel: propPanel$1,
59
- icon: createSvgStr(TextCursorInput),
517
+ icon: createSvgStr(Type),
518
+ uninterruptedEditMode: true,
60
519
  };
61
520
 
62
521
  const isValidSVG = (svgString) => {
@@ -199,7 +658,7 @@ const barcodeDefaults = [
199
658
  defaultSchema: {
200
659
  name: '',
201
660
  type: 'qrcode',
202
- content: 'https://pdfme.com/',
661
+ content: '',
203
662
  position,
204
663
  ...defaultColors,
205
664
  width: 30,
@@ -1600,13 +2059,13 @@ const schema$2 = {
1600
2059
  pdf: textSchema.pdf,
1601
2060
  propPanel: {
1602
2061
  ...textSchema.propPanel,
1603
- widgets: { ...propPanel$1.widgets, addOptions },
2062
+ widgets: { ...propPanel$2.widgets, addOptions },
1604
2063
  schema: (propPanelProps) => {
1605
- if (typeof propPanel$1.schema !== 'function') {
2064
+ if (typeof propPanel$2.schema !== 'function') {
1606
2065
  throw Error('Oops, is text schema no longer a function?');
1607
2066
  }
1608
2067
  // Safely call the parent schema function with proper type checking
1609
- const parentSchema = propPanel$1.schema(propPanelProps);
2068
+ const parentSchema = propPanel$2.schema(propPanelProps);
1610
2069
  // Create a type-safe return object
1611
2070
  return {
1612
2071
  ...parentSchema,
@@ -1764,4 +2223,4 @@ const schema = {
1764
2223
  icon: getCheckedIcon(),
1765
2224
  };
1766
2225
 
1767
- export { barcodes, schema as checkbox, date, dateTime, schema$1 as radioGroup, rectangle, schema$2 as select, svgSchema as svg, tableSchema as table, textSchema as text, time };
2226
+ export { barcodes, schema as checkbox, date, dateTime, schema$3 as multiVariableText, schema$1 as radioGroup, rectangle, schema$2 as select, svgSchema as svg, tableSchema as table, textSchema as text, time };