@berlysia/vertical-writing-slide-system 0.0.32 → 0.1.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.
- package/package.json +16 -16
- package/src/types/slide-metadata.ts +2 -0
- package/src/vite-plugin-slides.ts +130 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@berlysia/vertical-writing-slide-system",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"vertical-slides": "./cli.js"
|
|
@@ -15,6 +15,20 @@
|
|
|
15
15
|
"src",
|
|
16
16
|
"index.html"
|
|
17
17
|
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "tsc -b && vite build",
|
|
21
|
+
"build:cli": "tsc --project tsconfig.cli.json",
|
|
22
|
+
"preversion": "pnpm build:cli",
|
|
23
|
+
"lint": "eslint .",
|
|
24
|
+
"preview": "vite preview",
|
|
25
|
+
"build:pages": "node scripts/build-pages.ts",
|
|
26
|
+
"test:vrt": "playwright test",
|
|
27
|
+
"test:vrt:clear": "rm -rf tests/__snapshots__",
|
|
28
|
+
"test:vrt:update": "playwright test --update-snapshots",
|
|
29
|
+
"ai:test:vrt": "AI=1 playwright test",
|
|
30
|
+
"ai:test:vrt:update": "AI=1 playwright test --update-snapshots"
|
|
31
|
+
},
|
|
18
32
|
"dependencies": {
|
|
19
33
|
"@emotion/react": "^11.14.0",
|
|
20
34
|
"@mdx-js/mdx": "^3.1.0",
|
|
@@ -55,19 +69,5 @@
|
|
|
55
69
|
"typescript": "~5.8.2",
|
|
56
70
|
"typescript-eslint": "^8.26.0",
|
|
57
71
|
"unist-util-visit": "^5.0.0"
|
|
58
|
-
},
|
|
59
|
-
"scripts": {
|
|
60
|
-
"dev": "vite",
|
|
61
|
-
"build": "tsc -b && vite build",
|
|
62
|
-
"build:cli": "tsc --project tsconfig.cli.json",
|
|
63
|
-
"preversion": "pnpm build:cli",
|
|
64
|
-
"lint": "eslint .",
|
|
65
|
-
"preview": "vite preview",
|
|
66
|
-
"build:pages": "node scripts/build-pages.ts",
|
|
67
|
-
"test:vrt": "playwright test",
|
|
68
|
-
"test:vrt:clear": "rm -rf tests/__snapshots__",
|
|
69
|
-
"test:vrt:update": "playwright test --update-snapshots",
|
|
70
|
-
"ai:test:vrt": "AI=1 playwright test",
|
|
71
|
-
"ai:test:vrt:update": "AI=1 playwright test --update-snapshots"
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}
|
|
@@ -94,6 +94,22 @@ function loadAdjacentCSS(slidesDir: string, collection: string): string[] {
|
|
|
94
94
|
return [];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* OGP画像ファイル(ogp.png)が存在するか確認
|
|
99
|
+
* 存在する場合はファイル名を返す
|
|
100
|
+
*/
|
|
101
|
+
function checkOgpImage(slidesDir: string, collection: string): string | null {
|
|
102
|
+
const collectionDir = path.resolve(slidesDir, collection);
|
|
103
|
+
const ogpPath = path.resolve(collectionDir, "ogp.png");
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(ogpPath)) {
|
|
106
|
+
logger.info("Found OGP image: ogp.png");
|
|
107
|
+
return "ogp.png";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
/**
|
|
98
114
|
* 隣接スクリプト設定ファイルを検索して読み込み
|
|
99
115
|
*/
|
|
@@ -332,6 +348,12 @@ export default async function slidesPlugin(
|
|
|
332
348
|
slideMetadata = parsedFile.metadata;
|
|
333
349
|
const slideContent = parsedFile.content;
|
|
334
350
|
|
|
351
|
+
// OGP画像を検出してメタデータに設定
|
|
352
|
+
const ogpImageFile = checkOgpImage(config.slidesDir, config.collection);
|
|
353
|
+
if (ogpImageFile) {
|
|
354
|
+
slideMetadata.ogImage = `${base}slide-assets/${ogpImageFile}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
335
357
|
logger.info(`Slide metadata: ${JSON.stringify(slideMetadata)}`);
|
|
336
358
|
|
|
337
359
|
// 隣接CSSファイルを読み込み
|
|
@@ -575,6 +597,29 @@ export default async function slidesPlugin(
|
|
|
575
597
|
} else {
|
|
576
598
|
logger.info(`No images directory found at: ${sourceImagesDir}`);
|
|
577
599
|
}
|
|
600
|
+
|
|
601
|
+
// OGP画像をコピー
|
|
602
|
+
const targetAssetsDir = isExternalCLI
|
|
603
|
+
? path.resolve(process.cwd(), "public/slide-assets")
|
|
604
|
+
: path.resolve(resolvedConfig.root, "public/slide-assets");
|
|
605
|
+
const sourceOgpPath = path.resolve(
|
|
606
|
+
config.slidesDir,
|
|
607
|
+
config.collection,
|
|
608
|
+
"ogp.png",
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
if (fs.existsSync(sourceOgpPath)) {
|
|
612
|
+
try {
|
|
613
|
+
mkdirSync(targetAssetsDir, { recursive: true });
|
|
614
|
+
const targetOgpPath = path.join(targetAssetsDir, "ogp.png");
|
|
615
|
+
await copyFile(sourceOgpPath, targetOgpPath);
|
|
616
|
+
logger.info("Copied OGP image: ogp.png");
|
|
617
|
+
} catch (error) {
|
|
618
|
+
if (error instanceof Error) {
|
|
619
|
+
logger.error("Failed to copy OGP image", error);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
578
623
|
}
|
|
579
624
|
},
|
|
580
625
|
|
|
@@ -609,6 +654,21 @@ export default async function slidesPlugin(
|
|
|
609
654
|
: "<!-- CSS is included in the JS bundle -->";
|
|
610
655
|
|
|
611
656
|
const pageTitle = slideMetadata?.title || "Vertical Writing Slides";
|
|
657
|
+
|
|
658
|
+
// OGPメタタグを生成
|
|
659
|
+
const ogpTags: string[] = [];
|
|
660
|
+
if (slideMetadata?.title) {
|
|
661
|
+
ogpTags.push(`<meta property="og:title" content="${slideMetadata.title}" />`);
|
|
662
|
+
}
|
|
663
|
+
if (slideMetadata?.description) {
|
|
664
|
+
ogpTags.push(`<meta property="og:description" content="${slideMetadata.description}" />`);
|
|
665
|
+
}
|
|
666
|
+
if (slideMetadata?.ogImage) {
|
|
667
|
+
ogpTags.push(`<meta property="og:image" content="${slideMetadata.ogImage}" />`);
|
|
668
|
+
ogpTags.push(`<meta name="twitter:card" content="summary_large_image" />`);
|
|
669
|
+
ogpTags.push(`<meta name="twitter:image" content="${slideMetadata.ogImage}" />`);
|
|
670
|
+
}
|
|
671
|
+
|
|
612
672
|
const virtualIndexHtml = `<!doctype html>
|
|
613
673
|
<html lang="ja">
|
|
614
674
|
<head>
|
|
@@ -617,6 +677,7 @@ export default async function slidesPlugin(
|
|
|
617
677
|
<title>${pageTitle}</title>
|
|
618
678
|
${slideMetadata?.description ? `<meta name="description" content="${slideMetadata.description}" />` : ""}
|
|
619
679
|
${slideMetadata?.author ? `<meta name="author" content="${slideMetadata.author}" />` : ""}
|
|
680
|
+
${ogpTags.join("\n ")}
|
|
620
681
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
621
682
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
622
683
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Noto+Sans+Mono:wght@100..900&display=swap" rel="stylesheet">
|
|
@@ -689,6 +750,30 @@ export default async function slidesPlugin(
|
|
|
689
750
|
} else {
|
|
690
751
|
logger.warn(`No images directory found at: ${sourceImagesDir}`);
|
|
691
752
|
}
|
|
753
|
+
|
|
754
|
+
// OGP画像をバンドルに追加
|
|
755
|
+
const sourceOgpPath = path.resolve(
|
|
756
|
+
config.slidesDir,
|
|
757
|
+
config.collection,
|
|
758
|
+
"ogp.png",
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
if (fs.existsSync(sourceOgpPath)) {
|
|
762
|
+
try {
|
|
763
|
+
const ogpContent = fs.readFileSync(sourceOgpPath);
|
|
764
|
+
this.emitFile({
|
|
765
|
+
type: "asset",
|
|
766
|
+
fileName: "slide-assets/ogp.png",
|
|
767
|
+
source: ogpContent,
|
|
768
|
+
});
|
|
769
|
+
logger.info("Added OGP image to bundle: ogp.png");
|
|
770
|
+
} catch (error) {
|
|
771
|
+
logger.error(
|
|
772
|
+
"Failed to add OGP image to bundle",
|
|
773
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
692
777
|
}
|
|
693
778
|
},
|
|
694
779
|
|
|
@@ -803,6 +888,51 @@ export default async function slidesPlugin(
|
|
|
803
888
|
});
|
|
804
889
|
}
|
|
805
890
|
|
|
891
|
+
// OGPメタタグを追加
|
|
892
|
+
if (slideMetadata?.title) {
|
|
893
|
+
tags.push({
|
|
894
|
+
tag: "meta",
|
|
895
|
+
attrs: {
|
|
896
|
+
property: "og:title",
|
|
897
|
+
content: slideMetadata.title,
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
if (slideMetadata?.description) {
|
|
903
|
+
tags.push({
|
|
904
|
+
tag: "meta",
|
|
905
|
+
attrs: {
|
|
906
|
+
property: "og:description",
|
|
907
|
+
content: slideMetadata.description,
|
|
908
|
+
},
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (slideMetadata?.ogImage) {
|
|
913
|
+
tags.push({
|
|
914
|
+
tag: "meta",
|
|
915
|
+
attrs: {
|
|
916
|
+
property: "og:image",
|
|
917
|
+
content: slideMetadata.ogImage,
|
|
918
|
+
},
|
|
919
|
+
});
|
|
920
|
+
tags.push({
|
|
921
|
+
tag: "meta",
|
|
922
|
+
attrs: {
|
|
923
|
+
name: "twitter:card",
|
|
924
|
+
content: "summary_large_image",
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
tags.push({
|
|
928
|
+
tag: "meta",
|
|
929
|
+
attrs: {
|
|
930
|
+
name: "twitter:image",
|
|
931
|
+
content: slideMetadata.ogImage,
|
|
932
|
+
},
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
806
936
|
return {
|
|
807
937
|
html: htmlWithoutTitle,
|
|
808
938
|
tags,
|