diff --git a/.obsidian/app.json b/.obsidian/app.json index 9e26dfeeb..0967ef424 100644 --- a/.obsidian/app.json +++ b/.obsidian/app.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/.obsidian/appearance.json b/.obsidian/appearance.json index 9e26dfeeb..0967ef424 100644 --- a/.obsidian/appearance.json +++ b/.obsidian/appearance.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/.obsidian/core-plugins.json b/.obsidian/core-plugins.json index 3f2ab43ce..fabdb22db 100644 --- a/.obsidian/core-plugins.json +++ b/.obsidian/core-plugins.json @@ -1,32 +1,32 @@ { - "file-explorer": true, - "global-search": true, - "switcher": true, - "graph": true, - "backlink": true, - "canvas": true, - "outgoing-link": true, - "tag-pane": true, - "footnotes": false, - "properties": true, - "page-preview": true, - "daily-notes": true, - "templates": true, - "note-composer": true, - "command-palette": true, - "slash-command": false, - "editor-status": true, - "bookmarks": true, - "markdown-importer": false, - "zk-prefixer": false, - "random-note": false, - "outline": true, - "word-count": true, - "slides": false, - "audio-recorder": false, - "workspaces": false, - "file-recovery": true, - "publish": false, - "sync": true, - "bases": true -} \ No newline at end of file + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true +} diff --git a/.obsidian/workspace-mobile.json b/.obsidian/workspace-mobile.json index e9bed9138..ed002d231 100644 --- a/.obsidian/workspace-mobile.json +++ b/.obsidian/workspace-mobile.json @@ -1,166 +1,166 @@ { - "main": { - "id": "29fed73056d4c788", - "type": "split", - "children": [ - { - "id": "fa71f00e822e8432", - "type": "tabs", - "children": [ - { - "id": "ca9029e77020d44e", - "type": "leaf", - "state": { - "type": "empty", - "state": {}, - "icon": "lucide-file", - "title": "New tab" - } - } - ] - } - ], - "direction": "vertical" - }, - "left": { - "id": "c058602dcce78a26", - "type": "mobile-drawer", - "children": [ - { - "id": "d3fd5617bfecca06", - "type": "leaf", - "state": { - "type": "file-explorer", - "state": { - "sortOrder": "alphabetical", - "autoReveal": false - }, - "icon": "lucide-folder-closed", - "title": "Files" - } - }, - { - "id": "4d4379f2270ea3e0", - "type": "leaf", - "state": { - "type": "search", - "state": { - "query": "", - "matchingCase": false, - "explainSearch": false, - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical" - }, - "icon": "lucide-search", - "title": "Search" - } - }, - { - "id": "d99abf483fcdcb1a", - "type": "leaf", - "state": { - "type": "tag", - "state": { - "sortOrder": "frequency", - "useHierarchy": true, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-tags", - "title": "Tags" - } - }, - { - "id": "e546445f9f92dc38", - "type": "leaf", - "state": { - "type": "all-properties", - "state": { - "sortOrder": "frequency", - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-archive", - "title": "All properties" - } - }, - { - "id": "e6c7e20438cddc79", - "type": "leaf", - "state": { - "type": "bookmarks", - "state": {}, - "icon": "lucide-bookmark", - "title": "Bookmarks" - } - } - ], - "currentTab": 0 - }, - "right": { - "id": "86a48de8a457f6a9", - "type": "mobile-drawer", - "children": [ - { - "id": "35be497b23adb2a9", - "type": "leaf", - "state": { - "type": "backlink", - "state": { - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical", - "showSearch": false, - "searchQuery": "", - "backlinkCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-coming-in", - "title": "Backlinks" - } - }, - { - "id": "612fb695f1f31b4f", - "type": "leaf", - "state": { - "type": "outgoing-link", - "state": { - "linksCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-going-out", - "title": "Outgoing links" - } - }, - { - "id": "1d0fadefe7de7b83", - "type": "leaf", - "state": { - "type": "outline", - "state": { - "followCursor": false, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-list", - "title": "Outline" - } - } - ], - "currentTab": 0 - }, - "left-ribbon": { - "hiddenItems": { - "switcher:Open quick switcher": false, - "graph:Open graph view": false, - "canvas:Create new canvas": false, - "daily-notes:Open today's daily note": false, - "templates:Insert template": false, - "command-palette:Open command palette": false, - "bases:Create new base": false - } - }, - "active": "ca9029e77020d44e", - "lastOpenFiles": [] -} \ No newline at end of file + "main": { + "id": "29fed73056d4c788", + "type": "split", + "children": [ + { + "id": "fa71f00e822e8432", + "type": "tabs", + "children": [ + { + "id": "ca9029e77020d44e", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "c058602dcce78a26", + "type": "mobile-drawer", + "children": [ + { + "id": "d3fd5617bfecca06", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "4d4379f2270ea3e0", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "d99abf483fcdcb1a", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "e546445f9f92dc38", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "All properties" + } + }, + { + "id": "e6c7e20438cddc79", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ], + "currentTab": 0 + }, + "right": { + "id": "86a48de8a457f6a9", + "type": "mobile-drawer", + "children": [ + { + "id": "35be497b23adb2a9", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks" + } + }, + { + "id": "612fb695f1f31b4f", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links" + } + }, + { + "id": "1d0fadefe7de7b83", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "Outline" + } + } + ], + "currentTab": 0 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false, + "bases:Create new base": false + } + }, + "active": "ca9029e77020d44e", + "lastOpenFiles": [] +} diff --git a/astro.config.mjs b/astro.config.mjs index 00cf70dd7..b28e65982 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,18 +1,26 @@ import sitemap from "@astrojs/sitemap"; import svelte from "@astrojs/svelte"; import tailwind from "@astrojs/tailwind"; +import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections"; +import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers"; import swup from "@swup/astro"; +import expressiveCode from "astro-expressive-code"; import icon from "astro-icon"; import { defineConfig } from "astro/config"; +import { defineConfig, passthroughImageService } from "astro/config"; import rehypeAutolinkHeadings from "rehype-autolink-headings"; -import rehypeComponents from "rehype-components";/* Render the custom directive content */ +import rehypeComponents from "rehype-components"; /* Render the custom directive content */ +import rehypeExternalLinks from "rehype-external-links"; import rehypeKatex from "rehype-katex"; import rehypeSlug from "rehype-slug"; -import remarkDirective from "remark-directive";/* Handle directives */ +import remarkDirective from "remark-directive"; /* Handle directives */ import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives"; import remarkMath from "remark-math"; import remarkSectionize from "remark-sectionize"; import { imageFallbackConfig, siteConfig } from "./src/config.ts"; +import { expressiveCodeConfig } from "./src/config.ts"; +// import { pluginLanguageBadge } from "./src/plugins/expressive-code/language-badge.ts"; +import { pluginCustomCopyButton } from "./src/plugins/expressive-code/custom-copy-button.js"; import { AdmonitionComponent } from "./src/plugins/rehype-component-admonition.mjs"; import { GithubCardComponent } from "./src/plugins/rehype-component-github-card.mjs"; import { UrlCardComponent } from "./src/plugins/rehype-component-url-card.mjs"; @@ -20,76 +28,76 @@ import rehypeImageFallback from "./src/plugins/rehype-image-fallback.mjs"; import { parseDirectiveNode } from "./src/plugins/remark-directive-rehype.js"; import { remarkExcerpt } from "./src/plugins/remark-excerpt.js"; import { remarkReadingTime } from "./src/plugins/remark-reading-time.mjs"; -import rehypeExternalLinks from 'rehype-external-links'; -import expressiveCode from "astro-expressive-code"; -import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections"; -import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers"; -import { expressiveCodeConfig } from "./src/config.ts"; -// import { pluginLanguageBadge } from "./src/plugins/expressive-code/language-badge.ts"; -import { pluginCustomCopyButton } from "./src/plugins/expressive-code/custom-copy-button.js"; -import { defineConfig, passthroughImageService } from 'astro/config'; // https://astro.build/config export default defineConfig({ - image: { - service: passthroughImageService() - }, - site: "https://blog.acofork.com", - base: "/", - trailingSlash: "always", - output: "static", - redirects: { - "/donate": "/sponsors", - "/ak": "https://akile.io/register?aff_code=503fe5ea-e7c5-4d68-ae05-6de99513680e", - "/kook": "https://kook.vip/K29zpT", - "/long": "https://iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.in/", - "/mly": "https://muleyun.com/aff/GOTRJLPN", - "/tg": "https://t.me/+_07DERp7k1ljYTc1", - "/tit": "/posts/pin/", - "/tly": "https://tianlicloud.cn/aff/HNNCFKGP", - "/wly": "https://wl.awcmam.com/#/register?code=FNQwOQBM", - "/yyb": "https://www.rainyun.com/acofork_?s=bilibili", - "/iku": "https://ikuuu.de/auth/register?code=Bjou", - "/esa": "https://tianchi.aliyun.com/specials/promotion/freetier/esa?taskCode=25254&recordId=c856e61228828a0423417a767828d166" - }, - integrations: [tailwind({ - nesting: true, - }), swup({ - theme: false, - animationClass: "transition-swup-", // see https://swup.js.org/options/#animationselector - // the default value `transition-` cause transition delay - // when the Tailwind class `transition-all` is used - containers: ["main", "#toc"], - smoothScrolling: true, - cache: true, - preload: true, - accessibility: true, - updateHead: true, - updateBodyClass: false, - globalInstance: true, - }), icon({ - include: { - "preprocess: vitePreprocess(),": ["*"], - "fa6-brands": ["*"], - "fa6-regular": ["*"], - "fa6-solid": ["*"], - "simple-icons": ["*"], - "material-symbols-light": ["*"], - "material-symbols": ["*"], - }, - }), svelte(), sitemap(), - expressiveCode({ + image: { + service: passthroughImageService(), + }, + site: "https://blog.acofork.com", + base: "/", + trailingSlash: "always", + output: "static", + redirects: { + "/donate": "/sponsors", + "/ak": + "https://akile.io/register?aff_code=503fe5ea-e7c5-4d68-ae05-6de99513680e", + "/kook": "https://kook.vip/K29zpT", + "/long": + "https://iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.in/", + "/mly": "https://muleyun.com/aff/GOTRJLPN", + "/tg": "https://t.me/+_07DERp7k1ljYTc1", + "/tit": "/posts/pin/", + "/tly": "https://tianlicloud.cn/aff/HNNCFKGP", + "/wly": "https://wl.awcmam.com/#/register?code=FNQwOQBM", + "/yyb": "https://www.rainyun.com/acofork_?s=bilibili", + "/iku": "https://ikuuu.de/auth/register?code=Bjou", + "/esa": + "https://tianchi.aliyun.com/specials/promotion/freetier/esa?taskCode=25254&recordId=c856e61228828a0423417a767828d166", + }, + integrations: [ + tailwind({ + nesting: true, + }), + swup({ + theme: false, + animationClass: "transition-swup-", // see https://swup.js.org/options/#animationselector + // the default value `transition-` cause transition delay + // when the Tailwind class `transition-all` is used + containers: ["main", "#toc"], + smoothScrolling: true, + cache: true, + preload: true, + accessibility: true, + updateHead: true, + updateBodyClass: false, + globalInstance: true, + }), + icon({ + include: { + "preprocess: vitePreprocess(),": ["*"], + "fa6-brands": ["*"], + "fa6-regular": ["*"], + "fa6-solid": ["*"], + "simple-icons": ["*"], + "material-symbols-light": ["*"], + "material-symbols": ["*"], + }, + }), + svelte(), + sitemap(), + expressiveCode({ themes: [expressiveCodeConfig.theme, expressiveCodeConfig.theme], plugins: [ pluginCollapsibleSections(), pluginLineNumbers(), // pluginLanguageBadge(), - pluginCustomCopyButton() + pluginCustomCopyButton(), ], defaultProps: { wrap: true, overridesByLang: { - 'shellsession': { + shellsession: { showLineNumbers: false, }, }, @@ -99,7 +107,8 @@ export default defineConfig({ borderRadius: "0.25rem", borderColor: "none", codeFontSize: "0.875rem", - codeFontFamily: "'JetBrains Mono Variable', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", + codeFontFamily: + "'JetBrains Mono Variable', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", codeLineHeight: "1.5rem", frames: { editorBackground: "var(--codeblock-bg)", @@ -110,92 +119,92 @@ export default defineConfig({ editorActiveTabIndicatorBottomColor: "var(--primary)", editorActiveTabIndicatorTopColor: "none", editorTabBarBorderBottomColor: "var(--codeblock-topbar-bg)", - terminalTitlebarBorderBottomColor: "none" + terminalTitlebarBorderBottomColor: "none", }, textMarkers: { delHue: 0, insHue: 180, - markHue: 250 - } + markHue: 250, + }, }, frames: { showCopyToClipboardButton: false, - } + }, }), ], - markdown: { - remarkPlugins: [ - remarkMath, - remarkReadingTime, - remarkExcerpt, - remarkGithubAdmonitionsToDirectives, - remarkDirective, - remarkSectionize, - parseDirectiveNode, - ], - rehypePlugins: [ - rehypeKatex, - rehypeSlug, - [rehypeImageFallback, imageFallbackConfig], - [ - rehypeComponents, - { - components: { - github: GithubCardComponent, - url: UrlCardComponent, - note: (x, y) => AdmonitionComponent(x, y, "note"), - tip: (x, y) => AdmonitionComponent(x, y, "tip"), - important: (x, y) => AdmonitionComponent(x, y, "important"), - caution: (x, y) => AdmonitionComponent(x, y, "caution"), - warning: (x, y) => AdmonitionComponent(x, y, "warning"), - }, - }, - ], + markdown: { + remarkPlugins: [ + remarkMath, + remarkReadingTime, + remarkExcerpt, + remarkGithubAdmonitionsToDirectives, + remarkDirective, + remarkSectionize, + parseDirectiveNode, + ], + rehypePlugins: [ + rehypeKatex, + rehypeSlug, + [rehypeImageFallback, imageFallbackConfig], + [ + rehypeComponents, + { + components: { + github: GithubCardComponent, + url: UrlCardComponent, + note: (x, y) => AdmonitionComponent(x, y, "note"), + tip: (x, y) => AdmonitionComponent(x, y, "tip"), + important: (x, y) => AdmonitionComponent(x, y, "important"), + caution: (x, y) => AdmonitionComponent(x, y, "caution"), + warning: (x, y) => AdmonitionComponent(x, y, "warning"), + }, + }, + ], [ rehypeExternalLinks, { - target: '_blank', + target: "_blank", }, ], - [ - rehypeAutolinkHeadings, - { - behavior: "append", - properties: { - className: ["anchor"], - }, - content: { - type: "element", - tagName: "span", - properties: { - className: ["anchor-icon"], - "data-pagefind-ignore": true, - }, - children: [ - { - type: "text", - value: "#", - }, - ], - }, - }, - ], - ], - }, - vite: { - build: { - rollupOptions: { - onwarn(warning, warn) { - // temporarily suppress this warning - if ( - warning.message.includes("is dynamically imported by") && - warning.message.includes("but also statically imported by") - ) { - return; - } - warn(warning); - }, - }, - }, - }, -}); \ No newline at end of file + [ + rehypeAutolinkHeadings, + { + behavior: "append", + properties: { + className: ["anchor"], + }, + content: { + type: "element", + tagName: "span", + properties: { + className: ["anchor-icon"], + "data-pagefind-ignore": true, + }, + children: [ + { + type: "text", + value: "#", + }, + ], + }, + }, + ], + ], + }, + vite: { + build: { + rollupOptions: { + onwarn(warning, warn) { + // temporarily suppress this warning + if ( + warning.message.includes("is dynamically imported by") && + warning.message.includes("but also statically imported by") + ) { + return; + } + warn(warning); + }, + }, + }, + }, +}); diff --git a/biome.json b/biome.json index 4ccad49c3..b596a66b4 100644 --- a/biome.json +++ b/biome.json @@ -1,43 +1,50 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", - "vcs": { - "enabled": false, - "clientKind": "git", - "useIgnoreFile": false - }, - "files": { - "ignoreUnknown": false, - "ignore": ["src/**/*.css","src/public/**/*", "dist/**/*", "node_modules/**/*"] - }, - "formatter": { - "enabled": true, - "indentStyle": "tab" - }, - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "overrides": [ - { - "include": ["*.svelte", "*.astro", "*.vue"], - "linter": { - "rules": { - "style": { - "useConst": "off", - "useImportType": "off" - } - } - } - } - ] + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [ + "src/**/*.css", + "public/**/*", + ".astro/**/*", + "src/content/.obsidian/**/*", + "dist/**/*", + "node_modules/**/*" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "overrides": [ + { + "include": ["*.svelte", "*.astro", "*.vue"], + "linter": { + "rules": { + "style": { + "useConst": "off", + "useImportType": "off" + } + } + } + } + ] } diff --git a/edgeone.json b/edgeone.json index a14c92b94..c6aa67fd5 100644 --- a/edgeone.json +++ b/edgeone.json @@ -1,59 +1,59 @@ { - "redirects": [ - { - "source": "/ak", - "destination": "https://akile.io/register?aff_code=503fe5ea-e7c5-4d68-ae05-6de99513680e", - "statusCode": 302 - }, - { - "source": "/kook", - "destination": "https://kook.vip/K29zpT", - "statusCode": 302 - }, - { - "source": "/long", - "destination": "https://iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.in/", - "statusCode": 302 - }, - { - "source": "/mly", - "destination": "https://muleyun.com/aff/GOTRJLPN", - "statusCode": 302 - }, - { - "source": "/tg", - "destination": "https://t.me/+_07DERp7k1ljYTc1", - "statusCode": 302 - }, - { - "source": "/tit", - "destination": "/posts/pin/", - "statusCode": 302 - }, - { - "source": "/tly", - "destination": "https://tianlicloud.cn/aff/HNNCFKGP", - "statusCode": 302 - }, - { - "source": "/wly", - "destination": "https://wl.awcmam.com/#/register?code=FNQwOQBM", - "statusCode": 302 - }, - { - "source": "/yyb", - "destination": "https://www.rainyun.com/acofork_?s=bilibili", - "statusCode": 302 - }, - { - "source": "/iku", - "destination": "https://ikuuu.de/auth/register?code=Bjou", - "statusCode": 302 - }, - { - "source": "/esa", - "destination": "https://tianchi.aliyun.com/specials/promotion/freetier/esa?taskCode=25254&recordId=c856e61228828a0423417a767828d166", - "statusCode": 302 - } - ] -} \ No newline at end of file + "redirects": [ + { + "source": "/ak", + "destination": "https://akile.io/register?aff_code=503fe5ea-e7c5-4d68-ae05-6de99513680e", + "statusCode": 302 + }, + { + "source": "/kook", + "destination": "https://kook.vip/K29zpT", + "statusCode": 302 + }, + { + "source": "/long", + "destination": "https://iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii.in/", + "statusCode": 302 + }, + { + "source": "/mly", + "destination": "https://muleyun.com/aff/GOTRJLPN", + "statusCode": 302 + }, + { + "source": "/tg", + "destination": "https://t.me/+_07DERp7k1ljYTc1", + "statusCode": 302 + }, + { + "source": "/tit", + "destination": "/posts/pin/", + "statusCode": 302 + }, + { + "source": "/tly", + "destination": "https://tianlicloud.cn/aff/HNNCFKGP", + "statusCode": 302 + }, + { + "source": "/wly", + "destination": "https://wl.awcmam.com/#/register?code=FNQwOQBM", + "statusCode": 302 + }, + { + "source": "/yyb", + "destination": "https://www.rainyun.com/acofork_?s=bilibili", + "statusCode": 302 + }, + { + "source": "/iku", + "destination": "https://ikuuu.de/auth/register?code=Bjou", + "statusCode": 302 + }, + { + "source": "/esa", + "destination": "https://tianchi.aliyun.com/specials/promotion/freetier/esa?taskCode=25254&recordId=c856e61228828a0423417a767828d166", + "statusCode": 302 + } + ] +} diff --git a/frontmatter.json b/frontmatter.json index ac17e0af8..038180080 100644 --- a/frontmatter.json +++ b/frontmatter.json @@ -1,62 +1,62 @@ { - "$schema": "https://frontmatter.codes/frontmatter.schema.json", - "frontMatter.framework.id": "astro", - "frontMatter.preview.host": "http://localhost:4321", - "frontMatter.content.publicFolder": "public", - "frontMatter.content.pageFolders": [ - { - "title": "posts", - "path": "[[workspace]]/src/content/posts" - } - ], - "frontMatter.taxonomy.contentTypes": [ - { - "name": "default", - "pageBundle": true, - "previewPath": "'blog'", - "filePrefix": null, - "clearEmpty": true, - "fields": [ - { - "title": "title", - "name": "title", - "type": "string", - "single": true - }, - { - "title": "description", - "name": "description", - "type": "string" - }, - { - "title": "published", - "name": "published", - "type": "datetime", - "default": "{{now}}", - "isPublishDate": true - }, - { - "title": "preview", - "name": "image", - "type": "image", - "isPreviewImage": true - }, - { - "title": "tags", - "name": "tags", - "type": "list" - }, - { - "title": "draft", - "name": "draft", - "type": "boolean" - }, - { - "title": "language", - "name": "language", - "type": "string" - } - ] - } - ] + "$schema": "https://frontmatter.codes/frontmatter.schema.json", + "frontMatter.framework.id": "astro", + "frontMatter.preview.host": "http://localhost:4321", + "frontMatter.content.publicFolder": "public", + "frontMatter.content.pageFolders": [ + { + "title": "posts", + "path": "[[workspace]]/src/content/posts" + } + ], + "frontMatter.taxonomy.contentTypes": [ + { + "name": "default", + "pageBundle": true, + "previewPath": "'blog'", + "filePrefix": null, + "clearEmpty": true, + "fields": [ + { + "title": "title", + "name": "title", + "type": "string", + "single": true + }, + { + "title": "description", + "name": "description", + "type": "string" + }, + { + "title": "published", + "name": "published", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "preview", + "name": "image", + "type": "image", + "isPreviewImage": true + }, + { + "title": "tags", + "name": "tags", + "type": "list" + }, + { + "title": "draft", + "name": "draft", + "type": "boolean" + }, + { + "title": "language", + "name": "language", + "type": "string" + } + ] + } + ] } diff --git a/package.json b/package.json index 1e66f62a6..3de0b11d7 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@astrojs/check": "^0.9.4", + "@astrojs/react": "^4.4.2", "@astrojs/rss": "^4.0.11", "@astrojs/sitemap": "^3.3.1", "@astrojs/svelte": "7.0.12", @@ -27,6 +28,7 @@ "@fancyapps/ui": "^6.0.5", "@fontsource-variable/jetbrains-mono": "^5.2.5", "@fontsource/roboto": "^5.2.5", + "@gsap/react": "^2.1.2", "@iconify-json/fa6-brands": "^1.2.5", "@iconify-json/fa6-regular": "^1.2.3", "@iconify-json/fa6-solid": "^1.2.3", @@ -35,11 +37,14 @@ "@iconify/svelte": "^4.2.0", "@swup/astro": "^1.6.0", "@tailwindcss/typography": "^0.5.16", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@vercel/analytics": "^1.5.0", "astro": "5.7.9", "astro-expressive-code": "^0.41.3", "astro-icon": "^1.1.5", "glob": "^11.0.3", + "gsap": "^3.14.2", "hastscript": "^9.0.1", "katex": "^0.16.22", "markdown-it": "^14.1.0", @@ -47,6 +52,8 @@ "node-html-parser": "^7.0.1", "overlayscrollbars": "^2.11.1", "photoswipe": "^5.4.4", + "react": "^19.2.3", + "react-dom": "^19.2.3", "reading-time": "^1.5.0", "rehype-autolink-headings": "^7.1.0", "rehype-components": "^0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad553e48d..efa84478b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,6 +16,9 @@ importers: '@astrojs/check': specifier: ^0.9.4 version: 0.9.4(typescript@5.8.3) + '@astrojs/react': + specifier: ^4.4.2 + version: 4.4.2(@types/node@22.14.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(jiti@1.21.7)(lightningcss@1.29.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0) '@astrojs/rss': specifier: ^4.0.11 version: 4.0.11 @@ -30,7 +33,7 @@ importers: version: 6.0.2(astro@5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0))(tailwindcss@3.4.17) '@astrojs/vercel': specifier: ^8.2.7 - version: 8.2.7(astro@5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0))(rollup@2.79.2)(svelte@5.28.2) + version: 8.2.7(astro@5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0))(react@19.2.3)(rollup@2.79.2)(svelte@5.28.2) '@expressive-code/core': specifier: ^0.41.3 version: 0.41.3 @@ -49,6 +52,9 @@ importers: '@fontsource/roboto': specifier: ^5.2.5 version: 5.2.5 + '@gsap/react': + specifier: ^2.1.2 + version: 2.1.2(gsap@3.14.2)(react@19.2.3) '@iconify-json/fa6-brands': specifier: ^1.2.5 version: 1.2.5 @@ -73,9 +79,15 @@ importers: '@tailwindcss/typography': specifier: ^0.5.16 version: 0.5.16(tailwindcss@3.4.17) + '@types/react': + specifier: ^19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(svelte@5.28.2) + version: 1.5.0(react@19.2.3)(svelte@5.28.2) astro: specifier: 5.7.9 version: 5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0) @@ -88,6 +100,9 @@ importers: glob: specifier: ^11.0.3 version: 11.0.3 + gsap: + specifier: ^3.14.2 + version: 3.14.2 hastscript: specifier: ^9.0.1 version: 9.0.1 @@ -109,6 +124,12 @@ importers: photoswipe: specifier: ^5.4.4 version: 5.4.4 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) reading-time: specifier: ^1.5.0 version: 1.5.0 @@ -248,6 +269,15 @@ packages: resolution: {integrity: sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw==} engines: {node: ^18.17.1 || ^20.3.0 || >=22.0.0} + '@astrojs/react@4.4.2': + resolution: {integrity: sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0 + '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + '@astrojs/rss@4.0.11': resolution: {integrity: sha512-3e3H8i6kc97KGnn9iaZBJpIkdoQi8MmR5zH5R+dWsfCM44lLTszOqy1OBfGGxDt56mpQkYVtZJWoxMyWuUZBfw==} @@ -287,18 +317,34 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.26.8': resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + '@babel/core@7.26.10': resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.27.0': resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.25.9': resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} @@ -307,6 +353,10 @@ packages: resolution: {integrity: sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.27.0': resolution: {integrity: sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==} engines: {node: '>=6.9.0'} @@ -324,6 +374,10 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.25.9': resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} engines: {node: '>=6.9.0'} @@ -332,12 +386,22 @@ packages: resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.26.0': resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.25.9': resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} @@ -346,6 +410,10 @@ packages: resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.25.9': resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} engines: {node: '>=6.9.0'} @@ -378,10 +446,18 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.25.9': resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.25.9': resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} @@ -390,6 +466,10 @@ packages: resolution: {integrity: sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.27.0': resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} engines: {node: '>=6.0.0'} @@ -400,6 +480,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} engines: {node: '>=6.9.0'} @@ -723,6 +808,18 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx@7.25.9': resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} engines: {node: '>=6.9.0'} @@ -838,10 +935,18 @@ packages: resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.27.0': resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.27.0': resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} @@ -850,6 +955,10 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@biomejs/biome@1.9.4': resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} engines: {node: '>=14.21.3'} @@ -1130,6 +1239,12 @@ packages: '@fontsource/roboto@5.2.5': resolution: {integrity: sha512-70r2UZ0raqLn5W+sPeKhqlf8wGvUXFWlofaDlcbt/S3d06+17gXKr3VNqDODB0I1ASme3dGT5OJj9NABt7OTZQ==} + '@gsap/react@2.1.2': + resolution: {integrity: sha512-JqliybO1837UcgH2hVOM4VO+38APk3ECNrsuSM4MuXp+rbf+/2IG2K1YJiqfTcXQHH7XlA0m3ykniFYstfq0Iw==} + peerDependencies: + gsap: ^3.12.5 + react: '>=17' + '@iconify-json/fa6-brands@1.2.5': resolution: {integrity: sha512-U/iFfziz6jSN9zArOJZYTtoj2tQyh6MxPdI8M84DQ2kEulPaj8j+h9bqvjmzszNHmD7v+kmmKd/MLkMKk+3Zuw==} @@ -1418,10 +1533,16 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1439,6 +1560,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mapbox/node-pre-gyp@2.0.0': resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} engines: {node: '>=18'} @@ -1551,6 +1675,9 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/plugin-alias@3.1.9': resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} engines: {node: '>=8.0.0'} @@ -1928,6 +2055,14 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + '@types/resolve@1.17.1': resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} @@ -1999,6 +2134,12 @@ packages: '@vercel/routing-utils@5.1.1': resolution: {integrity: sha512-EyOik06V2fPXAbKY087BM7DMOQOJK+9mubwwox1TkDi21tMeJcMYwsXwepm6ZmyZ5u0j1TpJW172fP4MbzaCcg==} + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@volar/kit@2.4.12': resolution: {integrity: sha512-f9JE8oy9C2rBcCWxUYKUF23hOXz4mwgVXFjk7nHhxzplaoVjEOsKpBm8NI2nBH7Cwu8DRxDwBsbIxMl/8wlLxw==} peerDependencies: @@ -2500,6 +2641,9 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2967,6 +3111,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + gsap@3.14.2: + resolution: {integrity: sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==} + gzip-size@3.0.0: resolution: {integrity: sha512-6s8trQiK+OMzSaCSVXX+iqIcLV9tC+E73jrJrJTyS4h/AJhlxHvzFKqM1YLDJWRGgHX8uLkBeXkA0njNj39L4w==} engines: {node: '>=0.12.0'} @@ -4404,6 +4551,19 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} @@ -4657,6 +4817,9 @@ packages: sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + scrl@2.0.0: resolution: {integrity: sha512-BbbVXxrOn58Ge4wjOORIRVZamssQu08ISLL/AC2z9aATIsKqZLESwZVW5YR0Yz0C7qqDRHb4yNXJlQ8yW0SGHw==} @@ -5216,6 +5379,46 @@ packages: yaml: optional: true + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitefu@1.0.6: resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==} peerDependencies: @@ -5556,6 +5759,29 @@ snapshots: dependencies: prismjs: 1.30.0 + '@astrojs/react@4.4.2(@types/node@22.14.1)(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(jiti@1.21.7)(lightningcss@1.29.3)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0)': + dependencies: + '@types/react': 19.2.7 + '@types/react-dom': 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react': 4.7.0(vite@6.4.1(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0)) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + ultrahtml: 1.6.0 + vite: 6.4.1(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + '@astrojs/rss@4.0.11': dependencies: fast-xml-parser: 4.5.3 @@ -5621,10 +5847,10 @@ snapshots: semver: 7.7.1 vscode-languageserver-textdocument: 1.0.12 - '@astrojs/vercel@8.2.7(astro@5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0))(rollup@2.79.2)(svelte@5.28.2)': + '@astrojs/vercel@8.2.7(astro@5.7.9(patch_hash=ysrllw3gj6hylybllnynya5oma)(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(rollup@2.79.2)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(typescript@5.8.3)(yaml@2.7.0))(react@19.2.3)(rollup@2.79.2)(svelte@5.28.2)': dependencies: '@astrojs/internal-helpers': 0.7.2 - '@vercel/analytics': 1.5.0(svelte@5.28.2) + '@vercel/analytics': 1.5.0(react@19.2.3)(svelte@5.28.2) '@vercel/functions': 2.2.13 '@vercel/nft': 0.29.4(rollup@2.79.2) '@vercel/routing-utils': 5.1.1 @@ -5654,8 +5880,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.28.5': {} + '@babel/core@7.26.10': dependencies: '@ampproject/remapping': 2.3.0 @@ -5676,6 +5910,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.0 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.27.0': dependencies: '@babel/parser': 7.27.0 @@ -5684,6 +5938,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.25.9': dependencies: '@babel/types': 7.27.0 @@ -5696,6 +5958,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.27.0(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -5727,6 +5997,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-member-expression-to-functions@7.25.9': dependencies: '@babel/traverse': 7.27.0 @@ -5741,6 +6013,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -5750,12 +6029,23 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.25.9': dependencies: '@babel/types': 7.27.0 '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.27.1': {} + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -5783,16 +6073,18 @@ snapshots: '@babel/helper-string-parser@7.25.9': {} - '@babel/helper-string-parser@7.27.1': - optional: true + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-identifier@7.27.1': - optional: true + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-wrap-function@7.25.9': dependencies: '@babel/template': 7.27.0 @@ -5806,6 +6098,11 @@ snapshots: '@babel/template': 7.27.0 '@babel/types': 7.27.0 + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + '@babel/parser@7.27.0': dependencies: '@babel/types': 7.27.0 @@ -5813,7 +6110,10 @@ snapshots: '@babel/parser@7.28.0': dependencies: '@babel/types': 7.28.2 - optional: true + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.10)': dependencies: @@ -6162,6 +6462,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10)': dependencies: '@babel/core': 7.26.10 @@ -6358,6 +6668,12 @@ snapshots: '@babel/parser': 7.27.0 '@babel/types': 7.27.0 + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@babel/traverse@7.27.0': dependencies: '@babel/code-frame': 7.26.2 @@ -6370,6 +6686,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.27.0': dependencies: '@babel/helper-string-parser': 7.25.9 @@ -6379,7 +6707,11 @@ snapshots: dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - optional: true + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@biomejs/biome@1.9.4': optionalDependencies: @@ -6581,6 +6913,11 @@ snapshots: '@fontsource/roboto@5.2.5': {} + '@gsap/react@2.1.2(gsap@3.14.2)(react@19.2.3)': + dependencies: + gsap: 3.14.2 + react: 19.2.3 + '@iconify-json/fa6-brands@1.2.5': dependencies: '@iconify/types': 2.0.0 @@ -6814,12 +7151,22 @@ snapshots: dependencies: minipass: 7.1.2 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} @@ -6836,6 +7183,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@mapbox/node-pre-gyp@2.0.0': dependencies: consola: 3.4.2 @@ -6927,6 +7279,8 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rollup/plugin-alias@3.1.9(rollup@2.79.2)': dependencies: rollup: 2.79.2 @@ -7279,23 +7633,19 @@ snapshots: '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 - optional: true '@types/babel__generator@7.27.0': dependencies: '@babel/types': 7.28.2 - optional: true '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.28.0 '@babel/types': 7.28.2 - optional: true '@types/babel__traverse@7.28.0': dependencies: '@babel/types': 7.28.2 - optional: true '@types/debug@4.1.12': dependencies: @@ -7342,6 +7692,14 @@ snapshots: '@types/parse-json@4.0.2': {} + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + '@types/resolve@1.17.1': dependencies: '@types/node': 22.14.1 @@ -7370,8 +7728,9 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vercel/analytics@1.5.0(svelte@5.28.2)': + '@vercel/analytics@1.5.0(react@19.2.3)(svelte@5.28.2)': optionalDependencies: + react: 19.2.3 svelte: 5.28.2 '@vercel/functions@2.2.13': @@ -7409,6 +7768,18 @@ snapshots: optionalDependencies: ajv: 6.12.6 + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.1(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0) + transitivePeerDependencies: + - supports-color + '@volar/kit@2.4.12(typescript@5.8.3)': dependencies: '@volar/language-service': 2.4.12 @@ -8076,6 +8447,8 @@ snapshots: dependencies: css-tree: 2.2.1 + csstype@3.2.3: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -8614,6 +8987,8 @@ snapshots: graceful-fs@4.2.11: {} + gsap@3.14.2: {} + gzip-size@3.0.0: dependencies: duplexer: 0.1.2 @@ -10296,6 +10671,15 @@ snapshots: dependencies: safe-buffer: 5.2.1 + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-refresh@0.17.0: {} + + react@19.2.3: {} + read-cache@1.0.0: dependencies: pify: 2.3.0 @@ -10701,6 +11085,8 @@ snapshots: sax@1.4.1: {} + scheduler@0.27.0: {} + scrl@2.0.0: {} semver@6.3.1: {} @@ -11372,6 +11758,24 @@ snapshots: terser: 5.39.0 yaml: 2.7.0 + vite@6.4.1(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0): + dependencies: + esbuild: 0.25.3 + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.1 + tinyglobby: 0.2.13 + optionalDependencies: + '@types/node': 22.14.1 + fsevents: 2.3.3 + jiti: 1.21.7 + lightningcss: 1.29.3 + sass: 1.80.4 + stylus: 0.64.0 + terser: 5.39.0 + yaml: 2.7.0 + vitefu@1.0.6(vite@6.3.3(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0)): optionalDependencies: vite: 6.3.3(@types/node@22.14.1)(jiti@1.21.7)(lightningcss@1.29.3)(sass@1.80.4)(stylus@0.64.0)(terser@5.39.0)(yaml@2.7.0) diff --git a/postcss.config.mjs b/postcss.config.mjs index 632eeda9d..2c368be96 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,11 +1,11 @@ -import postcssImport from 'postcss-import'; -import postcssNesting from 'tailwindcss/nesting/index.js'; -import tailwindcss from 'tailwindcss'; +import postcssImport from "postcss-import"; +import tailwindcss from "tailwindcss"; +import postcssNesting from "tailwindcss/nesting/index.js"; export default { - plugins: { - 'postcss-import': postcssImport, // to combine multiple css files - 'tailwindcss/nesting': postcssNesting, - tailwindcss: tailwindcss, - } + plugins: { + "postcss-import": postcssImport, // to combine multiple css files + "tailwindcss/nesting": postcssNesting, + tailwindcss: tailwindcss, + }, }; diff --git a/public/js/random.js b/public/js/random.js index 7e560465b..37b04a33d 100644 --- a/public/js/random.js +++ b/public/js/random.js @@ -2,133 +2,142 @@ * Static Random Pic API * Generated by build script */ -(function() { - var counts = {"h":979,"v":3596}; - var domain = 'https://pic.acofork.com'; - - // State management for session consistency - var sessionRandomH = null; - var sessionRandomV = null; +(() => { + var counts = { h: 979, v: 3596 }; + var domain = "https://pic.acofork.com"; - // Helper: Get random URL for a type (h or v), persistent per session - function getRandomUrl(type) { - if (!counts[type] || counts[type] === 0) return ''; - - // Return existing session URL if available - if (type === 'h' && sessionRandomH) return sessionRandomH; - if (type === 'v' && sessionRandomV) return sessionRandomV; + // State management for session consistency + var sessionRandomH = null; + var sessionRandomV = null; - // Generate new if not exists - var num = Math.floor(Math.random() * counts[type]) + 1; - var url = domain + '/ri/' + type + '/' + num + '.webp'; + // Helper: Get random URL for a type (h or v), persistent per session + function getRandomUrl(type) { + if (!counts[type] || counts[type] === 0) return ""; - // Save to session state - if (type === 'h') sessionRandomH = url; - if (type === 'v') sessionRandomV = url; + // Return existing session URL if available + if (type === "h" && sessionRandomH) return sessionRandomH; + if (type === "v" && sessionRandomV) return sessionRandomV; - return url; - } + // Generate new if not exists + var num = Math.floor(Math.random() * counts[type]) + 1; + var url = domain + "/ri/" + type + "/" + num + ".webp"; - // Expose global functions - window.getRandomPicH = function() { return getRandomUrl('h'); }; - window.getRandomPicV = function() { return getRandomUrl('v'); }; + // Save to session state + if (type === "h") sessionRandomH = url; + if (type === "v") sessionRandomV = url; - // 1. Logic for Background (Customized based on user request) - function setRandomBackground() { - // Get random URL using the helper (Dynamic count & domain) - const bgUrl = getRandomUrl('h'); - - // Find the background box element - const bgBox = document.getElementById('bg-box'); - - if (bgBox) { - // Preload image - const img = new Image(); - img.onload = function() { - bgBox.style.backgroundImage = `url('${bgUrl}')`; - bgBox.classList.add('loaded'); - console.log('Random background loaded:', bgUrl); - - // Set CSS variables for transparency effects - document.documentElement.style.setProperty('--card-bg', 'var(--card-bg-transparent)'); - document.documentElement.style.setProperty('--float-panel-bg', 'var(--float-panel-bg-transparent)'); - }; - img.onerror = function() { - console.error('Failed to load background image:', bgUrl); - }; - img.src = bgUrl; - } else { - // Fallback: If no #bg-box, check for data-random-bg for backward compatibility/other elements - // This keeps the generic functionality available if needed, but prioritizes the user's specific logic above. - initGenericBackgrounds(); - } - } + return url; + } - // 2. Logic for Image Tags (Generic) - function initImgTags() { - var imgTags = document.getElementsByTagName('img'); - for (var i = 0; i < imgTags.length; i++) { - var img = imgTags[i]; - var alt = img.getAttribute('alt'); - var src = img.getAttribute('src'); + // Expose global functions + window.getRandomPicH = () => getRandomUrl("h"); + window.getRandomPicV = () => getRandomUrl("v"); - if (alt === 'random:h' || (src && src.indexOf('/random/h') !== -1)) { - img.src = getRandomUrl('h'); - } else if (alt === 'random:v' || (src && src.indexOf('/random/v') !== -1)) { - img.src = getRandomUrl('v'); - } - } - } + // 1. Logic for Background (Customized based on user request) + function setRandomBackground() { + // Get random URL using the helper (Dynamic count & domain) + const bgUrl = getRandomUrl("h"); - // Helper for generic data-random-bg (as a backup or secondary feature) - function initGenericBackgrounds() { - var bgElements = document.querySelectorAll('[data-random-bg]'); - bgElements.forEach(function(el) { - // Skip if it is the bg-box we already handled (though setRandomBackground handles #bg-box specifically) - if (el.id === 'bg-box') return; + // Find the background box element + const bgBox = document.getElementById("bg-box"); - var type = el.getAttribute('data-random-bg'); - if (type === 'h' || type === 'v') { - var url = getRandomUrl(type); - if (url) { - var img = new Image(); - img.onload = function() { - el.style.backgroundImage = 'url("' + url + '")'; - el.classList.add('loaded'); - }; - img.src = url; - } - } - }); - } + if (bgBox) { + // Preload image + const img = new Image(); + img.onload = () => { + bgBox.style.backgroundImage = `url('${bgUrl}')`; + bgBox.classList.add("loaded"); + console.log("Random background loaded:", bgUrl); - function init() { - setRandomBackground(); - initImgTags(); - } - - // Run on initial load - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { - init(); - } - - // Swup integration - function setupSwup() { - if (window.swup && window.swup.hooks) { - // Register hook for content replacement - window.swup.hooks.on('content:replace', init); - console.log('Random Pic API: Registered with Swup hooks.'); - } - } + // Set CSS variables for transparency effects + document.documentElement.style.setProperty( + "--card-bg", + "var(--card-bg-transparent)", + ); + document.documentElement.style.setProperty( + "--float-panel-bg", + "var(--float-panel-bg-transparent)", + ); + }; + img.onerror = () => { + console.error("Failed to load background image:", bgUrl); + }; + img.src = bgUrl; + } else { + // Fallback: If no #bg-box, check for data-random-bg for backward compatibility/other elements + // This keeps the generic functionality available if needed, but prioritizes the user's specific logic above. + initGenericBackgrounds(); + } + } - if (window.swup) { - setupSwup(); - } else { - document.addEventListener('swup:enable', setupSwup); - } + // 2. Logic for Image Tags (Generic) + function initImgTags() { + var imgTags = document.getElementsByTagName("img"); + for (var i = 0; i < imgTags.length; i++) { + var img = imgTags[i]; + var alt = img.getAttribute("alt"); + var src = img.getAttribute("src"); - // Legacy Swup support - document.addEventListener('swup:contentReplaced', init); -})(); \ No newline at end of file + if (alt === "random:h" || (src && src.indexOf("/random/h") !== -1)) { + img.src = getRandomUrl("h"); + } else if ( + alt === "random:v" || + (src && src.indexOf("/random/v") !== -1) + ) { + img.src = getRandomUrl("v"); + } + } + } + + // Helper for generic data-random-bg (as a backup or secondary feature) + function initGenericBackgrounds() { + var bgElements = document.querySelectorAll("[data-random-bg]"); + bgElements.forEach((el) => { + // Skip if it is the bg-box we already handled (though setRandomBackground handles #bg-box specifically) + if (el.id === "bg-box") return; + + var type = el.getAttribute("data-random-bg"); + if (type === "h" || type === "v") { + var url = getRandomUrl(type); + if (url) { + var img = new Image(); + img.onload = () => { + el.style.backgroundImage = 'url("' + url + '")'; + el.classList.add("loaded"); + }; + img.src = url; + } + } + }); + } + + function init() { + setRandomBackground(); + initImgTags(); + } + + // Run on initial load + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } + + // Swup integration + function setupSwup() { + if (window.swup && window.swup.hooks) { + // Register hook for content replacement + window.swup.hooks.on("content:replace", init); + console.log("Random Pic API: Registered with Swup hooks."); + } + } + + if (window.swup) { + setupSwup(); + } else { + document.addEventListener("swup:enable", setupSwup); + } + + // Legacy Swup support + document.addEventListener("swup:contentReplaced", init); +})(); diff --git a/public/js/umami-share.js b/public/js/umami-share.js index 08900a14c..711041ce7 100644 --- a/public/js/umami-share.js +++ b/public/js/umami-share.js @@ -1,91 +1,116 @@ -(function (global) { - const cacheKey = 'umami-share-cache'; - const cacheTTL = 3600_000; // 1h +((global) => { + const cacheKey = "umami-share-cache"; + const cacheTTL = 3600_000; // 1h - async function fetchShareData(baseUrl, shareId) { - const cached = localStorage.getItem(cacheKey); - if (cached) { - try { - const parsed = JSON.parse(cached); - if (Date.now() - parsed.timestamp < cacheTTL) { - return parsed.value; - } - } catch { - localStorage.removeItem(cacheKey); - } - } - const res = await fetch(`${baseUrl}/api/share/${shareId}`); - if (!res.ok) { - throw new Error('获取 Umami 分享信息失败'); - } - const data = await res.json(); - localStorage.setItem(cacheKey, JSON.stringify({ timestamp: Date.now(), value: data })); - return data; - } + async function fetchShareData(baseUrl, shareId) { + const cached = localStorage.getItem(cacheKey); + if (cached) { + try { + const parsed = JSON.parse(cached); + if (Date.now() - parsed.timestamp < cacheTTL) { + return parsed.value; + } + } catch { + localStorage.removeItem(cacheKey); + } + } + const res = await fetch(`${baseUrl}/api/share/${shareId}`); + if (!res.ok) { + throw new Error("获取 Umami 分享信息失败"); + } + const data = await res.json(); + localStorage.setItem( + cacheKey, + JSON.stringify({ timestamp: Date.now(), value: data }), + ); + return data; + } - /** - * 获取 Umami 分享数据(websiteId、token) - * 在缓存 TTL 内复用;并用全局 Promise 避免并发请求 - * @param {string} baseUrl - * @param {string} shareId - * @returns {Promise<{websiteId: string, token: string}>} - */ - global.getUmamiShareData = function (baseUrl, shareId) { - if (!global.__umamiSharePromise) { - global.__umamiSharePromise = fetchShareData(baseUrl, shareId).catch((err) => { - delete global.__umamiSharePromise; - throw err; - }); - } - return global.__umamiSharePromise; - }; + /** + * 获取 Umami 分享数据(websiteId、token) + * 在缓存 TTL 内复用;并用全局 Promise 避免并发请求 + * @param {string} baseUrl + * @param {string} shareId + * @returns {Promise<{websiteId: string, token: string}>} + */ + global.getUmamiShareData = (baseUrl, shareId) => { + if (!global.__umamiSharePromise) { + global.__umamiSharePromise = fetchShareData(baseUrl, shareId).catch( + (err) => { + delete global.__umamiSharePromise; + throw err; + }, + ); + } + return global.__umamiSharePromise; + }; - global.clearUmamiShareCache = function () { - localStorage.removeItem(cacheKey); - delete global.__umamiSharePromise; - }; + global.clearUmamiShareCache = () => { + localStorage.removeItem(cacheKey); + delete global.__umamiSharePromise; + }; - /** - * 获取 Umami 统计数据 - * 自动处理 token 获取和过期重试 - * @param {string} baseUrl - * @param {string} shareId - * @param {object} queryParams - * @returns {Promise} - */ - global.fetchUmamiStats = async function (baseUrl, shareId, queryParams) { - async function doFetch(isRetry = false) { - const { websiteId, token } = await global.getUmamiShareData(baseUrl, shareId); - const currentTimestamp = Date.now(); - const params = new URLSearchParams({ - startAt: 0, - endAt: currentTimestamp, - unit: 'hour', - timezone: queryParams.timezone || 'Asia/Shanghai', - compare: false, - ...queryParams - }); - - const statsUrl = `${baseUrl}/api/websites/${websiteId}/stats?${params.toString()}`; - - const res = await fetch(statsUrl, { - headers: { - 'x-umami-share-token': token - } - }); + // 初始化全局缓存 Map + if (!global.__umamiDataCache) { + global.__umamiDataCache = new Map(); + } - if (!res.ok) { - if (res.status === 401 && !isRetry) { - global.clearUmamiShareCache(); - return doFetch(true); - } - throw new Error('获取统计数据失败'); - } + /** + * 获取 Umami 统计数据 + * 自动处理 token 获取和过期重试 + * @param {string} baseUrl + * @param {string} shareId + * @param {object} queryParams + * @returns {Promise} + */ + global.fetchUmamiStats = async (baseUrl, shareId, queryParams) => { + // 生成缓存键:baseUrl + shareId + queryParams的字符串表示 + const cacheKey = `${baseUrl}|${shareId}|${JSON.stringify(queryParams)}`; + + // 检查全局内存缓存 + if (global.__umamiDataCache.has(cacheKey)) { + const data = global.__umamiDataCache.get(cacheKey); + // 标记数据来自缓存 + return { ...data, _fromCache: true }; + } - return await res.json(); - } + async function doFetch(isRetry = false) { + const { websiteId, token } = await global.getUmamiShareData( + baseUrl, + shareId, + ); + const currentTimestamp = Date.now(); + const params = new URLSearchParams({ + startAt: 0, + endAt: currentTimestamp, + unit: "hour", + timezone: queryParams.timezone || "Asia/Shanghai", + compare: false, + ...queryParams, + }); - return doFetch(); - }; + const statsUrl = `${baseUrl}/api/websites/${websiteId}/stats?${params.toString()}`; -})(window); \ No newline at end of file + const res = await fetch(statsUrl, { + headers: { + "x-umami-share-token": token, + }, + }); + + if (!res.ok) { + if (res.status === 401 && !isRetry) { + global.clearUmamiShareCache(); + return doFetch(true); + } + throw new Error("获取统计数据失败"); + } + + const data = await res.json(); + // 写入全局缓存 + global.__umamiDataCache.set(cacheKey, data); + return data; + } + + return doFetch(); + }; +})(window); diff --git a/scripts/clean-unused-images.js b/scripts/clean-unused-images.js index 96a80a81c..a51379e3b 100644 --- a/scripts/clean-unused-images.js +++ b/scripts/clean-unused-images.js @@ -1,9 +1,9 @@ #!/usr/bin/env node -import fs from 'fs'; -import path from 'path'; -import { glob } from 'glob'; -import { fileURLToPath } from 'url'; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import { glob } from "glob"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -14,233 +14,246 @@ const __dirname = path.dirname(__filename); * 查找 src/content/assets 中未被引用的图片并删除 */ -const CONTENT_DIR = path.join(process.cwd(), 'src/content'); -const POSTS_DIR = path.join(CONTENT_DIR, 'posts'); -const ASSETS_DIR = path.join(CONTENT_DIR, 'assets'); +const CONTENT_DIR = path.join(process.cwd(), "src/content"); +const POSTS_DIR = path.join(CONTENT_DIR, "posts"); +const ASSETS_DIR = path.join(CONTENT_DIR, "assets"); // 支持的图片格式 -const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.avif']; +const IMAGE_EXTENSIONS = [ + ".jpg", + ".jpeg", + ".png", + ".gif", + ".webp", + ".svg", + ".avif", +]; /** * 获取所有 markdown 文件 */ async function getAllMarkdownFiles() { - try { - const pattern = path.join(POSTS_DIR, '**/*.md').replace(/\\/g, '/'); - return await glob(pattern); - } catch (error) { - console.error('获取 markdown 文件失败:', error.message); - return []; - } + try { + const pattern = path.join(POSTS_DIR, "**/*.md").replace(/\\/g, "/"); + return await glob(pattern); + } catch (error) { + console.error("获取 markdown 文件失败:", error.message); + return []; + } } /** * 获取所有图片文件 */ async function getAllImageFiles() { - try { - const extensions = IMAGE_EXTENSIONS.join(','); - const pattern = path.join(ASSETS_DIR, `**/*{${extensions}}`).replace(/\\/g, '/'); - return await glob(pattern); - } catch (error) { - console.error('获取图片文件失败:', error.message); - return []; - } + try { + const extensions = IMAGE_EXTENSIONS.join(","); + const pattern = path + .join(ASSETS_DIR, `**/*{${extensions}}`) + .replace(/\\/g, "/"); + return await glob(pattern); + } catch (error) { + console.error("获取图片文件失败:", error.message); + return []; + } } /** * 从 markdown 内容中提取图片引用 */ function extractImageReferences(content) { - const references = new Set(); - - // 匹配 YAML frontmatter 中的 image 字段(支持带引号和不带引号的值) - const yamlImageRegex = /^---[\s\S]*?image:\s*(?:['"]([^'"]+)['"]|([^\s\n]+))[\s\S]*?^---/m; - let match = yamlImageRegex.exec(content); - if (match) { - // match[1] 是带引号的值,match[2] 是不带引号的值 - references.add(match[1] || match[2]); - } - - // 匹配 markdown 图片语法: ![alt](path) - const markdownImageRegex = /!\[.*?\]\(([^)]+)\)/g; - while ((match = markdownImageRegex.exec(content)) !== null) { - references.add(match[1]); - } - - // 匹配 HTML img 标签: - const htmlImageRegex = /]+src=["']([^"']+)["'][^>]*>/gi; - while ((match = htmlImageRegex.exec(content)) !== null) { - references.add(match[1]); - } - - // 匹配 Astro Image 组件引用 - const astroImageRegex = /import\s+.*?\s+from\s+["']([^"']+\.(jpg|jpeg|png|gif|webp|svg|avif))["']/gi; - while ((match = astroImageRegex.exec(content)) !== null) { - references.add(match[1]); - } - - return Array.from(references); + const references = new Set(); + + // 匹配 YAML frontmatter 中的 image 字段(支持带引号和不带引号的值) + const yamlImageRegex = + /^---[\s\S]*?image:\s*(?:['"]([^'"]+)['"]|([^\s\n]+))[\s\S]*?^---/m; + let match = yamlImageRegex.exec(content); + if (match) { + // match[1] 是带引号的值,match[2] 是不带引号的值 + references.add(match[1] || match[2]); + } + + // 匹配 markdown 图片语法: ![alt](path) + const markdownImageRegex = /!\[.*?\]\(([^)]+)\)/g; + while ((match = markdownImageRegex.exec(content)) !== null) { + references.add(match[1]); + } + + // 匹配 HTML img 标签: + const htmlImageRegex = /]+src=["']([^"']+)["'][^>]*>/gi; + while ((match = htmlImageRegex.exec(content)) !== null) { + references.add(match[1]); + } + + // 匹配 Astro Image 组件引用 + const astroImageRegex = + /import\s+.*?\s+from\s+["']([^"']+\.(jpg|jpeg|png|gif|webp|svg|avif))["']/gi; + while ((match = astroImageRegex.exec(content)) !== null) { + references.add(match[1]); + } + + return Array.from(references); } /** * 规范化路径,处理相对路径和绝对路径 */ function normalizePath(imagePath, markdownFilePath) { - // 跳过外部 URL - if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) { - return null; - } - - // 跳过以 / 开头的绝对路径(通常指向 public 目录) - if (imagePath.startsWith('/')) { - return null; - } - - // 处理相对路径 - if (imagePath.startsWith('./') || imagePath.startsWith('../')) { - const markdownDir = path.dirname(markdownFilePath); - return path.resolve(markdownDir, imagePath); - } - - // 处理直接的文件名或相对路径 - const markdownDir = path.dirname(markdownFilePath); - return path.resolve(markdownDir, imagePath); + // 跳过外部 URL + if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) { + return null; + } + + // 跳过以 / 开头的绝对路径(通常指向 public 目录) + if (imagePath.startsWith("/")) { + return null; + } + + // 处理相对路径 + if (imagePath.startsWith("./") || imagePath.startsWith("../")) { + const markdownDir = path.dirname(markdownFilePath); + return path.resolve(markdownDir, imagePath); + } + + // 处理直接的文件名或相对路径 + const markdownDir = path.dirname(markdownFilePath); + return path.resolve(markdownDir, imagePath); } /** * 主函数 */ async function cleanUnusedImages() { - console.log('🔍 开始扫描未使用的图片资源...'); - - // 检查目录是否存在 - if (!fs.existsSync(POSTS_DIR)) { - console.error(`❌ Posts 目录不存在: ${POSTS_DIR}`); - return; - } - - if (!fs.existsSync(ASSETS_DIR)) { - console.log(`ℹ️ Assets 目录不存在: ${ASSETS_DIR}`); - return; - } - - // 获取所有文件 - const markdownFiles = await getAllMarkdownFiles(); - const imageFiles = await getAllImageFiles(); - - console.log(`📄 找到 ${markdownFiles.length} 个 markdown 文件`); - console.log(`🖼️ 找到 ${imageFiles.length} 个图片文件`); - - if (imageFiles.length === 0) { - console.log('✅ 没有找到图片文件,无需清理'); - return; - } - - // 收集所有被引用的图片 - const referencedImages = new Set(); - - for (const mdFile of markdownFiles) { - try { - const content = fs.readFileSync(mdFile, 'utf-8'); - const references = extractImageReferences(content); - - for (const ref of references) { - const normalizedPath = normalizePath(ref, mdFile); - if (normalizedPath) { - const resolvedPath = path.resolve(normalizedPath); - referencedImages.add(resolvedPath); - } - } - } catch (error) { - console.warn(`⚠️ 读取文件失败: ${mdFile} - ${error.message}`); - } - } - - console.log(`🔗 找到 ${referencedImages.size} 个被引用的图片`); - - // 找出未被引用的图片 - const unusedImages = []; - - for (const imageFile of imageFiles) { - const resolvedImagePath = path.resolve(imageFile); - const isReferenced = referencedImages.has(resolvedImagePath); - - if (!isReferenced) { - unusedImages.push(imageFile); - } - } - - console.log(`🗑️ 找到 ${unusedImages.length} 个未使用的图片`); - - if (unusedImages.length === 0) { - console.log('✅ 所有图片都在使用中,无需清理'); - return; - } - - // 删除未使用的图片 - let deletedCount = 0; - - for (const unusedImage of unusedImages) { - try { - fs.unlinkSync(unusedImage); - console.log(`🗑️ 已删除: ${path.relative(process.cwd(), unusedImage)}`); - deletedCount++; - } catch (error) { - console.error(`❌ 删除失败: ${unusedImage} - ${error.message}`); - } - } - - // 清理空目录 - try { - cleanEmptyDirectories(ASSETS_DIR); - } catch (error) { - console.warn(`⚠️ 清理空目录时出错: ${error.message}`); - } - - console.log(`\n✅ 清理完成!删除了 ${deletedCount} 个未使用的图片文件`); + console.log("🔍 开始扫描未使用的图片资源..."); + + // 检查目录是否存在 + if (!fs.existsSync(POSTS_DIR)) { + console.error(`❌ Posts 目录不存在: ${POSTS_DIR}`); + return; + } + + if (!fs.existsSync(ASSETS_DIR)) { + console.log(`ℹ️ Assets 目录不存在: ${ASSETS_DIR}`); + return; + } + + // 获取所有文件 + const markdownFiles = await getAllMarkdownFiles(); + const imageFiles = await getAllImageFiles(); + + console.log(`📄 找到 ${markdownFiles.length} 个 markdown 文件`); + console.log(`🖼️ 找到 ${imageFiles.length} 个图片文件`); + + if (imageFiles.length === 0) { + console.log("✅ 没有找到图片文件,无需清理"); + return; + } + + // 收集所有被引用的图片 + const referencedImages = new Set(); + + for (const mdFile of markdownFiles) { + try { + const content = fs.readFileSync(mdFile, "utf-8"); + const references = extractImageReferences(content); + + for (const ref of references) { + const normalizedPath = normalizePath(ref, mdFile); + if (normalizedPath) { + const resolvedPath = path.resolve(normalizedPath); + referencedImages.add(resolvedPath); + } + } + } catch (error) { + console.warn(`⚠️ 读取文件失败: ${mdFile} - ${error.message}`); + } + } + + console.log(`🔗 找到 ${referencedImages.size} 个被引用的图片`); + + // 找出未被引用的图片 + const unusedImages = []; + + for (const imageFile of imageFiles) { + const resolvedImagePath = path.resolve(imageFile); + const isReferenced = referencedImages.has(resolvedImagePath); + + if (!isReferenced) { + unusedImages.push(imageFile); + } + } + + console.log(`🗑️ 找到 ${unusedImages.length} 个未使用的图片`); + + if (unusedImages.length === 0) { + console.log("✅ 所有图片都在使用中,无需清理"); + return; + } + + // 删除未使用的图片 + let deletedCount = 0; + + for (const unusedImage of unusedImages) { + try { + fs.unlinkSync(unusedImage); + console.log(`🗑️ 已删除: ${path.relative(process.cwd(), unusedImage)}`); + deletedCount++; + } catch (error) { + console.error(`❌ 删除失败: ${unusedImage} - ${error.message}`); + } + } + + // 清理空目录 + try { + cleanEmptyDirectories(ASSETS_DIR); + } catch (error) { + console.warn(`⚠️ 清理空目录时出错: ${error.message}`); + } + + console.log(`\n✅ 清理完成!删除了 ${deletedCount} 个未使用的图片文件`); } /** * 递归清理空目录 */ function cleanEmptyDirectories(dir) { - if (!fs.existsSync(dir)) return; - - const files = fs.readdirSync(dir); - - if (files.length === 0) { - fs.rmdirSync(dir); - console.log(`🗑️ 已删除空目录: ${path.relative(process.cwd(), dir)}`); - return; - } - - for (const file of files) { - const filePath = path.join(dir, file); - if (fs.statSync(filePath).isDirectory()) { - cleanEmptyDirectories(filePath); - } - } - - // 再次检查目录是否为空 - const remainingFiles = fs.readdirSync(dir); - if (remainingFiles.length === 0) { - fs.rmdirSync(dir); - console.log(`🗑️ 已删除空目录: ${path.relative(process.cwd(), dir)}`); - } + if (!fs.existsSync(dir)) return; + + const files = fs.readdirSync(dir); + + if (files.length === 0) { + fs.rmdirSync(dir); + console.log(`🗑️ 已删除空目录: ${path.relative(process.cwd(), dir)}`); + return; + } + + for (const file of files) { + const filePath = path.join(dir, file); + if (fs.statSync(filePath).isDirectory()) { + cleanEmptyDirectories(filePath); + } + } + + // 再次检查目录是否为空 + const remainingFiles = fs.readdirSync(dir); + if (remainingFiles.length === 0) { + fs.rmdirSync(dir); + console.log(`🗑️ 已删除空目录: ${path.relative(process.cwd(), dir)}`); + } } // 运行脚本 // 检查是否直接运行此脚本 const scriptPath = fileURLToPath(import.meta.url); -const isMainModule = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(scriptPath); +const isMainModule = + process.argv[1] && path.resolve(process.argv[1]) === path.resolve(scriptPath); if (isMainModule) { - cleanUnusedImages().catch(error => { - console.error('❌ 脚本执行失败:', error.message); - console.error(error.stack); - process.exit(1); - }); + cleanUnusedImages().catch((error) => { + console.error("❌ 脚本执行失败:", error.message); + console.error(error.stack); + process.exit(1); + }); } -export { cleanUnusedImages }; \ No newline at end of file +export { cleanUnusedImages }; diff --git a/scripts/new-post.js b/scripts/new-post.js index ce2b95cd9..22cca0448 100644 --- a/scripts/new-post.js +++ b/scripts/new-post.js @@ -1,48 +1,48 @@ /* This is a script to create a new post markdown file with front-matter */ -import fs from "fs" -import path from "path" +import fs from "fs"; +import path from "path"; function getDate() { - const today = new Date() - const year = today.getFullYear() - const month = String(today.getMonth() + 1).padStart(2, "0") - const day = String(today.getDate()).padStart(2, "0") - const hours = String(today.getHours()).padStart(2, "0") - const minutes = String(today.getMinutes()).padStart(2, "0") - const seconds = String(today.getSeconds()).padStart(2, "0") + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, "0"); + const day = String(today.getDate()).padStart(2, "0"); + const hours = String(today.getHours()).padStart(2, "0"); + const minutes = String(today.getMinutes()).padStart(2, "0"); + const seconds = String(today.getSeconds()).padStart(2, "0"); - return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}` + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`; } -const args = process.argv.slice(2) +const args = process.argv.slice(2); if (args.length === 0) { - console.error(`Error: No filename argument provided -Usage: npm run new-post -- `) - process.exit(1) // Terminate the script and return error code 1 + console.error(`Error: No filename argument provided +Usage: npm run new-post -- `); + process.exit(1); // Terminate the script and return error code 1 } -let fileName = args[0] +let fileName = args[0]; // Add .md extension if not present -const fileExtensionRegex = /\.(md|mdx)$/i +const fileExtensionRegex = /\.(md|mdx)$/i; if (!fileExtensionRegex.test(fileName)) { - fileName += ".md" + fileName += ".md"; } -const targetDir = "./src/content/posts/" -const fullPath = path.join(targetDir, fileName) +const targetDir = "./src/content/posts/"; +const fullPath = path.join(targetDir, fileName); if (fs.existsSync(fullPath)) { - console.error(`Error: File ${fullPath} already exists `) - process.exit(1) + console.error(`Error: File ${fullPath} already exists `); + process.exit(1); } // recursive mode creates multi-level directories -const dirPath = path.dirname(fullPath) +const dirPath = path.dirname(fullPath); if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }) + fs.mkdirSync(dirPath, { recursive: true }); } const content = `--- @@ -54,8 +54,8 @@ image: '' draft: false lang: '' --- -` +`; -fs.writeFileSync(path.join(targetDir, fileName), content) +fs.writeFileSync(path.join(targetDir, fileName), content); -console.log(`Post ${fullPath} created`) +console.log(`Post ${fullPath} created`); diff --git a/src/components/Footer.astro b/src/components/Footer.astro index 487749f41..92741cbc3 100644 --- a/src/components/Footer.astro +++ b/src/components/Footer.astro @@ -1,8 +1,8 @@ --- +import { execSync } from "node:child_process"; import { profileConfig } from "../config"; import { url } from "../utils/url-utils"; -import { execSync } from "child_process"; const currentYear = new Date().getFullYear(); @@ -10,18 +10,18 @@ let commitHash = "unknown"; let buildDate = "unknown"; try { - commitHash = execSync('git rev-parse --short=7 HEAD').toString().trim(); - - const date = new Date(); - const year = date.getFullYear(); - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const day = date.getDate().toString().padStart(2, '0'); - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); - const seconds = date.getSeconds().toString().padStart(2, '0'); - buildDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + commitHash = execSync("git rev-parse --short=7 HEAD").toString().trim(); + + const date = new Date(); + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const day = date.getDate().toString().padStart(2, "0"); + const hours = date.getHours().toString().padStart(2, "0"); + const minutes = date.getMinutes().toString().padStart(2, "0"); + const seconds = date.getSeconds().toString().padStart(2, "0"); + buildDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } catch (e) { - console.warn("Failed to get git info", e); + console.warn("Failed to get git info", e); } --- diff --git a/src/components/PostCard.astro b/src/components/PostCard.astro index 956a7914a..778e1b4ff 100644 --- a/src/components/PostCard.astro +++ b/src/components/PostCard.astro @@ -3,11 +3,11 @@ import path from "node:path"; import type { CollectionEntry } from "astro:content"; import { Icon } from "astro-icon/components"; -import { getDir } from "../utils/url-utils"; +import { umamiConfig } from "../config"; import { formatDateToYYYYMMDD } from "../utils/date-utils"; +import { getDir } from "../utils/url-utils"; import PostMetadata from "./PostMeta.astro"; import ImageWrapper from "./misc/ImageWrapper.astro"; -import { umamiConfig } from "../config"; interface Props { class?: string; @@ -21,16 +21,8 @@ interface Props { draft: boolean; style: string; } -const { - entry, - title, - url, - published, - updated, - image, - description, - style, -} = Astro.props; +const { entry, title, url, published, updated, image, description, style } = + Astro.props; const isPinned = entry.data.pinned === true; const className = Astro.props.class; @@ -107,4 +99,73 @@ const { remarkPluginFrontmatter } = await entry.render(); \ No newline at end of file + + + \ No newline at end of file diff --git a/src/components/PostMeta.astro b/src/components/PostMeta.astro index e8294a733..3970e96fd 100644 --- a/src/components/PostMeta.astro +++ b/src/components/PostMeta.astro @@ -1,9 +1,9 @@ --- import { Icon } from "astro-icon/components"; -import { getDir, url } from "../utils/url-utils"; -import { formatDateToYYYYMMDD } from "../utils/date-utils"; import { umamiConfig } from "../config"; +import { formatDateToYYYYMMDD } from "../utils/date-utils"; +import { url, getDir } from "../utils/url-utils"; interface Props { class: string; @@ -52,13 +52,13 @@ const className = Astro.props.class;
- - + 0 次
- - + 0 人
)} @@ -66,37 +66,75 @@ const className = Astro.props.class; {slug && ( + } + + // 页面加载完成后获取统计数据 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', fetchPageViews); + } else { + fetchPageViews(); + } + )} diff --git a/src/components/Search.svelte b/src/components/Search.svelte index 7849c416f..748ba9aa3 100644 --- a/src/components/Search.svelte +++ b/src/components/Search.svelte @@ -1,5 +1,4 @@ diff --git a/src/components/widget/Profile.astro b/src/components/widget/Profile.astro index d6951059a..228ed7807 100644 --- a/src/components/widget/Profile.astro +++ b/src/components/widget/Profile.astro @@ -1,6 +1,6 @@ --- import { Icon } from "astro-icon/components"; -import { profileConfig, umamiConfig, siteConfig } from "../../config"; +import { profileConfig, siteConfig, umamiConfig } from "../../config"; const config = profileConfig; --- @@ -35,14 +35,14 @@ const config = profileConfig; 访问量 -
-
+
0
访客数
-
-
+
0
@@ -68,6 +68,22 @@ const config = profileConfig; \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index e87b9e334..fbbae9eba 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,5 @@ import type { - ExpressiveCodeConfig, + ExpressiveCodeConfig, GitHubEditConfig, ImageFallbackConfig, LicenseConfig, @@ -79,13 +79,13 @@ export const navBarConfig: NavBarConfig = { { name: "他站", url: "/posts/other-sites/", // Internal links should not include the base path, as it is automatically added - external: true, // Show an external link icon and will open in a new tab + external: false, // Show an external link icon and will open in a new tab }, { name: "统计", url: "https://umami.acofork.com/share/CdkXbGgZr6ECKOyK", // Internal links should not include the base path, as it is automatically added external: true, // Show an external link icon and will open in a new tab - } + }, ], }; @@ -135,4 +135,4 @@ export const gitHubEditConfig: GitHubEditConfig = { baseUrl: "https://github.com/afoim/fuwari/blob/main/src/content/posts", }; -// todoConfig removed from here \ No newline at end of file +// todoConfig removed from here diff --git a/src/constants/link-presets.ts b/src/constants/link-presets.ts index d2d2fe84b..ac45d0e35 100644 --- a/src/constants/link-presets.ts +++ b/src/constants/link-presets.ts @@ -1,6 +1,5 @@ import { LinkPreset, type NavBarLink } from "@/types/config"; - export const LinkPresets: { [key in LinkPreset]: NavBarLink } = { [LinkPreset.Home]: { name: "首页", diff --git a/src/content/.obsidian/app.json b/src/content/.obsidian/app.json index 43917022a..d44beceab 100644 --- a/src/content/.obsidian/app.json +++ b/src/content/.obsidian/app.json @@ -1,6 +1,6 @@ { - "attachmentFolderPath": "assets/images", - "newLinkFormat": "relative", - "useMarkdownLinks": true, - "uriCallbacks": false -} \ No newline at end of file + "attachmentFolderPath": "assets/images", + "newLinkFormat": "relative", + "useMarkdownLinks": true, + "uriCallbacks": false +} diff --git a/src/content/.obsidian/appearance.json b/src/content/.obsidian/appearance.json index 9e26dfeeb..0967ef424 100644 --- a/src/content/.obsidian/appearance.json +++ b/src/content/.obsidian/appearance.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/src/content/.obsidian/community-plugins.json b/src/content/.obsidian/community-plugins.json index 5eeb4810d..7665fa11d 100644 --- a/src/content/.obsidian/community-plugins.json +++ b/src/content/.obsidian/community-plugins.json @@ -1,3 +1 @@ -[ - "obsidian-paste-image-rename" -] \ No newline at end of file +["obsidian-paste-image-rename"] diff --git a/src/content/.obsidian/core-plugins.json b/src/content/.obsidian/core-plugins.json index 0faa60d40..890c40be5 100644 --- a/src/content/.obsidian/core-plugins.json +++ b/src/content/.obsidian/core-plugins.json @@ -1,33 +1,33 @@ { - "file-explorer": true, - "global-search": true, - "switcher": true, - "graph": true, - "backlink": true, - "canvas": true, - "outgoing-link": true, - "tag-pane": true, - "footnotes": false, - "properties": false, - "page-preview": true, - "daily-notes": true, - "templates": true, - "note-composer": true, - "command-palette": true, - "slash-command": false, - "editor-status": true, - "bookmarks": true, - "markdown-importer": false, - "zk-prefixer": false, - "random-note": false, - "outline": true, - "word-count": true, - "slides": false, - "audio-recorder": false, - "workspaces": false, - "file-recovery": true, - "publish": false, - "sync": true, - "bases": true, - "webviewer": false -} \ No newline at end of file + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true, + "webviewer": false +} diff --git a/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json b/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json index 70514b34a..caa7e3f3c 100644 --- a/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json +++ b/src/content/.obsidian/plugins/obsidian-paste-image-rename/data.json @@ -1,10 +1,10 @@ { - "imageNamePattern": "{{fileName}}", - "dupNumberAtStart": false, - "dupNumberDelimiter": "-", - "dupNumberAlways": false, - "autoRename": true, - "handleAllAttachments": false, - "excludeExtensionPattern": "", - "disableRenameNotice": false -} \ No newline at end of file + "imageNamePattern": "{{fileName}}", + "dupNumberAtStart": false, + "dupNumberDelimiter": "-", + "dupNumberAlways": false, + "autoRename": true, + "handleAllAttachments": false, + "excludeExtensionPattern": "", + "disableRenameNotice": false +} diff --git a/src/content/.obsidian/plugins/obsidian-paste-image-rename/main.js b/src/content/.obsidian/plugins/obsidian-paste-image-rename/main.js index 4a560fc7b..9384dc2a2 100644 --- a/src/content/.obsidian/plugins/obsidian-paste-image-rename/main.js +++ b/src/content/.obsidian/plugins/obsidian-paste-image-rename/main.js @@ -1,871 +1,980 @@ /* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD */ -var __defProp = Object.defineProperty; -var __defProps = Object.defineProperties; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropDescs = Object.getOwnPropertyDescriptors; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __getOwnPropSymbols = Object.getOwnPropertySymbols; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __propIsEnum = Object.prototype.propertyIsEnumerable; -var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -var __spreadValues = (a, b) => { - for (var prop in b || (b = {})) - if (__hasOwnProp.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - if (__getOwnPropSymbols) - for (var prop of __getOwnPropSymbols(b)) { - if (__propIsEnum.call(b, prop)) - __defNormalProp(a, prop, b[prop]); - } - return a; +const __defProp = Object.defineProperty; +const __defProps = Object.defineProperties; +const __getOwnPropDesc = Object.getOwnPropertyDescriptor; +const __getOwnPropDescs = Object.getOwnPropertyDescriptors; +const __getOwnPropNames = Object.getOwnPropertyNames; +const __getOwnPropSymbols = Object.getOwnPropertySymbols; +const __hasOwnProp = Object.prototype.hasOwnProperty; +const __propIsEnum = Object.prototype.propertyIsEnumerable; +const __defNormalProp = (obj, key, value) => + key in obj + ? __defProp(obj, key, { + enumerable: true, + configurable: true, + writable: true, + value, + }) + : (obj[key] = value); +const __spreadValues = (a, b) => { + for (const prop in b || (b = {})) + if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); + if (__getOwnPropSymbols) + for (const prop of __getOwnPropSymbols(b)) { + if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); + } + return a; }; -var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); -var __commonJS = (cb, mod) => function __require() { - return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +const __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); +const __commonJS = (cb, mod) => + function __require() { + return ( + mod || + (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), + mod.exports + ); + }; +const __export = (target, all) => { + for (const name in all) + __defProp(target, name, { get: all[name], enumerable: true }); }; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); +const __copyProps = (to, from, except, desc) => { + if ((from && typeof from === "object") || typeof from === "function") { + for (const key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { + get: () => from[key], + enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, + }); + } + return to; }; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); -var __async = (__this, __arguments, generator) => { - return new Promise((resolve, reject) => { - var fulfilled = (value) => { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - }; - var rejected = (value) => { - try { - step(generator.throw(value)); - } catch (e) { - reject(e); - } - }; - var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); - step((generator = generator.apply(__this, __arguments)).next()); - }); +const __toCommonJS = (mod) => + __copyProps(__defProp({}, "__esModule", { value: true }), mod); +const __async = (__this, __arguments, generator) => { + return new Promise((resolve, reject) => { + const fulfilled = (value) => { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + }; + const rejected = (value) => { + try { + step(generator.throw(value)); + } catch (e) { + reject(e); + } + }; + const step = (x) => + x.done + ? resolve(x.value) + : Promise.resolve(x.value).then(fulfilled, rejected); + step((generator = generator.apply(__this, __arguments)).next()); + }); }; // package.json -var require_package = __commonJS({ - "package.json"(exports, module2) { - module2.exports = { - name: "obsidian-paste-image-rename", - version: "1.6.1", - main: "main.js", - scripts: { - start: "node esbuild.config.mjs", - build: "tsc -noEmit -skipLibCheck && BUILD_ENV=production node esbuild.config.mjs && cp manifest.json build", - version: "node version-bump.mjs && git add manifest.json versions.json", - release: "npm run build && gh release create ${npm_package_version} build/*" - }, - keywords: [], - author: "Reorx", - license: "MIT", - devDependencies: { - "@types/node": "^18.11.18", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", - "builtin-modules": "^3.3.0", - esbuild: "0.16.17", - obsidian: "^1.1.1", - tslib: "2.5.0", - typescript: "4.9.4" - }, - dependencies: { - "cash-dom": "^8.1.2" - } - }; - } +const require_package = __commonJS({ + "package.json"(exports, module2) { + module2.exports = { + name: "obsidian-paste-image-rename", + version: "1.6.1", + main: "main.js", + scripts: { + start: "node esbuild.config.mjs", + build: + "tsc -noEmit -skipLibCheck && BUILD_ENV=production node esbuild.config.mjs && cp manifest.json build", + version: "node version-bump.mjs && git add manifest.json versions.json", + release: + "npm run build && gh release create ${npm_package_version} build/*", + }, + keywords: [], + author: "Reorx", + license: "MIT", + devDependencies: { + "@types/node": "^18.11.18", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "builtin-modules": "^3.3.0", + esbuild: "0.16.17", + obsidian: "^1.1.1", + tslib: "2.5.0", + typescript: "4.9.4", + }, + dependencies: { + "cash-dom": "^8.1.2", + }, + }; + }, }); // src/main.ts -var main_exports = {}; +const main_exports = {}; __export(main_exports, { - default: () => PasteImageRenamePlugin + default: () => PasteImageRenamePlugin, }); module.exports = __toCommonJS(main_exports); -var import_obsidian2 = require("obsidian"); +const import_obsidian2 = require("obsidian"); // src/batch.ts -var import_obsidian = require("obsidian"); +const import_obsidian = require("obsidian"); // src/utils.ts -var DEBUG = false; -if (DEBUG) - console.log("DEBUG is enabled"); +const DEBUG = false; +if (DEBUG) console.log("DEBUG is enabled"); function debugLog(...args) { - if (DEBUG) { - console.log(new Date().toISOString().slice(11, 23), ...args); - } + if (DEBUG) { + console.log(new Date().toISOString().slice(11, 23), ...args); + } } function createElementTree(rootEl, opts) { - const result = { - el: rootEl.createEl(opts.tag, opts), - children: [] - }; - const children = opts.children || []; - for (const child of children) { - result.children.push(createElementTree(result.el, child)); - } - return result; + const result = { + el: rootEl.createEl(opts.tag, opts), + children: [], + }; + const children = opts.children || []; + for (const child of children) { + result.children.push(createElementTree(result.el, child)); + } + return result; } -var path = { - // Credit: @creationix/path.js - join(...partSegments) { - let parts = []; - for (let i = 0, l = partSegments.length; i < l; i++) { - parts = parts.concat(partSegments[i].split("/")); - } - const newParts = []; - for (let i = 0, l = parts.length; i < l; i++) { - const part = parts[i]; - if (!part || part === ".") - continue; - else - newParts.push(part); - } - if (parts[0] === "") - newParts.unshift(""); - return newParts.join("/"); - }, - // returns the last part of a path, e.g. 'foo.jpg' - basename(fullpath) { - const sp = fullpath.split("/"); - return sp[sp.length - 1]; - }, - // return extension without dot, e.g. 'jpg' - extension(fullpath) { - const positions = [...fullpath.matchAll(new RegExp("\\.", "gi"))].map((a) => a.index); - return fullpath.slice(positions[positions.length - 1] + 1); - } +const path = { + // Credit: @creationix/path.js + join(...partSegments) { + let parts = []; + for (let i = 0, l = partSegments.length; i < l; i++) { + parts = parts.concat(partSegments[i].split("/")); + } + const newParts = []; + for (let i = 0, l = parts.length; i < l; i++) { + const part = parts[i]; + if (!part || part === ".") continue; + newParts.push(part); + } + if (parts[0] === "") newParts.unshift(""); + return newParts.join("/"); + }, + // returns the last part of a path, e.g. 'foo.jpg' + basename(fullpath) { + const sp = fullpath.split("/"); + return sp[sp.length - 1]; + }, + // return extension without dot, e.g. 'jpg' + extension(fullpath) { + const positions = [...fullpath.matchAll(/\./gi)].map((a) => a.index); + return fullpath.slice(positions[positions.length - 1] + 1); + }, }; -var filenameNotAllowedChars = /[^\p{L}0-9~`!@$&*()\-_=+{};'",<.>? ]/ug; -var sanitizer = { - filename(s) { - return s.replace(filenameNotAllowedChars, "").trim(); - }, - delimiter(s) { - s = this.filename(s); - if (!s) - s = "-"; - return s; - } +const filenameNotAllowedChars = /[^\p{L}0-9~`!@$&*()\-_=+{};'",<.>? ]/gu; +const sanitizer = { + filename(s) { + return s.replace(filenameNotAllowedChars, "").trim(); + }, + delimiter(s) { + s = this.filename(s); + if (!s) s = "-"; + return s; + }, }; function escapeRegExp(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function lockInputMethodComposition(el) { - const state = { - lock: false - }; - el.addEventListener("compositionstart", () => { - state.lock = true; - }); - el.addEventListener("compositionend", () => { - state.lock = false; - }); - return state; + const state = { + lock: false, + }; + el.addEventListener("compositionstart", () => { + state.lock = true; + }); + el.addEventListener("compositionend", () => { + state.lock = false; + }); + return state; } // src/batch.ts -var ImageBatchRenameModal = class extends import_obsidian.Modal { - constructor(app, activeFile, renameFunc, onClose) { - super(app); - this.activeFile = activeFile; - this.renameFunc = renameFunc; - this.onCloseExtra = onClose; - this.state = { - namePattern: "", - extPattern: "", - nameReplace: "", - renameTasks: [] - }; - } - onOpen() { - this.containerEl.addClass("image-rename-modal"); - const { contentEl, titleEl } = this; - titleEl.setText("Batch rename embeded files"); - const namePatternSetting = new import_obsidian.Setting(contentEl).setName("Name pattern").setDesc("Please input the name pattern to match files (regex)").addText((text) => text.setValue(this.state.namePattern).onChange( - (value) => __async(this, null, function* () { - this.state.namePattern = value; - }) - )); - const npInputEl = namePatternSetting.controlEl.children[0]; - npInputEl.focus(); - const npInputState = lockInputMethodComposition(npInputEl); - npInputEl.addEventListener("keydown", (e) => __async(this, null, function* () { - if (e.key === "Enter" && !npInputState.lock) { - e.preventDefault(); - if (!this.state.namePattern) { - errorEl.innerText = 'Error: "Name pattern" could not be empty'; - errorEl.style.display = "block"; - return; - } - this.matchImageNames(tbodyEl); - } - })); - const extPatternSetting = new import_obsidian.Setting(contentEl).setName("Extension pattern").setDesc("Please input the extension pattern to match files (regex)").addText((text) => text.setValue(this.state.extPattern).onChange( - (value) => __async(this, null, function* () { - this.state.extPattern = value; - }) - )); - const extInputEl = extPatternSetting.controlEl.children[0]; - extInputEl.addEventListener("keydown", (e) => __async(this, null, function* () { - if (e.key === "Enter") { - e.preventDefault(); - this.matchImageNames(tbodyEl); - } - })); - const nameReplaceSetting = new import_obsidian.Setting(contentEl).setName("Name replace").setDesc("Please input the string to replace the matched name (use $1, $2 for regex groups)").addText((text) => text.setValue(this.state.nameReplace).onChange( - (value) => __async(this, null, function* () { - this.state.nameReplace = value; - }) - )); - const nrInputEl = nameReplaceSetting.controlEl.children[0]; - const nrInputState = lockInputMethodComposition(nrInputEl); - nrInputEl.addEventListener("keydown", (e) => __async(this, null, function* () { - if (e.key === "Enter" && !nrInputState.lock) { - e.preventDefault(); - this.matchImageNames(tbodyEl); - } - })); - const matchedContainer = contentEl.createDiv({ - cls: "matched-container" - }); - const tableET = createElementTree(matchedContainer, { - tag: "table", - children: [ - { - tag: "thead", - children: [ - { - tag: "tr", - children: [ - { - tag: "td", - text: "Original path" - }, - { - tag: "td", - text: "Renamed Name" - } - ] - } - ] - }, - { - tag: "tbody" - } - ] - }); - const tbodyEl = tableET.children[1].el; - const errorEl = contentEl.createDiv({ - cls: "error", - attr: { - style: "display: none;" - } - }); - new import_obsidian.Setting(contentEl).addButton((button) => { - button.setButtonText("Rename all").setClass("mod-cta").onClick(() => { - new ConfirmModal( - this.app, - "Confirm rename all", - `Are you sure? This will rename all the ${this.state.renameTasks.length} images matched the pattern.`, - () => { - this.renameAll(); - this.close(); - } - ).open(); - }); - }).addButton((button) => { - button.setButtonText("Cancel").onClick(() => { - this.close(); - }); - }); - } - onClose() { - const { contentEl } = this; - contentEl.empty(); - this.onCloseExtra(); - } - renameAll() { - return __async(this, null, function* () { - debugLog("renameAll", this.state); - for (const task of this.state.renameTasks) { - yield this.renameFunc(task.file, task.name); - } - }); - } - matchImageNames(tbodyEl) { - const { state } = this; - const renameTasks = []; - tbodyEl.empty(); - const fileCache = this.app.metadataCache.getFileCache(this.activeFile); - if (!fileCache || !fileCache.embeds) - return; - const namePatternRegex = new RegExp(state.namePattern, "g"); - const extPatternRegex = new RegExp(state.extPattern); - fileCache.embeds.forEach((embed) => { - const file = this.app.metadataCache.getFirstLinkpathDest(embed.link, this.activeFile.path); - if (!file) { - console.warn("file not found", embed.link); - return; - } - if (state.extPattern) { - const m0 = extPatternRegex.exec(file.extension); - if (!m0) - return; - } - const stem = file.basename; - namePatternRegex.lastIndex = 0; - const m1 = namePatternRegex.exec(stem); - if (!m1) - return; - let renamedName = file.name; - if (state.nameReplace) { - namePatternRegex.lastIndex = 0; - renamedName = stem.replace(namePatternRegex, state.nameReplace); - renamedName = `${renamedName}.${file.extension}`; - } - renameTasks.push({ - file, - name: renamedName - }); - createElementTree(tbodyEl, { - tag: "tr", - children: [ - { - tag: "td", - children: [ - { - tag: "span", - text: file.name - }, - { - tag: "div", - text: file.path, - attr: { - class: "file-path" - } - } - ] - }, - { - tag: "td", - children: [ - { - tag: "span", - text: renamedName - }, - { - tag: "div", - text: path.join(file.parent.path, renamedName), - attr: { - class: "file-path" - } - } - ] - } - ] - }); - }); - debugLog("new renameTasks", renameTasks); - state.renameTasks = renameTasks; - } +const ImageBatchRenameModal = class extends import_obsidian.Modal { + constructor(app, activeFile, renameFunc, onClose) { + super(app); + this.activeFile = activeFile; + this.renameFunc = renameFunc; + this.onCloseExtra = onClose; + this.state = { + namePattern: "", + extPattern: "", + nameReplace: "", + renameTasks: [], + }; + } + onOpen() { + this.containerEl.addClass("image-rename-modal"); + const { contentEl, titleEl } = this; + titleEl.setText("Batch rename embeded files"); + const namePatternSetting = new import_obsidian.Setting(contentEl) + .setName("Name pattern") + .setDesc("Please input the name pattern to match files (regex)") + .addText((text) => + text.setValue(this.state.namePattern).onChange((value) => + __async(this, null, function* () { + this.state.namePattern = value; + }), + ), + ); + const npInputEl = namePatternSetting.controlEl.children[0]; + npInputEl.focus(); + const npInputState = lockInputMethodComposition(npInputEl); + npInputEl.addEventListener("keydown", (e) => + __async(this, null, function* () { + if (e.key === "Enter" && !npInputState.lock) { + e.preventDefault(); + if (!this.state.namePattern) { + errorEl.innerText = 'Error: "Name pattern" could not be empty'; + errorEl.style.display = "block"; + return; + } + this.matchImageNames(tbodyEl); + } + }), + ); + const extPatternSetting = new import_obsidian.Setting(contentEl) + .setName("Extension pattern") + .setDesc("Please input the extension pattern to match files (regex)") + .addText((text) => + text.setValue(this.state.extPattern).onChange((value) => + __async(this, null, function* () { + this.state.extPattern = value; + }), + ), + ); + const extInputEl = extPatternSetting.controlEl.children[0]; + extInputEl.addEventListener("keydown", (e) => + __async(this, null, function* () { + if (e.key === "Enter") { + e.preventDefault(); + this.matchImageNames(tbodyEl); + } + }), + ); + const nameReplaceSetting = new import_obsidian.Setting(contentEl) + .setName("Name replace") + .setDesc( + "Please input the string to replace the matched name (use $1, $2 for regex groups)", + ) + .addText((text) => + text.setValue(this.state.nameReplace).onChange((value) => + __async(this, null, function* () { + this.state.nameReplace = value; + }), + ), + ); + const nrInputEl = nameReplaceSetting.controlEl.children[0]; + const nrInputState = lockInputMethodComposition(nrInputEl); + nrInputEl.addEventListener("keydown", (e) => + __async(this, null, function* () { + if (e.key === "Enter" && !nrInputState.lock) { + e.preventDefault(); + this.matchImageNames(tbodyEl); + } + }), + ); + const matchedContainer = contentEl.createDiv({ + cls: "matched-container", + }); + const tableET = createElementTree(matchedContainer, { + tag: "table", + children: [ + { + tag: "thead", + children: [ + { + tag: "tr", + children: [ + { + tag: "td", + text: "Original path", + }, + { + tag: "td", + text: "Renamed Name", + }, + ], + }, + ], + }, + { + tag: "tbody", + }, + ], + }); + const tbodyEl = tableET.children[1].el; + const errorEl = contentEl.createDiv({ + cls: "error", + attr: { + style: "display: none;", + }, + }); + new import_obsidian.Setting(contentEl) + .addButton((button) => { + button + .setButtonText("Rename all") + .setClass("mod-cta") + .onClick(() => { + new ConfirmModal( + this.app, + "Confirm rename all", + `Are you sure? This will rename all the ${this.state.renameTasks.length} images matched the pattern.`, + () => { + this.renameAll(); + this.close(); + }, + ).open(); + }); + }) + .addButton((button) => { + button.setButtonText("Cancel").onClick(() => { + this.close(); + }); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + this.onCloseExtra(); + } + renameAll() { + return __async(this, null, function* () { + debugLog("renameAll", this.state); + for (const task of this.state.renameTasks) { + yield this.renameFunc(task.file, task.name); + } + }); + } + matchImageNames(tbodyEl) { + const { state } = this; + const renameTasks = []; + tbodyEl.empty(); + const fileCache = this.app.metadataCache.getFileCache(this.activeFile); + if (!fileCache || !fileCache.embeds) return; + const namePatternRegex = new RegExp(state.namePattern, "g"); + const extPatternRegex = new RegExp(state.extPattern); + fileCache.embeds.forEach((embed) => { + const file = this.app.metadataCache.getFirstLinkpathDest( + embed.link, + this.activeFile.path, + ); + if (!file) { + console.warn("file not found", embed.link); + return; + } + if (state.extPattern) { + const m0 = extPatternRegex.exec(file.extension); + if (!m0) return; + } + const stem = file.basename; + namePatternRegex.lastIndex = 0; + const m1 = namePatternRegex.exec(stem); + if (!m1) return; + let renamedName = file.name; + if (state.nameReplace) { + namePatternRegex.lastIndex = 0; + renamedName = stem.replace(namePatternRegex, state.nameReplace); + renamedName = `${renamedName}.${file.extension}`; + } + renameTasks.push({ + file, + name: renamedName, + }); + createElementTree(tbodyEl, { + tag: "tr", + children: [ + { + tag: "td", + children: [ + { + tag: "span", + text: file.name, + }, + { + tag: "div", + text: file.path, + attr: { + class: "file-path", + }, + }, + ], + }, + { + tag: "td", + children: [ + { + tag: "span", + text: renamedName, + }, + { + tag: "div", + text: path.join(file.parent.path, renamedName), + attr: { + class: "file-path", + }, + }, + ], + }, + ], + }); + }); + debugLog("new renameTasks", renameTasks); + state.renameTasks = renameTasks; + } }; -var ConfirmModal = class extends import_obsidian.Modal { - constructor(app, title, message, onConfirm) { - super(app); - this.title = title; - this.message = message; - this.onConfirm = onConfirm; - } - onOpen() { - const { contentEl, titleEl } = this; - titleEl.setText(this.title); - contentEl.createEl("p", { - text: this.message - }); - new import_obsidian.Setting(contentEl).addButton((button) => { - button.setButtonText("Yes").setClass("mod-warning").onClick(() => { - this.onConfirm(); - this.close(); - }); - }).addButton((button) => { - button.setButtonText("No").onClick(() => { - this.close(); - }); - }); - } +const ConfirmModal = class extends import_obsidian.Modal { + constructor(app, title, message, onConfirm) { + super(app); + this.title = title; + this.message = message; + this.onConfirm = onConfirm; + } + onOpen() { + const { contentEl, titleEl } = this; + titleEl.setText(this.title); + contentEl.createEl("p", { + text: this.message, + }); + new import_obsidian.Setting(contentEl) + .addButton((button) => { + button + .setButtonText("Yes") + .setClass("mod-warning") + .onClick(() => { + this.onConfirm(); + this.close(); + }); + }) + .addButton((button) => { + button.setButtonText("No").onClick(() => { + this.close(); + }); + }); + } }; // src/template.ts -var dateTmplRegex = /{{DATE:([^}]+)}}/gm; -var frontmatterTmplRegex = /{{frontmatter:([^}]+)}}/gm; -var replaceDateVar = (s, date) => { - const m = dateTmplRegex.exec(s); - if (!m) - return s; - return s.replace(m[0], date.format(m[1])); +const dateTmplRegex = /{{DATE:([^}]+)}}/gm; +const frontmatterTmplRegex = /{{frontmatter:([^}]+)}}/gm; +const replaceDateVar = (s, date) => { + const m = dateTmplRegex.exec(s); + if (!m) return s; + return s.replace(m[0], date.format(m[1])); }; -var replaceFrontmatterVar = (s, frontmatter) => { - if (!frontmatter) - return s; - const m = frontmatterTmplRegex.exec(s); - if (!m) - return s; - return s.replace(m[0], frontmatter[m[1]] || ""); +const replaceFrontmatterVar = (s, frontmatter) => { + if (!frontmatter) return s; + const m = frontmatterTmplRegex.exec(s); + if (!m) return s; + return s.replace(m[0], frontmatter[m[1]] || ""); }; -var renderTemplate = (tmpl, data, frontmatter) => { - const now = window.moment(); - let text = tmpl; - let newtext; - while ((newtext = replaceDateVar(text, now)) != text) { - text = newtext; - } - while ((newtext = replaceFrontmatterVar(text, frontmatter)) != text) { - text = newtext; - } - text = text.replace(/{{imageNameKey}}/gm, data.imageNameKey).replace(/{{fileName}}/gm, data.fileName).replace(/{{dirName}}/gm, data.dirName).replace(/{{firstHeading}}/gm, data.firstHeading); - return text; +const renderTemplate = (tmpl, data, frontmatter) => { + const now = window.moment(); + let text = tmpl; + let newtext; + while ((newtext = replaceDateVar(text, now)) !== text) { + text = newtext; + } + while ((newtext = replaceFrontmatterVar(text, frontmatter)) !== text) { + text = newtext; + } + text = text + .replace(/{{imageNameKey}}/gm, data.imageNameKey) + .replace(/{{fileName}}/gm, data.fileName) + .replace(/{{dirName}}/gm, data.dirName) + .replace(/{{firstHeading}}/gm, data.firstHeading); + return text; }; // src/main.ts -var DEFAULT_SETTINGS = { - imageNamePattern: "{{fileName}}", - dupNumberAtStart: false, - dupNumberDelimiter: "-", - dupNumberAlways: false, - autoRename: false, - handleAllAttachments: false, - excludeExtensionPattern: "", - disableRenameNotice: false +const DEFAULT_SETTINGS = { + imageNamePattern: "{{fileName}}", + dupNumberAtStart: false, + dupNumberDelimiter: "-", + dupNumberAlways: false, + autoRename: false, + handleAllAttachments: false, + excludeExtensionPattern: "", + disableRenameNotice: false, }; -var PASTED_IMAGE_PREFIX = "Pasted image "; -var PasteImageRenamePlugin = class extends import_obsidian2.Plugin { - constructor() { - super(...arguments); - this.modals = []; - } - onload() { - return __async(this, null, function* () { - const pkg = require_package(); - console.log(`Plugin loading: ${pkg.name} ${pkg.version} BUILD_ENV=${"production"}`); - yield this.loadSettings(); - this.registerEvent( - this.app.vault.on("create", (file) => { - if (!(file instanceof import_obsidian2.TFile)) - return; - const timeGapMs = new Date().getTime() - file.stat.ctime; - if (timeGapMs > 1e3) - return; - if (isMarkdownFile(file)) - return; - if (isPastedImage(file)) { - debugLog("pasted image created", file); - this.startRenameProcess(file, this.settings.autoRename); - } else { - if (this.settings.handleAllAttachments) { - debugLog("handleAllAttachments for file", file); - if (this.testExcludeExtension(file)) { - debugLog("excluded file by ext", file); - return; - } - this.startRenameProcess(file, this.settings.autoRename); - } - } - }) - ); - const startBatchRenameProcess = () => { - this.openBatchRenameModal(); - }; - this.addCommand({ - id: "batch-rename-embeded-files", - name: "Batch rename embeded files (in the current file)", - callback: startBatchRenameProcess - }); - if (DEBUG) { - this.addRibbonIcon("wand-glyph", "Batch rename embeded files", startBatchRenameProcess); - } - const batchRenameAllImages = () => { - this.batchRenameAllImages(); - }; - this.addCommand({ - id: "batch-rename-all-images", - name: "Batch rename all images instantly (in the current file)", - callback: batchRenameAllImages - }); - if (DEBUG) { - this.addRibbonIcon("wand-glyph", "Batch rename all images instantly (in the current file)", batchRenameAllImages); - } - this.addSettingTab(new SettingTab(this.app, this)); - }); - } - startRenameProcess(file, autoRename = false) { - return __async(this, null, function* () { - const activeFile = this.getActiveFile(); - if (!activeFile) { - new import_obsidian2.Notice("Error: No active file found."); - return; - } - const { stem, newName, isMeaningful } = this.generateNewName(file, activeFile); - debugLog("generated newName:", newName, isMeaningful); - if (!isMeaningful || !autoRename) { - this.openRenameModal(file, isMeaningful ? stem : "", activeFile.path); - return; - } - this.renameFile(file, newName, activeFile.path, true); - }); - } - renameFile(file, inputNewName, sourcePath, replaceCurrentLine) { - return __async(this, null, function* () { - const { name: newName } = yield this.deduplicateNewName(inputNewName, file); - debugLog("deduplicated newName:", newName); - const originName = file.name; - const linkText = this.app.fileManager.generateMarkdownLink(file, sourcePath); - const newPath = path.join(file.parent.path, newName); - try { - yield this.app.fileManager.renameFile(file, newPath); - } catch (err) { - new import_obsidian2.Notice(`Failed to rename ${newName}: ${err}`); - throw err; - } - if (!replaceCurrentLine) { - return; - } - const newLinkText = this.app.fileManager.generateMarkdownLink(file, sourcePath); - debugLog("replace text", linkText, newLinkText); - const editor = this.getActiveEditor(); - if (!editor) { - new import_obsidian2.Notice(`Failed to rename ${newName}: no active editor`); - return; - } - const cursor = editor.getCursor(); - const line = editor.getLine(cursor.line); - const replacedLine = line.replace(linkText, newLinkText); - debugLog("current line -> replaced line", line, replacedLine); - editor.transaction({ - changes: [ - { - from: __spreadProps(__spreadValues({}, cursor), { ch: 0 }), - to: __spreadProps(__spreadValues({}, cursor), { ch: line.length }), - text: replacedLine - } - ] - }); - if (!this.settings.disableRenameNotice) { - new import_obsidian2.Notice(`Renamed ${originName} to ${newName}`); - } - }); - } - openRenameModal(file, newName, sourcePath) { - const modal = new ImageRenameModal( - this.app, - file, - newName, - (confirmedName) => { - debugLog("confirmedName:", confirmedName); - this.renameFile(file, confirmedName, sourcePath, true); - }, - () => { - this.modals.splice(this.modals.indexOf(modal), 1); - } - ); - this.modals.push(modal); - modal.open(); - debugLog("modals count", this.modals.length); - } - openBatchRenameModal() { - const activeFile = this.getActiveFile(); - const modal = new ImageBatchRenameModal( - this.app, - activeFile, - (file, name) => __async(this, null, function* () { - yield this.renameFile(file, name, activeFile.path); - }), - () => { - this.modals.splice(this.modals.indexOf(modal), 1); - } - ); - this.modals.push(modal); - modal.open(); - } - batchRenameAllImages() { - return __async(this, null, function* () { - const activeFile = this.getActiveFile(); - const fileCache = this.app.metadataCache.getFileCache(activeFile); - if (!fileCache || !fileCache.embeds) - return; - const extPatternRegex = /jpe?g|png|gif|tiff|webp/i; - for (const embed of fileCache.embeds) { - const file = this.app.metadataCache.getFirstLinkpathDest(embed.link, activeFile.path); - if (!file) { - console.warn("file not found", embed.link); - return; - } - const m0 = extPatternRegex.exec(file.extension); - if (!m0) - return; - const { newName, isMeaningful } = this.generateNewName(file, activeFile); - debugLog("generated newName:", newName, isMeaningful); - if (!isMeaningful) { - new import_obsidian2.Notice("Failed to batch rename images: the generated name is not meaningful"); - break; - } - yield this.renameFile(file, newName, activeFile.path, false); - } - }); - } - // returns a new name for the input file, with extension - generateNewName(file, activeFile) { - let imageNameKey = ""; - let firstHeading = ""; - let frontmatter; - const fileCache = this.app.metadataCache.getFileCache(activeFile); - if (fileCache) { - debugLog("frontmatter", fileCache.frontmatter); - frontmatter = fileCache.frontmatter; - imageNameKey = (frontmatter == null ? void 0 : frontmatter.imageNameKey) || ""; - firstHeading = getFirstHeading(fileCache.headings); - } else { - console.warn("could not get file cache from active file", activeFile.name); - } - const stem = renderTemplate( - this.settings.imageNamePattern, - { - imageNameKey, - fileName: activeFile.basename, - dirName: activeFile.parent.name, - firstHeading - }, - frontmatter - ); - const meaninglessRegex = new RegExp(`[${this.settings.dupNumberDelimiter}\\s]`, "gm"); - return { - stem, - newName: stem + "." + file.extension, - isMeaningful: stem.replace(meaninglessRegex, "") !== "" - }; - } - // newName: foo.ext - deduplicateNewName(newName, file) { - return __async(this, null, function* () { - const dir = file.parent.path; - const listed = yield this.app.vault.adapter.list(dir); - debugLog("sibling files", listed); - const newNameExt = path.extension(newName), newNameStem = newName.slice(0, newName.length - newNameExt.length - 1), newNameStemEscaped = escapeRegExp(newNameStem), delimiter = this.settings.dupNumberDelimiter, delimiterEscaped = escapeRegExp(delimiter); - let dupNameRegex; - if (this.settings.dupNumberAtStart) { - dupNameRegex = new RegExp( - `^(?\\d+)${delimiterEscaped}(?${newNameStemEscaped})\\.${newNameExt}$` - ); - } else { - dupNameRegex = new RegExp( - `^(?${newNameStemEscaped})${delimiterEscaped}(?\\d+)\\.${newNameExt}$` - ); - } - debugLog("dupNameRegex", dupNameRegex); - const dupNameNumbers = []; - let isNewNameExist = false; - for (let sibling of listed.files) { - sibling = path.basename(sibling); - if (sibling == newName) { - isNewNameExist = true; - continue; - } - const m = dupNameRegex.exec(sibling); - if (!m) - continue; - dupNameNumbers.push(parseInt(m.groups.number)); - } - if (isNewNameExist || this.settings.dupNumberAlways) { - const newNumber = dupNameNumbers.length > 0 ? Math.max(...dupNameNumbers) + 1 : 1; - if (this.settings.dupNumberAtStart) { - newName = `${newNumber}${delimiter}${newNameStem}.${newNameExt}`; - } else { - newName = `${newNameStem}${delimiter}${newNumber}.${newNameExt}`; - } - } - return { - name: newName, - stem: newName.slice(0, newName.length - newNameExt.length - 1), - extension: newNameExt - }; - }); - } - getActiveFile() { - const view = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView); - const file = view == null ? void 0 : view.file; - debugLog("active file", file == null ? void 0 : file.path); - return file; - } - getActiveEditor() { - const view = this.app.workspace.getActiveViewOfType(import_obsidian2.MarkdownView); - return view == null ? void 0 : view.editor; - } - onunload() { - this.modals.map((modal) => modal.close()); - } - testExcludeExtension(file) { - const pattern = this.settings.excludeExtensionPattern; - if (!pattern) - return false; - return new RegExp(pattern).test(file.extension); - } - loadSettings() { - return __async(this, null, function* () { - this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData()); - }); - } - saveSettings() { - return __async(this, null, function* () { - yield this.saveData(this.settings); - }); - } +const PASTED_IMAGE_PREFIX = "Pasted image "; +const PasteImageRenamePlugin = class extends import_obsidian2.Plugin { + constructor() { + super(...arguments); + this.modals = []; + } + onload() { + return __async(this, null, function* () { + const pkg = require_package(); + console.log( + `Plugin loading: ${pkg.name} ${pkg.version} BUILD_ENV=${"production"}`, + ); + yield this.loadSettings(); + this.registerEvent( + this.app.vault.on("create", (file) => { + if (!(file instanceof import_obsidian2.TFile)) return; + const timeGapMs = new Date().getTime() - file.stat.ctime; + if (timeGapMs > 1e3) return; + if (isMarkdownFile(file)) return; + if (isPastedImage(file)) { + debugLog("pasted image created", file); + this.startRenameProcess(file, this.settings.autoRename); + } else { + if (this.settings.handleAllAttachments) { + debugLog("handleAllAttachments for file", file); + if (this.testExcludeExtension(file)) { + debugLog("excluded file by ext", file); + return; + } + this.startRenameProcess(file, this.settings.autoRename); + } + } + }), + ); + const startBatchRenameProcess = () => { + this.openBatchRenameModal(); + }; + this.addCommand({ + id: "batch-rename-embeded-files", + name: "Batch rename embeded files (in the current file)", + callback: startBatchRenameProcess, + }); + if (DEBUG) { + this.addRibbonIcon( + "wand-glyph", + "Batch rename embeded files", + startBatchRenameProcess, + ); + } + const batchRenameAllImages = () => { + this.batchRenameAllImages(); + }; + this.addCommand({ + id: "batch-rename-all-images", + name: "Batch rename all images instantly (in the current file)", + callback: batchRenameAllImages, + }); + if (DEBUG) { + this.addRibbonIcon( + "wand-glyph", + "Batch rename all images instantly (in the current file)", + batchRenameAllImages, + ); + } + this.addSettingTab(new SettingTab(this.app, this)); + }); + } + startRenameProcess(file, autoRename = false) { + return __async(this, null, function* () { + const activeFile = this.getActiveFile(); + if (!activeFile) { + new import_obsidian2.Notice("Error: No active file found."); + return; + } + const { stem, newName, isMeaningful } = this.generateNewName( + file, + activeFile, + ); + debugLog("generated newName:", newName, isMeaningful); + if (!isMeaningful || !autoRename) { + this.openRenameModal(file, isMeaningful ? stem : "", activeFile.path); + return; + } + this.renameFile(file, newName, activeFile.path, true); + }); + } + renameFile(file, inputNewName, sourcePath, replaceCurrentLine) { + return __async(this, null, function* () { + const { name: newName } = yield this.deduplicateNewName( + inputNewName, + file, + ); + debugLog("deduplicated newName:", newName); + const originName = file.name; + const linkText = this.app.fileManager.generateMarkdownLink( + file, + sourcePath, + ); + const newPath = path.join(file.parent.path, newName); + try { + yield this.app.fileManager.renameFile(file, newPath); + } catch (err) { + new import_obsidian2.Notice(`Failed to rename ${newName}: ${err}`); + throw err; + } + if (!replaceCurrentLine) { + return; + } + const newLinkText = this.app.fileManager.generateMarkdownLink( + file, + sourcePath, + ); + debugLog("replace text", linkText, newLinkText); + const editor = this.getActiveEditor(); + if (!editor) { + new import_obsidian2.Notice( + `Failed to rename ${newName}: no active editor`, + ); + return; + } + const cursor = editor.getCursor(); + const line = editor.getLine(cursor.line); + const replacedLine = line.replace(linkText, newLinkText); + debugLog("current line -> replaced line", line, replacedLine); + editor.transaction({ + changes: [ + { + from: __spreadProps(__spreadValues({}, cursor), { ch: 0 }), + to: __spreadProps(__spreadValues({}, cursor), { ch: line.length }), + text: replacedLine, + }, + ], + }); + if (!this.settings.disableRenameNotice) { + new import_obsidian2.Notice(`Renamed ${originName} to ${newName}`); + } + }); + } + openRenameModal(file, newName, sourcePath) { + const modal = new ImageRenameModal( + this.app, + file, + newName, + (confirmedName) => { + debugLog("confirmedName:", confirmedName); + this.renameFile(file, confirmedName, sourcePath, true); + }, + () => { + this.modals.splice(this.modals.indexOf(modal), 1); + }, + ); + this.modals.push(modal); + modal.open(); + debugLog("modals count", this.modals.length); + } + openBatchRenameModal() { + const activeFile = this.getActiveFile(); + const modal = new ImageBatchRenameModal( + this.app, + activeFile, + (file, name) => + __async(this, null, function* () { + yield this.renameFile(file, name, activeFile.path); + }), + () => { + this.modals.splice(this.modals.indexOf(modal), 1); + }, + ); + this.modals.push(modal); + modal.open(); + } + batchRenameAllImages() { + return __async(this, null, function* () { + const activeFile = this.getActiveFile(); + const fileCache = this.app.metadataCache.getFileCache(activeFile); + if (!fileCache || !fileCache.embeds) return; + const extPatternRegex = /jpe?g|png|gif|tiff|webp/i; + for (const embed of fileCache.embeds) { + const file = this.app.metadataCache.getFirstLinkpathDest( + embed.link, + activeFile.path, + ); + if (!file) { + console.warn("file not found", embed.link); + return; + } + const m0 = extPatternRegex.exec(file.extension); + if (!m0) return; + const { newName, isMeaningful } = this.generateNewName( + file, + activeFile, + ); + debugLog("generated newName:", newName, isMeaningful); + if (!isMeaningful) { + new import_obsidian2.Notice( + "Failed to batch rename images: the generated name is not meaningful", + ); + break; + } + yield this.renameFile(file, newName, activeFile.path, false); + } + }); + } + // returns a new name for the input file, with extension + generateNewName(file, activeFile) { + let imageNameKey = ""; + let firstHeading = ""; + let frontmatter; + const fileCache = this.app.metadataCache.getFileCache(activeFile); + if (fileCache) { + debugLog("frontmatter", fileCache.frontmatter); + frontmatter = fileCache.frontmatter; + imageNameKey = + (frontmatter == null ? void 0 : frontmatter.imageNameKey) || ""; + firstHeading = getFirstHeading(fileCache.headings); + } else { + console.warn( + "could not get file cache from active file", + activeFile.name, + ); + } + const stem = renderTemplate( + this.settings.imageNamePattern, + { + imageNameKey, + fileName: activeFile.basename, + dirName: activeFile.parent.name, + firstHeading, + }, + frontmatter, + ); + const meaninglessRegex = new RegExp( + `[${this.settings.dupNumberDelimiter}\\s]`, + "gm", + ); + return { + stem, + newName: `${stem}.${file.extension}`, + isMeaningful: stem.replace(meaninglessRegex, "") !== "", + }; + } + // newName: foo.ext + deduplicateNewName(newName, file) { + return __async(this, null, function* () { + const dir = file.parent.path; + const listed = yield this.app.vault.adapter.list(dir); + debugLog("sibling files", listed); + const newNameExt = path.extension(newName); + const newNameStem = newName.slice( + 0, + newName.length - newNameExt.length - 1, + ); + const newNameStemEscaped = escapeRegExp(newNameStem); + const delimiter = this.settings.dupNumberDelimiter; + const delimiterEscaped = escapeRegExp(delimiter); + let dupNameRegex; + if (this.settings.dupNumberAtStart) { + dupNameRegex = new RegExp( + `^(?\\d+)${delimiterEscaped}(?${newNameStemEscaped})\\.${newNameExt}$`, + ); + } else { + dupNameRegex = new RegExp( + `^(?${newNameStemEscaped})${delimiterEscaped}(?\\d+)\\.${newNameExt}$`, + ); + } + debugLog("dupNameRegex", dupNameRegex); + const dupNameNumbers = []; + let isNewNameExist = false; + for (let sibling of listed.files) { + sibling = path.basename(sibling); + if (sibling === newName) { + isNewNameExist = true; + continue; + } + const m = dupNameRegex.exec(sibling); + if (!m) continue; + dupNameNumbers.push(Number.parseInt(m.groups.number)); + } + if (isNewNameExist || this.settings.dupNumberAlways) { + const newNumber = + dupNameNumbers.length > 0 ? Math.max(...dupNameNumbers) + 1 : 1; + if (this.settings.dupNumberAtStart) { + newName = `${newNumber}${delimiter}${newNameStem}.${newNameExt}`; + } else { + newName = `${newNameStem}${delimiter}${newNumber}.${newNameExt}`; + } + } + return { + name: newName, + stem: newName.slice(0, newName.length - newNameExt.length - 1), + extension: newNameExt, + }; + }); + } + getActiveFile() { + const view = this.app.workspace.getActiveViewOfType( + import_obsidian2.MarkdownView, + ); + const file = view == null ? void 0 : view.file; + debugLog("active file", file == null ? void 0 : file.path); + return file; + } + getActiveEditor() { + const view = this.app.workspace.getActiveViewOfType( + import_obsidian2.MarkdownView, + ); + return view == null ? void 0 : view.editor; + } + onunload() { + this.modals.map((modal) => modal.close()); + } + testExcludeExtension(file) { + const pattern = this.settings.excludeExtensionPattern; + if (!pattern) return false; + return new RegExp(pattern).test(file.extension); + } + loadSettings() { + return __async(this, null, function* () { + this.settings = Object.assign( + {}, + DEFAULT_SETTINGS, + yield this.loadData(), + ); + }); + } + saveSettings() { + return __async(this, null, function* () { + yield this.saveData(this.settings); + }); + } }; function getFirstHeading(headings) { - if (headings && headings.length > 0) { - for (const heading of headings) { - if (heading.level === 1) { - return heading.heading; - } - } - } - return ""; + if (headings && headings.length > 0) { + for (const heading of headings) { + if (heading.level === 1) { + return heading.heading; + } + } + } + return ""; } function isPastedImage(file) { - if (file instanceof import_obsidian2.TFile) { - if (file.name.startsWith(PASTED_IMAGE_PREFIX)) { - return true; - } - } - return false; + if (file instanceof import_obsidian2.TFile) { + if (file.name.startsWith(PASTED_IMAGE_PREFIX)) { + return true; + } + } + return false; } function isMarkdownFile(file) { - if (file instanceof import_obsidian2.TFile) { - if (file.extension === "md") { - return true; - } - } - return false; + if (file instanceof import_obsidian2.TFile) { + if (file.extension === "md") { + return true; + } + } + return false; } -var ImageRenameModal = class extends import_obsidian2.Modal { - constructor(app, src, stem, renameFunc, onClose) { - super(app); - this.src = src; - this.stem = stem; - this.renameFunc = renameFunc; - this.onCloseExtra = onClose; - } - onOpen() { - this.containerEl.addClass("image-rename-modal"); - const { contentEl, titleEl } = this; - titleEl.setText("Rename image"); - const imageContainer = contentEl.createDiv({ - cls: "image-container" - }); - imageContainer.createEl("img", { - attr: { - src: this.app.vault.getResourcePath(this.src) - } - }); - let stem = this.stem; - const ext = this.src.extension; - const getNewName = (stem2) => stem2 + "." + ext; - const getNewPath = (stem2) => path.join(this.src.parent.path, getNewName(stem2)); - const infoET = createElementTree(contentEl, { - tag: "ul", - cls: "info", - children: [ - { - tag: "li", - children: [ - { - tag: "span", - text: "Origin path" - }, - { - tag: "span", - text: this.src.path - } - ] - }, - { - tag: "li", - children: [ - { - tag: "span", - text: "New path" - }, - { - tag: "span", - text: getNewPath(stem) - } - ] - } - ] - }); - const doRename = () => __async(this, null, function* () { - debugLog("doRename", `stem=${stem}`); - this.renameFunc(getNewName(stem)); - }); - const nameSetting = new import_obsidian2.Setting(contentEl).setName("New name").setDesc("Please input the new name for the image (without extension)").addText((text) => text.setValue(stem).onChange( - (value) => __async(this, null, function* () { - stem = sanitizer.filename(value); - infoET.children[1].children[1].el.innerText = getNewPath(stem); - }) - )); - const nameInputEl = nameSetting.controlEl.children[0]; - nameInputEl.focus(); - const nameInputState = lockInputMethodComposition(nameInputEl); - nameInputEl.addEventListener("keydown", (e) => __async(this, null, function* () { - if (e.key === "Enter" && !nameInputState.lock) { - e.preventDefault(); - if (!stem) { - errorEl.innerText = 'Error: "New name" could not be empty'; - errorEl.style.display = "block"; - return; - } - doRename(); - this.close(); - } - })); - const errorEl = contentEl.createDiv({ - cls: "error", - attr: { - style: "display: none;" - } - }); - new import_obsidian2.Setting(contentEl).addButton((button) => { - button.setButtonText("Rename").onClick(() => { - doRename(); - this.close(); - }); - }).addButton((button) => { - button.setButtonText("Cancel").onClick(() => { - this.close(); - }); - }); - } - onClose() { - const { contentEl } = this; - contentEl.empty(); - this.onCloseExtra(); - } +const ImageRenameModal = class extends import_obsidian2.Modal { + constructor(app, src, stem, renameFunc, onClose) { + super(app); + this.src = src; + this.stem = stem; + this.renameFunc = renameFunc; + this.onCloseExtra = onClose; + } + onOpen() { + this.containerEl.addClass("image-rename-modal"); + const { contentEl, titleEl } = this; + titleEl.setText("Rename image"); + const imageContainer = contentEl.createDiv({ + cls: "image-container", + }); + imageContainer.createEl("img", { + attr: { + src: this.app.vault.getResourcePath(this.src), + }, + }); + let stem = this.stem; + const ext = this.src.extension; + const getNewName = (stem2) => `${stem2}.${ext}`; + const getNewPath = (stem2) => + path.join(this.src.parent.path, getNewName(stem2)); + const infoET = createElementTree(contentEl, { + tag: "ul", + cls: "info", + children: [ + { + tag: "li", + children: [ + { + tag: "span", + text: "Origin path", + }, + { + tag: "span", + text: this.src.path, + }, + ], + }, + { + tag: "li", + children: [ + { + tag: "span", + text: "New path", + }, + { + tag: "span", + text: getNewPath(stem), + }, + ], + }, + ], + }); + const doRename = () => + __async(this, null, function* () { + debugLog("doRename", `stem=${stem}`); + this.renameFunc(getNewName(stem)); + }); + const nameSetting = new import_obsidian2.Setting(contentEl) + .setName("New name") + .setDesc("Please input the new name for the image (without extension)") + .addText((text) => + text.setValue(stem).onChange((value) => + __async(this, null, function* () { + stem = sanitizer.filename(value); + infoET.children[1].children[1].el.innerText = getNewPath(stem); + }), + ), + ); + const nameInputEl = nameSetting.controlEl.children[0]; + nameInputEl.focus(); + const nameInputState = lockInputMethodComposition(nameInputEl); + nameInputEl.addEventListener("keydown", (e) => + __async(this, null, function* () { + if (e.key === "Enter" && !nameInputState.lock) { + e.preventDefault(); + if (!stem) { + errorEl.innerText = 'Error: "New name" could not be empty'; + errorEl.style.display = "block"; + return; + } + doRename(); + this.close(); + } + }), + ); + const errorEl = contentEl.createDiv({ + cls: "error", + attr: { + style: "display: none;", + }, + }); + new import_obsidian2.Setting(contentEl) + .addButton((button) => { + button.setButtonText("Rename").onClick(() => { + doRename(); + this.close(); + }); + }) + .addButton((button) => { + button.setButtonText("Cancel").onClick(() => { + this.close(); + }); + }); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + this.onCloseExtra(); + } }; -var imageNamePatternDesc = ` +const imageNamePatternDesc = ` The pattern indicates how the new name should be generated. Available variables: @@ -878,67 +987,134 @@ Here are some examples from pattern to image names (repeat in sequence), variabl - {{imageNameKey}}: foo, foo-1, foo-2 - {{imageNameKey}}-{{DATE:YYYYMMDD}}: foo-20220408, foo-20220408-1, foo-20220408-2 `; -var SettingTab = class extends import_obsidian2.PluginSettingTab { - constructor(app, plugin) { - super(app, plugin); - this.plugin = plugin; - } - display() { - const { containerEl } = this; - containerEl.empty(); - new import_obsidian2.Setting(containerEl).setName("Image name pattern").setDesc(imageNamePatternDesc).setClass("long-description-setting-item").addText((text) => text.setPlaceholder("{{imageNameKey}}").setValue(this.plugin.settings.imageNamePattern).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.imageNamePattern = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Duplicate number at start (or end)").setDesc(`If enabled, duplicate number will be added at the start as prefix for the image name, otherwise it will be added at the end as suffix for the image name.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.dupNumberAtStart).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.dupNumberAtStart = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Duplicate number delimiter").setDesc(`The delimiter to generate the number prefix/suffix for duplicated names. For example, if the value is "-", the suffix will be like "-1", "-2", "-3", and the prefix will be like "1-", "2-", "3-". Only characters that are valid in file names are allowed.`).addText((text) => text.setValue(this.plugin.settings.dupNumberDelimiter).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.dupNumberDelimiter = sanitizer.delimiter(value); - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Always add duplicate number").setDesc(`If enabled, duplicate number will always be added to the image name. Otherwise, it will only be added when the name is duplicated.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.dupNumberAlways).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.dupNumberAlways = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Auto rename").setDesc(`By default, the rename modal will always be shown to confirm before renaming, if this option is set, the image will be auto renamed after pasting.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.autoRename).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.autoRename = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Handle all attachments").setDesc(`By default, the plugin only handles images that starts with "Pasted image " in name, +const SettingTab = class extends import_obsidian2.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + new import_obsidian2.Setting(containerEl) + .setName("Image name pattern") + .setDesc(imageNamePatternDesc) + .setClass("long-description-setting-item") + .addText((text) => + text + .setPlaceholder("{{imageNameKey}}") + .setValue(this.plugin.settings.imageNamePattern) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.imageNamePattern = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Duplicate number at start (or end)") + .setDesc( + "If enabled, duplicate number will be added at the start as prefix for the image name, otherwise it will be added at the end as suffix for the image name.", + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.dupNumberAtStart) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.dupNumberAtStart = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Duplicate number delimiter") + .setDesc( + `The delimiter to generate the number prefix/suffix for duplicated names. For example, if the value is "-", the suffix will be like "-1", "-2", "-3", and the prefix will be like "1-", "2-", "3-". Only characters that are valid in file names are allowed.`, + ) + .addText((text) => + text + .setValue(this.plugin.settings.dupNumberDelimiter) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.dupNumberDelimiter = + sanitizer.delimiter(value); + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Always add duplicate number") + .setDesc( + "If enabled, duplicate number will always be added to the image name. Otherwise, it will only be added when the name is duplicated.", + ) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.dupNumberAlways) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.dupNumberAlways = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Auto rename") + .setDesc( + "By default, the rename modal will always be shown to confirm before renaming, if this option is set, the image will be auto renamed after pasting.", + ) + .addToggle((toggle) => + toggle.setValue(this.plugin.settings.autoRename).onChange((value) => + __async(this, null, function* () { + this.plugin.settings.autoRename = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Handle all attachments") + .setDesc(`By default, the plugin only handles images that starts with "Pasted image " in name, which is the prefix Obsidian uses to create images from pasted content. - If this option is set, the plugin will handle all attachments that are created in the vault.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.handleAllAttachments).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.handleAllAttachments = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Exclude extension pattern").setDesc(`This option is only useful when "Handle all attachments" is enabled. - Write a Regex pattern to exclude certain extensions from being handled. Only the first line will be used.`).setClass("single-line-textarea").addTextArea((text) => text.setPlaceholder("docx?|xlsx?|pptx?|zip|rar").setValue(this.plugin.settings.excludeExtensionPattern).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.excludeExtensionPattern = value; - yield this.plugin.saveSettings(); - }) - )); - new import_obsidian2.Setting(containerEl).setName("Disable rename notice").setDesc(`Turn off this option if you don't want to see the notice when renaming images. - Note that Obsidian may display a notice when a link has changed, this option cannot disable that.`).addToggle((toggle) => toggle.setValue(this.plugin.settings.disableRenameNotice).onChange( - (value) => __async(this, null, function* () { - this.plugin.settings.disableRenameNotice = value; - yield this.plugin.saveSettings(); - }) - )); - } + If this option is set, the plugin will handle all attachments that are created in the vault.`) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.handleAllAttachments) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.handleAllAttachments = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Exclude extension pattern") + .setDesc(`This option is only useful when "Handle all attachments" is enabled. + Write a Regex pattern to exclude certain extensions from being handled. Only the first line will be used.`) + .setClass("single-line-textarea") + .addTextArea((text) => + text + .setPlaceholder("docx?|xlsx?|pptx?|zip|rar") + .setValue(this.plugin.settings.excludeExtensionPattern) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.excludeExtensionPattern = value; + yield this.plugin.saveSettings(); + }), + ), + ); + new import_obsidian2.Setting(containerEl) + .setName("Disable rename notice") + .setDesc(`Turn off this option if you don't want to see the notice when renaming images. + Note that Obsidian may display a notice when a link has changed, this option cannot disable that.`) + .addToggle((toggle) => + toggle + .setValue(this.plugin.settings.disableRenameNotice) + .onChange((value) => + __async(this, null, function* () { + this.plugin.settings.disableRenameNotice = value; + yield this.plugin.saveSettings(); + }), + ), + ); + } }; -/* nosourcemap */ \ No newline at end of file +/* nosourcemap */ diff --git a/src/content/.obsidian/plugins/obsidian-paste-image-rename/manifest.json b/src/content/.obsidian/plugins/obsidian-paste-image-rename/manifest.json index 152d9133c..ab94dbbba 100644 --- a/src/content/.obsidian/plugins/obsidian-paste-image-rename/manifest.json +++ b/src/content/.obsidian/plugins/obsidian-paste-image-rename/manifest.json @@ -7,4 +7,4 @@ "author": "Reorx", "authorUrl": "https://github.com/reorx", "isDesktopOnly": false -} \ No newline at end of file +} diff --git a/src/content/.obsidian/workspace-mobile.json b/src/content/.obsidian/workspace-mobile.json index ea94a8229..782991ab3 100644 --- a/src/content/.obsidian/workspace-mobile.json +++ b/src/content/.obsidian/workspace-mobile.json @@ -1,181 +1,181 @@ { - "main": { - "id": "11c40c0eff369230", - "type": "split", - "children": [ - { - "id": "a3b78448b99fd05e", - "type": "tabs", - "children": [ - { - "id": "a48cfb809f321ac1", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "posts/mobile-git.md", - "mode": "source", - "source": false - }, - "icon": "lucide-file", - "title": "mobile-git" - } - } - ] - } - ], - "direction": "vertical" - }, - "left": { - "id": "084d99fc7dfae6d5", - "type": "mobile-drawer", - "children": [ - { - "id": "913290ce74f3c790", - "type": "leaf", - "state": { - "type": "file-explorer", - "state": { - "sortOrder": "alphabetical", - "autoReveal": false - }, - "icon": "lucide-folder-closed", - "title": "文件列表" - } - }, - { - "id": "715c6bf7efb3af34", - "type": "leaf", - "state": { - "type": "search", - "state": { - "query": "", - "matchingCase": false, - "explainSearch": false, - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical" - }, - "icon": "lucide-search", - "title": "搜索" - } - }, - { - "id": "1867f88e0e5be91e", - "type": "leaf", - "state": { - "type": "tag", - "state": { - "sortOrder": "frequency", - "useHierarchy": true, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-tags", - "title": "标签" - } - }, - { - "id": "c81497cc77ac87dc", - "type": "leaf", - "state": { - "type": "bookmarks", - "state": {}, - "icon": "lucide-bookmark", - "title": "书签" - } - } - ], - "currentTab": 0 - }, - "right": { - "id": "00f2d94402e22f34", - "type": "mobile-drawer", - "children": [ - { - "id": "c18847a8fc329542", - "type": "leaf", - "state": { - "type": "backlink", - "state": { - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical", - "showSearch": false, - "searchQuery": "", - "backlinkCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-coming-in", - "title": "反向链接" - } - }, - { - "id": "a0983a2e5ea8f256", - "type": "leaf", - "state": { - "type": "outgoing-link", - "state": { - "linksCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-going-out", - "title": "出链" - } - }, - { - "id": "289ab02c28f6d4b5", - "type": "leaf", - "state": { - "type": "outline", - "state": { - "followCursor": false, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-list", - "title": "大纲" - } - } - ], - "currentTab": 0 - }, - "left-ribbon": { - "hiddenItems": { - "switcher:打开快速切换": false, - "graph:查看关系图谱": false, - "canvas:新建白板": false, - "daily-notes:打开/创建今天的日记": false, - "templates:插入模板": false, - "command-palette:打开命令面板": false, - "bases:创建新数据库": false - } - }, - "active": "a48cfb809f321ac1", - "lastOpenFiles": [ - "posts/163-free-domain-email.md", - "posts/acg-randompic-api.md", - "posts/mobile-git.md", - "posts/warden-worker.md", - "posts/record.md", - "assets/images/Screenshot_2025-11-24-07-56-33-62_a2e3670364a4153bdb03dad30c8d4108.jpg", - "assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108 1.jpg", - "assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd 1.jpg", - "assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108.jpg", - "assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd.jpg", - "posts/wx-zfb-card.md", - "assets/images/Screenshot_2025-11-11-14-18-53-34_51606159b24eff83e24a54116878fe3e.jpg", - "assets/images/Screenshot_2025-11-11-14-17-32-08_51606159b24eff83e24a54116878fe3e.jpg", - "assets/images/Screenshot_2025-11-11-14-15-59-46_51606159b24eff83e24a54116878fe3e.jpg", - "assets/images/Screenshot_2025-11-11-14-15-01-63_b5a5c5cb02ca09c784c5d88160e2ec24.jpg", - "assets/images/Screenshot_2025-11-11-14-13-03-99_a2e3670364a4153bdb03dad30c8d4108.jpg", - "posts/check-notebook.md", - "未命名.md", - "posts/zte-f450-bridge.md", - "posts/unknown-upload.md", - "posts/rvc.md", - "posts/index-tts2.md", - "posts/hook-steam-drm.md", - "posts/first-pc.md" - ] -} \ No newline at end of file + "main": { + "id": "11c40c0eff369230", + "type": "split", + "children": [ + { + "id": "a3b78448b99fd05e", + "type": "tabs", + "children": [ + { + "id": "a48cfb809f321ac1", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "posts/mobile-git.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "mobile-git" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "084d99fc7dfae6d5", + "type": "mobile-drawer", + "children": [ + { + "id": "913290ce74f3c790", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "文件列表" + } + }, + { + "id": "715c6bf7efb3af34", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "搜索" + } + }, + { + "id": "1867f88e0e5be91e", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "标签" + } + }, + { + "id": "c81497cc77ac87dc", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "书签" + } + } + ], + "currentTab": 0 + }, + "right": { + "id": "00f2d94402e22f34", + "type": "mobile-drawer", + "children": [ + { + "id": "c18847a8fc329542", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "反向链接" + } + }, + { + "id": "a0983a2e5ea8f256", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "出链" + } + }, + { + "id": "289ab02c28f6d4b5", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "大纲" + } + } + ], + "currentTab": 0 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:打开快速切换": false, + "graph:查看关系图谱": false, + "canvas:新建白板": false, + "daily-notes:打开/创建今天的日记": false, + "templates:插入模板": false, + "command-palette:打开命令面板": false, + "bases:创建新数据库": false + } + }, + "active": "a48cfb809f321ac1", + "lastOpenFiles": [ + "posts/163-free-domain-email.md", + "posts/acg-randompic-api.md", + "posts/mobile-git.md", + "posts/warden-worker.md", + "posts/record.md", + "assets/images/Screenshot_2025-11-24-07-56-33-62_a2e3670364a4153bdb03dad30c8d4108.jpg", + "assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108 1.jpg", + "assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd 1.jpg", + "assets/images/Screenshot_2025-11-24-07-56-23-48_a2e3670364a4153bdb03dad30c8d4108.jpg", + "assets/images/Screenshot_2025-11-24-07-55-54-35_df198e732186825c8df26e3c5a10d7cd.jpg", + "posts/wx-zfb-card.md", + "assets/images/Screenshot_2025-11-11-14-18-53-34_51606159b24eff83e24a54116878fe3e.jpg", + "assets/images/Screenshot_2025-11-11-14-17-32-08_51606159b24eff83e24a54116878fe3e.jpg", + "assets/images/Screenshot_2025-11-11-14-15-59-46_51606159b24eff83e24a54116878fe3e.jpg", + "assets/images/Screenshot_2025-11-11-14-15-01-63_b5a5c5cb02ca09c784c5d88160e2ec24.jpg", + "assets/images/Screenshot_2025-11-11-14-13-03-99_a2e3670364a4153bdb03dad30c8d4108.jpg", + "posts/check-notebook.md", + "未命名.md", + "posts/zte-f450-bridge.md", + "posts/unknown-upload.md", + "posts/rvc.md", + "posts/index-tts2.md", + "posts/hook-steam-drm.md", + "posts/first-pc.md" + ] +} diff --git a/src/content/config.ts b/src/content/config.ts index 4e756c226..17e1d7d51 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -21,7 +21,7 @@ const postsCollection = defineCollection({ }); const assetsCollection = defineCollection({ - type: 'data', + type: "data", schema: z.object({ title: z.string().optional(), description: z.string().optional(), diff --git a/src/content/posts/.obsidian/app.json b/src/content/posts/.obsidian/app.json index ff8234001..ae391dd65 100644 --- a/src/content/posts/.obsidian/app.json +++ b/src/content/posts/.obsidian/app.json @@ -1,4 +1,4 @@ { - "promptDelete": true, - "trashOption": "none" -} \ No newline at end of file + "promptDelete": true, + "trashOption": "none" +} diff --git a/src/content/posts/.obsidian/appearance.json b/src/content/posts/.obsidian/appearance.json index 9e26dfeeb..0967ef424 100644 --- a/src/content/posts/.obsidian/appearance.json +++ b/src/content/posts/.obsidian/appearance.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/src/content/posts/.obsidian/core-plugins.json b/src/content/posts/.obsidian/core-plugins.json index 3f2ab43ce..fabdb22db 100644 --- a/src/content/posts/.obsidian/core-plugins.json +++ b/src/content/posts/.obsidian/core-plugins.json @@ -1,32 +1,32 @@ { - "file-explorer": true, - "global-search": true, - "switcher": true, - "graph": true, - "backlink": true, - "canvas": true, - "outgoing-link": true, - "tag-pane": true, - "footnotes": false, - "properties": true, - "page-preview": true, - "daily-notes": true, - "templates": true, - "note-composer": true, - "command-palette": true, - "slash-command": false, - "editor-status": true, - "bookmarks": true, - "markdown-importer": false, - "zk-prefixer": false, - "random-note": false, - "outline": true, - "word-count": true, - "slides": false, - "audio-recorder": false, - "workspaces": false, - "file-recovery": true, - "publish": false, - "sync": true, - "bases": true -} \ No newline at end of file + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "footnotes": false, + "properties": true, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": true, + "bases": true +} diff --git a/src/content/posts/.obsidian/workspace-mobile.json b/src/content/posts/.obsidian/workspace-mobile.json index c94e71229..ecad75358 100644 --- a/src/content/posts/.obsidian/workspace-mobile.json +++ b/src/content/posts/.obsidian/workspace-mobile.json @@ -1,178 +1,178 @@ { - "main": { - "id": "b56121844276eb64", - "type": "split", - "children": [ - { - "id": "5ebeefd580f6adb0", - "type": "tabs", - "children": [ - { - "id": "9275ec63876b11eb", - "type": "leaf", - "state": { - "type": "markdown", - "state": { - "file": "bypass-gfw.md", - "mode": "source", - "source": false - }, - "icon": "lucide-file", - "title": "bypass-gfw" - } - } - ] - } - ], - "direction": "vertical" - }, - "left": { - "id": "2c63954b82322467", - "type": "mobile-drawer", - "children": [ - { - "id": "b60744b183a2d99f", - "type": "leaf", - "state": { - "type": "file-explorer", - "state": { - "sortOrder": "alphabetical", - "autoReveal": false - }, - "icon": "lucide-folder-closed", - "title": "文件列表" - } - }, - { - "id": "064f298a0f7669c8", - "type": "leaf", - "state": { - "type": "search", - "state": { - "query": "", - "matchingCase": false, - "explainSearch": false, - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical" - }, - "icon": "lucide-search", - "title": "搜索" - } - }, - { - "id": "ce5a69fb9d850226", - "type": "leaf", - "state": { - "type": "tag", - "state": { - "sortOrder": "frequency", - "useHierarchy": true, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-tags", - "title": "标签" - } - }, - { - "id": "f7825454aa9bc774", - "type": "leaf", - "state": { - "type": "all-properties", - "state": { - "sortOrder": "frequency", - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-archive", - "title": "添加笔记属性" - } - }, - { - "id": "dd09c289f8179e54", - "type": "leaf", - "state": { - "type": "bookmarks", - "state": {}, - "icon": "lucide-bookmark", - "title": "书签" - } - } - ], - "currentTab": 0 - }, - "right": { - "id": "e743b64e11c3794a", - "type": "mobile-drawer", - "children": [ - { - "id": "0863d73f4ac810d8", - "type": "leaf", - "state": { - "type": "backlink", - "state": { - "file": "bypass-gfw.md", - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical", - "showSearch": false, - "searchQuery": "", - "backlinkCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-coming-in", - "title": "反向链接" - } - }, - { - "id": "c9198bbec9a9f70e", - "type": "leaf", - "state": { - "type": "outgoing-link", - "state": { - "file": "bypass-gfw.md", - "linksCollapsed": false, - "unlinkedCollapsed": true - }, - "icon": "links-going-out", - "title": "出链" - } - }, - { - "id": "966d5ae3954dd9cf", - "type": "leaf", - "state": { - "type": "outline", - "state": { - "file": "bypass-gfw.md", - "followCursor": false, - "showSearch": false, - "searchQuery": "" - }, - "icon": "lucide-list", - "title": "大纲" - } - } - ], - "currentTab": 0 - }, - "left-ribbon": { - "hiddenItems": { - "switcher:打开快速切换": false, - "graph:查看关系图谱": false, - "canvas:新建白板": false, - "daily-notes:打开/创建今天的日记": false, - "templates:插入模板": false, - "command-palette:打开命令面板": false, - "bases:创建新数据库": false - } - }, - "active": "9275ec63876b11eb", - "lastOpenFiles": [ - "mobile-git.md", - "bypass-ustc-verifying 1.md", - "csp.md", - "bypass-gfw.md" - ] -} \ No newline at end of file + "main": { + "id": "b56121844276eb64", + "type": "split", + "children": [ + { + "id": "5ebeefd580f6adb0", + "type": "tabs", + "children": [ + { + "id": "9275ec63876b11eb", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "bypass-gfw.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "bypass-gfw" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "2c63954b82322467", + "type": "mobile-drawer", + "children": [ + { + "id": "b60744b183a2d99f", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "文件列表" + } + }, + { + "id": "064f298a0f7669c8", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "搜索" + } + }, + { + "id": "ce5a69fb9d850226", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-tags", + "title": "标签" + } + }, + { + "id": "f7825454aa9bc774", + "type": "leaf", + "state": { + "type": "all-properties", + "state": { + "sortOrder": "frequency", + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-archive", + "title": "添加笔记属性" + } + }, + { + "id": "dd09c289f8179e54", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "书签" + } + } + ], + "currentTab": 0 + }, + "right": { + "id": "e743b64e11c3794a", + "type": "mobile-drawer", + "children": [ + { + "id": "0863d73f4ac810d8", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "bypass-gfw.md", + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "反向链接" + } + }, + { + "id": "c9198bbec9a9f70e", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "file": "bypass-gfw.md", + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "出链" + } + }, + { + "id": "966d5ae3954dd9cf", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "bypass-gfw.md", + "followCursor": false, + "showSearch": false, + "searchQuery": "" + }, + "icon": "lucide-list", + "title": "大纲" + } + } + ], + "currentTab": 0 + }, + "left-ribbon": { + "hiddenItems": { + "switcher:打开快速切换": false, + "graph:查看关系图谱": false, + "canvas:新建白板": false, + "daily-notes:打开/创建今天的日记": false, + "templates:插入模板": false, + "command-palette:打开命令面板": false, + "bases:创建新数据库": false + } + }, + "active": "9275ec63876b11eb", + "lastOpenFiles": [ + "mobile-git.md", + "bypass-ustc-verifying 1.md", + "csp.md", + "bypass-gfw.md" + ] +} diff --git a/src/data/friends/22.json b/src/data/friends/22.json index e992d3cb0..0038c5d35 100644 --- a/src/data/friends/22.json +++ b/src/data/friends/22.json @@ -1,6 +1,6 @@ { - "name": "22", - "avatar": "/favicon/22.png", - "description": "https://space.bilibili.com/44681442", - "url": "https://space.bilibili.com/44681442" -} \ No newline at end of file + "name": "22", + "avatar": "/favicon/22.png", + "description": "https://space.bilibili.com/44681442", + "url": "https://space.bilibili.com/44681442" +} diff --git a/src/data/friends/Ad_closeNN_の_Blog.json b/src/data/friends/Ad_closeNN_の_Blog.json index 3b78f70fb..401ad3547 100644 --- a/src/data/friends/Ad_closeNN_の_Blog.json +++ b/src/data/friends/Ad_closeNN_の_Blog.json @@ -1,6 +1,6 @@ { - "name": "Ad_closeNN の Blog", - "avatar": "https://adclosenn.top/assets/avatar.jpg", - "description": "Ad_closeNN 的小站,时不时会刷新一些野生东西 | ✨ 欢迎友链,祝大家天天开心! ✨", - "url": "https://adclosenn.top" -} \ No newline at end of file + "name": "Ad_closeNN の Blog", + "avatar": "https://adclosenn.top/assets/avatar.jpg", + "description": "Ad_closeNN 的小站,时不时会刷新一些野生东西 | ✨ 欢迎友链,祝大家天天开心! ✨", + "url": "https://adclosenn.top" +} diff --git a/src/data/friends/AirTouch's_Blog.json b/src/data/friends/AirTouch's_Blog.json index 833e613c4..50c59b5fc 100644 --- a/src/data/friends/AirTouch's_Blog.json +++ b/src/data/friends/AirTouch's_Blog.json @@ -1,6 +1,6 @@ { - "name": "AirTouch's Blog", - "avatar": "https://blog.airtouch.top/avatar.webp", - "description": "岩间琉璃云间月", - "url": "https://xsl.im" -} \ No newline at end of file + "name": "AirTouch's Blog", + "avatar": "https://blog.airtouch.top/avatar.webp", + "description": "岩间琉璃云间月", + "url": "https://xsl.im" +} diff --git a/src/data/friends/AlexMa's_Blog.json b/src/data/friends/AlexMa's_Blog.json index 1cbe8e4d5..a9e6f09ed 100644 --- a/src/data/friends/AlexMa's_Blog.json +++ b/src/data/friends/AlexMa's_Blog.json @@ -1,6 +1,6 @@ { - "name": "AlexMa's Blog", - "avatar": "https://blog-backend.alexma.top/api/v2/objects/avatar/112zjnt1f3c2cf3prp.webp", - "description": "Create things with love.", - "url": "https://blog.alexma.top" -} \ No newline at end of file + "name": "AlexMa's Blog", + "avatar": "https://blog-backend.alexma.top/api/v2/objects/avatar/112zjnt1f3c2cf3prp.webp", + "description": "Create things with love.", + "url": "https://blog.alexma.top" +} diff --git a/src/data/friends/Aliom的个人博客.json b/src/data/friends/Aliom的个人博客.json index 27fb4ac86..25710eae9 100644 --- a/src/data/friends/Aliom的个人博客.json +++ b/src/data/friends/Aliom的个人博客.json @@ -1,6 +1,6 @@ { - "name": "Aliom的个人博客", - "avatar": "https://avatars.githubusercontent.com/u/182760295?v=4", - "description": "Aliom的线上小窝", - "url": "https://bailanwa.dpdns.org" -} \ No newline at end of file + "name": "Aliom的个人博客", + "avatar": "https://avatars.githubusercontent.com/u/182760295?v=4", + "description": "Aliom的线上小窝", + "url": "https://bailanwa.dpdns.org" +} diff --git a/src/data/friends/CappuccinoZ's_Blog.json b/src/data/friends/CappuccinoZ's_Blog.json index 0eafeed0c..219fd14e0 100644 --- a/src/data/friends/CappuccinoZ's_Blog.json +++ b/src/data/friends/CappuccinoZ's_Blog.json @@ -1,6 +1,6 @@ { - "name": "CappuccinoZ's Blog", - "avatar": "https://kitakita.top/img/avatar2.png", - "description": "良机一直就在你眼前", - "url": "https://kitakita.top" -} \ No newline at end of file + "name": "CappuccinoZ's Blog", + "avatar": "https://kitakita.top/img/avatar2.png", + "description": "良机一直就在你眼前", + "url": "https://kitakita.top" +} diff --git a/src/data/friends/Clina's_Blog.json b/src/data/friends/Clina's_Blog.json index de25ae291..c9e819ed3 100644 --- a/src/data/friends/Clina's_Blog.json +++ b/src/data/friends/Clina's_Blog.json @@ -1,6 +1,6 @@ { - "name": "Clina's Blog", - "avatar": "https://codeberg.org/Clina/fuwari/raw/branch/main/public/photos/photo1.png", - "description": "Clina的Blog", - "url": "https://clina.webn.cc" -} \ No newline at end of file + "name": "Clina's Blog", + "avatar": "https://codeberg.org/Clina/fuwari/raw/branch/main/public/photos/photo1.png", + "description": "Clina的Blog", + "url": "https://clina.webn.cc" +} diff --git a/src/data/friends/Codfish_Blog.json b/src/data/friends/Codfish_Blog.json index 1460742e9..ebf64dcaa 100644 --- a/src/data/friends/Codfish_Blog.json +++ b/src/data/friends/Codfish_Blog.json @@ -1,6 +1,6 @@ { - "name": "Codfish Blog", - "avatar": "https://codfish.top/favicon/1.jpg", - "description": "🐟🐟🐟", - "url": "https://codfish.top" -} \ No newline at end of file + "name": "Codfish Blog", + "avatar": "https://codfish.top/favicon/1.jpg", + "description": "🐟🐟🐟", + "url": "https://codfish.top" +} diff --git a/src/data/friends/Dahi_Blog.json b/src/data/friends/Dahi_Blog.json index 62ae2e3ff..c2e42af49 100644 --- a/src/data/friends/Dahi_Blog.json +++ b/src/data/friends/Dahi_Blog.json @@ -1,6 +1,6 @@ { - "name": "Dahi Blog", - "avatar": "https://oxs.dahi.icu/pic/avatar.png", - "description": "時光流轉,願你有一天能與重要的人重逢", - "url": "https://dahi.icu/" -} \ No newline at end of file + "name": "Dahi Blog", + "avatar": "https://oxs.dahi.icu/pic/avatar.png", + "description": "時光流轉,願你有一天能與重要的人重逢", + "url": "https://dahi.icu/" +} diff --git a/src/data/friends/Eilo's_Blog.json b/src/data/friends/Eilo's_Blog.json index 17fe11597..7fd3c72ce 100644 --- a/src/data/friends/Eilo's_Blog.json +++ b/src/data/friends/Eilo's_Blog.json @@ -1,6 +1,6 @@ { - "name": "Eilo's Blog", - "avatar": "https://www.eilo.uk/img/logo.png", - "description": "等等我记一下", - "url": "https://www.eilo.uk/" -} \ No newline at end of file + "name": "Eilo's Blog", + "avatar": "https://www.eilo.uk/img/logo.png", + "description": "等等我记一下", + "url": "https://www.eilo.uk/" +} diff --git a/src/data/friends/Elykia.json b/src/data/friends/Elykia.json index e2aba8610..dfb6a372b 100644 --- a/src/data/friends/Elykia.json +++ b/src/data/friends/Elykia.json @@ -1,6 +1,6 @@ { - "name": "Elykia", - "avatar": "https://bu.dusays.com/2024/10/25/671b2438203a6.gif", - "description": "致以无暇之人", - "url": "https://blog.elykia.cn" -} \ No newline at end of file + "name": "Elykia", + "avatar": "https://bu.dusays.com/2024/10/25/671b2438203a6.gif", + "description": "致以无暇之人", + "url": "https://blog.elykia.cn" +} diff --git a/src/data/friends/Elysiam.json b/src/data/friends/Elysiam.json index 0dcb3a9fd..12a6f66c7 100644 --- a/src/data/friends/Elysiam.json +++ b/src/data/friends/Elysiam.json @@ -1,6 +1,6 @@ { - "name": "Elysiam", - "avatar": "https://img.170529.xyz/2025/08/favicon-compress.avif", - "description": "个人的博客小站,分享下技术和生活。May you, the beauty of this world, always shine", - "url": "https://blog.170529.xyz" -} \ No newline at end of file + "name": "Elysiam", + "avatar": "https://img.170529.xyz/2025/08/favicon-compress.avif", + "description": "个人的博客小站,分享下技术和生活。May you, the beauty of this world, always shine", + "url": "https://blog.170529.xyz" +} diff --git a/src/data/friends/Eunie的博客.json b/src/data/friends/Eunie的博客.json index 37d9a2592..999943d4d 100644 --- a/src/data/friends/Eunie的博客.json +++ b/src/data/friends/Eunie的博客.json @@ -1,6 +1,6 @@ { - "name": "Eunie的博客", - "avatar": "https://www.eunie.online/upload/tMZ6yUjk7rxhCqQ.png", - "description": "https://www.eunie.online/", - "url": "https://www.eunie.online/" -} \ No newline at end of file + "name": "Eunie的博客", + "avatar": "https://www.eunie.online/upload/tMZ6yUjk7rxhCqQ.png", + "description": "https://www.eunie.online/", + "url": "https://www.eunie.online/" +} diff --git a/src/data/friends/Falling42.json b/src/data/friends/Falling42.json index a1dcbece6..97af35fce 100644 --- a/src/data/friends/Falling42.json +++ b/src/data/friends/Falling42.json @@ -1,6 +1,6 @@ { - "name": "Falling42", - "avatar": "https://s2.loli.net/2025/05/17/Cbm7ogkBWAq3ST8.png", - "description": "Fate lies within the light cone.", - "url": "https://falling42.net/" -} \ No newline at end of file + "name": "Falling42", + "avatar": "https://s2.loli.net/2025/05/17/Cbm7ogkBWAq3ST8.png", + "description": "Fate lies within the light cone.", + "url": "https://falling42.net/" +} diff --git a/src/data/friends/Gavin's_Blog.json b/src/data/friends/Gavin's_Blog.json index d1a2d4f93..66e5af222 100644 --- a/src/data/friends/Gavin's_Blog.json +++ b/src/data/friends/Gavin's_Blog.json @@ -1,6 +1,6 @@ { - "name": "Gavin's Blog", - "avatar": "https://gavin-blog.pages.dev/assets/website/avatar.webp", - "description": "一个内容成分复杂的博客小站。", - "url": "https://gavin-blog.pages.dev" -} \ No newline at end of file + "name": "Gavin's Blog", + "avatar": "https://gavin-blog.pages.dev/assets/website/avatar.webp", + "description": "一个内容成分复杂的博客小站。", + "url": "https://gavin-blog.pages.dev" +} diff --git a/src/data/friends/Gemsly.json b/src/data/friends/Gemsly.json index 2b328fb94..2ba9f2a23 100644 --- a/src/data/friends/Gemsly.json +++ b/src/data/friends/Gemsly.json @@ -1,6 +1,6 @@ { - "name": "Gemsly's Blog", - "avatar": "https://picdn.gemsly.top/api/rfile/avatar-origin.jpg", - "description": "誰染花香入境", - "url": "https://blog.harrverse.com" + "name": "Gemsly's Blog", + "avatar": "https://picdn.gemsly.top/api/rfile/avatar-origin.jpg", + "description": "誰染花香入境", + "url": "https://blog.harrverse.com" } diff --git a/src/data/friends/Google搜索_siterbq._找到的.json b/src/data/friends/Google搜索_siterbq._找到的.json index d4ea1cfd3..0b1a573e9 100644 --- a/src/data/friends/Google搜索_siterbq._找到的.json +++ b/src/data/friends/Google搜索_siterbq._找到的.json @@ -1,6 +1,6 @@ { - "name": "Google搜索 site:rbq.* 找到的", - "avatar": "https://saltedfishes.com/favicon.ico", - "description": "「Tragedy is coming 'as I wish'......」", - "url": "https://saltedfishes.com" -} \ No newline at end of file + "name": "Google搜索 site:rbq.* 找到的", + "avatar": "https://saltedfishes.com/favicon.ico", + "description": "「Tragedy is coming 'as I wish'......」", + "url": "https://saltedfishes.com" +} diff --git a/src/data/friends/Gunbrad的博客.json b/src/data/friends/Gunbrad的博客.json index fcb7404d2..1b396add7 100644 --- a/src/data/friends/Gunbrad的博客.json +++ b/src/data/friends/Gunbrad的博客.json @@ -1,6 +1,6 @@ { - "name": "Gunbrad的博客", - "avatar": "https://gunbrad.xyz/favicon/icon.jpg", - "description": "https://gunbrad.xyz", - "url": "https://gunbrad.xyz" -} \ No newline at end of file + "name": "Gunbrad的博客", + "avatar": "https://gunbrad.xyz/favicon/icon.jpg", + "description": "https://gunbrad.xyz", + "url": "https://gunbrad.xyz" +} diff --git a/src/data/friends/Honesty.json b/src/data/friends/Honesty.json index 54bbde9cd..1cdb6ef64 100644 --- a/src/data/friends/Honesty.json +++ b/src/data/friends/Honesty.json @@ -1,6 +1,6 @@ { - "name": "Honesty", - "avatar": "https://www.hehouhui.cn/images/avatar.jpeg", - "description": "从浩瀚无垠的知识中看到一缕光", - "url": "https://blog.hehouhui.cn" + "name": "Honesty", + "avatar": "https://www.hehouhui.cn/images/avatar.jpeg", + "description": "从浩瀚无垠的知识中看到一缕光", + "url": "https://blog.hehouhui.cn" } diff --git a/src/data/friends/LINUX.DO.json b/src/data/friends/LINUX.DO.json index 6b40f82d6..3152bb83b 100644 --- a/src/data/friends/LINUX.DO.json +++ b/src/data/friends/LINUX.DO.json @@ -1,6 +1,6 @@ { - "name": "LINUX.DO", - "avatar": "https://linux.do/uploads/default/optimized/3X/7/d/7de31932a4fd533496cfe35979a4d9d995bb5c63_2_180x180.png", - "description": "https://linux.do", - "url": "https://linux.do" -} \ No newline at end of file + "name": "LINUX.DO", + "avatar": "https://linux.do/uploads/default/optimized/3X/7/d/7de31932a4fd533496cfe35979a4d9d995bb5c63_2_180x180.png", + "description": "https://linux.do", + "url": "https://linux.do" +} diff --git a/src/data/friends/LegspCpd_Blog.json b/src/data/friends/LegspCpd_Blog.json index fad91435f..540344133 100644 --- a/src/data/friends/LegspCpd_Blog.json +++ b/src/data/friends/LegspCpd_Blog.json @@ -1,6 +1,6 @@ { - "name": "LegspCpd Blog", - "avatar": "https://img.2xxt.qzz.io/file/image/blog/me/Image_1764400332601.jpg", - "description": "脆弱的防线已经被我们撕碎,现在让这里变得更加混乱!", - "url": "https://blog.2xgh.qzz.io" -} \ No newline at end of file + "name": "LegspCpd Blog", + "avatar": "https://img.2xxt.qzz.io/file/image/blog/me/Image_1764400332601.jpg", + "description": "脆弱的防线已经被我们撕碎,现在让这里变得更加混乱!", + "url": "https://blog.2xgh.qzz.io" +} diff --git a/src/data/friends/MC_Kero_blog.json b/src/data/friends/MC_Kero_blog.json index 5ed32626c..27defb06d 100644 --- a/src/data/friends/MC_Kero_blog.json +++ b/src/data/friends/MC_Kero_blog.json @@ -1,6 +1,6 @@ { - "name": "MC_Kero blog", - "avatar": "https://pub-3f1fec3195b64bb4974955567a77da00.r2.dev/OIP.jpeg", - "description": "MC_Kero的博客", - "url": "https://blog.mckero.com" -} \ No newline at end of file + "name": "MC_Kero blog", + "avatar": "https://pub-3f1fec3195b64bb4974955567a77da00.r2.dev/OIP.jpeg", + "description": "MC_Kero的博客", + "url": "https://blog.mckero.com" +} diff --git a/src/data/friends/Maple_Blog.json b/src/data/friends/Maple_Blog.json index 015201ea7..2a69a62ea 100644 --- a/src/data/friends/Maple_Blog.json +++ b/src/data/friends/Maple_Blog.json @@ -1,6 +1,6 @@ { - "name": "Maple Blog", - "avatar": "https://blog.oowo.cc/assets/home/home.png", - "description": "趁着年轻,好好犯病。", - "url": "https://blog.oowo.cc" -} \ No newline at end of file + "name": "Maple Blog", + "avatar": "https://blog.oowo.cc/assets/home/home.png", + "description": "趁着年轻,好好犯病。", + "url": "https://blog.oowo.cc" +} diff --git a/src/data/friends/Mo的记事簿.json b/src/data/friends/Mo的记事簿.json index 492ae3eda..d08737c20 100644 --- a/src/data/friends/Mo的记事簿.json +++ b/src/data/friends/Mo的记事簿.json @@ -1,6 +1,6 @@ { - "name": "Mo的记事簿", - "avatar": "https://blog.xiowo.net/img/avatar.png", - "description": "万年鸽王,哈哈OvO", - "url": "https://blog.xiowo.net/" -} \ No newline at end of file + "name": "Mo的记事簿", + "avatar": "https://blog.xiowo.net/img/avatar.png", + "description": "万年鸽王,哈哈OvO", + "url": "https://blog.xiowo.net/" +} diff --git a/src/data/friends/NodeSeek.json b/src/data/friends/NodeSeek.json index 0e0450ecf..aac206b6c 100644 --- a/src/data/friends/NodeSeek.json +++ b/src/data/friends/NodeSeek.json @@ -1,6 +1,6 @@ { - "name": "NodeSeek", - "avatar": "/favicon/nodeseek.png", - "description": "https://www.nodeseek.com", - "url": "https://www.nodeseek.com" -} \ No newline at end of file + "name": "NodeSeek", + "avatar": "/favicon/nodeseek.png", + "description": "https://www.nodeseek.com", + "url": "https://www.nodeseek.com" +} diff --git a/src/data/friends/PARKSI's_Blog.json b/src/data/friends/PARKSI's_Blog.json index 37a9b1229..20fd71a9b 100644 --- a/src/data/friends/PARKSI's_Blog.json +++ b/src/data/friends/PARKSI's_Blog.json @@ -1,6 +1,6 @@ { - "name": "PARKSI's Blog", - "avatar": "https://blog.lparksi.com/images/icon.webp", - "description": "Redefine Yourself", - "url": "https://blog.lparksi.com/" -} \ No newline at end of file + "name": "PARKSI's Blog", + "avatar": "https://blog.lparksi.com/images/icon.webp", + "description": "Redefine Yourself", + "url": "https://blog.lparksi.com/" +} diff --git a/src/data/friends/Petric_Blog.json b/src/data/friends/Petric_Blog.json index 66c6e0c62..d441611da 100644 --- a/src/data/friends/Petric_Blog.json +++ b/src/data/friends/Petric_Blog.json @@ -1,6 +1,6 @@ { - "name": "Petric Blog", - "avatar": "https://www.petricw.com/photo.jpg", - "description": "代码如诗 / 逻辑为歌", - "url": "https://www.petricw.com" -} \ No newline at end of file + "name": "Petric Blog", + "avatar": "https://www.petricw.com/photo.jpg", + "description": "代码如诗 / 逻辑为歌", + "url": "https://www.petricw.com" +} diff --git a/src/data/friends/QC3284's_Blog.json b/src/data/friends/QC3284's_Blog.json index a7ccc9402..9c3f292a6 100644 --- a/src/data/friends/QC3284's_Blog.json +++ b/src/data/friends/QC3284's_Blog.json @@ -1,6 +1,6 @@ { - "name": "QC3284's Blog", - "avatar": "https://cdn4.xcqcoo.top/gh/QC3284/233@main/test_db_qc3284.png", - "description": "欢迎来到我的博客! 一名乐于探索新事物的爱好者-QC3284", - "url": "https://www.xcqcoo.top" -} \ No newline at end of file + "name": "QC3284's Blog", + "avatar": "https://cdn4.xcqcoo.top/gh/QC3284/233@main/test_db_qc3284.png", + "description": "欢迎来到我的博客! 一名乐于探索新事物的爱好者-QC3284", + "url": "https://www.xcqcoo.top" +} diff --git a/src/data/friends/RCBlog.json b/src/data/friends/RCBlog.json index eb1dba2fe..f9255c089 100644 --- a/src/data/friends/RCBlog.json +++ b/src/data/friends/RCBlog.json @@ -1,6 +1,6 @@ { - "name": "RC-Blog", - "avatar": "https://yamr.cc/favicon/favicon.svg", - "description": "是,是这样喵?", - "url": "https://yamr.cc/" -} \ No newline at end of file + "name": "RC-Blog", + "avatar": "https://yamr.cc/favicon/favicon.svg", + "description": "是,是这样喵?", + "url": "https://yamr.cc/" +} diff --git a/src/data/friends/RukaaX_Blogs.json b/src/data/friends/RukaaX_Blogs.json index 11198c9c7..0d8ef97a3 100644 --- a/src/data/friends/RukaaX_Blogs.json +++ b/src/data/friends/RukaaX_Blogs.json @@ -1,6 +1,6 @@ { - "name": "RukaaX Blogs", - "avatar": "https://blogs.rukaax.top/wp-content/uploads/2025/07/cropped-1753627554-ICON.webp", - "description": "RukaaX-一名高中牲的博客", - "url": "https://blogs.rukaax.top/" -} \ No newline at end of file + "name": "RukaaX Blogs", + "avatar": "https://blogs.rukaax.top/wp-content/uploads/2025/07/cropped-1753627554-ICON.webp", + "description": "RukaaX-一名高中牲的博客", + "url": "https://blogs.rukaax.top/" +} diff --git a/src/data/friends/SISUBENYs_Blog.json b/src/data/friends/SISUBENYs_Blog.json index 6ac45785a..ab39b853d 100644 --- a/src/data/friends/SISUBENYs_Blog.json +++ b/src/data/friends/SISUBENYs_Blog.json @@ -1,6 +1,6 @@ -{ - "name": "SISUBNEY", - "avatar": "https://sisubeny.space/img/ff_hu_5652443c9e3495e9.png", - "description": "SISUBENY's Blog", - "url": "https://sisubeny.space/" - } \ No newline at end of file +{ + "name": "SISUBNEY", + "avatar": "https://sisubeny.space/img/ff_hu_5652443c9e3495e9.png", + "description": "SISUBENY's Blog", + "url": "https://sisubeny.space/" +} diff --git a/src/data/friends/Shattered217のblog.json b/src/data/friends/Shattered217のblog.json index 6af8a8cc8..31ce1443d 100644 --- a/src/data/friends/Shattered217のblog.json +++ b/src/data/friends/Shattered217のblog.json @@ -1,6 +1,6 @@ { - "name": "Shattered217のblog", - "avatar": "https://blog.shattered.top/wp-content/uploads/2023/06/cropped-android-chrome-512x512-1.png", - "description": "成分复杂", - "url": "https://blog.shattered.top/" -} \ No newline at end of file + "name": "Shattered217のblog", + "avatar": "https://blog.shattered.top/wp-content/uploads/2023/06/cropped-android-chrome-512x512-1.png", + "description": "成分复杂", + "url": "https://blog.shattered.top/" +} diff --git a/src/data/friends/SrP-BloG.json b/src/data/friends/SrP-BloG.json index a1a3681ca..78d34fe85 100644 --- a/src/data/friends/SrP-BloG.json +++ b/src/data/friends/SrP-BloG.json @@ -1,6 +1,6 @@ { - "name": "SrP-BloG", - "avatar": "https://blog.srprolin.top/favicon/favicon.ico", - "description": "因为会忘,所以有了这里。", - "url": "https://blog.srprolin.top" -} \ No newline at end of file + "name": "SrP-BloG", + "avatar": "https://blog.srprolin.top/favicon/favicon.ico", + "description": "因为会忘,所以有了这里。", + "url": "https://blog.srprolin.top" +} diff --git a/src/data/friends/Stanley的技术博客.json b/src/data/friends/Stanley的技术博客.json index ce6d0278c..2197453d2 100644 --- a/src/data/friends/Stanley的技术博客.json +++ b/src/data/friends/Stanley的技术博客.json @@ -1,6 +1,6 @@ { - "name": "Stanley的技术博客", - "avatar": "https://main.lixvyao.com/favicon.png", - "description": "给时光以生命 给岁月以文明。", - "url": "https://www.lixvyao.com" -} \ No newline at end of file + "name": "Stanley的技术博客", + "avatar": "https://main.lixvyao.com/favicon.png", + "description": "给时光以生命 给岁月以文明。", + "url": "https://www.lixvyao.com" +} diff --git a/src/data/friends/Starlr_blog.json b/src/data/friends/Starlr_blog.json index d6275da82..aa3f53983 100644 --- a/src/data/friends/Starlr_blog.json +++ b/src/data/friends/Starlr_blog.json @@ -1,6 +1,6 @@ { - "name": "Starlr blog", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3385016019&spec=5", - "description": "懒得写文章喵", - "url": "https://www.xc-lr.cn" -} \ No newline at end of file + "name": "Starlr blog", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3385016019&spec=5", + "description": "懒得写文章喵", + "url": "https://www.xc-lr.cn" +} diff --git a/src/data/friends/THW's_Blog.json b/src/data/friends/THW's_Blog.json index e7be20611..a9723ab6c 100644 --- a/src/data/friends/THW's_Blog.json +++ b/src/data/friends/THW's_Blog.json @@ -1,6 +1,6 @@ { - "name": "THW's Blog", - "avatar": "https://img.dooki.cloud/i/2025/08/29/68b09e116c4b6.webp", - "description": "Live a good life meet slowly", - "url": "https://thw.lol/" -} \ No newline at end of file + "name": "THW's Blog", + "avatar": "https://img.dooki.cloud/i/2025/08/29/68b09e116c4b6.webp", + "description": "Live a good life meet slowly", + "url": "https://thw.lol/" +} diff --git a/src/data/friends/Tavre_Blog.json b/src/data/friends/Tavre_Blog.json index afec197a8..6292ea045 100644 --- a/src/data/friends/Tavre_Blog.json +++ b/src/data/friends/Tavre_Blog.json @@ -1,6 +1,6 @@ { - "name": "Tavre Blog", - "avatar": "https://q1.qlogo.cn/g?b=qq&nk=2987304764&s=640", - "description": "Tavre的个人技术记录博客", - "url": "https://blog.tavr.top" + "name": "Tavre Blog", + "avatar": "https://q1.qlogo.cn/g?b=qq&nk=2987304764&s=640", + "description": "Tavre的个人技术记录博客", + "url": "https://blog.tavr.top" } diff --git a/src/data/friends/Text03's_Blog.json b/src/data/friends/Text03's_Blog.json index 4fbf58ee4..084171f47 100644 --- a/src/data/friends/Text03's_Blog.json +++ b/src/data/friends/Text03's_Blog.json @@ -1,6 +1,6 @@ { - "name": "Text03's Blog", - "avatar": "https://text03.com/avatar", - "description": "如果生活把你推到了板边,记得升龙 →↓↘+👊", - "url": "https://text03.com" -} \ No newline at end of file + "name": "Text03's Blog", + "avatar": "https://text03.com/avatar", + "description": "如果生活把你推到了板边,记得升龙 →↓↘+👊", + "url": "https://text03.com" +} diff --git a/src/data/friends/Wer_Blog.json b/src/data/friends/Wer_Blog.json index f31ee80a7..aa709107c 100644 --- a/src/data/friends/Wer_Blog.json +++ b/src/data/friends/Wer_Blog.json @@ -1,6 +1,6 @@ { - "name": "Wer Blog", - "avatar": "https://blog.isyyo.com/favicon/logo.png", - "description": "The only way to do great is to love what you do", - "url": "https://blog.isyyo.com/" -} \ No newline at end of file + "name": "Wer Blog", + "avatar": "https://blog.isyyo.com/favicon/logo.png", + "description": "The only way to do great is to love what you do", + "url": "https://blog.isyyo.com/" +} diff --git a/src/data/friends/XUIOO.json b/src/data/friends/XUIOO.json index 8eaa2bbb2..3d7728b01 100644 --- a/src/data/friends/XUIOO.json +++ b/src/data/friends/XUIOO.json @@ -1,6 +1,6 @@ { - "name": "XUIOO", - "avatar": "https://blog.xuioo.com/27c69590279fdc3be2cd7c901e3d07ad.jpg", - "description": "一叶障目,不见泰山;两耳塞豆,不闻雷霆。", - "url": "https://blog.xuioo.com" -} \ No newline at end of file + "name": "XUIOO", + "avatar": "https://blog.xuioo.com/27c69590279fdc3be2cd7c901e3d07ad.jpg", + "description": "一叶障目,不见泰山;两耳塞豆,不闻雷霆。", + "url": "https://blog.xuioo.com" +} diff --git a/src/data/friends/YoungLee的手帐.json b/src/data/friends/YoungLee的手帐.json index e3a74d475..c84f2d7e1 100644 --- a/src/data/friends/YoungLee的手帐.json +++ b/src/data/friends/YoungLee的手帐.json @@ -1,6 +1,6 @@ { - "name": "YoungLee的手帐", - "avatar": "https://younglee.cn/icon.svg", - "description": "写作恐惧症患者,需要咖啡续命的学者", - "url": "https://younglee.cn" -} \ No newline at end of file + "name": "YoungLee的手帐", + "avatar": "https://younglee.cn/icon.svg", + "description": "写作恐惧症患者,需要咖啡续命的学者", + "url": "https://younglee.cn" +} diff --git a/src/data/friends/Zero_-_Higanbana.json b/src/data/friends/Zero_-_Higanbana.json index 57ad30b51..66e7bca10 100644 --- a/src/data/friends/Zero_-_Higanbana.json +++ b/src/data/friends/Zero_-_Higanbana.json @@ -1,6 +1,6 @@ { - "name": "Zero - Higanbana", - "avatar": "https://vtdd.vip/_astro/lx.U15Ju0Ce_1mLhMp.webp", - "description": "https://vtdd.vip/", - "url": "https://vtdd.vip/" -} \ No newline at end of file + "name": "Zero - Higanbana", + "avatar": "https://vtdd.vip/_astro/lx.U15Ju0Ce_1mLhMp.webp", + "description": "https://vtdd.vip/", + "url": "https://vtdd.vip/" +} diff --git a/src/data/friends/Zirkeln.json b/src/data/friends/Zirkeln.json index 2077d0395..207e827a5 100644 --- a/src/data/friends/Zirkeln.json +++ b/src/data/friends/Zirkeln.json @@ -1,6 +1,6 @@ { - "name": "Zirkeln", - "avatar": "https://zrn.net/wp-content/uploads/2025/07/seia_avatar.jpg.webp", - "description": "欢迎来到我的个人网站!这里是我作为一名计算机科学爱好者和 ACG(动画、漫画、游戏)迷,分享我对计算机技术、动画、漫画小说和电子音乐(EDM)等多元兴趣的乐园。", - "url": "https://zrn.net" -} \ No newline at end of file + "name": "Zirkeln", + "avatar": "https://zrn.net/wp-content/uploads/2025/07/seia_avatar.jpg.webp", + "description": "欢迎来到我的个人网站!这里是我作为一名计算机科学爱好者和 ACG(动画、漫画、游戏)迷,分享我对计算机技术、动画、漫画小说和电子音乐(EDM)等多元兴趣的乐园。", + "url": "https://zrn.net" +} diff --git a/src/data/friends/b23kim博客.json b/src/data/friends/b23kim博客.json index bafa4b3dc..22e60a9b6 100644 --- a/src/data/friends/b23kim博客.json +++ b/src/data/friends/b23kim博客.json @@ -1,6 +1,6 @@ { - "name": "b23.kim", - "avatar": null, - "description": "请输入内容404的个人发电之作。记住:“黑客是客,博客也是客”", - "url": "https://www.b23.kim/?source=2xnz" + "name": "b23.kim", + "avatar": null, + "description": "请输入内容404的个人发电之作。记住:“黑客是客,博客也是客”", + "url": "https://www.b23.kim/?source=2xnz" } diff --git a/src/data/friends/bbb-lsy07.json b/src/data/friends/bbb-lsy07.json index c5d2a78d0..924d67970 100644 --- a/src/data/friends/bbb-lsy07.json +++ b/src/data/friends/bbb-lsy07.json @@ -1,6 +1,6 @@ { - "name": "bbb-lsy07", - "avatar": "https://images.6uu.us/20250511114301488.JPG", - "description": "科技激荡人文,洞见智慧本真。", - "url": "https://blog.6uu.us" -} \ No newline at end of file + "name": "bbb-lsy07", + "avatar": "https://images.6uu.us/20250511114301488.JPG", + "description": "科技激荡人文,洞见智慧本真。", + "url": "https://blog.6uu.us" +} diff --git a/src/data/friends/chuzouX_Blog.json b/src/data/friends/chuzouX_Blog.json index 14520b0f9..e773324c5 100644 --- a/src/data/friends/chuzouX_Blog.json +++ b/src/data/friends/chuzouX_Blog.json @@ -1,6 +1,6 @@ { - "name": "chuzouX Blog", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3451860760&spec=0", - "description": "欢迎大家光临本站,希望大家在这里可以找到自己想要的东西,祝大家玩的开心!!", - "url": "https://chuzoux.top" -} \ No newline at end of file + "name": "chuzouX Blog", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3451860760&spec=0", + "description": "欢迎大家光临本站,希望大家在这里可以找到自己想要的东西,祝大家玩的开心!!", + "url": "https://chuzoux.top" +} diff --git a/src/data/friends/fishcpy的小破站.json b/src/data/friends/fishcpy的小破站.json index 18e757c6d..6604941ee 100644 --- a/src/data/friends/fishcpy的小破站.json +++ b/src/data/friends/fishcpy的小破站.json @@ -1,6 +1,6 @@ { - "name": "fishcpy的小破站", - "avatar": "https://www.fis.ink/img/logo.png", - "description": "非淡泊无以明志,非宁静无以致远", - "url": "https://blog.fis.ink" -} \ No newline at end of file + "name": "fishcpy的小破站", + "avatar": "https://www.fis.ink/img/logo.png", + "description": "非淡泊无以明志,非宁静无以致远", + "url": "https://blog.fis.ink" +} diff --git a/src/data/friends/fulie_blog.json b/src/data/friends/fulie_blog.json index 8dbd27256..1d41c3f13 100644 --- a/src/data/friends/fulie_blog.json +++ b/src/data/friends/fulie_blog.json @@ -1,6 +1,6 @@ { - "name": "fulie blog", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3672492995&spec=5", - "description": "永不后悔", - "url": "https://pcb.im/" -} \ No newline at end of file + "name": "fulie blog", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3672492995&spec=5", + "description": "永不后悔", + "url": "https://pcb.im/" +} diff --git a/src/data/friends/mccsjs.json b/src/data/friends/mccsjs.json index fcc784f16..7b911f254 100644 --- a/src/data/friends/mccsjs.json +++ b/src/data/friends/mccsjs.json @@ -1,6 +1,6 @@ { - "name": "mccsjs", - "avatar": "https://seln.cn/img/head.jpg", - "description": "点一盏灯,等一个迷路的夜🍁🍁🍁", - "url": "https://seln.cn" -} \ No newline at end of file + "name": "mccsjs", + "avatar": "https://seln.cn/img/head.jpg", + "description": "点一盏灯,等一个迷路的夜🍁🍁🍁", + "url": "https://seln.cn" +} diff --git a/src/data/friends/moehans_blog.json b/src/data/friends/moehans_blog.json index 2159314f3..2b0b1d907 100644 --- a/src/data/friends/moehans_blog.json +++ b/src/data/friends/moehans_blog.json @@ -1,6 +1,6 @@ { - "name": "MOEHANS BLOG", - "avatar": "https://www.moehans.com/_astro/head.DdUHS2R0_fuDv3.webp", - "description": "MOEHANS BLOG,Powered By Vercel", - "url": "https://www.moehans.com/" + "name": "MOEHANS BLOG", + "avatar": "https://www.moehans.com/_astro/head.DdUHS2R0_fuDv3.webp", + "description": "MOEHANS BLOG,Powered By Vercel", + "url": "https://www.moehans.com/" } diff --git a/src/data/friends/mxd's_Planet.json b/src/data/friends/mxd's_Planet.json index 6a6750ced..073847ec1 100644 --- a/src/data/friends/mxd's_Planet.json +++ b/src/data/friends/mxd's_Planet.json @@ -1,6 +1,6 @@ { - "name": "mxd's Planet", - "avatar": "https://res.mxdyeah.top/favicon.webp", - "description": "平平无奇的菜鸟,喜欢干点自己喜欢做的事情。在这里学习更多有关Linux、*BSD知识,加入我们的大家庭一起交流分享!欢迎在此列表的其他网站互换友链!", - "url": "https://www.mxdyeah.top/" -} \ No newline at end of file + "name": "mxd's Planet", + "avatar": "https://res.mxdyeah.top/favicon.webp", + "description": "平平无奇的菜鸟,喜欢干点自己喜欢做的事情。在这里学习更多有关Linux、*BSD知识,加入我们的大家庭一起交流分享!欢迎在此列表的其他网站互换友链!", + "url": "https://www.mxdyeah.top/" +} diff --git a/src/data/friends/soulter.json b/src/data/friends/soulter.json index 80a70c441..831d5c981 100644 --- a/src/data/friends/soulter.json +++ b/src/data/friends/soulter.json @@ -1,6 +1,6 @@ { - "name": "Soulter's Blog", - "avatar": "https://avatars.githubusercontent.com/u/37870767", - "description": "The world is your canvas!", - "url": "https://blog.soulter.top" + "name": "Soulter's Blog", + "avatar": "https://avatars.githubusercontent.com/u/37870767", + "description": "The world is your canvas!", + "url": "https://blog.soulter.top" } diff --git a/src/data/friends/wyf9's_Blog.json b/src/data/friends/wyf9's_Blog.json index 2df159073..ee9ab5ed9 100644 --- a/src/data/friends/wyf9's_Blog.json +++ b/src/data/friends/wyf9's_Blog.json @@ -1,6 +1,6 @@ { - "name": "wyf9's Blog", - "avatar": "https://wyf9.top/favicon/android-chrome-192x192.png", - "description": "什么都有的个人 blog?", - "url": "https://wyf9.top/" -} \ No newline at end of file + "name": "wyf9's Blog", + "avatar": "https://wyf9.top/favicon/android-chrome-192x192.png", + "description": "什么都有的个人 blog?", + "url": "https://wyf9.top/" +} diff --git a/src/data/friends/yCENzh's_Blog.json b/src/data/friends/yCENzh's_Blog.json index 2d6401c0a..e883633de 100644 --- a/src/data/friends/yCENzh's_Blog.json +++ b/src/data/friends/yCENzh's_Blog.json @@ -1,6 +1,6 @@ { - "name": "yCENzh's Blog", - "avatar": "https://fuwari.oh1.top/avatar.webp", - "description": "Ciallo~(∠・ω< )⌒☆", - "url": "https://fuwari.oh1.top/" -} \ No newline at end of file + "name": "yCENzh's Blog", + "avatar": "https://fuwari.oh1.top/avatar.webp", + "description": "Ciallo~(∠・ω< )⌒☆", + "url": "https://fuwari.oh1.top/" +} diff --git a/src/data/friends/yunsen2025的小窝.json b/src/data/friends/yunsen2025的小窝.json index a32273414..d235a30bd 100644 --- a/src/data/friends/yunsen2025的小窝.json +++ b/src/data/friends/yunsen2025的小窝.json @@ -1,6 +1,6 @@ { - "name": "yunsen2025的小窝", - "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", - "description": "纯粹的计算机爱好者", - "url": "https://www.yunsen2025.top" -} \ No newline at end of file + "name": "yunsen2025的小窝", + "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", + "description": "纯粹的计算机爱好者", + "url": "https://www.yunsen2025.top" +} diff --git a/src/data/friends/zhongzhong's_blog.json b/src/data/friends/zhongzhong's_blog.json index 677721988..172c219ee 100644 --- a/src/data/friends/zhongzhong's_blog.json +++ b/src/data/friends/zhongzhong's_blog.json @@ -1,6 +1,6 @@ { - "name": "zhongzhong's blog", - "avatar": "https://zhongzhong.space/wp-content/uploads/2025/11/avatar.webp", - "description": "小白的半成品拼好站", - "url": "https://zhongzhong.space" -} \ No newline at end of file + "name": "zhongzhong's blog", + "avatar": "https://zhongzhong.space/wp-content/uploads/2025/11/avatar.webp", + "description": "小白的半成品拼好站", + "url": "https://zhongzhong.space" +} diff --git a/src/data/friends/zty012_blog_(2y.nz).json b/src/data/friends/zty012_blog_(2y.nz).json index 060ee4c41..6866f3a1c 100644 --- a/src/data/friends/zty012_blog_(2y.nz).json +++ b/src/data/friends/zty012_blog_(2y.nz).json @@ -1,6 +1,6 @@ { - "name": "zty012 blog (2y.nz)", - "avatar": "https://2y.nz/favicon.png", - "description": "一个初中生的blog", - "url": "https://2y.nz" -} \ No newline at end of file + "name": "zty012 blog (2y.nz)", + "avatar": "https://2y.nz/favicon.png", + "description": "一个初中生的blog", + "url": "https://2y.nz" +} diff --git a/src/data/friends/ノート_of_我.json b/src/data/friends/ノート_of_我.json index f029f9421..44554d41e 100644 --- a/src/data/friends/ノート_of_我.json +++ b/src/data/friends/ノート_of_我.json @@ -1,6 +1,6 @@ { - "name": "ノート of 我", - "avatar": "https://avatars.githubusercontent.com/u/29329988", - "description": "一个简单的笔记", - "url": "https://note-of-me.top" -} \ No newline at end of file + "name": "ノート of 我", + "avatar": "https://avatars.githubusercontent.com/u/29329988", + "description": "一个简单的笔记", + "url": "https://note-of-me.top" +} diff --git a/src/data/friends/一叶舟记.json b/src/data/friends/一叶舟记.json index 2494b3439..57a0ef31c 100644 --- a/src/data/friends/一叶舟记.json +++ b/src/data/friends/一叶舟记.json @@ -1,6 +1,6 @@ -{ - "name": "一叶舟记", - "avatar": "https://img.fastmirror.net/s/2025/09/12/68c39893a84aa.png", - "description": "向利而生,随心而活", - "url": "https://blog.lonzov.top/" -} \ No newline at end of file +{ + "name": "一叶舟记", + "avatar": "https://img.fastmirror.net/s/2025/09/12/68c39893a84aa.png", + "description": "向利而生,随心而活", + "url": "https://blog.lonzov.top/" +} diff --git a/src/data/friends/临明小狐狸の小窝.json b/src/data/friends/临明小狐狸の小窝.json index 71ba0ba2a..c99fa7f7f 100644 --- a/src/data/friends/临明小狐狸の小窝.json +++ b/src/data/friends/临明小狐狸の小窝.json @@ -1,6 +1,6 @@ { - "name": "临明小狐狸の小窝", - "avatar": "https://lmxhl.top/wp-content/uploads/2025/08/cropped-g.jpg", - "description": "Ciallo~(∠・ω< )⌒★", - "url": "https://lmxhl.top" + "name": "临明小狐狸の小窝", + "avatar": "https://lmxhl.top/wp-content/uploads/2025/08/cropped-g.jpg", + "description": "Ciallo~(∠・ω< )⌒★", + "url": "https://lmxhl.top" } diff --git a/src/data/friends/人类好困的小破博客.json b/src/data/friends/人类好困的小破博客.json index 35ab8b7ee..d52e97a58 100644 --- a/src/data/friends/人类好困的小破博客.json +++ b/src/data/friends/人类好困的小破博客.json @@ -1,6 +1,6 @@ { - "name": "人类好困的小破博客", - "avatar": "https://avatars.githubusercontent.com/u/119118549?v=4", - "description": "一个菜鸟装逼用的站", - "url": "https://blog.haokun.me" + "name": "人类好困的小破博客", + "avatar": "https://avatars.githubusercontent.com/u/119118549?v=4", + "description": "一个菜鸟装逼用的站", + "url": "https://blog.haokun.me" } diff --git a/src/data/friends/千年共振.json b/src/data/friends/千年共振.json index 3c33a5ce1..01ec53fe0 100644 --- a/src/data/friends/千年共振.json +++ b/src/data/friends/千年共振.json @@ -1,6 +1,6 @@ { - "name": "千年共振", - "avatar": "https://img.remit.ee/api/file/BQACAgUAAyEGAASHRsPbAAEBfBVovV-BBiGQfhx8x5AqxWQsf6Yh8QACEz4AAk_q6VViPZEKaaiLvDYE.jpg/", - "description": "安卓人写的苹果博客", - "url": "https://blog.01101.top" -} \ No newline at end of file + "name": "千年共振", + "avatar": "https://img.remit.ee/api/file/BQACAgUAAyEGAASHRsPbAAEBfBVovV-BBiGQfhx8x5AqxWQsf6Yh8QACEz4AAk_q6VViPZEKaaiLvDYE.jpg/", + "description": "安卓人写的苹果博客", + "url": "https://blog.01101.top" +} diff --git a/src/data/friends/呐吼.json b/src/data/friends/呐吼.json index b08bd9a02..4cd4f11cb 100644 --- a/src/data/friends/呐吼.json +++ b/src/data/friends/呐吼.json @@ -1,6 +1,6 @@ { - "name": "呐吼", - "avatar": "https://www.nahou.com/i/icon.webp", - "description": "不止于沉默", - "url": "https://www.nahou.com/" -} \ No newline at end of file + "name": "呐吼", + "avatar": "https://www.nahou.com/i/icon.webp", + "description": "不止于沉默", + "url": "https://www.nahou.com/" +} diff --git a/src/data/friends/在教了我很多的好朋友.json b/src/data/friends/在教了我很多的好朋友.json index d8779e6c8..dfc6f144b 100644 --- a/src/data/friends/在教了我很多的好朋友.json +++ b/src/data/friends/在教了我很多的好朋友.json @@ -1,6 +1,6 @@ { - "name": "在***教了我很多的好朋友", - "avatar": "/favicon/ie-cx.png", - "description": "https://ie.cx", - "url": "https://ie.cx" -} \ No newline at end of file + "name": "在***教了我很多的好朋友", + "avatar": "/favicon/ie-cx.png", + "description": "https://ie.cx", + "url": "https://ie.cx" +} diff --git a/src/data/friends/夏夜流萤.json b/src/data/friends/夏夜流萤.json index f0f8c091d..ed65310d5 100644 --- a/src/data/friends/夏夜流萤.json +++ b/src/data/friends/夏夜流萤.json @@ -1,6 +1,6 @@ { - "name": "夏夜流萤", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=7618557&spec=0", - "description": "总有一场相遇,是互相喜欢的!", - "url": "https://blog.cuteleaf.cn" -} \ No newline at end of file + "name": "夏夜流萤", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=7618557&spec=0", + "description": "总有一场相遇,是互相喜欢的!", + "url": "https://blog.cuteleaf.cn" +} diff --git a/src/data/friends/夜轻Blog.json b/src/data/friends/夜轻Blog.json index 7555b4455..cf84a0c7d 100644 --- a/src/data/friends/夜轻Blog.json +++ b/src/data/friends/夜轻Blog.json @@ -1,6 +1,6 @@ { - "name": "夜轻Blog", - "avatar": "https://blog.yeqing.net/favicon.ico", - "description": "一个人", - "url": "https://blog.yeqing.net/" -} \ No newline at end of file + "name": "夜轻Blog", + "avatar": "https://blog.yeqing.net/favicon.ico", + "description": "一个人", + "url": "https://blog.yeqing.net/" +} diff --git a/src/data/friends/奇妙页.json b/src/data/friends/奇妙页.json index 36c8490a0..13033aae3 100644 --- a/src/data/friends/奇妙页.json +++ b/src/data/friends/奇妙页.json @@ -1,6 +1,6 @@ { - "name": "奇妙页", - "avatar": "https://www.qmye.com/wp-content/uploads/favicon.ico", - "description": "探索AI创意与灵感的奇妙世界", - "url": "https://www.qmye.com" -} \ No newline at end of file + "name": "奇妙页", + "avatar": "https://www.qmye.com/wp-content/uploads/favicon.ico", + "description": "探索AI创意与灵感的奇妙世界", + "url": "https://www.qmye.com" +} diff --git a/src/data/friends/奶昔论坛.json b/src/data/friends/奶昔论坛.json index ec79e6608..5b462a63c 100644 --- a/src/data/friends/奶昔论坛.json +++ b/src/data/friends/奶昔论坛.json @@ -1,6 +1,6 @@ { - "name": "奶昔论坛", - "avatar": "https://forum.naixi.net/favicon.ico", - "description": "https://forum.naixi.net/", - "url": "https://forum.naixi.net/" -} \ No newline at end of file + "name": "奶昔论坛", + "avatar": "https://forum.naixi.net/favicon.ico", + "description": "https://forum.naixi.net/", + "url": "https://forum.naixi.net/" +} diff --git a/src/data/friends/宝塔开心版.json b/src/data/friends/宝塔开心版.json index aaae5f38c..b60f4465f 100644 --- a/src/data/friends/宝塔开心版.json +++ b/src/data/friends/宝塔开心版.json @@ -1,6 +1,6 @@ { - "name": "宝塔开心版", - "avatar": "https://www.bt.cn/favicon.ico", - "description": "https://bt.sb", - "url": "https://bt.sb" -} \ No newline at end of file + "name": "宝塔开心版", + "avatar": "https://www.bt.cn/favicon.ico", + "description": "https://bt.sb", + "url": "https://bt.sb" +} diff --git a/src/data/friends/小宇blog.json b/src/data/friends/小宇blog.json index 93bec89f1..edc3d94c1 100644 --- a/src/data/friends/小宇blog.json +++ b/src/data/friends/小宇blog.json @@ -1,6 +1,6 @@ { - "name": "小宇blog", - "avatar": "https://blog.skyxy.qzz.io/images/favicon.svg", - "description": "很烂的blog,不怎么更新", - "url": "https://blog.skyxy.qzz.io/" -} \ No newline at end of file + "name": "小宇blog", + "avatar": "https://blog.skyxy.qzz.io/images/favicon.svg", + "description": "很烂的blog,不怎么更新", + "url": "https://blog.skyxy.qzz.io/" +} diff --git a/src/data/friends/小张の小站.json b/src/data/friends/小张の小站.json index 0056e160f..65aae55d7 100644 --- a/src/data/friends/小张の小站.json +++ b/src/data/friends/小张の小站.json @@ -1,6 +1,6 @@ { - "name": "小张の小站", - "avatar": "https://img.xiaozhangya.xin/file/1753185126703_avatar.jpg", - "description": "🌟 随时一起探索新事物,让我们一起成长、一起分享,让这片小小的数字世界充满更多的光辉✨!", - "url": "https://hub.xiaozhangya.xin" -} \ No newline at end of file + "name": "小张の小站", + "avatar": "https://img.xiaozhangya.xin/file/1753185126703_avatar.jpg", + "description": "🌟 随时一起探索新事物,让我们一起成长、一起分享,让这片小小的数字世界充满更多的光辉✨!", + "url": "https://hub.xiaozhangya.xin" +} diff --git a/src/data/friends/小猫顶.json b/src/data/friends/小猫顶.json index bf19143d5..3d44b773b 100644 --- a/src/data/friends/小猫顶.json +++ b/src/data/friends/小猫顶.json @@ -1,6 +1,6 @@ { - "name": "小猫顶", - "avatar": "https://avatars.githubusercontent.com/u/155360005", - "description": "无聊的一天搭配神经的我~", - "url": "https://www.mauz.top" + "name": "小猫顶", + "avatar": "https://avatars.githubusercontent.com/u/155360005", + "description": "无聊的一天搭配神经的我~", + "url": "https://www.mauz.top" } diff --git a/src/data/friends/小白のBlog.json b/src/data/friends/小白のBlog.json index 34663786e..79f9c5e20 100644 --- a/src/data/friends/小白のBlog.json +++ b/src/data/friends/小白のBlog.json @@ -1,6 +1,6 @@ { - "name": "小白のBlog", - "avatar": "https://r2cfimg.showby.top/2024/09/19/66eb06a884fb9.jpg", - "description": "Ciallo~(∠・ω< )⌒☆", - "url": "https://www.showby.top/" -} \ No newline at end of file + "name": "小白のBlog", + "avatar": "https://r2cfimg.showby.top/2024/09/19/66eb06a884fb9.jpg", + "description": "Ciallo~(∠・ω< )⌒☆", + "url": "https://www.showby.top/" +} diff --git a/src/data/friends/尘の个人博客.json b/src/data/friends/尘の个人博客.json index 64a4d8e22..b1baefccf 100644 --- a/src/data/friends/尘の个人博客.json +++ b/src/data/friends/尘の个人博客.json @@ -1,6 +1,6 @@ { - "name": "尘の个人博客", - "avatar": "https://blog.mcxiaochen.top/favicon.ico", - "description": "一个高中生 UP 搭的博客 QwQ", - "url": "https://blog.mcxiaochen.top/" -} \ No newline at end of file + "name": "尘の个人博客", + "avatar": "https://blog.mcxiaochen.top/favicon.ico", + "description": "一个高中生 UP 搭的博客 QwQ", + "url": "https://blog.mcxiaochen.top/" +} diff --git a/src/data/friends/巳忆笔记.json b/src/data/friends/巳忆笔记.json index b5512dfa4..3eabcf9dd 100644 --- a/src/data/friends/巳忆笔记.json +++ b/src/data/friends/巳忆笔记.json @@ -1,6 +1,6 @@ { - "name": "巳忆笔记", - "avatar": "https://tc.siyi171.xyz/PicGo/logo.png", - "description": "记住所有美好回忆", - "url": "https://siyi171.xyz/" -} \ No newline at end of file + "name": "巳忆笔记", + "avatar": "https://tc.siyi171.xyz/PicGo/logo.png", + "description": "记住所有美好回忆", + "url": "https://siyi171.xyz/" +} diff --git a/src/data/friends/带我入坑网站的超级大坏比.json b/src/data/friends/带我入坑网站的超级大坏比.json index f06504141..04d2c8a0c 100644 --- a/src/data/friends/带我入坑网站的超级大坏比.json +++ b/src/data/friends/带我入坑网站的超级大坏比.json @@ -1,6 +1,6 @@ { - "name": "带我入坑网站的超级大坏比", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3347510139&spec=5", - "description": "Dmocken的免费推广位", - "url": "https://phira.dmocken.top" -} \ No newline at end of file + "name": "带我入坑网站的超级大坏比", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3347510139&spec=5", + "description": "Dmocken的免费推广位", + "url": "https://phira.dmocken.top" +} diff --git a/src/data/friends/幻空博客.json b/src/data/friends/幻空博客.json index dc4deb50f..d5fc93d92 100644 --- a/src/data/friends/幻空博客.json +++ b/src/data/friends/幻空博客.json @@ -1,6 +1,6 @@ { - "name": "幻空博客", - "avatar": "https://img.huankong.top/i/2022/11/29/6385ef8c7a675.ico", - "description": "https://blog.huankong.top", - "url": "https://blog.huankong.top" -} \ No newline at end of file + "name": "幻空博客", + "avatar": "https://img.huankong.top/i/2022/11/29/6385ef8c7a675.ico", + "description": "https://blog.huankong.top", + "url": "https://blog.huankong.top" +} diff --git a/src/data/friends/或许是一只龙的博客.json b/src/data/friends/或许是一只龙的博客.json index bcf5db24c..706e6e36b 100644 --- a/src/data/friends/或许是一只龙的博客.json +++ b/src/data/friends/或许是一只龙的博客.json @@ -1,6 +1,6 @@ { - "name": "或许是一只龙的博客", - "avatar": "https://fastr2.497995.xyz/image.jpg", - "description": "写点小东西", - "url": "https://www.497995.xyz/" -} \ No newline at end of file + "name": "或许是一只龙的博客", + "avatar": "https://fastr2.497995.xyz/image.jpg", + "description": "写点小东西", + "url": "https://www.497995.xyz/" +} diff --git a/src/data/friends/摸鱼の客栈.json b/src/data/friends/摸鱼の客栈.json index a54e3f1df..24b07cb6a 100644 --- a/src/data/friends/摸鱼の客栈.json +++ b/src/data/friends/摸鱼の客栈.json @@ -1,6 +1,6 @@ { - "name": "摸鱼の客栈", - "avatar": "https://blog.hanbaka.cn/logo.gif", - "description": "没事摸摸鱼,反正焦虑也解决不了问题", - "url": "https://blog.hanbaka.cn" + "name": "摸鱼の客栈", + "avatar": "https://blog.hanbaka.cn/logo.gif", + "description": "没事摸摸鱼,反正焦虑也解决不了问题", + "url": "https://blog.hanbaka.cn" } diff --git a/src/data/friends/星辰の笔记.json b/src/data/friends/星辰の笔记.json index c83a46e2e..1225d0f14 100644 --- a/src/data/friends/星辰の笔记.json +++ b/src/data/friends/星辰の笔记.json @@ -1,6 +1,6 @@ { - "name": "星辰の笔记", - "avatar": "https://blog.strarry.top/avatar.jpg", - "description": "Code | Think | Share | Repeat", - "url": "https://blog.strarry.top/" -} \ No newline at end of file + "name": "星辰の笔记", + "avatar": "https://blog.strarry.top/avatar.jpg", + "description": "Code | Think | Share | Repeat", + "url": "https://blog.strarry.top/" +} diff --git a/src/data/friends/晓晴博客.json b/src/data/friends/晓晴博客.json index 651454a30..76685e1ab 100644 --- a/src/data/friends/晓晴博客.json +++ b/src/data/friends/晓晴博客.json @@ -1,6 +1,6 @@ { - "name": "晓晴博客", - "avatar": "https://www.toubiec.cn/usr/themes/Joe/logo.ico", - "description": "https://www.toubiec.cn", - "url": "https://www.toubiec.cn" -} \ No newline at end of file + "name": "晓晴博客", + "avatar": "https://www.toubiec.cn/usr/themes/Joe/logo.ico", + "description": "https://www.toubiec.cn", + "url": "https://www.toubiec.cn" +} diff --git a/src/data/friends/晓正杨博客.json b/src/data/friends/晓正杨博客.json index 565ded1f0..2a6364c69 100644 --- a/src/data/friends/晓正杨博客.json +++ b/src/data/friends/晓正杨博客.json @@ -1,6 +1,6 @@ { - "name": "晓正杨博客", - "avatar": "https://q1.qlogo.cn/g?b=qq&nk=2540797494&s=640", - "description": "让代码更有价值,让学生生活不再枯燥", - "url": "https://blog.yang233.eu.org" -} \ No newline at end of file + "name": "晓正杨博客", + "avatar": "https://q1.qlogo.cn/g?b=qq&nk=2540797494&s=640", + "description": "让代码更有价值,让学生生活不再枯燥", + "url": "https://blog.yang233.eu.org" +} diff --git a/src/data/friends/曾经天天和我斗嘴并且抄我博客的😡.json b/src/data/friends/曾经天天和我斗嘴并且抄我博客的😡.json index f1e0de01e..af9dab224 100644 --- a/src/data/friends/曾经天天和我斗嘴并且抄我博客的😡.json +++ b/src/data/friends/曾经天天和我斗嘴并且抄我博客的😡.json @@ -1,6 +1,6 @@ { - "name": "曾经天天和我斗嘴并且抄我博客的😡", - "avatar": "https://obj.muyoung.com/logo/favicon.ico", - "description": "https://blog.muyoung.com", - "url": "https://blog.muyoung.com" -} \ No newline at end of file + "name": "曾经天天和我斗嘴并且抄我博客的😡", + "avatar": "https://obj.muyoung.com/logo/favicon.ico", + "description": "https://blog.muyoung.com", + "url": "https://blog.muyoung.com" +} diff --git a/src/data/friends/森资源.json b/src/data/friends/森资源.json index 041ae80b8..8f035af06 100644 --- a/src/data/friends/森资源.json +++ b/src/data/friends/森资源.json @@ -1,6 +1,6 @@ { - "name": "森资源", - "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", - "description": "牛逼的资源网站(没开站还)", - "url": "https://senziyuan.236668.xyz" -} \ No newline at end of file + "name": "森资源", + "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", + "description": "牛逼的资源网站(没开站还)", + "url": "https://senziyuan.236668.xyz" +} diff --git a/src/data/friends/百里博客.json b/src/data/friends/百里博客.json index c40605d31..18b22469d 100644 --- a/src/data/friends/百里博客.json +++ b/src/data/friends/百里博客.json @@ -1,6 +1,6 @@ { - "name": "百里博客", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3166269605&spec=5", - "description": "一个高中生的博客", - "url": "https://blog.my0811.cn" -} \ No newline at end of file + "name": "百里博客", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3166269605&spec=5", + "description": "一个高中生的博客", + "url": "https://blog.my0811.cn" +} diff --git a/src/data/friends/石墨烯积木的个人博客.json b/src/data/friends/石墨烯积木的个人博客.json index 71cf2d244..e7f3e16a1 100644 --- a/src/data/friends/石墨烯积木的个人博客.json +++ b/src/data/friends/石墨烯积木的个人博客.json @@ -1,6 +1,6 @@ { - "name": "石墨烯积木的个人博客", - "avatar": "https://cdn.jsdelivr.net/gh/shimoxi123/img/img/6894b49f58cb8da5c80fd8e5.webp", - "description": "分享一些有趣的东西", - "url": "https://www.shimoxi.qzz.io/" -} \ No newline at end of file + "name": "石墨烯积木的个人博客", + "avatar": "https://cdn.jsdelivr.net/gh/shimoxi123/img/img/6894b49f58cb8da5c80fd8e5.webp", + "description": "分享一些有趣的东西", + "url": "https://www.shimoxi.qzz.io/" +} diff --git a/src/data/friends/硅基漫游指南.json b/src/data/friends/硅基漫游指南.json index 8440e3d5d..87c1b99d1 100644 --- a/src/data/friends/硅基漫游指南.json +++ b/src/data/friends/硅基漫游指南.json @@ -1,6 +1,6 @@ { - "name": "硅基漫游指南", - "avatar": "https://blog.lparksi.com/images/icon.webp", - "description": "Face life with hope.", - "url": "https://blog.helong.online/" -} \ No newline at end of file + "name": "硅基漫游指南", + "avatar": "https://blog.lparksi.com/images/icon.webp", + "description": "Face life with hope.", + "url": "https://blog.helong.online/" +} diff --git a/src/data/friends/私人grgk.json b/src/data/friends/私人grgk.json index 92efc3f30..cb3482ea7 100644 --- a/src/data/friends/私人grgk.json +++ b/src/data/friends/私人grgk.json @@ -1,6 +1,6 @@ { - "name": "私人grgk", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=1662352474&spec=5", - "description": "https://sub.grgk.me", - "url": "https://sub.grgk.me" -} \ No newline at end of file + "name": "私人grgk", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=1662352474&spec=5", + "description": "https://sub.grgk.me", + "url": "https://sub.grgk.me" +} diff --git a/src/data/friends/粉白妙妙屋.json b/src/data/friends/粉白妙妙屋.json index bf223b33a..02cf66ea7 100644 --- a/src/data/friends/粉白妙妙屋.json +++ b/src/data/friends/粉白妙妙屋.json @@ -1,6 +1,6 @@ { - "name": "粉白妙妙屋", - "avatar": "https://dlink.host/wx2.sinaimg.cn/large/007udx8Wly8i0h6etwni7g301j01lq3k.gif", - "description": "我见青山多妩媚", - "url": "https://383842.xyz" -} \ No newline at end of file + "name": "粉白妙妙屋", + "avatar": "https://dlink.host/wx2.sinaimg.cn/large/007udx8Wly8i0h6etwni7g301j01lq3k.gif", + "description": "我见青山多妩媚", + "url": "https://383842.xyz" +} diff --git a/src/data/friends/裕裕裕的小破宅.json b/src/data/friends/裕裕裕的小破宅.json index e8bac96e5..1b2191b39 100644 --- a/src/data/friends/裕裕裕的小破宅.json +++ b/src/data/friends/裕裕裕的小破宅.json @@ -1,6 +1,6 @@ { - "name": "裕裕裕的小破宅", - "avatar": "https://yu-blog.top/img/avatar.jpg", - "description": "一个17岁高中生的小破宅", - "url": "https://yu-blog.top/" + "name": "裕裕裕的小破宅", + "avatar": "https://yu-blog.top/img/avatar.jpg", + "description": "一个17岁高中生的小破宅", + "url": "https://yu-blog.top/" } diff --git a/src/data/friends/酷丁博客.json b/src/data/friends/酷丁博客.json index 580cd7417..cb407ecdc 100644 --- a/src/data/friends/酷丁博客.json +++ b/src/data/friends/酷丁博客.json @@ -1,6 +1,6 @@ { - "name": "酷丁博客", - "avatar": "https://cold04.com/favicon.ico", - "description": "一般路过的计算机萌新", - "url": "https://cold04.com" + "name": "酷丁博客", + "avatar": "https://cold04.com/favicon.ico", + "description": "一般路过的计算机萌新", + "url": "https://cold04.com" } diff --git a/src/data/friends/鈴奈咲桜のブログ.json b/src/data/friends/鈴奈咲桜のブログ.json index 8b97762b3..d5995b109 100644 --- a/src/data/friends/鈴奈咲桜のブログ.json +++ b/src/data/friends/鈴奈咲桜のブログ.json @@ -1,6 +1,6 @@ { - "name": "鈴奈咲桜のブログ", - "avatar": "https://blog.sakura.ink/_astro/avatar.B8d6PP-N_2a0uDN.webp", - "description": "愛することを忘れないで", - "url": "https://blog.sakura.ink" -} \ No newline at end of file + "name": "鈴奈咲桜のブログ", + "avatar": "https://blog.sakura.ink/_astro/avatar.B8d6PP-N_2a0uDN.webp", + "description": "愛することを忘れないで", + "url": "https://blog.sakura.ink" +} diff --git a/src/data/friends/钨鸮的孤岛电台.json b/src/data/friends/钨鸮的孤岛电台.json index bcda7fef4..d7c453a44 100644 --- a/src/data/friends/钨鸮的孤岛电台.json +++ b/src/data/friends/钨鸮的孤岛电台.json @@ -1,6 +1,6 @@ { - "name": "钨鸮的孤岛电台", - "avatar": "https://blog.2w2.top/img/avatar.webp", - "description": "钨在所有金属中排名第七,因此这是一只重磅鸮", - "url": "https://blog.2w2.top" -} \ No newline at end of file + "name": "钨鸮的孤岛电台", + "avatar": "https://blog.2w2.top/img/avatar.webp", + "description": "钨在所有金属中排名第七,因此这是一只重磅鸮", + "url": "https://blog.2w2.top" +} diff --git a/src/data/friends/随思南游.json b/src/data/friends/随思南游.json index bb2c3416c..7b676d6c6 100644 --- a/src/data/friends/随思南游.json +++ b/src/data/friends/随思南游.json @@ -1,6 +1,6 @@ { - "name": "随思南游", - "avatar": "https://ssnur.com/tx.jpg", - "description": "生如夏欢之绚烂,死如秋叶之静", - "url": "https://ssnur.com/" -} \ No newline at end of file + "name": "随思南游", + "avatar": "https://ssnur.com/tx.jpg", + "description": "生如夏欢之绚烂,死如秋叶之静", + "url": "https://ssnur.com/" +} diff --git a/src/data/friends/雨砚Blog.json b/src/data/friends/雨砚Blog.json index b469e6540..b7a5eee8e 100644 --- a/src/data/friends/雨砚Blog.json +++ b/src/data/friends/雨砚Blog.json @@ -1,6 +1,6 @@ { - "name": "雨砚Blog", - "avatar": "https://www.yuyano.com/img/0/favicon.webp", - "description": "一个科技博客丫", - "url": "https://www.yuyano.com" -} \ No newline at end of file + "name": "雨砚Blog", + "avatar": "https://www.yuyano.com/img/0/favicon.webp", + "description": "一个科技博客丫", + "url": "https://www.yuyano.com" +} diff --git a/src/data/friends/雨雪冰屋.json b/src/data/friends/雨雪冰屋.json index aefbf6639..dfccbf0bb 100644 --- a/src/data/friends/雨雪冰屋.json +++ b/src/data/friends/雨雪冰屋.json @@ -1,6 +1,6 @@ { - "name": "雨雪冰屋", - "avatar": "https://iamyukino.cn/favicon.ico", - "description": "可爱的个人网站!一隅雨雪一炉窝,可能会融化持久堆?", - "url": "https://iamyukino.cn" -} \ No newline at end of file + "name": "雨雪冰屋", + "avatar": "https://iamyukino.cn/favicon.ico", + "description": "可爱的个人网站!一隅雨雪一炉窝,可能会融化持久堆?", + "url": "https://iamyukino.cn" +} diff --git a/src/data/friends/雾创岛.json b/src/data/friends/雾创岛.json index 9a2894b40..ff0c2c8f5 100644 --- a/src/data/friends/雾创岛.json +++ b/src/data/friends/雾创岛.json @@ -1,6 +1,6 @@ { - "name": "雾创岛", - "avatar": "https://www.tr0.cn/favicon.ico", - "description": "https://www.tr0.cn", - "url": "https://www.tr0.cn" -} \ No newline at end of file + "name": "雾创岛", + "avatar": "https://www.tr0.cn/favicon.ico", + "description": "https://www.tr0.cn", + "url": "https://www.tr0.cn" +} diff --git a/src/data/friends/青序栈.json b/src/data/friends/青序栈.json index 3e68bfc67..fa0482695 100644 --- a/src/data/friends/青序栈.json +++ b/src/data/friends/青序栈.json @@ -1,6 +1,6 @@ { - "name": "青序栈", - "avatar": "https://qxzhan.cn/favicon.png", - "description": "青序成栈·向简而生", - "url": "https://qxzhan.cn/" -} \ No newline at end of file + "name": "青序栈", + "avatar": "https://qxzhan.cn/favicon.png", + "description": "青序成栈·向简而生", + "url": "https://qxzhan.cn/" +} diff --git a/src/data/friends/静谧小窝.json b/src/data/friends/静谧小窝.json index 4d81f862d..2fc6b85cc 100644 --- a/src/data/friends/静谧小窝.json +++ b/src/data/friends/静谧小窝.json @@ -1,6 +1,6 @@ { - "name": "静谧小窝", - "avatar": "https://imgproxy.yaotustarlight.im/image/fa26532f-4a98-4a1f-84dd-91e2818c41ce", - "description": "在星光下,静谧的小窝里藏着无数轻柔的梦", - "url": "https://blog.yaotustarlight.im" + "name": "静谧小窝", + "avatar": "https://imgproxy.yaotustarlight.im/image/fa26532f-4a98-4a1f-84dd-91e2818c41ce", + "description": "在星光下,静谧的小窝里藏着无数轻柔的梦", + "url": "https://blog.yaotustarlight.im" } diff --git a/src/data/friends/非常酷的Kotlin_Compose_Web_WASM_全Canvas网站.json b/src/data/friends/非常酷的Kotlin_Compose_Web_WASM_全Canvas网站.json index 7d89d5ec4..95c1231b6 100644 --- a/src/data/friends/非常酷的Kotlin_Compose_Web_WASM_全Canvas网站.json +++ b/src/data/friends/非常酷的Kotlin_Compose_Web_WASM_全Canvas网站.json @@ -1,6 +1,6 @@ { - "name": "非常酷的Kotlin Compose Web WASM 全Canvas网站", - "avatar": "/favicon/yurn.png", - "description": "https://nyayurin.github.io/Yutori-Docs/", - "url": "https://nyayurin.github.io/Yutori-Docs/" -} \ No newline at end of file + "name": "非常酷的Kotlin Compose Web WASM 全Canvas网站", + "avatar": "/favicon/yurn.png", + "description": "https://nyayurin.github.io/Yutori-Docs/", + "url": "https://nyayurin.github.io/Yutori-Docs/" +} diff --git a/src/data/sponsors/AlexMa233.json b/src/data/sponsors/AlexMa233.json index 6f49b5d61..f6512c49b 100644 --- a/src/data/sponsors/AlexMa233.json +++ b/src/data/sponsors/AlexMa233.json @@ -1,6 +1,6 @@ { - "name": "AlexMa233", - "avatar": "https://blog-backend.alexma.top/api/v2/objects/avatar/112zjnt1f3c2cf3prp.webp", - "date": "2025-07-27", - "amount": "5 B币" -} \ No newline at end of file + "name": "AlexMa233", + "avatar": "https://blog-backend.alexma.top/api/v2/objects/avatar/112zjnt1f3c2cf3prp.webp", + "date": "2025-07-27", + "amount": "5 B币" +} diff --git a/src/data/sponsors/HEISEN黑森.json b/src/data/sponsors/HEISEN黑森.json index 752bb6aaa..922667f6e 100644 --- a/src/data/sponsors/HEISEN黑森.json +++ b/src/data/sponsors/HEISEN黑森.json @@ -1,6 +1,6 @@ { - "name": "HEISEN黑森", - "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", - "date": "2025-08-09", - "amount": "两个月充电" -} \ No newline at end of file + "name": "HEISEN黑森", + "avatar": "https://img.alicdn.com/bao/uploaded/i4/O1CN01TWOpM42DSyY5nC0hM_!!0-mtopupload.jpg", + "date": "2025-08-09", + "amount": "两个月充电" +} diff --git a/src/data/sponsors/MingTone.json b/src/data/sponsors/MingTone.json index 974a88205..64ff54fe6 100644 --- a/src/data/sponsors/MingTone.json +++ b/src/data/sponsors/MingTone.json @@ -1,6 +1,6 @@ { - "name": "MingTone", - "avatar": "https://q.qlogo.cn/g?b=qq&nk=2244347713&s=160", - "date": "2025-07-21", - "amount": "50 ¥" -} \ No newline at end of file + "name": "MingTone", + "avatar": "https://q.qlogo.cn/g?b=qq&nk=2244347713&s=160", + "date": "2025-07-21", + "amount": "50 ¥" +} diff --git a/src/data/sponsors/chuzouX.json b/src/data/sponsors/chuzouX.json index a4eae0ec2..b61b23e3d 100644 --- a/src/data/sponsors/chuzouX.json +++ b/src/data/sponsors/chuzouX.json @@ -1,6 +1,6 @@ { - "name": "chuzouX", - "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3451860760&spec=0", - "date": "2025-09-07", - "amount": "10¥" -} \ No newline at end of file + "name": "chuzouX", + "avatar": "https://q2.qlogo.cn/headimg_dl?dst_uin=3451860760&spec=0", + "date": "2025-09-07", + "amount": "10¥" +} diff --git a/src/data/sponsors/zz4zz.json b/src/data/sponsors/zz4zz.json index 00b136d60..2783e1bfe 100644 --- a/src/data/sponsors/zz4zz.json +++ b/src/data/sponsors/zz4zz.json @@ -1,6 +1,6 @@ { - "name": "zz4zz", - "avatar": "https://zhongzhong.space/wp-content/uploads/2025/11/avatar.webp", - "date": "2025-11-15", - "amount": "10¥" -} \ No newline at end of file + "name": "zz4zz", + "avatar": "https://zhongzhong.space/wp-content/uploads/2025/11/avatar.webp", + "date": "2025-11-15", + "amount": "10¥" +} diff --git a/src/data/sponsors/匿名用户.json b/src/data/sponsors/匿名用户.json index 4b85da7a7..72dbc813f 100644 --- a/src/data/sponsors/匿名用户.json +++ b/src/data/sponsors/匿名用户.json @@ -1,6 +1,6 @@ { - "name": "匿名用户", - "avatar": null, - "date": "2025-07-15", - "amount": "19.80¥" -} \ No newline at end of file + "name": "匿名用户", + "avatar": null, + "date": "2025-07-15", + "amount": "19.80¥" +} diff --git a/src/data/sponsors/夜轻.json b/src/data/sponsors/夜轻.json index 347690adf..ae52d8ace 100644 --- a/src/data/sponsors/夜轻.json +++ b/src/data/sponsors/夜轻.json @@ -1,6 +1,6 @@ { - "name": "夜轻", - "avatar": "https://blog.yeqing.net/favicon.ico", - "date": "2025-07-11", - "amount": "78 ¥" -} \ No newline at end of file + "name": "夜轻", + "avatar": "https://blog.yeqing.net/favicon.ico", + "date": "2025-07-11", + "amount": "78 ¥" +} diff --git a/src/data/sponsors/明镜台.json b/src/data/sponsors/明镜台.json index 9228e7466..13c735f87 100644 --- a/src/data/sponsors/明镜台.json +++ b/src/data/sponsors/明镜台.json @@ -1,6 +1,6 @@ { - "name": "明镜台", - "avatar": "/sponsors/mjt.jpg", - "date": "2025-08-02", - "amount": "100 ¥" -} \ No newline at end of file + "name": "明镜台", + "avatar": "/sponsors/mjt.jpg", + "date": "2025-08-02", + "amount": "100 ¥" +} diff --git a/src/data/sponsors/请输入内容404.json b/src/data/sponsors/请输入内容404.json index a5720af9a..d518b0390 100644 --- a/src/data/sponsors/请输入内容404.json +++ b/src/data/sponsors/请输入内容404.json @@ -1,6 +1,6 @@ { - "name": "请输入内容404", - "avatar": "https://i0.hdslb.com/bfs/face/cd7027786846c4a49e4c8046b536ca0f5923cb16.jpg", - "date": "2025-12-17", - "amount": "11.45 CNY" + "name": "请输入内容404", + "avatar": "https://i0.hdslb.com/bfs/face/cd7027786846c4a49e4c8046b536ca0f5923cb16.jpg", + "date": "2025-12-17", + "amount": "11.45 CNY" } diff --git a/src/data/sponsors/酷丁同学.json b/src/data/sponsors/酷丁同学.json index 2a83e0f89..aea4e76b1 100644 --- a/src/data/sponsors/酷丁同学.json +++ b/src/data/sponsors/酷丁同学.json @@ -1,6 +1,6 @@ { - "name": "酷丁同学", - "avatar": "https://coldin.top/src/avatar.webp", - "date": "2025-07-15", - "amount": "5 USDC" -} \ No newline at end of file + "name": "酷丁同学", + "avatar": "https://coldin.top/src/avatar.webp", + "date": "2025-07-15", + "amount": "5 USDC" +} diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 04d64bf1c..49694fc95 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -1,5 +1,5 @@ --- -import Analytics from '@vercel/analytics/astro' +import Analytics from "@vercel/analytics/astro"; import "@fontsource/roboto/400.css"; import "@fontsource/roboto/500.css"; import "@fontsource/roboto/700.css"; @@ -730,3 +730,12 @@ if (window.swup) { document.addEventListener("swup:enable", setup) } + + diff --git a/src/pages/404.astro b/src/pages/404.astro index 3b7f418e5..dbefaf642 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,8 +1,8 @@ --- +import { siteConfig } from "@/config"; import Layout from "@/layouts/Layout.astro"; import MainGridLayout from "@/layouts/MainGridLayout.astro"; import { Icon } from "astro-icon/components"; -import { siteConfig } from "@/config"; --- diff --git a/src/pages/friends.astro b/src/pages/friends.astro index 25703c173..71a1d6b9c 100644 --- a/src/pages/friends.astro +++ b/src/pages/friends.astro @@ -1,10 +1,15 @@ --- +import { siteConfig } from "@/config"; +import type { Friend } from "@/types/data"; import MainGridLayout from "@layouts/MainGridLayout.astro"; import { Icon } from "astro-icon/components"; -import type { Friend } from "@/types/data"; -import { siteConfig } from "@/config"; -const friends = Object.values(import.meta.glob("@/data/friends/*.json", { eager: true, import: "default" })); +const friends = Object.values( + import.meta.glob("@/data/friends/*.json", { + eager: true, + import: "default", + }), +); --- diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro index 74548046d..ba38ff214 100644 --- a/src/pages/posts/[...slug].astro +++ b/src/pages/posts/[...slug].astro @@ -10,7 +10,7 @@ import { Icon } from "astro-icon/components"; import { gitHubEditConfig, licenseConfig } from "src/config"; import PostMetadata from "../../components/PostMeta.astro"; import ImageWrapper from "../../components/misc/ImageWrapper.astro"; -import { profileConfig, siteConfig } from "../../config"; +import { profileConfig, siteConfig, umamiConfig } from "../../config"; import { formatDateToYYYYMMDD } from "../../utils/date-utils"; export async function getStaticPaths() { @@ -31,9 +31,9 @@ const jsonLd = { "@type": "BlogPosting", headline: entry.data.title, description: entry.data.description || entry.data.title, - author: { - "@type": "Person", - name: profileConfig.name, + author: { + "@type": "Person", + name: profileConfig.name, url: Astro.site, }, datePublished: formatDateToYYYYMMDD(entry.data.published), @@ -278,6 +278,60 @@ const jsonLd = { document.addEventListener('swup:contentReplaced', setupImageLoading); + +