protae5544 commited on
Commit
47d80d9
·
verified ·
1 Parent(s): 9a9a400

ทำให้ปรับแต่งได้ยึดหยุ่นกว่านี้สามารถนำเข้าไฟล์แสดงผลและบันทึกได้จากหน้าเว็บ

Browse files
Files changed (2) hide show
  1. index.html +23 -24
  2. script.js +217 -9
index.html CHANGED
@@ -59,40 +59,39 @@
59
  <div class="template-viewer flex flex-col items-center gap-8 pb-20" id="template-view">
60
 
61
  <!-- Page 1 -->
62
- <div id="page1" class="page bg-white shadow-2xl relative overflow-hidden origin-top transition-transform duration-200">
63
  <img id="bg1" loading="lazy" class="absolute inset-0 w-full h-full object-cover pointer-events-none z-0" src="" alt="BG 1">
64
- <img id="photo" loading="lazy" class="field absolute border border-slate-200 z-10 rounded object-cover bg-slate-100" style="top:46px;left:725px;width:110px;height:138px;" src="https://via.placeholder.com/110x138?text=Photo" alt="Photo">
65
- <img id="qr1" loading="lazy" class="field absolute z-10 bg-white" style="top:977px;left:762.5px;width:69px;height:69px;" src="" alt="QR 1">
66
 
67
  <!-- Dynamic Text Fields -->
68
- <div id="f1" class="field absolute whitespace-nowrap z-20 font-sans font-bold cursor-default select-none border border-transparent" style="top:80px;left:230px;font-size:16px;">f1</div>
69
- <div id="f2" class="field absolute whitespace-nowrap z-20 font-sans font-bold cursor-default select-none border border-transparent" style="top:110px;left:230px;font-size:16px;">f2</div>
70
- <div id="f3" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:150px;left:230px;font-size:14px;">f3</div>
71
- <div id="f4" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:180px;left:230px;font-size:14px;">f4</div>
72
- <div id="f5" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:180px;left:580px;font-size:14px;">f5</div>
73
- <div id="f6" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:210px;left:580px;font-size:14px;">f6</div>
74
- <div id="f7" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:240px;left:230px;font-size:14px;">f7</div>
75
- <div id="f8" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:240px;left:580px;font-size:14px;">f8</div>
76
- <div id="f9" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:270px;left:230px;font-size:14px;">f9</div>
77
- <div id="f10" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:300px;left:230px;font-size:14px;">f10</div>
78
  </div>
79
 
80
  <!-- Page 2 -->
81
- <div id="page2" class="page bg-white shadow-2xl relative overflow-hidden origin-top transition-transform duration-200">
82
  <img id="bg2" loading="lazy" class="absolute inset-0 w-full h-full object-cover pointer-events-none z-0" src="" alt="BG 2">
83
- <img id="qr2" loading="lazy" class="field absolute z-10 bg-white" style="top:925px;left:120px;width:90px;height:90px;" src="" alt="QR 2">
84
 
85
- <div id="f12" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:60px;left:200px;font-size:19px;">f12</div>
86
- <div id="f13" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:100px;left:200px;font-size:19px;">f13</div>
87
- <div id="f14" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:140px;left:200px;font-size:19px;">f14</div>
88
- <div id="f15" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:180px;left:200px;font-size:19px;">f15</div>
89
- <div id="f16" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:180px;left:550px;font-size:19px;">f16</div>
90
- <div id="f17" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:220px;left:200px;font-size:19px;">f17</div>
91
- <div id="f18" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:220px;left:550px;font-size:19px;">f18</div>
92
  </div>
93
  </div>
94
-
95
- <!-- PDF View -->
96
  <div class="pdf-viewer hidden flex-col items-center gap-6 pb-20" id="pdf-view">
97
  <div id="pdf-content" class="flex flex-col items-center"></div>
98
  </div>
 
59
  <div class="template-viewer flex flex-col items-center gap-8 pb-20" id="template-view">
60
 
61
  <!-- Page 1 -->
62
+ <div id="page1" class="page bg-white shadow-2xl relative overflow-hidden origin-top transition-transform duration-200" data-page="1">
63
  <img id="bg1" loading="lazy" class="absolute inset-0 w-full h-full object-cover pointer-events-none z-0" src="" alt="BG 1">
64
+ <img id="photo" loading="lazy" class="field absolute border border-slate-200 z-10 rounded object-cover bg-slate-100" style="top:46px;left:725px;width:110px;height:138px;" src="https://via.placeholder.com/110x138?text=Photo" draggable="true" data-field-type="image" alt="Photo">
65
+ <img id="qr1" loading="lazy" class="field absolute z-10 bg-white" style="top:977px;left:762.5px;width:69px;height:69px;" src="" draggable="true" data-field-type="image" alt="QR 1">
66
 
67
  <!-- Dynamic Text Fields -->
68
+ <div id="f1" class="field absolute whitespace-nowrap z-20 font-sans font-bold cursor-default select-none border border-transparent" style="top:80px;left:230px;font-size:16px;" draggable="true" data-field-type="text">f1</div>
69
+ <div id="f2" class="field absolute whitespace-nowrap z-20 font-sans font-bold cursor-default select-none border border-transparent" style="top:110px;left:230px;font-size:16px;" draggable="true" data-field-type="text">f2</div>
70
+ <div id="f3" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:150px;left:230px;font-size:14px;" draggable="true" data-field-type="text">f3</div>
71
+ <div id="f4" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:180px;left:230px;font-size:14px;" draggable="true" data-field-type="text">f4</div>
72
+ <div id="f5" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:180px;left:580px;font-size:14px;" draggable="true" data-field-type="text">f5</div>
73
+ <div id="f6" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:210px;left:580px;font-size:14px;" draggable="true" data-field-type="text">f6</div>
74
+ <div id="f7" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:240px;left:230px;font-size:14px;" draggable="true" data-field-type="text">f7</div>
75
+ <div id="f8" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:240px;left:580px;font-size:14px;" draggable="true" data-field-type="text">f8</div>
76
+ <div id="f9" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:270px;left:230px;font-size:14px;" draggable="true" data-field-type="text">f9</div>
77
+ <div id="f10" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:300px;left:230px;font-size:14px;" draggable="true" data-field-type="text">f10</div>
78
  </div>
79
 
80
  <!-- Page 2 -->
81
+ <div id="page2" class="page bg-white shadow-2xl relative overflow-hidden origin-top transition-transform duration-200" data-page="2">
82
  <img id="bg2" loading="lazy" class="absolute inset-0 w-full h-full object-cover pointer-events-none z-0" src="" alt="BG 2">
83
+ <img id="qr2" loading="lazy" class="field absolute z-10 bg-white" style="top:925px;left:120px;width:90px;height:90px;" src="" draggable="true" data-field-type="image" alt="QR 2">
84
 
85
+ <div id="f12" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:60px;left:200px;font-size:19px;" draggable="true" data-field-type="text">f12</div>
86
+ <div id="f13" class="field absolute whitespace-nowrap z-20 font-sans font-normal cursor-default select-none border border-transparent" style="top:100px;left:200px;font-size:19px;" draggable="true" data-field-type="text">f13</div>
87
+ <div id="f14" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:140px;left:200px;font-size:19px;" draggable="true" data-field-type="text">f14</div>
88
+ <div id="f15" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:180px;left:200px;font-size:19px;" draggable="true" data-field-type="text">f15</div>
89
+ <div id="f16" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:180px;left:550px;font-size:19px;" draggable="true" data-field-type="text">f16</div>
90
+ <div id="f17" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:220px;left:200px;font-size:19px;" draggable="true" data-field-type="text">f17</div>
91
+ <div id="f18" class="field absolute whitespace-nowrap z-20 font-sans font-light cursor-default select-none border border-transparent" style="top:220px;left:550px;font-size:19px;" draggable="true" data-field-type="text">f18</div>
92
  </div>
93
  </div>
94
+ <!-- PDF View -->
 
95
  <div class="pdf-viewer hidden flex-col items-center gap-6 pb-20" id="pdf-view">
96
  <div id="pdf-content" class="flex flex-col items-center"></div>
97
  </div>
script.js CHANGED
@@ -1,3 +1,4 @@
 
1
  'use strict';
2
 
3
  /**
@@ -17,9 +18,14 @@ const state = {
17
  currentView: 'template', // 'template' | 'pdf'
18
  designMode: false,
19
  selectedFieldId: null,
20
- fieldConfig: {}
 
 
 
 
 
 
21
  };
22
-
23
  // --- Database Config ---
24
  const DB_NAME = 'PDFWizardDB';
25
  const STORE = 'docs';
@@ -263,6 +269,7 @@ function toggleDesignMode() {
263
  if(standardPanel) standardPanel.style.display = 'none';
264
  if(designPanel) designPanel.style.display = 'block';
265
  toast('Design Mode Active', 'info');
 
266
  } else {
267
  if(standardPanel) standardPanel.style.display = 'block';
268
  if(designPanel) designPanel.style.display = 'none';
@@ -271,6 +278,21 @@ function toggleDesignMode() {
271
  }
272
  }
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  function selectField(id) {
275
  if (!state.designMode) return;
276
  deselectField();
@@ -400,6 +422,7 @@ function onMouseUp() {
400
  // --- Layout Config ---
401
  function saveFieldConfig() {
402
  localStorage.setItem('pdf_field_config', JSON.stringify(state.fieldConfig));
 
403
  }
404
 
405
  function loadFieldConfig() {
@@ -414,8 +437,95 @@ function loadFieldConfig() {
414
  }
415
  });
416
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  }
418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  // --- File Handling ---
420
  function handleBgFile(page, file) {
421
  if (!file) return;
@@ -624,9 +734,10 @@ async function downloadPDF() {
624
  loading(true, 'Generating PDF...');
625
  try {
626
  const data = state.data[state.currentIndex];
 
627
  const element = document.createElement('div');
628
 
629
- // Prepare HTML snapshot
630
  element.style.width = '892px';
631
  element.style.position = 'absolute';
632
  element.style.left = '-9999px';
@@ -650,14 +761,11 @@ async function downloadPDF() {
650
  if(state.bg2B64) bg2.src = state.bg2B64;
651
 
652
  // Populate Data
653
- const f1 = element.querySelector('#f1');
654
- // ... (simplified population, assuming showData already updated DOM, but we are cloning)
655
- // Better to rely on the DOM state before clone or manually inject
656
  fieldIds.forEach(id => {
657
  const key = fieldToKey[id];
658
  const val = data[key] || '';
659
  const el = element.querySelector('#'+id);
660
- if(el) el.innerHTML = val ? `<b>${val}</b>` : '';
661
  });
662
 
663
  const photo = element.querySelector('#photo');
@@ -667,7 +775,7 @@ async function downloadPDF() {
667
 
668
  const opt = {
669
  margin: 0,
670
- filename: `${data['1'] || 'doc'}.pdf`,
671
  image: { type: 'jpeg', quality: 0.98 },
672
  html2canvas: { scale: 2, useCORS: true },
673
  jsPDF: { unit: 'px', format: [892, 1261], orientation: 'portrait' }
@@ -675,7 +783,18 @@ async function downloadPDF() {
675
 
676
  await html2pdf().set(opt).from(element).save();
677
  document.body.removeChild(element);
678
- toast('PDF Downloaded', 'success');
 
 
 
 
 
 
 
 
 
 
 
679
  } catch (e) {
680
  toast('Generation Failed', 'error');
681
  debugLog(e);
@@ -684,6 +803,95 @@ async function downloadPDF() {
684
  }
685
  }
686
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  async function downloadAllPDFs() {
688
  if (state.data.length === 0) return;
689
  if (!confirm(`Download ${state.data.length} PDFs?`)) return;
 
1
+
2
  'use strict';
3
 
4
  /**
 
18
  currentView: 'template', // 'template' | 'pdf'
19
  designMode: false,
20
  selectedFieldId: null,
21
+ fieldConfig: {},
22
+ currentLayoutConfig: {
23
+ fields: {},
24
+ pages: [],
25
+ metadata: {}
26
+ },
27
+ isLayoutLoaded: false
28
  };
 
29
  // --- Database Config ---
30
  const DB_NAME = 'PDFWizardDB';
31
  const STORE = 'docs';
 
269
  if(standardPanel) standardPanel.style.display = 'none';
270
  if(designPanel) designPanel.style.display = 'block';
271
  toast('Design Mode Active', 'info');
272
+ updateFieldPositionsFromState();
273
  } else {
274
  if(standardPanel) standardPanel.style.display = 'block';
275
  if(designPanel) designPanel.style.display = 'none';
 
278
  }
279
  }
280
 
281
+ function updateFieldPositionsFromState() {
282
+ if (state.currentLayoutConfig.fields) {
283
+ Object.keys(state.currentLayoutConfig.fields).forEach(fieldId => {
284
+ const field = state.currentLayoutConfig.fields[fieldId];
285
+ const element = document.getElementById(fieldId);
286
+ if (element && field.position) {
287
+ element.style.top = field.position.top;
288
+ element.style.left = field.position.left;
289
+ if (field.style) {
290
+ Object.assign(element.style, field.style);
291
+ }
292
+ }
293
+ });
294
+ }
295
+ }
296
  function selectField(id) {
297
  if (!state.designMode) return;
298
  deselectField();
 
422
  // --- Layout Config ---
423
  function saveFieldConfig() {
424
  localStorage.setItem('pdf_field_config', JSON.stringify(state.fieldConfig));
425
+ saveLayoutToStorage();
426
  }
427
 
428
  function loadFieldConfig() {
 
437
  }
438
  });
439
  }
440
+ loadLayoutFromStorage();
441
+ }
442
+
443
+ function saveLayoutToStorage() {
444
+ const layoutData = {
445
+ fields: {},
446
+ pages: [],
447
+ metadata: {
448
+ version: '1.0',
449
+ savedAt: new Date().toISOString(),
450
+ name: state.currentLayoutName || 'default'
451
+ }
452
+ };
453
+
454
+ // Capture all field positions and styles
455
+ document.querySelectorAll('.field').forEach(field => {
456
+ layoutData.fields[field.id] = {
457
+ position: {
458
+ top: field.style.top,
459
+ left: field.style.left
460
+ },
461
+ style: {
462
+ fontSize: field.style.fontSize,
463
+ fontWeight: field.style.fontWeight,
464
+ color: field.style.color,
465
+ fontFamily: field.style.fontFamily
466
+ },
467
+ type: field.tagName === 'IMG' ? 'image' : 'text',
468
+ page: field.closest('.page')?.id || 'page1'
469
+ };
470
+ });
471
+
472
+ // Capture page configurations
473
+ ['page1', 'page2'].forEach(pageId => {
474
+ const page = document.getElementById(pageId);
475
+ if (page) {
476
+ layoutData.pages.push({
477
+ id: pageId,
478
+ background: page.querySelector('img[src*="bg"]')?.src || '',
479
+ dimensions: {
480
+ width: page.offsetWidth,
481
+ height: page.offsetHeight
482
+ }
483
+ });
484
+ }
485
+ });
486
+
487
+ state.currentLayoutConfig = layoutData;
488
+ return layoutData;
489
+ }
490
+
491
+ function loadLayoutFromStorage() {
492
+ const saved = localStorage.getItem('pdf_layout_config');
493
+ if (saved) {
494
+ try {
495
+ state.currentLayoutConfig = JSON.parse(saved);
496
+ applyLayoutConfig(state.currentLayoutConfig);
497
+ state.isLayoutLoaded = true;
498
+ } catch (e) {
499
+ console.warn('Failed to load layout config:', e);
500
+ }
501
+ }
502
  }
503
 
504
+ function applyLayoutConfig(config) {
505
+ // Apply field configurations
506
+ Object.keys(config.fields).forEach(fieldId => {
507
+ const fieldConfig = config.fields[fieldId];
508
+ const element = document.getElementById(fieldId);
509
+ if (element && fieldConfig.position) {
510
+ element.style.top = fieldConfig.position.top;
511
+ element.style.left = fieldConfig.position.left;
512
+ if (fieldConfig.style) {
513
+ Object.assign(element.style, fieldConfig.style);
514
+ }
515
+ }
516
+ });
517
+
518
+ // Apply page backgrounds
519
+ config.pages?.forEach(pageConfig => {
520
+ const pageElement = document.getElementById(pageConfig.id);
521
+ if (pageElement && pageConfig.background) {
522
+ const bgImg = pageElement.querySelector('img[id^="bg"]');
523
+ if (bgImg) {
524
+ bgImg.src = pageConfig.background;
525
+ }
526
+ }
527
+ });
528
+ }
529
  // --- File Handling ---
530
  function handleBgFile(page, file) {
531
  if (!file) return;
 
734
  loading(true, 'Generating PDF...');
735
  try {
736
  const data = state.data[state.currentIndex];
737
+ const layout = await saveLayoutToStorage(); // Capture current layout
738
  const element = document.createElement('div');
739
 
740
+ // Prepare HTML snapshot with current layout
741
  element.style.width = '892px';
742
  element.style.position = 'absolute';
743
  element.style.left = '-9999px';
 
761
  if(state.bg2B64) bg2.src = state.bg2B64;
762
 
763
  // Populate Data
 
 
 
764
  fieldIds.forEach(id => {
765
  const key = fieldToKey[id];
766
  const val = data[key] || '';
767
  const el = element.querySelector('#'+id);
768
+ if(el) el.innerHTML = val ? `<b>${sanitize(val)}</b>` : '';
769
  });
770
 
771
  const photo = element.querySelector('#photo');
 
775
 
776
  const opt = {
777
  margin: 0,
778
+ filename: `${data['1'] || state.currentLayoutName || 'doc'}.pdf`,
779
  image: { type: 'jpeg', quality: 0.98 },
780
  html2canvas: { scale: 2, useCORS: true },
781
  jsPDF: { unit: 'px', format: [892, 1261], orientation: 'portrait' }
 
783
 
784
  await html2pdf().set(opt).from(element).save();
785
  document.body.removeChild(element);
786
+
787
+ // Save layout to history
788
+ const doc = {
789
+ id: Date.now().toString(),
790
+ fileName: data['1'] ? `${data['1']}.pdf` : 'generated.pdf',
791
+ date: new Date().toISOString(),
792
+ layout: layout,
793
+ data: data
794
+ };
795
+ await saveDoc(doc);
796
+
797
+ toast('PDF Downloaded & Layout Saved', 'success');
798
  } catch (e) {
799
  toast('Generation Failed', 'error');
800
  debugLog(e);
 
803
  }
804
  }
805
 
806
+ async function exportLayout() {
807
+ const layout = await saveLayoutToStorage();
808
+ const blob = new Blob([JSON.stringify(layout, null, 2)], { type: 'application/json' });
809
+ const link = document.createElement('a');
810
+ link.href = URL.createObjectURL(blob);
811
+ link.download = `${layout.metadata.name || 'layout'}_${new Date().getTime()}.json`;
812
+ link.click();
813
+ toast('Layout Exported', 'success');
814
+ }
815
+
816
+ async function importLayout(file) {
817
+ if (!file || !file.name.endsWith('.json')) {
818
+ toast('Please select a layout JSON file', 'error');
819
+ return;
820
+ }
821
+
822
+ const reader = new FileReader();
823
+ reader.onload = async (e) => {
824
+ try {
825
+ const layout = JSON.parse(e.target.result);
826
+ if (!layout.fields || !layout.pages) {
827
+ throw new Error('Invalid layout format');
828
+ }
829
+ state.currentLayoutConfig = layout;
830
+ applyLayoutConfig(layout);
831
+
832
+ // Update form inputs
833
+ document.getElementById('layout-name-input').value = layout.metadata?.name || 'Unnamed Layout';
834
+
835
+ toast('Layout Imported Successfully', 'success');
836
+ state.isLayoutLoaded = true;
837
+ } catch (err) {
838
+ toast('Failed to import layout: ' + err.message, 'error');
839
+ debugLog(err);
840
+ }
841
+ };
842
+ reader.readAsText(file);
843
+ }
844
+
845
+ async function saveLayoutToDB() {
846
+ try {
847
+ const layout = saveLayoutToStorage();
848
+ const doc = {
849
+ id: `layout_${Date.now()}`,
850
+ fileName: `${layout.metadata.name}_layout.json`,
851
+ date: new Date().toISOString(),
852
+ type: 'layout',
853
+ data: layout,
854
+ metadata: layout.metadata
855
+ };
856
+ await saveDoc(doc);
857
+ toast('Layout Saved to History', 'success');
858
+ return layout;
859
+ } catch (err) {
860
+ toast('Failed to save layout', 'error');
861
+ debugLog(err);
862
+ return null;
863
+ }
864
+ }
865
+
866
+ function newLayout() {
867
+ const confirmNew = confirm('Create new layout? Unsaved changes will be lost.');
868
+ if (!confirmNew) return;
869
+
870
+ // Reset all fields to default positions
871
+ document.querySelectorAll('.field').forEach(field => {
872
+ field.style.top = '';
873
+ field.style.left = '';
874
+ field.style.fontSize = '';
875
+ field.style.fontWeight = '';
876
+ field.style.color = '';
877
+ field.classList.remove('selected');
878
+ });
879
+
880
+ state.fieldConfig = {};
881
+ state.currentLayoutConfig = {
882
+ fields: {},
883
+ pages: [],
884
+ metadata: {}
885
+ };
886
+ state.isLayoutLoaded = false;
887
+ state.currentLayoutName = 'New Layout';
888
+
889
+ deselectField();
890
+ localStorage.removeItem('pdf_field_config');
891
+ localStorage.removeItem('pdf_layout_config');
892
+
893
+ toast('New Layout Created', 'success');
894
+ }
895
  async function downloadAllPDFs() {
896
  if (state.data.length === 0) return;
897
  if (!confirm(`Download ${state.data.length} PDFs?`)) return;