chore: fix the invalid svg images.
This commit is contained in:
parent
11e9972df6
commit
232d9eb19b
@ -178,9 +178,12 @@ const options: z.input<typeof Options> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
thumbnail: ({ src, width, height }) => {
|
thumbnail: ({ src, width, height }) => {
|
||||||
|
if (src.endsWith('.svg')) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
if (isProd()) {
|
if (isProd()) {
|
||||||
// Add upyun thumbnail support.
|
// Add upyun thumbnail support.
|
||||||
return `${src}!upyun520/both/${width}x${height}/quality/100/unsharp/true/progressive/true`;
|
return `${src}!upyun520/both/${width}x${height}/format/webp/quality/100/unsharp/true/progressive/true`;
|
||||||
}
|
}
|
||||||
// See https://docs.astro.build/en/reference/image-service-reference/#local-services
|
// See https://docs.astro.build/en/reference/image-service-reference/#local-services
|
||||||
// Remember to add the localhost to you image service settings.
|
// Remember to add the localhost to you image service settings.
|
||||||
|
59
package-lock.json
generated
59
package-lock.json
generated
@ -42,8 +42,8 @@
|
|||||||
"resize-sensor": "^0.0.6",
|
"resize-sensor": "^0.0.6",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^5.0.7",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.5.2",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-select": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ampproject/remapping": {
|
"node_modules/@ampproject/remapping": {
|
||||||
@ -3290,6 +3290,13 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/boolbase": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/bootstrap": {
|
"node_modules/bootstrap": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||||
@ -3833,6 +3840,23 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-selector-parser": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/mdevils"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://patreon.com/mdevils"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@ -6701,6 +6725,19 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nth-check": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/obuf": {
|
"node_modules/obuf": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||||
@ -8654,6 +8691,24 @@
|
|||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unist-util-select": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unist-util-select/-/unist-util-select-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-4A5mfokSHG/rNQ4g7gSbdEs+H586xyd24sdJqF1IWamqrLHvYb+DH48fzxowyOhOfK7YSqX+XlCojAyuuyyT2A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/unist": "^3.0.0",
|
||||||
|
"css-selector-parser": "^3.0.0",
|
||||||
|
"devlop": "^1.1.0",
|
||||||
|
"nth-check": "^2.0.0",
|
||||||
|
"zwitch": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/unified"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unist-util-stringify-position": {
|
"node_modules/unist-util-stringify-position": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
"resize-sensor": "^0.0.6",
|
"resize-sensor": "^0.0.6",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^5.0.7",
|
||||||
"sharp": "^0.33.4",
|
"sharp": "^0.33.4",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.5.2",
|
||||||
"unist-util-visit": "^5.0.0"
|
"unist-util-select": "^5.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import type { Literal, Node, Parent } from 'unist';
|
import type { Literal, Node, Parent } from 'unist';
|
||||||
import { visit } from 'unist-util-visit';
|
import { selectAll } from 'unist-util-select';
|
||||||
|
import options from '../options';
|
||||||
import { imageMetadata } from '../src/helpers/images';
|
import { imageMetadata } from '../src/helpers/images';
|
||||||
|
import { urlJoin } from '../src/helpers/tools';
|
||||||
|
|
||||||
export type ImageNode = Parent & {
|
type ImageNode = Parent & {
|
||||||
url: string;
|
url: string;
|
||||||
alt: string;
|
alt: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -11,36 +13,55 @@ export type ImageNode = Parent & {
|
|||||||
attributes: (Literal & { name: string })[];
|
attributes: (Literal & { name: string })[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MdxJsxAttribute = {
|
||||||
|
type: 'mdxJsxAttribute';
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type MdxJsxFlowElement = Parent & {
|
||||||
|
name: string;
|
||||||
|
attributes: MdxJsxAttribute[];
|
||||||
|
};
|
||||||
|
|
||||||
export const astroImage = () => {
|
export const astroImage = () => {
|
||||||
return async (tree: Node) => {
|
return async (tree: Node) => {
|
||||||
const images: ImageNode[] = [];
|
// Find all the img node.
|
||||||
|
const imgs = selectAll('mdxJsxFlowElement', tree)
|
||||||
// Find all the local image node.
|
.map((node) => node as MdxJsxFlowElement)
|
||||||
visit(tree, 'image', (node: Node, _, __: Parent) => {
|
.filter((node) => node.name === 'img');
|
||||||
const imageNode = node as ImageNode;
|
for (const img of imgs) {
|
||||||
// Skip remote images.
|
const srcAttribute = img.attributes.find((attribute) => attribute.name === 'src');
|
||||||
if (imageNode.url.startsWith('http')) {
|
if (srcAttribute) {
|
||||||
return;
|
const src = srcAttribute.value;
|
||||||
|
if (src.startsWith('/')) {
|
||||||
|
srcAttribute.value = urlJoin(options.assetsPrefix(), src);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
images.push(imageNode);
|
// Find all the image node.
|
||||||
});
|
const imageNodes = selectAll('image', tree)
|
||||||
|
.map((node) => node as ImageNode)
|
||||||
|
.filter((imageNode) => !imageNode.url.startsWith('http'))
|
||||||
|
.map(transformAstroImage);
|
||||||
|
|
||||||
// Process images.
|
// Process image with blur metadata.
|
||||||
await Promise.all(images.map(transformAstroImage));
|
await Promise.all(imageNodes);
|
||||||
return tree;
|
return tree;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const transformAstroImage = async (imageNode: ImageNode) => {
|
const transformAstroImage = async (imageNode: ImageNode) => {
|
||||||
|
imageNode.type = 'mdxJsxFlowElement';
|
||||||
|
imageNode.name = 'Image';
|
||||||
|
|
||||||
|
try {
|
||||||
const metadata = await imageMetadata(imageNode.url);
|
const metadata = await imageMetadata(imageNode.url);
|
||||||
if (metadata == null) {
|
if (metadata == null) {
|
||||||
throw new Error(`Failed to get image metadata: ${imageNode.url}`);
|
throw new Error(`Failed to get image metadata: ${imageNode.url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert original node to next/image
|
|
||||||
imageNode.type = 'mdxJsxFlowElement';
|
|
||||||
imageNode.name = 'Image';
|
|
||||||
imageNode.attributes = [
|
imageNode.attributes = [
|
||||||
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
|
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
|
||||||
{ type: 'mdxJsxAttribute', name: 'src', value: metadata.src },
|
{ type: 'mdxJsxAttribute', name: 'src', value: metadata.src },
|
||||||
@ -50,4 +71,14 @@ const transformAstroImage = async (imageNode: ImageNode) => {
|
|||||||
{ type: 'mdxJsxAttribute', name: 'blurWidth', value: metadata.blurWidth },
|
{ type: 'mdxJsxAttribute', name: 'blurWidth', value: metadata.blurWidth },
|
||||||
{ type: 'mdxJsxAttribute', name: 'blurHeight', value: metadata.blurHeight },
|
{ type: 'mdxJsxAttribute', name: 'blurHeight', value: metadata.blurHeight },
|
||||||
];
|
];
|
||||||
|
} catch (error) {
|
||||||
|
imageNode.attributes = [
|
||||||
|
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
|
||||||
|
{
|
||||||
|
type: 'mdxJsxAttribute',
|
||||||
|
name: 'src',
|
||||||
|
value: imageNode.url.startsWith('/') ? urlJoin(options.assetsPrefix(), imageNode.url) : imageNode.url,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
BIN
public/images/2024/06/2024062101010412.jpg
Normal file
BIN
public/images/2024/06/2024062101010412.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
@ -6,7 +6,7 @@ import options from '@/options';
|
|||||||
const pinnedSlug = options.settings.post.category ?? [];
|
const pinnedSlug = options.settings.post.category ?? [];
|
||||||
const pinnedCategories = pinnedSlug
|
const pinnedCategories = pinnedSlug
|
||||||
.map((slug) => getCategory(undefined, slug))
|
.map((slug) => getCategory(undefined, slug))
|
||||||
.flatMap((category) => (category !== undefined ? [category] : []));
|
.filter((category) => category !== undefined);
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="row g-2 g-md-4 list-grouped mt-3 mt-md-4">
|
<div class="row g-2 g-md-4 list-grouped mt-3 mt-md-4">
|
||||||
|
@ -46,28 +46,28 @@ cover: /images/2024/04/2024041405050511.png
|
|||||||
|
|
||||||
这也使得这道题在弄懂原理后相当无趣,但考虑我这朋友确实经验尚浅,所以我还是给他继续讲下去,顺带着给了代码实现。这篇博客便是当时内容的摘录整理。
|
这也使得这道题在弄懂原理后相当无趣,但考虑我这朋友确实经验尚浅,所以我还是给他继续讲下去,顺带着给了代码实现。这篇博客便是当时内容的摘录整理。
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step1.svg'} width={400} height={400} alt={'Step 1'} />
|
<img src='/images/recaps/algo-minimal-costs/step1.svg' width='400px' height='400px' alt='Step 1' />
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
做任何算法题,第一步是理解题意,第二步是设想最简单的情况,再慢慢推导到复杂情况。首先,我们先不考虑存在阻塞的情况。最简单场景里,顾客和维修点在一个
|
做任何算法题,第一步是理解题意,第二步是设想最简单的情况,再慢慢推导到复杂情况。首先,我们先不考虑存在阻塞的情况。最简单场景里,顾客和维修点在一个
|
||||||
1 x 1 的格子的一条边上,这个时候他们间的最短距离为 1。
|
1 x 1 的格子的一条边上,这个时候他们间的最短距离为 1。
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step2.svg'} width={400} height={400} alt={'Step 2'} />
|
<img src='/images/recaps/algo-minimal-costs/step2.svg' width='400px' height='400px' alt='Step 2' />
|
||||||
|
|
||||||
<center>然后我们更进一步,如果他们在一个格子的对角线上呢?他们间的最短路径有两条,为 2。</center>
|
<center>然后我们更进一步,如果他们在一个格子的对角线上呢?他们间的最短路径有两条,为 2。</center>
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step3.svg'} width={400} height={400} alt={'Step 3'} />
|
<img src='/images/recaps/algo-minimal-costs/step3.svg' width='400px' height='400px' alt='Step 3' />
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
结合初中的几何学知识,我们首先知道一个基本知识,两点之间,直线最短。所以,维修点和顾客在同一条直线上时,他们之间的距离就是直线距离。
|
结合初中的几何学知识,我们首先知道一个基本知识,两点之间,直线最短。所以,维修点和顾客在同一条直线上时,他们之间的距离就是直线距离。
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step4.svg'} width={800} height={400} alt={'Step 4'} />
|
<img src='/images/recaps/algo-minimal-costs/step4.svg' width='800px' height='400px' alt='Step 4' />
|
||||||
|
|
||||||
<center>然后我们再稍微复杂一点,此时顾客和维修点之间是田字格,最短路径就有三条,距离为 3。</center>
|
<center>然后我们再稍微复杂一点,此时顾客和维修点之间是田字格,最短路径就有三条,距离为 3。</center>
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step5.svg'} width={800} height={400} alt={'Step 5'} />
|
<img src='/images/recaps/algo-minimal-costs/step5.svg' width='800px' height='400px' alt='Step 5' />
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
等到田字格的时候,相信聪明的你已经发现了规律。那就是顾客到维修点的最短距离,等于他们所形成的矩形的横纵两条边边长的总和。按照上面右侧图片所示的箭头所行走的距离都等于这个最短路径。
|
等到田字格的时候,相信聪明的你已经发现了规律。那就是顾客到维修点的最短距离,等于他们所形成的矩形的横纵两条边边长的总和。按照上面右侧图片所示的箭头所行走的距离都等于这个最短路径。
|
||||||
@ -75,7 +75,7 @@ cover: /images/2024/04/2024041405050511.png
|
|||||||
|
|
||||||
一般情况下,面试场景的编码题已经可以开始写了。对应的编程思路就是,从维修点出发,在与顾客构成的矩形边界里面,不断逼近,只要能走通那么我们之间就有了最短距离。再把不同维修点到顾客的最短距离排序,选出最小的距离来进行计算费用。
|
一般情况下,面试场景的编码题已经可以开始写了。对应的编程思路就是,从维修点出发,在与顾客构成的矩形边界里面,不断逼近,只要能走通那么我们之间就有了最短距离。再把不同维修点到顾客的最短距离排序,选出最小的距离来进行计算费用。
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step6.svg'} width={800} height={400} alt={'Step 6'} />
|
<img src='/images/recaps/algo-minimal-costs/step6.svg' width='800px' height='400px' alt='Step 6' />
|
||||||
|
|
||||||
倘若以上面的推论作为最终编码的方式,虽然不能说完全错误,但是在当下这个面试很卷的时代,还是有可能被 PASS,为什么呢?因为我们还没有引入阻塞的概念。我们随便画两种阻塞的情况,并且假定这里都属于在当时条件下的最短路径,那么阁下又该如何应对?😆
|
倘若以上面的推论作为最终编码的方式,虽然不能说完全错误,但是在当下这个面试很卷的时代,还是有可能被 PASS,为什么呢?因为我们还没有引入阻塞的概念。我们随便画两种阻塞的情况,并且假定这里都属于在当时条件下的最短路径,那么阁下又该如何应对?😆
|
||||||
|
|
||||||
@ -88,11 +88,11 @@ cover: /images/2024/04/2024041405050511.png
|
|||||||
|
|
||||||
以此为基础,我们就可以稍微来复习一下大学的算法知识了,贪心算法(贪婪算法)。贪心算法的定义网上随随便便都能找到,这里就不再复述,我们更多地是需要去思考在这个场景的贪心算法如何使用。
|
以此为基础,我们就可以稍微来复习一下大学的算法知识了,贪心算法(贪婪算法)。贪心算法的定义网上随随便便都能找到,这里就不再复述,我们更多地是需要去思考在这个场景的贪心算法如何使用。
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step7.svg'} width={800} height={400} alt={'Step 7'} />
|
<img src='/images/recaps/algo-minimal-costs/step7.svg' width='800px' height='400px' alt='Step 7' />
|
||||||
|
|
||||||
贪心算法的第一步,就是找寻从顾客开始,所有可能能行走方向距离为 1 的点有哪些(图中蓝色的点)。接着,我们可以以这些距离为 1 的点为基础,去找寻所有距离为 2 的点(图中绿色的点)。以此类推,直到所有的点都没有下一个可以行走的点了。而每计算一次距离为 N 的点的时候,都可以尝试看看里面是否有对应的维修点,如果有,那么终止检索,这个 N 便是最短距离。
|
贪心算法的第一步,就是找寻从顾客开始,所有可能能行走方向距离为 1 的点有哪些(图中蓝色的点)。接着,我们可以以这些距离为 1 的点为基础,去找寻所有距离为 2 的点(图中绿色的点)。以此类推,直到所有的点都没有下一个可以行走的点了。而每计算一次距离为 N 的点的时候,都可以尝试看看里面是否有对应的维修点,如果有,那么终止检索,这个 N 便是最短距离。
|
||||||
|
|
||||||
<img src={'/images/recaps/algo-minimal-costs/step8.svg'} width={800} height={400} alt={'Step 8'} />
|
<img src='/images/recaps/algo-minimal-costs/step8.svg' width='800px' height='400px' alt='Step 8' />
|
||||||
|
|
||||||
如上图所示,在我们查找距离为 4 的点的时候,我们就能找到目标维修店,那么我们可以认定,起最短距离就是 4。
|
如上图所示,在我们查找距离为 4 的点的时候,我们就能找到目标维修店,那么我们可以认定,起最短距离就是 4。
|
||||||
|
|
||||||
@ -166,3 +166,5 @@ private boolean availablePoint(int[][] routines, int x, int y) {
|
|||||||
return x >= 0 && x < routines.length && y >= 0 && y <= routines[0].length && (routines[x][y] == 0 || routines[x][y] == -1);
|
return x >= 0 && x < routines.length && y >= 0 && y <= routines[0].length && (routines[x][y] == 0 || routines[x][y] == -1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
![Fin](/images/2024/06/2024062101010412.jpg)
|
||||||
|
@ -13,7 +13,7 @@ const title = `${query} 查询结果`;
|
|||||||
|
|
||||||
const searchResults = searchPosts(query)
|
const searchResults = searchPosts(query)
|
||||||
.map((slug) => posts.find((post) => post.slug === slug))
|
.map((slug) => posts.find((post) => post.slug === slug))
|
||||||
.flatMap((post) => (post == null ? [] : [post]))
|
.filter((post) => post !== undefined)
|
||||||
.slice(0, options.settings.pagination.search);
|
.slice(0, options.settings.pagination.search);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user