惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
MyScale Blog
MyScale Blog
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
The Cloudflare Blog
Martin Fowler
Martin Fowler
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium
MongoDB | Blog
MongoDB | Blog
I
InfoQ
WordPress大学
WordPress大学
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
H
Help Net Security

fishcpy的小破站

fishcpy的小破站 提醒大家注意用电用火安全 | fishcpy的小破站 新版本词幕使用体验 | fishcpy的小破站 词幕-墨.状态栏歌词替代品 | fishcpy的小破站 cloudflare tunnel优化 | fishcpy的小破站 使用napcat推送uptime kuma事件到QQ群或个人消息 | fishcpy的小破站 关于cloudflare分配.1 | fishcpy的小破站 新主题astro-theme-fishcpy | fishcpy的小破站 非人哉:限时玩家评价 | fishcpy的小破站 trae AI 编辑器SOLO模式貌似会注入广告 | fishcpy的小破站 给你的Fuwari加一个链接大卡片 | fishcpy的小破站 Uptime Kuma监控美化 | fishcpy的小破站 我名字的由来 | fishcpy的小破站 我的第一个图片API发布啦 | fishcpy的小破站 记一次和福瑞朋友去漫展 | fishcpy的小破站 腾讯EO加速cloudflare内网穿透 | fishcpy的小破站 江西研学 | fishcpy的小破站 哪吒监控 V1美化 | fishcpy的小破站 可以屏蔽的测速ip | fishcpy的小破站 使用1panel搭建免费开源的icp虚拟备案系统 | fishcpy的小破站 25.5.3-5.4被攻击经过 | fishcpy的小破站 小米平板7pro澎湃os2Beta版使用体验 | fishcpy的小破站 音乐播放器预览 | fishcpy的小破站 爱国图片 | fishcpy的小破站 极域 | fishcpy的小破站 一班热榜历史记录2024-2025(下半学期) | fishcpy的小破站 启用cloudflare cdn后js无法正常加载解决办法 | fishcpy的小破站 给你的博客加上一个十年倒计时吧 | fishcpy的小破站 水-1 | fishcpy的小破站 在byrutgame上下载免费游戏 | fishcpy的小破站
给你的Fuwari加一个AI摘要 | fishcpy的小破站
fishcpy, fishcpy@qq.com · 2025-09-08 · via fishcpy的小破站

预览

1757701946450.webp
1757701946450.webp

现在教程开始!

修改文件前请注意备份,防止修改失败无法回退

新建src/components/misc/AISummary.astro文件

astro
---
export interface Props {
  content: string;
}

const { content } = Astro.props;

// 如果没有内容,不渲染组件
if (!content || content.trim() === '') {
  return null;
}
---

{content && (
  <div class="ai-summary">
    <div class="ai-title">
      <div class="ai-title-left">
        <i>🤖</i>
        <span class="ai-title-text">AI 摘要</span>
      </div>
      <div class="ai-tag">fishcpy AI</div>
    </div>
    <div class="ai-explanation" data-content={content}></div>
  </div>
)}

<script>
  // 检查当前页面路径是否包含 "posts"
  function isPostsPage() {
    return window.location.pathname.includes('/posts/');
  }

  // 全局函数,用于初始化AI打字效果
  function initAITyping() {
    // 只在包含 "posts" 的页面才执行AI总结功能
    if (!isPostsPage()) {
      return;
    }

    // 查找所有AI摘要容器
    const aiSummaryContainers = document.querySelectorAll('.ai-summary');
    
    aiSummaryContainers.forEach(container => {
      const textElement = container.querySelector('.ai-explanation');
      
      if (!textElement) {
        return;
      }

      // 检查是否已经初始化过
      if (textElement.hasAttribute('data-initialized')) {
        return;
      }

      const content = textElement.getAttribute('data-content');
      if (!content) {
        return;
      }

      // 标记为已初始化
      textElement.setAttribute('data-initialized', 'true');
      
      // 清空文本内容,准备打字效果
      textElement.textContent = '';
      textElement.classList.remove('typing-complete');
      
      let index = 0;
      const typeSpeed = 30; // 打字速度(毫秒)
      
      function typeWriter() {
        if (index < content.length) {
          textElement.textContent += content.charAt(index);
          index++;
          setTimeout(typeWriter, typeSpeed);
        } else {
          // 打字完成后隐藏光标(通过CSS控制)
          textElement.classList.add('typing-complete');
        }
      }
      
      // 延迟开始打字效果
      setTimeout(typeWriter, 800);
    });
  }

  // 页面加载完成时初始化
  function handlePageLoad() {
    setTimeout(initAITyping, 100);
  }

  // 监听页面导航事件(适用于Astro的客户端路由)
  function setupNavigationListeners() {
    // DOMContentLoaded事件
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', handlePageLoad);
    } else {
      handlePageLoad();
    }

    // 监听Astro的页面导航事件
    document.addEventListener('astro:page-load', handlePageLoad);
    
    // 监听浏览器的popstate事件(后退/前进按钮)
    window.addEventListener('popstate', handlePageLoad);
    
    // 监听pushstate和replacestate事件
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;
    
    history.pushState = function() {
      originalPushState.apply(history, arguments);
      setTimeout(handlePageLoad, 100);
    };
    
    history.replaceState = function() {
      originalReplaceState.apply(history, arguments);
      setTimeout(handlePageLoad, 100);
    };
  }

  // 立即设置监听器
  setupNavigationListeners();
</script>

在src/content/config.ts插入下方代码,13行下左右,注意+号要删除

ts
tags: z.array(z.string()).optional().default([]),
category: z.string().optional().nullable().default(""),
lang: z.string().optional().default(""),
+       ai: z.string().optional().default(""),

/* For internal use */
prevTitle: z.string().default(""),

在src/pages/posts/...slug.astro插入下方代码,注意+号要删除

astro
import { profileConfig, siteConfig } from "../../config";
import { formatDateToYYYYMMDD } from "../../utils/date-utils";
import Comment from "@components/comment/index.astro";
+ import AISummary from "@components/misc/AISummary.astro";

export async function getStaticPaths() {
    const blogEntries = await getSortedPosts();
@@ -84,6 +85,9 @@ const jsonLd = {
</div>
</div>

+            <!-- AI Summary -->
+            {entry.data.description && <AISummary content={entry.data.description} class="onload-animation" />}

<!-- metadata -->
<div class="onload-animation">
<PostMetadata

在src/styles/main.css底部添加下方代码

css
/* =================== */
/* 📘 AI 摘要模块样式 */
/* =================== */

.ai-summary {
    background: var(--card-bg);
    border: 1px solid var(--line-divider);
    border-radius: 12px;
    padding: 8px 8px 12px 8px;
    line-height: 1.3;
    flex-direction: column;
    margin-bottom: 16px;
    display: flex;
    gap: 5px;
    position: relative;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    transition: all 0.3s;
}

.ai-summary:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    transform: translateY(-1px);
}

.ai-summary .ai-explanation {
    z-index: 10;
    padding: 8px 12px;
    font-size: 15px;
    line-height: 1.4;
    @apply text-90;
    text-align: justify;
}

/* ✅ 打字机光标动画 */
.ai-summary .ai-explanation::after {
    content: '';
    display: inline-block;
    width: 8px;
    height: 2px;
    margin-left: 2px;
    @apply bg-black/90 dark:bg-white/90;
    vertical-align: bottom;
    animation: blink-underline 1s ease-in-out infinite;
    transition: all 0.3s;
    position: relative;
    bottom: 3px;
}

/* 打字完成后隐藏光标 */
.ai-summary .ai-explanation.typing-complete::after {
    display: none;
}

.ai-summary .ai-title {
    z-index: 10;
    font-size: 14px;
    display: flex;
    border-radius: 8px;
    align-items: center;
    position: relative;
    padding: 0 12px;
    cursor: default;
    user-select: none;
}

.ai-summary .ai-title .ai-title-left {
    display: flex;
    align-items: center;
    color: var(--primary);
}

.ai-summary .ai-title .ai-title-left i {
    margin-right: 3px;
    display: flex;
    color: var(--primary);
    border-radius: 20px;
    justify-content: center;
    align-items: center;
}

.ai-summary .ai-title .ai-title-left .ai-title-text {
    font-weight: 500;
}

.ai-summary .ai-title .ai-tag {
    color: var(--btn-content);
    font-weight: 300;
    margin-left: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: 0.3s;
}

/* ✅ 打字机光标闪烁动画 */
@keyframes blink-underline {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0;
    }
}

最后在src/styles/variables.styl 大约19行后面添加下方代码

styl
  --page-bg: oklch(0.95 0.01 var(--hue)) oklch(0.16 0.014 var(--hue))
  --card-bg: white oklch(0.23 0.015 var(--hue))

+  // AI Summary 相关变量
+  --ai-title-font-color: #0883b7 #0883b7
+  --ai-maskbg: rgba(255, 255, 255, 0.85) rgba(0, 0, 0, 0.85)
+  --ai-bg: conic-gradient(from 1.5708rad at 50% 50%, #d6b300 0%, #42A2FF 54%, #d6b300 100%) conic-gradient(from 1.5708rad at 50% 50%, rgba(214, 178, 0, 0.46) 0%, rgba(66, 161, 255, 0.53) 54%, rgba(214, 178, 0, 0.49) 100%)
+  --ai-card-secondbg: #f1f3f8 #3e3f41
+  --ai-text: #4c4948 #ffffffb3
+  --ai-secondtext: #3c3c43cc #a1a2b8

  --btn-content: oklch(0.55 0.12 var(--hue)) oklch(0.75 0.1 var(--hue))
  --btn-regular-bg: oklch(0.95 0.025 var(--hue)) oklch(0.33 0.035 var(--hue))

样式参考