feat: source highlighting for code blocks
This commit is contained in:
81
package-lock.json
generated
81
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/dom": "^1.7.5",
|
"@floating-ui/dom": "^1.7.5",
|
||||||
|
"@highlightjs/cdn-assets": "^11.11.1",
|
||||||
"@libsql/client": "^0.17.0",
|
"@libsql/client": "^0.17.0",
|
||||||
"@milkdown/kit": "^7.18.0",
|
"@milkdown/kit": "^7.18.0",
|
||||||
"@milkdown/plugin-block": "^7.18.0",
|
"@milkdown/plugin-block": "^7.18.0",
|
||||||
@@ -178,6 +179,7 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -750,6 +752,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.1.tgz",
|
||||||
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
|
"integrity": "sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@@ -826,6 +829,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.4.tgz",
|
||||||
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
|
"integrity": "sha512-8y7xqG/hpB53l25CIoit9/ngxdfoG+fx+V3SHBrinnhOtLvKHRyAJJuHzkWrR4YXXLX8eXBsejgAAxHUOdW1yw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -847,6 +851,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.13.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.13.tgz",
|
||||||
"integrity": "sha512-QBO8ZsgJLCbI28KdY0/oDy5NQLqOQVZCozBknxc2/7L98V+TVYFHnfaCsnGh1U+alpd2LOkStVwYY7nW2R1xbw==",
|
"integrity": "sha512-QBO8ZsgJLCbI28KdY0/oDy5NQLqOQVZCozBknxc2/7L98V+TVYFHnfaCsnGh1U+alpd2LOkStVwYY7nW2R1xbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -942,6 +947,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.19.0"
|
"node": ">=20.19.0"
|
||||||
},
|
},
|
||||||
@@ -982,6 +988,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.19.0"
|
"node": ">=20.19.0"
|
||||||
}
|
}
|
||||||
@@ -1376,7 +1383,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -1398,7 +1404,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -1415,7 +1420,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1430,7 +1434,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -2669,6 +2672,15 @@
|
|||||||
"@hapi/hoek": "^11.0.2"
|
"@hapi/hoek": "^11.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@highlightjs/cdn-assets": {
|
||||||
|
"version": "11.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@highlightjs/cdn-assets/-/cdn-assets-11.11.1.tgz",
|
||||||
|
"integrity": "sha512-VEPdHzwelZ12hEX18BHduqxMZGolcUsrbeokHYxOUIm8X2+M7nx5QPtPeQgRxR9XjhdLv4/7DD5BWOlSrJ3k7Q==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
@@ -3978,6 +3990,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.0.tgz",
|
||||||
"integrity": "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg==",
|
"integrity": "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@libsql/core": "^0.17.0",
|
"@libsql/core": "^0.17.0",
|
||||||
"@libsql/hrana-client": "^0.9.0",
|
"@libsql/hrana-client": "^0.9.0",
|
||||||
@@ -5184,8 +5197,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
@@ -5414,6 +5426,7 @@
|
|||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -5424,6 +5437,7 @@
|
|||||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
@@ -5531,6 +5545,7 @@
|
|||||||
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
|
"integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.56.0",
|
"@typescript-eslint/scope-manager": "8.56.0",
|
||||||
"@typescript-eslint/types": "8.56.0",
|
"@typescript-eslint/types": "8.56.0",
|
||||||
@@ -5910,6 +5925,7 @@
|
|||||||
"integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==",
|
"integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "4.0.18",
|
"@vitest/utils": "4.0.18",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
@@ -6096,6 +6112,7 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -6129,6 +6146,7 @@
|
|||||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -6651,6 +6669,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -7346,8 +7365,7 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
@@ -7484,7 +7502,8 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/d3-cloud": {
|
"node_modules/d3-cloud": {
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
@@ -7762,6 +7781,7 @@
|
|||||||
"integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
|
"integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.7.0",
|
"app-builder-lib": "26.7.0",
|
||||||
"builder-util": "26.4.1",
|
"builder-util": "26.4.1",
|
||||||
@@ -7863,8 +7883,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/dompurify": {
|
"node_modules/dompurify": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
@@ -8337,7 +8356,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -8358,7 +8376,6 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -8385,16 +8402,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encoding": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
|
||||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
@@ -8502,6 +8509,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"esbuild": "bin/esbuild"
|
"esbuild": "bin/esbuild"
|
||||||
},
|
},
|
||||||
@@ -8579,6 +8587,7 @@
|
|||||||
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -10086,6 +10095,7 @@
|
|||||||
"integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
|
"integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@acemir/cssom": "^0.9.31",
|
"@acemir/cssom": "^0.9.31",
|
||||||
"@asamuzakjp/dom-selector": "^6.7.6",
|
"@asamuzakjp/dom-selector": "^6.7.6",
|
||||||
@@ -10421,7 +10431,6 @@
|
|||||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"lz-string": "bin/bin.js"
|
"lz-string": "bin/bin.js"
|
||||||
}
|
}
|
||||||
@@ -11691,7 +11700,6 @@
|
|||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@@ -11704,6 +11712,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
|
||||||
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dompurify": "3.2.7",
|
"dompurify": "3.2.7",
|
||||||
"marked": "14.0.0"
|
"marked": "14.0.0"
|
||||||
@@ -12265,6 +12274,7 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -12340,7 +12350,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -12358,7 +12367,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -12379,7 +12387,6 @@
|
|||||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1",
|
"ansi-regex": "^5.0.1",
|
||||||
"ansi-styles": "^5.0.0",
|
"ansi-styles": "^5.0.0",
|
||||||
@@ -12395,7 +12402,6 @@
|
|||||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -12550,6 +12556,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
|
||||||
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
"integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"orderedmap": "^2.0.0"
|
"orderedmap": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -12583,6 +12590,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
|
||||||
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
"integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.0.0",
|
"prosemirror-model": "^1.0.0",
|
||||||
"prosemirror-transform": "^1.0.0",
|
"prosemirror-transform": "^1.0.0",
|
||||||
@@ -12616,6 +12624,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz",
|
||||||
"integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==",
|
"integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"prosemirror-model": "^1.20.0",
|
"prosemirror-model": "^1.20.0",
|
||||||
"prosemirror-state": "^1.0.0",
|
"prosemirror-state": "^1.0.0",
|
||||||
@@ -12693,6 +12702,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -12758,6 +12768,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -12787,8 +12798,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react-refresh": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
@@ -13093,7 +13103,6 @@
|
|||||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
},
|
},
|
||||||
@@ -13836,7 +13845,6 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -14116,7 +14124,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.21.0",
|
"version": "4.21.0",
|
||||||
@@ -14666,6 +14675,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -14955,6 +14965,7 @@
|
|||||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -15514,6 +15525,7 @@
|
|||||||
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.0.18",
|
"@vitest/expect": "4.0.18",
|
||||||
"@vitest/mocker": "4.0.18",
|
"@vitest/mocker": "4.0.18",
|
||||||
@@ -15591,6 +15603,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.28.tgz",
|
||||||
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
|
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.28",
|
"@vue/compiler-dom": "3.5.28",
|
||||||
"@vue/compiler-sfc": "3.5.28",
|
"@vue/compiler-sfc": "3.5.28",
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/dom": "^1.7.5",
|
"@floating-ui/dom": "^1.7.5",
|
||||||
|
"@highlightjs/cdn-assets": "^11.11.1",
|
||||||
"@libsql/client": "^0.17.0",
|
"@libsql/client": "^0.17.0",
|
||||||
"@milkdown/kit": "^7.18.0",
|
"@milkdown/kit": "^7.18.0",
|
||||||
"@milkdown/plugin-block": "^7.18.0",
|
"@milkdown/plugin-block": "^7.18.0",
|
||||||
|
|||||||
@@ -307,6 +307,8 @@ export class BlogGenerationEngine {
|
|||||||
let sitemapWritten = false;
|
let sitemapWritten = false;
|
||||||
let rssWritten = false;
|
let rssWritten = false;
|
||||||
let atomWritten = false;
|
let atomWritten = false;
|
||||||
|
const generatedHashCache = new Map<string, string | null>();
|
||||||
|
const knownOutputDirectories = new Set<string>();
|
||||||
|
|
||||||
if (includeCore) {
|
if (includeCore) {
|
||||||
onProgress(10, 'Writing sitemap and feeds...');
|
onProgress(10, 'Writing sitemap and feeds...');
|
||||||
@@ -333,7 +335,10 @@ export class BlogGenerationEngine {
|
|||||||
reportUnitProgress('Atom feed written');
|
reportUnitProgress('Atom feed written');
|
||||||
|
|
||||||
onProgress(15, 'Copying assets...');
|
onProgress(15, 'Copying assets...');
|
||||||
await copyPreviewAssets(htmlDir);
|
await copyPreviewAssets(htmlDir, {
|
||||||
|
projectId: options.projectId,
|
||||||
|
hashCache: generatedHashCache,
|
||||||
|
});
|
||||||
reportUnitProgress('Assets copied');
|
reportUnitProgress('Assets copied');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,9 +353,6 @@ export class BlogGenerationEngine {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const knownOutputDirectories = new Set<string>();
|
|
||||||
const generatedHashCache = new Map<string, string | null>();
|
|
||||||
|
|
||||||
const writePage = (projectId: string, urlPath: string, content: string) => writeHtmlPage({
|
const writePage = (projectId: string, urlPath: string, content: string) => writeHtmlPage({
|
||||||
projectId,
|
projectId,
|
||||||
htmlDir,
|
htmlDir,
|
||||||
|
|||||||
@@ -4,6 +4,25 @@ import * as path from 'node:path';
|
|||||||
import { getGeneratedFileHash, setGeneratedFileHash } from '../database/generatedFileHashStore';
|
import { getGeneratedFileHash, setGeneratedFileHash } from '../database/generatedFileHashStore';
|
||||||
import { PREVIEW_ASSETS, PREVIEW_IMAGE_ASSETS } from './PageRenderer';
|
import { PREVIEW_ASSETS, PREVIEW_IMAGE_ASSETS } from './PageRenderer';
|
||||||
|
|
||||||
|
type PreviewAssetMap = Record<string, { contentType: string; modulePath?: string; sourceText?: string }>;
|
||||||
|
type PreviewImageAssetMap = Record<string, { modulePath: string; contentType: string }>;
|
||||||
|
|
||||||
|
function toBuffer(value: unknown): Buffer {
|
||||||
|
if (Buffer.isBuffer(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return Buffer.from(value, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value instanceof Uint8Array) {
|
||||||
|
return Buffer.from(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.alloc(0);
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeGeneratedUrlPath(urlPath: string): string {
|
export function normalizeGeneratedUrlPath(urlPath: string): string {
|
||||||
const trimmed = (urlPath || '').trim();
|
const trimmed = (urlPath || '').trim();
|
||||||
if (!trimmed || trimmed === '/') {
|
if (!trimmed || trimmed === '/') {
|
||||||
@@ -28,6 +47,10 @@ export function computeContentHash(content: string): string {
|
|||||||
return crypto.createHash('sha256').update(content).digest('hex');
|
return crypto.createHash('sha256').update(content).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function computeBufferHash(content: Buffer): string {
|
||||||
|
return crypto.createHash('sha256').update(content).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
export async function writeFileIfHashChanged(params: {
|
export async function writeFileIfHashChanged(params: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
@@ -104,24 +127,67 @@ export async function writeHtmlPage(params: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function copyPreviewAssets(htmlDir: string): Promise<void> {
|
export async function copyPreviewAssets(htmlDir: string, options?: {
|
||||||
|
projectId?: string;
|
||||||
|
hashCache?: Map<string, string | null>;
|
||||||
|
previewAssets?: PreviewAssetMap;
|
||||||
|
previewImageAssets?: PreviewImageAssetMap;
|
||||||
|
readModuleFile?: (modulePath: string) => Promise<Buffer>;
|
||||||
|
getGeneratedFileHash?: (projectId: string, relativePath: string) => Promise<string | null>;
|
||||||
|
setGeneratedFileHash?: (projectId: string, relativePath: string, hash: string) => Promise<void>;
|
||||||
|
}): Promise<void> {
|
||||||
const assetsDir = path.join(htmlDir, 'assets');
|
const assetsDir = path.join(htmlDir, 'assets');
|
||||||
const imagesDir = path.join(htmlDir, 'images');
|
const imagesDir = path.join(htmlDir, 'images');
|
||||||
await fs.mkdir(assetsDir, { recursive: true });
|
await fs.mkdir(assetsDir, { recursive: true });
|
||||||
await fs.mkdir(imagesDir, { recursive: true });
|
await fs.mkdir(imagesDir, { recursive: true });
|
||||||
|
|
||||||
for (const [filename, definition] of Object.entries(PREVIEW_ASSETS)) {
|
const projectId = options?.projectId;
|
||||||
const destPath = path.join(assetsDir, filename);
|
const hashCache = options?.hashCache;
|
||||||
const content = definition.sourceText !== undefined
|
const readModuleFile = options?.readModuleFile ?? (async (modulePath: string) => toBuffer(await fs.readFile(require.resolve(modulePath))));
|
||||||
? Buffer.from(definition.sourceText, 'utf-8')
|
const getHash = options?.getGeneratedFileHash ?? getGeneratedFileHash;
|
||||||
: await fs.readFile(require.resolve(definition.modulePath as string));
|
const setHash = options?.setGeneratedFileHash ?? setGeneratedFileHash;
|
||||||
await fs.writeFile(destPath, content);
|
const previewAssets = options?.previewAssets ?? PREVIEW_ASSETS;
|
||||||
|
const previewImageAssets = options?.previewImageAssets ?? PREVIEW_IMAGE_ASSETS;
|
||||||
|
|
||||||
|
const writeBinaryIfHashChanged = async (filePath: string, relativePath: string, content: Buffer): Promise<void> => {
|
||||||
|
if (!projectId) {
|
||||||
|
await fs.writeFile(filePath, content);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [filename, definition] of Object.entries(PREVIEW_IMAGE_ASSETS)) {
|
const hash = computeBufferHash(content);
|
||||||
const sourcePath = require.resolve(definition.modulePath);
|
let previousHash: string | null;
|
||||||
|
if (hashCache && hashCache.has(relativePath)) {
|
||||||
|
previousHash = hashCache.get(relativePath) ?? null;
|
||||||
|
} else {
|
||||||
|
previousHash = await getHash(projectId, relativePath);
|
||||||
|
hashCache?.set(relativePath, previousHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousHash === hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, content);
|
||||||
|
await setHash(projectId, relativePath, hash);
|
||||||
|
hashCache?.set(relativePath, hash);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [filename, definition] of Object.entries(previewAssets)) {
|
||||||
|
const destPath = path.join(assetsDir, filename);
|
||||||
|
const relativePath = path.posix.join('assets', filename);
|
||||||
|
const content = definition.sourceText !== undefined
|
||||||
|
? Buffer.from(definition.sourceText, 'utf-8')
|
||||||
|
: toBuffer(await readModuleFile(definition.modulePath as string));
|
||||||
|
|
||||||
|
await writeBinaryIfHashChanged(destPath, relativePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [filename, definition] of Object.entries(previewImageAssets)) {
|
||||||
const destPath = path.join(imagesDir, filename);
|
const destPath = path.join(imagesDir, filename);
|
||||||
const content = await fs.readFile(sourcePath);
|
const relativePath = path.posix.join('images', filename);
|
||||||
await fs.writeFile(destPath, content);
|
const content = toBuffer(await readModuleFile(definition.modulePath));
|
||||||
|
|
||||||
|
await writeBinaryIfHashChanged(destPath, relativePath, content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { MediaData } from './MediaEngine';
|
|||||||
import type { PostData } from './PostEngine';
|
import type { PostData } from './PostEngine';
|
||||||
import type { MenuDocument, MenuItemData } from './MenuEngine';
|
import type { MenuDocument, MenuItemData } from './MenuEngine';
|
||||||
import { PICO_THEME_NAMES } from '../shared/picoThemes';
|
import { PICO_THEME_NAMES } from '../shared/picoThemes';
|
||||||
|
import { CODE_ENHANCEMENTS_RUNTIME_JS } from './assets/codeEnhancementsRuntime';
|
||||||
import { TAG_CLOUD_RUNTIME_JS } from './assets/tagCloudRuntime';
|
import { TAG_CLOUD_RUNTIME_JS } from './assets/tagCloudRuntime';
|
||||||
import { resolveRenderLanguageFromProjectPreferences, translateRender } from '../shared/i18n';
|
import { resolveRenderLanguageFromProjectPreferences, translateRender } from '../shared/i18n';
|
||||||
|
|
||||||
@@ -135,6 +136,29 @@ export interface PreviewAssetDefinition {
|
|||||||
sourceText?: string;
|
sourceText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function annotateCodeBlocksWithLanguage(html: string): string {
|
||||||
|
if (!html) {
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html.replace(/<code\b([^>]*)>/gi, (fullMatch, rawAttributes: string) => {
|
||||||
|
if (/\bdata-code-language\s*=/.test(rawAttributes)) {
|
||||||
|
return fullMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classMatch = rawAttributes.match(/\bclass\s*=\s*"([^"]*)"/i);
|
||||||
|
const classList = classMatch?.[1] ?? '';
|
||||||
|
const languageMatch = classList.match(/(?:^|\s)language-([\w.+-]+)(?:\s|$)/i);
|
||||||
|
const language = languageMatch?.[1]?.toLowerCase();
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
return fullMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<code${rawAttributes} data-code-language="${escapeHtml(language)}">`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export interface TagUsageEntry {
|
export interface TagUsageEntry {
|
||||||
tag: string;
|
tag: string;
|
||||||
count: number;
|
count: number;
|
||||||
@@ -178,6 +202,18 @@ export const PREVIEW_ASSETS: Record<string, PreviewAssetDefinition> = {
|
|||||||
modulePath: 'lightbox2/dist/js/lightbox-plus-jquery.min.js',
|
modulePath: 'lightbox2/dist/js/lightbox-plus-jquery.min.js',
|
||||||
contentType: 'application/javascript; charset=utf-8',
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
},
|
},
|
||||||
|
'highlight.min.css': {
|
||||||
|
modulePath: '@highlightjs/cdn-assets/styles/github-dark.min.css',
|
||||||
|
contentType: 'text/css; charset=utf-8',
|
||||||
|
},
|
||||||
|
'highlight.min.js': {
|
||||||
|
modulePath: '@highlightjs/cdn-assets/highlight.min.js',
|
||||||
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
|
},
|
||||||
|
'code-enhancements.js': {
|
||||||
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
|
sourceText: CODE_ENHANCEMENTS_RUNTIME_JS,
|
||||||
|
},
|
||||||
'd3.layout.cloud.js': {
|
'd3.layout.cloud.js': {
|
||||||
modulePath: 'd3-cloud/build/d3.layout.cloud.js',
|
modulePath: 'd3-cloud/build/d3.layout.cloud.js',
|
||||||
contentType: 'application/javascript; charset=utf-8',
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
@@ -829,7 +865,8 @@ export class PageRenderer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const markdownHtml = await marked.parse(withMacros, { async: true, gfm: true, breaks: false });
|
const markdownHtml = await marked.parse(withMacros, { async: true, gfm: true, breaks: false });
|
||||||
return rewriteRenderedHtmlUrls(markdownHtml, rewriteContext);
|
const annotatedMarkdownHtml = annotateCodeBlocksWithLanguage(markdownHtml);
|
||||||
|
return rewriteRenderedHtmlUrls(annotatedMarkdownHtml, rewriteContext);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
137
src/main/engine/assets/codeEnhancementsRuntime.ts
Normal file
137
src/main/engine/assets/codeEnhancementsRuntime.ts
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
export const CODE_ENHANCEMENTS_RUNTIME_JS = String.raw`(function () {
|
||||||
|
function resolveCodeLanguage(codeElement) {
|
||||||
|
if (!codeElement) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
var direct = codeElement.getAttribute('data-code-language');
|
||||||
|
if (typeof direct === 'string' && direct.trim()) {
|
||||||
|
return direct.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
var className = codeElement.className || '';
|
||||||
|
var classMatch = className.match(/(?:^|\s)language-([\w.+-]+)/i);
|
||||||
|
if (classMatch && classMatch[1]) {
|
||||||
|
return classMatch[1].toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function fallbackCopy(value) {
|
||||||
|
var textarea = document.createElement('textarea');
|
||||||
|
textarea.value = value;
|
||||||
|
textarea.setAttribute('readonly', 'readonly');
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.opacity = '0';
|
||||||
|
textarea.style.pointerEvents = 'none';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.focus();
|
||||||
|
textarea.select();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return document.execCommand('copy');
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyCodeToClipboard(value) {
|
||||||
|
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(value);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return fallbackCopy(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackCopy(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureCopyButton(preElement, codeElement) {
|
||||||
|
if (!preElement || preElement.querySelector(':scope > .code-copy-button')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
preElement.classList.add('code-block-enhanced');
|
||||||
|
|
||||||
|
var button = document.createElement('button');
|
||||||
|
button.type = 'button';
|
||||||
|
button.className = 'code-copy-button';
|
||||||
|
button.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
|
var icon = document.createElement('span');
|
||||||
|
icon.className = 'code-copy-icon';
|
||||||
|
icon.textContent = '⧉';
|
||||||
|
button.appendChild(icon);
|
||||||
|
|
||||||
|
button.addEventListener('click', async function () {
|
||||||
|
var codeText = codeElement.textContent || '';
|
||||||
|
var copied = await copyCodeToClipboard(codeText);
|
||||||
|
preElement.classList.remove('code-copy-failed');
|
||||||
|
preElement.classList.remove('code-copy-success');
|
||||||
|
preElement.classList.add(copied ? 'code-copy-success' : 'code-copy-failed');
|
||||||
|
|
||||||
|
if (copied) {
|
||||||
|
icon.textContent = '✓';
|
||||||
|
window.setTimeout(function () {
|
||||||
|
preElement.classList.remove('code-copy-success');
|
||||||
|
icon.textContent = '⧉';
|
||||||
|
}, 1200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(function () {
|
||||||
|
preElement.classList.remove('code-copy-failed');
|
||||||
|
}, 1200);
|
||||||
|
});
|
||||||
|
|
||||||
|
preElement.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightCodeBlock(codeElement) {
|
||||||
|
var highlighter = window.hljs;
|
||||||
|
if (!highlighter || typeof highlighter.highlightElement !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeElement.getAttribute('data-code-highlighted') === 'true') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
highlighter.highlightElement(codeElement);
|
||||||
|
codeElement.setAttribute('data-code-highlighted', 'true');
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCodeBlocks() {
|
||||||
|
var codeNodes = document.querySelectorAll('pre > code');
|
||||||
|
codeNodes.forEach(function (codeElement) {
|
||||||
|
var preElement = codeElement.parentElement;
|
||||||
|
if (!preElement || preElement.tagName !== 'PRE') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var language = resolveCodeLanguage(codeElement);
|
||||||
|
if (language) {
|
||||||
|
codeElement.setAttribute('data-code-language', language);
|
||||||
|
preElement.setAttribute('data-code-language', language);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureCopyButton(preElement, codeElement);
|
||||||
|
highlightCodeBlock(codeElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initCodeBlocks, { once: true });
|
||||||
|
} else {
|
||||||
|
initCodeBlocks();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
`;
|
||||||
@@ -5,9 +5,12 @@
|
|||||||
{% assign resolved_pico_stylesheet_href = pico_stylesheet_href | default: '/assets/pico.min.css' %}
|
{% assign resolved_pico_stylesheet_href = pico_stylesheet_href | default: '/assets/pico.min.css' %}
|
||||||
<link rel="stylesheet" href="{{ resolved_pico_stylesheet_href }}" />
|
<link rel="stylesheet" href="{{ resolved_pico_stylesheet_href }}" />
|
||||||
<link rel="stylesheet" href="/assets/lightbox.min.css" />
|
<link rel="stylesheet" href="/assets/lightbox.min.css" />
|
||||||
|
<link rel="stylesheet" href="/assets/highlight.min.css" />
|
||||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
|
||||||
<link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml" />
|
<link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml" />
|
||||||
{% render 'partials/styles' %}
|
{% render 'partials/styles' %}
|
||||||
|
<script defer src="/assets/highlight.min.js"></script>
|
||||||
|
<script defer src="/assets/code-enhancements.js"></script>
|
||||||
<script defer src="/assets/d3.layout.cloud.js"></script>
|
<script defer src="/assets/d3.layout.cloud.js"></script>
|
||||||
<script defer src="/assets/tag-cloud.js"></script>
|
<script defer src="/assets/tag-cloud.js"></script>
|
||||||
<script defer src="/assets/lightbox.min.js"></script>
|
<script defer src="/assets/lightbox.min.js"></script>
|
||||||
|
|||||||
@@ -18,6 +18,30 @@
|
|||||||
.blog-menu-item-with-children:hover > .blog-menu-submenu,
|
.blog-menu-item-with-children:hover > .blog-menu-submenu,
|
||||||
.blog-menu-item-with-children:focus-within > .blog-menu-submenu { display: block; }
|
.blog-menu-item-with-children:focus-within > .blog-menu-submenu { display: block; }
|
||||||
.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); }
|
.post { border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); padding: 1rem; background: var(--pico-card-background-color, var(--card-background-color)); }
|
||||||
|
.post pre { position: relative; overflow-x: auto; border: 1px solid var(--pico-muted-border-color, var(--muted-border-color)); border-radius: .3rem; margin: .9rem 0; padding: .85rem .9rem; background: var(--pico-code-background-color, rgba(33, 38, 45, .82)); }
|
||||||
|
.post pre code { display: block; font-size: .88rem; line-height: 1.5; }
|
||||||
|
.code-copy-button {
|
||||||
|
position: absolute;
|
||||||
|
top: .4rem;
|
||||||
|
right: .4rem;
|
||||||
|
border: 1px solid var(--pico-muted-border-color, var(--muted-border-color));
|
||||||
|
background: var(--pico-card-background-color, var(--card-background-color));
|
||||||
|
color: var(--pico-muted-color, var(--muted-color));
|
||||||
|
border-radius: .25rem;
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: .88;
|
||||||
|
}
|
||||||
|
.code-copy-button:hover,
|
||||||
|
.code-copy-button:focus-visible { opacity: 1; color: var(--pico-color, var(--color)); }
|
||||||
|
.code-copy-icon { font-size: .95rem; line-height: 1; }
|
||||||
|
.code-copy-success .code-copy-button { color: var(--pico-ins-color, rgb(53, 117, 56)); border-color: var(--pico-ins-color, rgb(53, 117, 56)); }
|
||||||
|
.code-copy-failed .code-copy-button { color: var(--pico-del-color, rgb(183, 72, 72)); border-color: var(--pico-del-color, rgb(183, 72, 72)); }
|
||||||
.post iframe { width: 100%; min-height: 20rem; }
|
.post iframe { width: 100%; min-height: 20rem; }
|
||||||
.macro-gallery, .macro-photo-archive, .macro-tag-cloud { border: 1px dashed var(--pico-muted-border-color, var(--muted-border-color)); padding: .75rem; margin: 1rem 0; }
|
.macro-gallery, .macro-photo-archive, .macro-tag-cloud { border: 1px dashed var(--pico-muted-border-color, var(--muted-border-color)); padding: .75rem; margin: 1rem 0; }
|
||||||
.gallery-container { display: grid; gap: .5rem; }
|
.gallery-container { display: grid; gap: .5rem; }
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { mkdir, readFile } from 'node:fs/promises';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
import {
|
import {
|
||||||
|
copyPreviewAssets,
|
||||||
normalizeGeneratedUrlPath,
|
normalizeGeneratedUrlPath,
|
||||||
urlPathToHtmlIndexPath,
|
urlPathToHtmlIndexPath,
|
||||||
writeFileIfHashChanged,
|
writeFileIfHashChanged,
|
||||||
@@ -145,4 +146,73 @@ describe('BlogGenerationOutputService', () => {
|
|||||||
|
|
||||||
expect(ensureDirectory).toHaveBeenCalledTimes(1);
|
expect(ensureDirectory).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('copies preview assets with hash checks so unchanged files are not rewritten', async () => {
|
||||||
|
const tempRoot = path.join('/tmp', makeTempName());
|
||||||
|
const htmlDir = path.join(tempRoot, 'html');
|
||||||
|
await mkdir(htmlDir, { recursive: true });
|
||||||
|
|
||||||
|
const hashStore = new Map<string, string>();
|
||||||
|
const getHash = vi.fn(async (_projectId: string, relativePath: string) => hashStore.get(relativePath) ?? null);
|
||||||
|
const setHash = vi.fn(async (_projectId: string, relativePath: string, hash: string) => {
|
||||||
|
hashStore.set(relativePath, hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
const hashCache = new Map<string, string | null>();
|
||||||
|
|
||||||
|
await copyPreviewAssets(htmlDir, {
|
||||||
|
projectId: 'project-a',
|
||||||
|
hashCache,
|
||||||
|
previewAssets: {
|
||||||
|
'runtime.js': {
|
||||||
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
|
sourceText: 'console.log("runtime");',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previewImageAssets: {
|
||||||
|
'pixel.png': {
|
||||||
|
modulePath: 'virtual:pixel.png',
|
||||||
|
contentType: 'image/png',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readModuleFile: async (modulePath: string) => {
|
||||||
|
if (modulePath === 'virtual:pixel.png') {
|
||||||
|
return Buffer.from([0, 1, 2, 3]);
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected module path: ${modulePath}`);
|
||||||
|
},
|
||||||
|
getGeneratedFileHash: getHash,
|
||||||
|
setGeneratedFileHash: setHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
await copyPreviewAssets(htmlDir, {
|
||||||
|
projectId: 'project-a',
|
||||||
|
hashCache,
|
||||||
|
previewAssets: {
|
||||||
|
'runtime.js': {
|
||||||
|
contentType: 'application/javascript; charset=utf-8',
|
||||||
|
sourceText: 'console.log("runtime");',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
previewImageAssets: {
|
||||||
|
'pixel.png': {
|
||||||
|
modulePath: 'virtual:pixel.png',
|
||||||
|
contentType: 'image/png',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
readModuleFile: async (modulePath: string) => {
|
||||||
|
if (modulePath === 'virtual:pixel.png') {
|
||||||
|
return Buffer.from([0, 1, 2, 3]);
|
||||||
|
}
|
||||||
|
throw new Error(`Unexpected module path: ${modulePath}`);
|
||||||
|
},
|
||||||
|
getGeneratedFileHash: getHash,
|
||||||
|
setGeneratedFileHash: setHash,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(setHash).toHaveBeenCalledTimes(2);
|
||||||
|
expect(await readFile(path.join(htmlDir, 'assets', 'runtime.js'), 'utf-8')).toBe('console.log("runtime");');
|
||||||
|
const imageContent = await readFile(path.join(htmlDir, 'images', 'pixel.png'));
|
||||||
|
expect(Array.from(imageContent)).toEqual([0, 1, 2, 3]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -304,7 +304,10 @@ describe('PreviewServer', () => {
|
|||||||
|
|
||||||
expect(rootHtml).toContain('href="/assets/pico.min.css"');
|
expect(rootHtml).toContain('href="/assets/pico.min.css"');
|
||||||
expect(rootHtml).toContain('href="/assets/lightbox.min.css"');
|
expect(rootHtml).toContain('href="/assets/lightbox.min.css"');
|
||||||
|
expect(rootHtml).toContain('href="/assets/highlight.min.css"');
|
||||||
expect(rootHtml).toContain('src="/assets/lightbox.min.js"');
|
expect(rootHtml).toContain('src="/assets/lightbox.min.js"');
|
||||||
|
expect(rootHtml).toContain('src="/assets/highlight.min.js"');
|
||||||
|
expect(rootHtml).toContain('src="/assets/code-enhancements.js"');
|
||||||
expect(rootHtml).toContain('src="/assets/d3.layout.cloud.js"');
|
expect(rootHtml).toContain('src="/assets/d3.layout.cloud.js"');
|
||||||
expect(rootHtml).toContain('src="/assets/tag-cloud.js"');
|
expect(rootHtml).toContain('src="/assets/tag-cloud.js"');
|
||||||
expect(rootHtml).not.toContain('function parseWords(');
|
expect(rootHtml).not.toContain('function parseWords(');
|
||||||
@@ -322,6 +325,18 @@ describe('PreviewServer', () => {
|
|||||||
expect(lightboxJsResponse.status).toBe(200);
|
expect(lightboxJsResponse.status).toBe(200);
|
||||||
expect(lightboxJsResponse.headers.get('content-type')).toContain('application/javascript');
|
expect(lightboxJsResponse.headers.get('content-type')).toContain('application/javascript');
|
||||||
|
|
||||||
|
const highlightCssResponse = await fetch(`${server.getBaseUrl()}/assets/highlight.min.css`);
|
||||||
|
expect(highlightCssResponse.status).toBe(200);
|
||||||
|
expect(highlightCssResponse.headers.get('content-type')).toContain('text/css');
|
||||||
|
|
||||||
|
const highlightJsResponse = await fetch(`${server.getBaseUrl()}/assets/highlight.min.js`);
|
||||||
|
expect(highlightJsResponse.status).toBe(200);
|
||||||
|
expect(highlightJsResponse.headers.get('content-type')).toContain('application/javascript');
|
||||||
|
|
||||||
|
const codeEnhancementsResponse = await fetch(`${server.getBaseUrl()}/assets/code-enhancements.js`);
|
||||||
|
expect(codeEnhancementsResponse.status).toBe(200);
|
||||||
|
expect(codeEnhancementsResponse.headers.get('content-type')).toContain('application/javascript');
|
||||||
|
|
||||||
const d3CloudJsResponse = await fetch(`${server.getBaseUrl()}/assets/d3.layout.cloud.js`);
|
const d3CloudJsResponse = await fetch(`${server.getBaseUrl()}/assets/d3.layout.cloud.js`);
|
||||||
expect(d3CloudJsResponse.status).toBe(200);
|
expect(d3CloudJsResponse.status).toBe(200);
|
||||||
expect(d3CloudJsResponse.headers.get('content-type')).toContain('application/javascript');
|
expect(d3CloudJsResponse.headers.get('content-type')).toContain('application/javascript');
|
||||||
@@ -339,6 +354,23 @@ describe('PreviewServer', () => {
|
|||||||
expect(lightboxLoadingImageResponse.headers.get('content-type')).toContain('image/gif');
|
expect(lightboxLoadingImageResponse.headers.get('content-type')).toContain('image/gif');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps markdown code block html minimal and includes code language metadata', async () => {
|
||||||
|
const postWithCode = makePost({
|
||||||
|
content: '```python\nprint("hello")\n```',
|
||||||
|
});
|
||||||
|
|
||||||
|
server = new PreviewServer({
|
||||||
|
postEngine: makeEngine([postWithCode]),
|
||||||
|
settingsEngine: makeSettings(50),
|
||||||
|
getActiveProjectContext: async () => ({ projectId: 'default' }),
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.start(0);
|
||||||
|
|
||||||
|
const html = await (await fetch(`${server.getBaseUrl()}/`)).text();
|
||||||
|
expect(html).toContain('<code class="language-python" data-code-language="python">');
|
||||||
|
});
|
||||||
|
|
||||||
it('does not set project context or run startup sync for static asset requests', async () => {
|
it('does not set project context or run startup sync for static asset requests', async () => {
|
||||||
const postEngine = makeEngine([makePost()]);
|
const postEngine = makeEngine([makePost()]);
|
||||||
const mediaEngine = {
|
const mediaEngine = {
|
||||||
|
|||||||
Reference in New Issue
Block a user