From c0bc2ba7af42a22b677b044b0907f43ef90e50ff Mon Sep 17 00:00:00 2001 From: hand-dot Date: Mon, 18 May 2026 12:20:46 +0900 Subject: [PATCH] feat(playground): standardize template metadata --- playground/e2e/authoringStarterFixtures.ts | 10 +- playground/e2e/fileWorkspace.test.ts | 9 +- .../template-assets/a4-blank/metadata.json | 6 +- .../address-label-10/metadata.json | 2 + .../address-label-30/metadata.json | 2 + .../address-label-6/metadata.json | 2 + .../certificate-black/metadata.json | 6 +- .../certificate-blue/metadata.json | 2 + .../certificate-gold/metadata.json | 2 + .../certificate-white/metadata.json | 2 + .../metadata.json | 2 + .../inline-markdown-mvt/metadata.json | 2 + .../invoice-blue/metadata.json | 2 + .../invoice-green/metadata.json | 2 + .../invoice-ja-simple-landscape/metadata.json | 2 + .../invoice-ja-simple/metadata.json | 2 + .../invoice-white/metadata.json | 2 + .../template-assets/invoice/metadata.json | 6 +- .../location-arrow/metadata.json | 2 + .../location-number/metadata.json | 2 + .../public/template-assets/manifest.json | 246 ++++++++-------- .../template-assets/manifests/6.1.2.json | 246 ++++++++-------- .../new-sale-quotation/metadata.json | 2 + .../template-assets/pedigree/metadata.json | 6 +- .../template-assets/qr-lines/metadata.json | 6 +- .../template-assets/qr-title/metadata.json | 2 + .../template-assets/quotes/metadata.json | 6 +- .../z-fold-brochure/metadata.json | 2 + .../scripts/generate-templates-list-json.mjs | 26 +- playground/src/helper.ts | 53 +++- playground/src/lib/authoringStarters.ts | 10 +- playground/src/lib/fileWorkspace.ts | 9 +- playground/src/routes/Designer.tsx | 25 +- playground/src/routes/FormAndViewer.tsx | 33 ++- playground/src/routes/Templates.tsx | 268 ++++++++++-------- 35 files changed, 616 insertions(+), 391 deletions(-) diff --git a/playground/e2e/authoringStarterFixtures.ts b/playground/e2e/authoringStarterFixtures.ts index ec1b2f4d..2ccc8fcc 100644 --- a/playground/e2e/authoringStarterFixtures.ts +++ b/playground/e2e/authoringStarterFixtures.ts @@ -26,17 +26,17 @@ export const readAuthoringStarterFixtures = (kind: 'jsx' | 'md2pdf'): AuthoringS if (!fs.existsSync(sourcePath) || !fs.existsSync(metadataPath)) return []; const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8')) as { - description?: string; - sourceKind?: string; - title?: string; + description: string; + sourceKind: string; + title: string; }; if (metadata.sourceKind !== kind) return []; return [ { assetName: entry.name, - description: metadata.description ?? '', - label: metadata.title ?? entry.name, + description: metadata.description, + label: metadata.title, source: fs.readFileSync(sourcePath, 'utf-8'), }, ]; diff --git a/playground/e2e/fileWorkspace.test.ts b/playground/e2e/fileWorkspace.test.ts index 447cb6a4..9eb303c5 100644 --- a/playground/e2e/fileWorkspace.test.ts +++ b/playground/e2e/fileWorkspace.test.ts @@ -110,7 +110,12 @@ describe('file workspace helpers', () => { invoice.addFile('template.json', serializeTemplateForFileWorkspace(blankTemplate)); invoice.addFile( 'metadata.json', - JSON.stringify({ description: 'Invoice template', tags: ['Invoice'], title: 'Invoice' }), + JSON.stringify({ + description: 'Invoice template', + sourceKind: 'designer', + tags: ['Invoice'], + title: 'Invoice', + }), ); root .addDirectory('.cache') @@ -197,6 +202,8 @@ describe('file workspace helpers', () => { const templateFile = await directory.getFileHandle('template.json'); expect(entry.name).toBe('untitled-template'); + expect(entry.sourceKind).toBe('designer'); + expect(entry.title).toBe('Untitled Template'); expect(templateFile.text).toContain('"schemas": ['); }); }); diff --git a/playground/public/template-assets/a4-blank/metadata.json b/playground/public/template-assets/a4-blank/metadata.json index 6733f0bc..8f8ba871 100644 --- a/playground/public/template-assets/a4-blank/metadata.json +++ b/playground/public/template-assets/a4-blank/metadata.json @@ -1,8 +1,10 @@ { - "order": 130, + "title": "A4 Blank", "description": "A clean blank A4 document for starting from scratch in Designer.", + "sourceKind": "designer", "tags": [ "Blank", "Starter" - ] + ], + "order": 130 } diff --git a/playground/public/template-assets/address-label-10/metadata.json b/playground/public/template-assets/address-label-10/metadata.json index ddd84cb2..34bb8347 100644 --- a/playground/public/template-assets/address-label-10/metadata.json +++ b/playground/public/template-assets/address-label-10/metadata.json @@ -1,5 +1,7 @@ { + "title": "Address Label 10", "description": "A 10-label address sheet for shipping and mailing workflows.", + "sourceKind": "designer", "tags": [ "Labels", "Shipping" diff --git a/playground/public/template-assets/address-label-30/metadata.json b/playground/public/template-assets/address-label-30/metadata.json index ed472d27..a26f4035 100644 --- a/playground/public/template-assets/address-label-30/metadata.json +++ b/playground/public/template-assets/address-label-30/metadata.json @@ -1,5 +1,7 @@ { + "title": "Address Label 30", "description": "A compact 30-label address sheet for dense mailing labels.", + "sourceKind": "designer", "tags": [ "Labels", "Shipping" diff --git a/playground/public/template-assets/address-label-6/metadata.json b/playground/public/template-assets/address-label-6/metadata.json index 717c114b..d20ef648 100644 --- a/playground/public/template-assets/address-label-6/metadata.json +++ b/playground/public/template-assets/address-label-6/metadata.json @@ -1,5 +1,7 @@ { + "title": "Address Label 6", "description": "A larger 6-label address sheet with room for longer addresses.", + "sourceKind": "designer", "tags": [ "Labels", "Shipping" diff --git a/playground/public/template-assets/certificate-black/metadata.json b/playground/public/template-assets/certificate-black/metadata.json index 735ee049..f64d6189 100644 --- a/playground/public/template-assets/certificate-black/metadata.json +++ b/playground/public/template-assets/certificate-black/metadata.json @@ -1,8 +1,10 @@ { - "order": 120, + "title": "Certificate Black", "description": "A high-contrast certificate layout with a formal dark theme.", + "sourceKind": "designer", "tags": [ "Certificate", "Award" - ] + ], + "order": 120 } diff --git a/playground/public/template-assets/certificate-blue/metadata.json b/playground/public/template-assets/certificate-blue/metadata.json index 3790fe7f..40bb4193 100644 --- a/playground/public/template-assets/certificate-blue/metadata.json +++ b/playground/public/template-assets/certificate-blue/metadata.json @@ -1,5 +1,7 @@ { + "title": "Certificate Blue", "description": "A polished blue certificate layout for awards and completion documents.", + "sourceKind": "designer", "tags": [ "Certificate", "Award" diff --git a/playground/public/template-assets/certificate-gold/metadata.json b/playground/public/template-assets/certificate-gold/metadata.json index a94d7491..1ed256aa 100644 --- a/playground/public/template-assets/certificate-gold/metadata.json +++ b/playground/public/template-assets/certificate-gold/metadata.json @@ -1,5 +1,7 @@ { + "title": "Certificate Gold", "description": "A warm gold certificate layout with a classic presentation style.", + "sourceKind": "designer", "tags": [ "Certificate", "Award" diff --git a/playground/public/template-assets/certificate-white/metadata.json b/playground/public/template-assets/certificate-white/metadata.json index 87869ab2..62807ccb 100644 --- a/playground/public/template-assets/certificate-white/metadata.json +++ b/playground/public/template-assets/certificate-white/metadata.json @@ -1,5 +1,7 @@ { + "title": "Certificate White", "description": "A minimal certificate layout that works well with light branding.", + "sourceKind": "designer", "tags": [ "Certificate", "Award" diff --git a/playground/public/template-assets/hihokensha-shikaku-shutoku-todoke/metadata.json b/playground/public/template-assets/hihokensha-shikaku-shutoku-todoke/metadata.json index cc6a2fb4..33fa4314 100644 --- a/playground/public/template-assets/hihokensha-shikaku-shutoku-todoke/metadata.json +++ b/playground/public/template-assets/hihokensha-shikaku-shutoku-todoke/metadata.json @@ -1,5 +1,7 @@ { + "title": "Social Insurance Enrollment Form", "description": "A Japanese social insurance form template for structured government-style documents.", + "sourceKind": "designer", "tags": [ "Government", "CJK", diff --git a/playground/public/template-assets/inline-markdown-mvt/metadata.json b/playground/public/template-assets/inline-markdown-mvt/metadata.json index aebb82fe..4357daed 100644 --- a/playground/public/template-assets/inline-markdown-mvt/metadata.json +++ b/playground/public/template-assets/inline-markdown-mvt/metadata.json @@ -1,5 +1,7 @@ { + "title": "Inline Markdown MVT", "description": "A focused demo of inline markdown and MultiVariableText editing.", + "sourceKind": "designer", "tags": [ "Markdown", "MVT", diff --git a/playground/public/template-assets/invoice-blue/metadata.json b/playground/public/template-assets/invoice-blue/metadata.json index 38a53d2c..e49f6faf 100644 --- a/playground/public/template-assets/invoice-blue/metadata.json +++ b/playground/public/template-assets/invoice-blue/metadata.json @@ -1,5 +1,7 @@ { + "title": "Invoice Blue", "description": "A blue invoice variant for a more branded business document.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", diff --git a/playground/public/template-assets/invoice-green/metadata.json b/playground/public/template-assets/invoice-green/metadata.json index de8c8b12..40690e2b 100644 --- a/playground/public/template-assets/invoice-green/metadata.json +++ b/playground/public/template-assets/invoice-green/metadata.json @@ -1,5 +1,7 @@ { + "title": "Invoice Green", "description": "A green invoice variant with a calm accounting-oriented look.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", diff --git a/playground/public/template-assets/invoice-ja-simple-landscape/metadata.json b/playground/public/template-assets/invoice-ja-simple-landscape/metadata.json index e22c917a..4c926237 100644 --- a/playground/public/template-assets/invoice-ja-simple-landscape/metadata.json +++ b/playground/public/template-assets/invoice-ja-simple-landscape/metadata.json @@ -1,5 +1,7 @@ { + "title": "Japanese Invoice Landscape", "description": "A landscape Japanese invoice layout for wider table content.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", diff --git a/playground/public/template-assets/invoice-ja-simple/metadata.json b/playground/public/template-assets/invoice-ja-simple/metadata.json index 5d5359d2..cce5f610 100644 --- a/playground/public/template-assets/invoice-ja-simple/metadata.json +++ b/playground/public/template-assets/invoice-ja-simple/metadata.json @@ -1,5 +1,7 @@ { + "title": "Japanese Invoice", "description": "A simple Japanese invoice layout with CJK font usage.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", diff --git a/playground/public/template-assets/invoice-white/metadata.json b/playground/public/template-assets/invoice-white/metadata.json index 6a4914c9..cf6d978f 100644 --- a/playground/public/template-assets/invoice-white/metadata.json +++ b/playground/public/template-assets/invoice-white/metadata.json @@ -1,5 +1,7 @@ { + "title": "Invoice White", "description": "A restrained white invoice layout with a clean printable style.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", diff --git a/playground/public/template-assets/invoice/metadata.json b/playground/public/template-assets/invoice/metadata.json index fe786efc..06e79272 100644 --- a/playground/public/template-assets/invoice/metadata.json +++ b/playground/public/template-assets/invoice/metadata.json @@ -1,10 +1,12 @@ { - "order": 90, + "title": "Invoice", "description": "A practical invoice with customer details, line items, totals, and payment notes.", + "sourceKind": "designer", "tags": [ "Invoice", "Business", "Table", "Visual" - ] + ], + "order": 90 } diff --git a/playground/public/template-assets/location-arrow/metadata.json b/playground/public/template-assets/location-arrow/metadata.json index c641fd65..26758b40 100644 --- a/playground/public/template-assets/location-arrow/metadata.json +++ b/playground/public/template-assets/location-arrow/metadata.json @@ -1,5 +1,7 @@ { + "title": "Location Arrow", "description": "A location marker template that highlights points with arrow indicators.", + "sourceKind": "designer", "tags": [ "Map", "Visual" diff --git a/playground/public/template-assets/location-number/metadata.json b/playground/public/template-assets/location-number/metadata.json index 83a8b943..9997e41e 100644 --- a/playground/public/template-assets/location-number/metadata.json +++ b/playground/public/template-assets/location-number/metadata.json @@ -1,5 +1,7 @@ { + "title": "Location Number", "description": "A location marker template that labels points with numbered badges.", + "sourceKind": "designer", "tags": [ "Map", "Visual" diff --git a/playground/public/template-assets/manifest.json b/playground/public/template-assets/manifest.json index 27a118e6..9ca3a0b4 100644 --- a/playground/public/template-assets/manifest.json +++ b/playground/public/template-assets/manifest.json @@ -27,7 +27,8 @@ "Business", "Table", "Visual" - ] + ], + "title": "Invoice" }, { "name": "quotes", @@ -54,7 +55,8 @@ "Business", "Table", "Visual" - ] + ], + "title": "Quotes" }, { "name": "pedigree", @@ -80,7 +82,8 @@ "QR", "Image", "Visual" - ] + ], + "title": "Pedigree" }, { "name": "certificate-black", @@ -103,7 +106,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Black" }, { "name": "a4-blank", @@ -122,7 +126,8 @@ "tags": [ "Blank", "Starter" - ] + ], + "title": "A4 Blank" }, { "name": "qr-lines", @@ -144,7 +149,8 @@ "tags": [ "QR", "Label" - ] + ], + "title": "QR Lines" }, { "name": "address-label-10", @@ -164,7 +170,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 10" }, { "name": "address-label-30", @@ -184,7 +191,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 30" }, { "name": "address-label-6", @@ -204,7 +212,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 6" }, { "name": "md2pdf-article", @@ -251,7 +260,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Blue" }, { "name": "certificate-gold", @@ -273,7 +283,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Gold" }, { "name": "certificate-white", @@ -295,7 +306,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate White" }, { "name": "jsx-form-fields", @@ -325,29 +337,6 @@ ], "title": "Form fields" }, - { - "name": "hihokensha-shikaku-shutoku-todoke", - "author": "EedgeY", - "path": "hihokensha-shikaku-shutoku-todoke/template.json", - "thumbnailPath": "hihokensha-shikaku-shutoku-todoke/thumbnail.png", - "pageCount": 1, - "fieldCount": 104, - "schemaTypes": [ - "text" - ], - "fontNames": [ - "NotoSansJP" - ], - "hasCJK": true, - "basePdfKind": "dataUri", - "description": "A Japanese social insurance form template for structured government-style documents.", - "sourceKind": "designer", - "tags": [ - "Government", - "CJK", - "Form" - ] - }, { "name": "inline-markdown-mvt", "author": "pdfme", @@ -370,7 +359,52 @@ "Markdown", "MVT", "Form" - ] + ], + "title": "Inline Markdown MVT" + }, + { + "name": "invoice-blue", + "author": "pdfme", + "path": "invoice-blue/template.json", + "thumbnailPath": "invoice-blue/thumbnail.png", + "pageCount": 1, + "fieldCount": 35, + "schemaTypes": [ + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "dataUri", + "description": "A blue invoice variant for a more branded business document.", + "sourceKind": "designer", + "tags": [ + "Invoice", + "Business", + "Table" + ], + "title": "Invoice Blue" + }, + { + "name": "invoice-green", + "author": "pdfme", + "path": "invoice-green/template.json", + "thumbnailPath": "invoice-green/thumbnail.png", + "pageCount": 1, + "fieldCount": 25, + "schemaTypes": [ + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "dataUri", + "description": "A green invoice variant with a calm accounting-oriented look.", + "sourceKind": "designer", + "tags": [ + "Invoice", + "Business", + "Table" + ], + "title": "Invoice Green" }, { "name": "jsx-invoice", @@ -406,10 +440,10 @@ "title": "Invoice layout" }, { - "name": "invoice-blue", + "name": "invoice-white", "author": "pdfme", - "path": "invoice-blue/template.json", - "thumbnailPath": "invoice-blue/thumbnail.png", + "path": "invoice-white/template.json", + "thumbnailPath": "invoice-white/thumbnail.png", "pageCount": 1, "fieldCount": 35, "schemaTypes": [ @@ -418,34 +452,14 @@ "fontNames": [], "hasCJK": false, "basePdfKind": "dataUri", - "description": "A blue invoice variant for a more branded business document.", + "description": "A restrained white invoice layout with a clean printable style.", "sourceKind": "designer", "tags": [ "Invoice", "Business", "Table" - ] - }, - { - "name": "invoice-green", - "author": "pdfme", - "path": "invoice-green/template.json", - "thumbnailPath": "invoice-green/thumbnail.png", - "pageCount": 1, - "fieldCount": 25, - "schemaTypes": [ - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "dataUri", - "description": "A green invoice variant with a calm accounting-oriented look.", - "sourceKind": "designer", - "tags": [ - "Invoice", - "Business", - "Table" - ] + "title": "Invoice White" }, { "name": "invoice-ja-simple", @@ -472,7 +486,8 @@ "Invoice", "Business", "CJK" - ] + ], + "title": "Japanese Invoice" }, { "name": "invoice-ja-simple-landscape", @@ -499,28 +514,8 @@ "Invoice", "Business", "CJK" - ] - }, - { - "name": "invoice-white", - "author": "pdfme", - "path": "invoice-white/template.json", - "thumbnailPath": "invoice-white/thumbnail.png", - "pageCount": 1, - "fieldCount": 35, - "schemaTypes": [ - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "dataUri", - "description": "A restrained white invoice layout with a clean printable style.", - "sourceKind": "designer", - "tags": [ - "Invoice", - "Business", - "Table" - ] + "title": "Japanese Invoice Landscape" }, { "name": "jsx-japanese-notice", @@ -594,7 +589,8 @@ "tags": [ "Map", "Visual" - ] + ], + "title": "Location Arrow" }, { "name": "location-number", @@ -615,32 +611,8 @@ "tags": [ "Map", "Visual" - ] - }, - { - "name": "new-sale-quotation", - "author": "pdfme", - "path": "new-sale-quotation/template.json", - "thumbnailPath": "new-sale-quotation/thumbnail.png", - "pageCount": 3, - "fieldCount": 49, - "schemaTypes": [ - "image", - "line", - "rectangle", - "table", - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "blank", - "description": "A sales quotation template with product rows and business summary fields.", - "sourceKind": "designer", - "tags": [ - "Quote", - "Business", - "Table" - ] + "title": "Location Number" }, { "name": "md2pdf-overview", @@ -689,7 +661,8 @@ "tags": [ "QR", "Label" - ] + ], + "title": "QR Title" }, { "name": "md2pdf-release-notes", @@ -771,6 +744,56 @@ ], "title": "Research paper" }, + { + "name": "new-sale-quotation", + "author": "pdfme", + "path": "new-sale-quotation/template.json", + "thumbnailPath": "new-sale-quotation/thumbnail.png", + "pageCount": 3, + "fieldCount": 49, + "schemaTypes": [ + "image", + "line", + "rectangle", + "table", + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "blank", + "description": "A sales quotation template with product rows and business summary fields.", + "sourceKind": "designer", + "tags": [ + "Quote", + "Business", + "Table" + ], + "title": "Sales Quotation" + }, + { + "name": "hihokensha-shikaku-shutoku-todoke", + "author": "EedgeY", + "path": "hihokensha-shikaku-shutoku-todoke/template.json", + "thumbnailPath": "hihokensha-shikaku-shutoku-todoke/thumbnail.png", + "pageCount": 1, + "fieldCount": 104, + "schemaTypes": [ + "text" + ], + "fontNames": [ + "NotoSansJP" + ], + "hasCJK": true, + "basePdfKind": "dataUri", + "description": "A Japanese social insurance form template for structured government-style documents.", + "sourceKind": "designer", + "tags": [ + "Government", + "CJK", + "Form" + ], + "title": "Social Insurance Enrollment Form" + }, { "name": "z-fold-brochure", "author": "hitomi-t260g", @@ -796,7 +819,8 @@ "tags": [ "Brochure", "Print" - ] + ], + "title": "Z-Fold Brochure" } ] } \ No newline at end of file diff --git a/playground/public/template-assets/manifests/6.1.2.json b/playground/public/template-assets/manifests/6.1.2.json index 27a118e6..9ca3a0b4 100644 --- a/playground/public/template-assets/manifests/6.1.2.json +++ b/playground/public/template-assets/manifests/6.1.2.json @@ -27,7 +27,8 @@ "Business", "Table", "Visual" - ] + ], + "title": "Invoice" }, { "name": "quotes", @@ -54,7 +55,8 @@ "Business", "Table", "Visual" - ] + ], + "title": "Quotes" }, { "name": "pedigree", @@ -80,7 +82,8 @@ "QR", "Image", "Visual" - ] + ], + "title": "Pedigree" }, { "name": "certificate-black", @@ -103,7 +106,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Black" }, { "name": "a4-blank", @@ -122,7 +126,8 @@ "tags": [ "Blank", "Starter" - ] + ], + "title": "A4 Blank" }, { "name": "qr-lines", @@ -144,7 +149,8 @@ "tags": [ "QR", "Label" - ] + ], + "title": "QR Lines" }, { "name": "address-label-10", @@ -164,7 +170,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 10" }, { "name": "address-label-30", @@ -184,7 +191,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 30" }, { "name": "address-label-6", @@ -204,7 +212,8 @@ "tags": [ "Labels", "Shipping" - ] + ], + "title": "Address Label 6" }, { "name": "md2pdf-article", @@ -251,7 +260,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Blue" }, { "name": "certificate-gold", @@ -273,7 +283,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate Gold" }, { "name": "certificate-white", @@ -295,7 +306,8 @@ "tags": [ "Certificate", "Award" - ] + ], + "title": "Certificate White" }, { "name": "jsx-form-fields", @@ -325,29 +337,6 @@ ], "title": "Form fields" }, - { - "name": "hihokensha-shikaku-shutoku-todoke", - "author": "EedgeY", - "path": "hihokensha-shikaku-shutoku-todoke/template.json", - "thumbnailPath": "hihokensha-shikaku-shutoku-todoke/thumbnail.png", - "pageCount": 1, - "fieldCount": 104, - "schemaTypes": [ - "text" - ], - "fontNames": [ - "NotoSansJP" - ], - "hasCJK": true, - "basePdfKind": "dataUri", - "description": "A Japanese social insurance form template for structured government-style documents.", - "sourceKind": "designer", - "tags": [ - "Government", - "CJK", - "Form" - ] - }, { "name": "inline-markdown-mvt", "author": "pdfme", @@ -370,7 +359,52 @@ "Markdown", "MVT", "Form" - ] + ], + "title": "Inline Markdown MVT" + }, + { + "name": "invoice-blue", + "author": "pdfme", + "path": "invoice-blue/template.json", + "thumbnailPath": "invoice-blue/thumbnail.png", + "pageCount": 1, + "fieldCount": 35, + "schemaTypes": [ + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "dataUri", + "description": "A blue invoice variant for a more branded business document.", + "sourceKind": "designer", + "tags": [ + "Invoice", + "Business", + "Table" + ], + "title": "Invoice Blue" + }, + { + "name": "invoice-green", + "author": "pdfme", + "path": "invoice-green/template.json", + "thumbnailPath": "invoice-green/thumbnail.png", + "pageCount": 1, + "fieldCount": 25, + "schemaTypes": [ + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "dataUri", + "description": "A green invoice variant with a calm accounting-oriented look.", + "sourceKind": "designer", + "tags": [ + "Invoice", + "Business", + "Table" + ], + "title": "Invoice Green" }, { "name": "jsx-invoice", @@ -406,10 +440,10 @@ "title": "Invoice layout" }, { - "name": "invoice-blue", + "name": "invoice-white", "author": "pdfme", - "path": "invoice-blue/template.json", - "thumbnailPath": "invoice-blue/thumbnail.png", + "path": "invoice-white/template.json", + "thumbnailPath": "invoice-white/thumbnail.png", "pageCount": 1, "fieldCount": 35, "schemaTypes": [ @@ -418,34 +452,14 @@ "fontNames": [], "hasCJK": false, "basePdfKind": "dataUri", - "description": "A blue invoice variant for a more branded business document.", + "description": "A restrained white invoice layout with a clean printable style.", "sourceKind": "designer", "tags": [ "Invoice", "Business", "Table" - ] - }, - { - "name": "invoice-green", - "author": "pdfme", - "path": "invoice-green/template.json", - "thumbnailPath": "invoice-green/thumbnail.png", - "pageCount": 1, - "fieldCount": 25, - "schemaTypes": [ - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "dataUri", - "description": "A green invoice variant with a calm accounting-oriented look.", - "sourceKind": "designer", - "tags": [ - "Invoice", - "Business", - "Table" - ] + "title": "Invoice White" }, { "name": "invoice-ja-simple", @@ -472,7 +486,8 @@ "Invoice", "Business", "CJK" - ] + ], + "title": "Japanese Invoice" }, { "name": "invoice-ja-simple-landscape", @@ -499,28 +514,8 @@ "Invoice", "Business", "CJK" - ] - }, - { - "name": "invoice-white", - "author": "pdfme", - "path": "invoice-white/template.json", - "thumbnailPath": "invoice-white/thumbnail.png", - "pageCount": 1, - "fieldCount": 35, - "schemaTypes": [ - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "dataUri", - "description": "A restrained white invoice layout with a clean printable style.", - "sourceKind": "designer", - "tags": [ - "Invoice", - "Business", - "Table" - ] + "title": "Japanese Invoice Landscape" }, { "name": "jsx-japanese-notice", @@ -594,7 +589,8 @@ "tags": [ "Map", "Visual" - ] + ], + "title": "Location Arrow" }, { "name": "location-number", @@ -615,32 +611,8 @@ "tags": [ "Map", "Visual" - ] - }, - { - "name": "new-sale-quotation", - "author": "pdfme", - "path": "new-sale-quotation/template.json", - "thumbnailPath": "new-sale-quotation/thumbnail.png", - "pageCount": 3, - "fieldCount": 49, - "schemaTypes": [ - "image", - "line", - "rectangle", - "table", - "text" ], - "fontNames": [], - "hasCJK": false, - "basePdfKind": "blank", - "description": "A sales quotation template with product rows and business summary fields.", - "sourceKind": "designer", - "tags": [ - "Quote", - "Business", - "Table" - ] + "title": "Location Number" }, { "name": "md2pdf-overview", @@ -689,7 +661,8 @@ "tags": [ "QR", "Label" - ] + ], + "title": "QR Title" }, { "name": "md2pdf-release-notes", @@ -771,6 +744,56 @@ ], "title": "Research paper" }, + { + "name": "new-sale-quotation", + "author": "pdfme", + "path": "new-sale-quotation/template.json", + "thumbnailPath": "new-sale-quotation/thumbnail.png", + "pageCount": 3, + "fieldCount": 49, + "schemaTypes": [ + "image", + "line", + "rectangle", + "table", + "text" + ], + "fontNames": [], + "hasCJK": false, + "basePdfKind": "blank", + "description": "A sales quotation template with product rows and business summary fields.", + "sourceKind": "designer", + "tags": [ + "Quote", + "Business", + "Table" + ], + "title": "Sales Quotation" + }, + { + "name": "hihokensha-shikaku-shutoku-todoke", + "author": "EedgeY", + "path": "hihokensha-shikaku-shutoku-todoke/template.json", + "thumbnailPath": "hihokensha-shikaku-shutoku-todoke/thumbnail.png", + "pageCount": 1, + "fieldCount": 104, + "schemaTypes": [ + "text" + ], + "fontNames": [ + "NotoSansJP" + ], + "hasCJK": true, + "basePdfKind": "dataUri", + "description": "A Japanese social insurance form template for structured government-style documents.", + "sourceKind": "designer", + "tags": [ + "Government", + "CJK", + "Form" + ], + "title": "Social Insurance Enrollment Form" + }, { "name": "z-fold-brochure", "author": "hitomi-t260g", @@ -796,7 +819,8 @@ "tags": [ "Brochure", "Print" - ] + ], + "title": "Z-Fold Brochure" } ] } \ No newline at end of file diff --git a/playground/public/template-assets/new-sale-quotation/metadata.json b/playground/public/template-assets/new-sale-quotation/metadata.json index f8978d2a..3c5b05c4 100644 --- a/playground/public/template-assets/new-sale-quotation/metadata.json +++ b/playground/public/template-assets/new-sale-quotation/metadata.json @@ -1,5 +1,7 @@ { + "title": "Sales Quotation", "description": "A sales quotation template with product rows and business summary fields.", + "sourceKind": "designer", "tags": [ "Quote", "Business", diff --git a/playground/public/template-assets/pedigree/metadata.json b/playground/public/template-assets/pedigree/metadata.json index 98d69f62..2f26c037 100644 --- a/playground/public/template-assets/pedigree/metadata.json +++ b/playground/public/template-assets/pedigree/metadata.json @@ -1,10 +1,12 @@ { - "order": 110, + "title": "Pedigree", "description": "A pedigree-style relationship chart for structured family or lineage data.", + "sourceKind": "designer", "tags": [ "Chart", "QR", "Image", "Visual" - ] + ], + "order": 110 } diff --git a/playground/public/template-assets/qr-lines/metadata.json b/playground/public/template-assets/qr-lines/metadata.json index 6390ab51..2dfaeb70 100644 --- a/playground/public/template-assets/qr-lines/metadata.json +++ b/playground/public/template-assets/qr-lines/metadata.json @@ -1,8 +1,10 @@ { - "order": 140, + "title": "QR Lines", "description": "A QR code template with line-based metadata and compact supporting text.", + "sourceKind": "designer", "tags": [ "QR", "Label" - ] + ], + "order": 140 } diff --git a/playground/public/template-assets/qr-title/metadata.json b/playground/public/template-assets/qr-title/metadata.json index 5fde2616..c562dc02 100644 --- a/playground/public/template-assets/qr-title/metadata.json +++ b/playground/public/template-assets/qr-title/metadata.json @@ -1,5 +1,7 @@ { + "title": "QR Title", "description": "A QR code template with a prominent title and simple scan instructions.", + "sourceKind": "designer", "tags": [ "QR", "Label" diff --git a/playground/public/template-assets/quotes/metadata.json b/playground/public/template-assets/quotes/metadata.json index 481b3e07..607d7229 100644 --- a/playground/public/template-assets/quotes/metadata.json +++ b/playground/public/template-assets/quotes/metadata.json @@ -1,10 +1,12 @@ { - "order": 100, + "title": "Quotes", "description": "A quote document layout for proposals, estimates, and short commercial offers.", + "sourceKind": "designer", "tags": [ "Quote", "Business", "Table", "Visual" - ] + ], + "order": 100 } diff --git a/playground/public/template-assets/z-fold-brochure/metadata.json b/playground/public/template-assets/z-fold-brochure/metadata.json index edf9c406..32e9d7e5 100644 --- a/playground/public/template-assets/z-fold-brochure/metadata.json +++ b/playground/public/template-assets/z-fold-brochure/metadata.json @@ -1,5 +1,7 @@ { + "title": "Z-Fold Brochure", "description": "A z-fold brochure layout for tri-fold print and promotional material.", + "sourceKind": "designer", "tags": [ "Brochure", "Print" diff --git a/playground/scripts/generate-templates-list-json.mjs b/playground/scripts/generate-templates-list-json.mjs index d0160aa7..d20339e6 100644 --- a/playground/scripts/generate-templates-list-json.mjs +++ b/playground/scripts/generate-templates-list-json.mjs @@ -90,7 +90,9 @@ function normalizeMetadata(rawMetadata) { } const metadata = {}; - if (typeof rawMetadata.title === 'string') metadata.title = rawMetadata.title; + if (typeof rawMetadata.title === 'string' && rawMetadata.title.trim()) { + metadata.title = rawMetadata.title.trim(); + } if (typeof rawMetadata.description === 'string') metadata.description = rawMetadata.description; if (typeof rawMetadata.order === 'number' && Number.isFinite(rawMetadata.order)) { metadata.order = rawMetadata.order; @@ -127,15 +129,21 @@ function validateTemplateMetadata(metadataByTemplate, templateDirs) { for (const [name, rawMetadata] of Object.entries(metadataByTemplate)) { const metadata = normalizeMetadata(rawMetadata); + if (!metadata.title) { + throw new Error(`template asset metadata entry "${name}" must include title.`); + } if (!metadata.description) { throw new Error(`template asset metadata entry "${name}" must include description.`); } + if (!metadata.sourceKind) { + throw new Error(`template asset metadata entry "${name}" must include sourceKind.`); + } if (!metadata.tags || metadata.tags.length === 0) { throw new Error(`template asset metadata entry "${name}" must include tags.`); } const inferredSourceKind = inferSourceKind(name); - if (metadata.sourceKind && metadata.sourceKind !== inferredSourceKind) { + if (metadata.sourceKind !== inferredSourceKind) { throw new Error( `template asset metadata entry "${name}" has sourceKind "${metadata.sourceKind}", expected "${inferredSourceKind}".`, ); @@ -167,7 +175,13 @@ function buildTemplateEntry(name, templateJson, rawMetadata) { const schemas = normalizeSchemas(templateJson.schemas); const flattenedSchemas = schemas.flat(); const metadata = normalizeMetadata(rawMetadata); - const sourceKind = metadata.sourceKind ?? inferSourceKind(name); + if (!metadata.title) { + throw new Error(`template asset metadata entry "${name}" must include title.`); + } + if (!metadata.sourceKind) { + throw new Error(`template asset metadata entry "${name}" must include sourceKind.`); + } + const sourceKind = metadata.sourceKind; const schemaTypes = [ ...new Set(flattenedSchemas.map((schema) => schema.type).filter(Boolean)), ].sort(); @@ -190,7 +204,7 @@ function buildTemplateEntry(name, templateJson, rawMetadata) { description: metadata.description, order: metadata.order, sourceKind, - tags: metadata.tags ?? [], + tags: metadata.tags, title: metadata.title, }; } @@ -212,9 +226,7 @@ function compareTemplateEntries(a, b) { if (a.order != null) return -1; if (b.order != null) return 1; - const aTitle = a.title ?? a.name; - const bTitle = b.title ?? b.name; - const titleResult = aTitle.localeCompare(bTitle); + const titleResult = a.title.localeCompare(b.title); if (titleResult !== 0) return titleResult; return a.name.localeCompare(b.name); diff --git a/playground/src/helper.ts b/playground/src/helper.ts index f242feed..81f807fe 100644 --- a/playground/src/helper.ts +++ b/playground/src/helper.ts @@ -10,12 +10,20 @@ import { Form, Viewer, Designer } from '@pdfme/ui'; import { generate, generateForm } from '@pdfme/generator'; import { getPlugins } from './plugins'; -export function fromKebabCase(str: string): string { - return str - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} +const templateAssetSourceKinds = ['designer', 'jsx', 'md2pdf'] as const; + +type TemplateAssetSourceKind = (typeof templateAssetSourceKinds)[number]; + +export type TemplateAssetMetadata = { + description: string; + order?: number; + sourceKind: TemplateAssetSourceKind; + tags: string[]; + title: string; +}; + +const isTemplateAssetSourceKind = (value: unknown): value is TemplateAssetSourceKind => + templateAssetSourceKinds.includes(value as TemplateAssetSourceKind); export const getFontsData = (): Font => ({ ...getDefaultFont(), @@ -150,4 +158,37 @@ export const getTemplateById = async (templateId: string): Promise