diff --git a/src/app/(site)/reports/[skill]/not-found.tsx b/src/app/(site)/reports/[skill]/not-found.tsx new file mode 100644 index 0000000..86cb45d --- /dev/null +++ b/src/app/(site)/reports/[skill]/not-found.tsx @@ -0,0 +1,20 @@ +import Link from "next/link"; +import { SubpageShell } from "@/components/subpage-shell"; + +export default function SkillNotFound() { + return ( + +
+
+

SKILL 未找到

+

+ 你访问的技能不存在或已下线,返回 SKILL 广场看看其他能力。 +

+ + 返回 SKILL 广场 + +
+
+
+ ); +} diff --git a/src/app/(site)/reports/[skill]/page.tsx b/src/app/(site)/reports/[skill]/page.tsx new file mode 100644 index 0000000..db0d0b6 --- /dev/null +++ b/src/app/(site)/reports/[skill]/page.tsx @@ -0,0 +1,69 @@ +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { Suspense } from "react"; +import { SubpageShell } from "@/components/subpage-shell"; +import { JsonLd } from "@/components/json-ld"; +import { SkillDetail } from "@/components/skill-detail"; +import { + getAllSkillIds, + getSkillById, + categoryMeta, +} from "@/content/skills"; +import { pageMetadata } from "@/lib/site-metadata"; + +type PageProps = { + params: Promise<{ skill: string }>; +}; + +export function generateStaticParams() { + return getAllSkillIds().map((skill) => ({ skill })); +} + +export async function generateMetadata({ + params, +}: PageProps): Promise { + const { skill: id } = await params; + const skill = getSkillById(id); + if (!skill) { + return { title: "SKILL 未找到", robots: { index: false, follow: false } }; + } + return pageMetadata({ + title: `${skill.title} - SKILL 详情`, + description: skill.body, + path: `/reports/${skill.id}`, + keywords: [skill.title, skill.slug, "SKILL", "海宇数科"], + }); +} + +export default async function SkillDetailPage({ params }: PageProps) { + const { skill: id } = await params; + const skill = getSkillById(id); + if (!skill) { + notFound(); + } + + const cat = categoryMeta[skill.category]; + const jsonLd = { + "@context": "https://schema.org", + "@type": "SoftwareApplication", + name: skill.title, + description: skill.body, + applicationCategory: "BusinessApplication", + operatingSystem: "Web", + url: `/reports/${skill.id}`, + keywords: [skill.title, skill.slug, cat.label].join(", "), + }; + + return ( + + +
+
+ + + +
+
+
+ ); +} diff --git a/src/app/(site)/reports/page.tsx b/src/app/(site)/reports/page.tsx index e6ccab7..7233011 100644 --- a/src/app/(site)/reports/page.tsx +++ b/src/app/(site)/reports/page.tsx @@ -1,24 +1,34 @@ import type { Metadata } from "next"; import { SubpageShell } from "@/components/subpage-shell"; import { PageHero } from "@/components/page-hero"; -import { SectionIntelligentReport } from "@/components/section-intelligent-report"; +import { SkillHowToUse } from "@/components/skill-how-to-use"; +import { SkillMarketplace } from "@/components/skill-marketplace"; import { site } from "@/content/site"; import { pageMetadata } from "@/lib/site-metadata"; -const s = site.intelligentReport; +const s = site.skillMarket; export const metadata: Metadata = pageMetadata({ title: s.title, description: s.lead, path: "/reports", - keywords: ["智能报告", "KYB", "企业核验", "尽调", "海宇数科"], + keywords: ["智能报告", "SKILL", "KYB", "企业核验", "尽调", "海宇数科"], }); export default function ReportsPage() { return ( - +
+
+ +
+
+
+
+ +
+
); } diff --git a/src/components/skill-detail.tsx b/src/components/skill-detail.tsx new file mode 100644 index 0000000..6deb6e1 --- /dev/null +++ b/src/components/skill-detail.tsx @@ -0,0 +1,250 @@ +"use client"; + +import Link from "next/link"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import type { Skill } from "@/content/skills"; +import { categoryMeta } from "@/content/skills"; +import { site } from "@/content/site"; +import { SkillExampleContent } from "@/components/skill-example-content"; + +type Tab = "experience" | "install" | "example"; + +const TABS: { id: Tab; label: string }[] = [ + { id: "experience", label: "在线运行" }, + { id: "install", label: "安装方式" }, + { id: "example", label: "示例报告" }, +]; + +type Report = (typeof site.intelligentReport.reports)[number]; + +type SkillDetailProps = { + skill: Skill; +}; + +export function SkillDetail({ skill }: SkillDetailProps) { + const router = useRouter(); + const searchParams = useSearchParams(); + const cat = categoryMeta[skill.category]; + + const rawTab = searchParams.get("tab"); + const tab: Tab = rawTab === "install" || rawTab === "example" ? rawTab : "experience"; + + const [company, setCompany] = useState(skill.detail.runDefaultCompany); + const [promptCopied, setPromptCopied] = useState(false); + const [configCopied, setConfigCopied] = useState(false); + + const reports = site.intelligentReport.reports; + const reportById = useMemo(() => { + const map: Record = {}; + for (const r of reports) map[r.id] = r; + return map; + }, [reports]); + const example = skill.exampleId ? reportById[skill.exampleId] : undefined; + + const switchTab = useCallback( + (next: Tab) => { + const params = new URLSearchParams(searchParams.toString()); + if (next === "experience") params.delete("tab"); + else params.set("tab", next); + const qs = params.toString(); + router.replace(qs ? `?${qs}` : "?", { scroll: false }); + }, + [router, searchParams] + ); + + useEffect(() => { + if (!promptCopied) return; + const t = setTimeout(() => setPromptCopied(false), 1800); + return () => clearTimeout(t); + }, [promptCopied]); + + useEffect(() => { + if (!configCopied) return; + const t = setTimeout(() => setConfigCopied(false), 1800); + return () => clearTimeout(t); + }, [configCopied]); + + const copyPrompt = async () => { + try { + await navigator.clipboard.writeText(skill.detail.installPrompt); + setPromptCopied(true); + } catch { + /* ignore */ + } + }; + + const copyConfig = async () => { + try { + await navigator.clipboard.writeText( + JSON.stringify(site.skillMarket.howToUse.configJson, null, 2) + ); + setConfigCopied(true); + } catch { + /* ignore */ + } + }; + + return ( +
+ + +
+

+ {skill.title} +

+

{skill.body}

+
+ +
+ {TABS.map((t) => { + const active = t.id === tab; + return ( + + ); + })} +
+ +
+ {tab === "experience" ? ( +
+
+

+ 在线运行 · {skill.detail.runSubtitle} +

+

+ 输入你想查询的公司 · AI 在对话页自动执行 SKILL 并生成报告 +

+
+
+ + setCompany(e.target.value)} + placeholder={skill.detail.runPlaceholder} + className="skill-run__input" + /> + {company ? ( + + ) : null} +
+ +

+ 一键运行通道即将开放,当前可先在「安装方式」中接入 MCP 后于对话页使用本 SKILL。 +

+
+ ) : null} + + {tab === "install" ? ( +
+
+

+ 完成海宇数科 MCP 接入 +

+

+ 注册账号、获取 API Key、粘贴配置到你的 AI 工具。只需做一次,所有 SKILL 共用。 +

+
+
+ + + {site.skillMarket.howToUse.configLabel} + +
+ +
+
+
+                  
+                    {JSON.stringify(site.skillMarket.howToUse.configJson, null, 2)}
+                  
+                
+
+
+ +
+

+ 加载本 SKILL 到 AI 工具 +

+

+ 复制下方提示词,粘贴到 AI 对话框即可加载「{skill.title}」技能。 +

+
+
+ + + AI 加载提示词 + +
+ +
+
+
+                  {skill.detail.installPrompt}
+                
+
+
+
+ ) : null} + + {tab === "example" ? ( +
+ {example ? ( + + ) : ( +
+

{skill.detail.exampleMissingHint}

+ +
+ )} +
+ ) : null} +
+
+ ); +} diff --git a/src/components/skill-example-content.tsx b/src/components/skill-example-content.tsx new file mode 100644 index 0000000..d03c1ab --- /dev/null +++ b/src/components/skill-example-content.tsx @@ -0,0 +1,42 @@ +import { site } from "@/content/site"; + +type Report = (typeof site.intelligentReport.reports)[number]; + +type SkillExampleContentProps = { + report: Report; +}; + +/** + * 示例报告内容渲染 — 卡片弹窗与详情页「示例报告」tab 共用同一套结构与样式。 + * 外层(overlay/dialog/footer)由各自调用方自行包裹。 + */ +export function SkillExampleContent({ report }: SkillExampleContentProps) { + return ( +
+
+

{report.audience}

+

+ {report.example.title} +

+

{report.example.meta}

+
+
+ {report.example.sections.map((sec) => ( +
+

{sec.heading}

+ {"body" in sec && sec.body ? ( +

{sec.body}

+ ) : null} + {"bullets" in sec && sec.bullets ? ( +
    + {sec.bullets.map((item) => ( +
  • {item}
  • + ))} +
+ ) : null} +
+ ))} +
+
+ ); +} diff --git a/src/components/skill-how-to-use.tsx b/src/components/skill-how-to-use.tsx new file mode 100644 index 0000000..dab078d --- /dev/null +++ b/src/components/skill-how-to-use.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { useState } from "react"; +import { site } from "@/content/site"; +import { ScrollReveal } from "@/components/scroll-reveal"; + +export function SkillHowToUse() { + const h = site.skillMarket.howToUse; + const [copied, setCopied] = useState(false); + const [codeOpen, setCodeOpen] = useState(false); + + const jsonString = JSON.stringify(h.configJson, null, 2); + const preview = jsonString.split("\n").slice(0, 3).join("\n"); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(jsonString); + setCopied(true); + setTimeout(() => setCopied(false), 1800); + } catch { + // 剪贴板不可用时静默失败 + } + }; + + return ( + +
+

{h.eyebrow}

+

{h.title}

+

{h.subtitle}

+
+ +
+
    + {h.steps.map((step) => ( +
  1. + + {step.no} + +
    +

    + {step.title} +

    +

    + {step.desc} +

    +
    +
  2. + ))} +
+ +
+

+ {h.configIntro} +

+ +
+
+ + + {h.configLabel} + +
+ + +
+
+
+              {codeOpen ? jsonString : preview + "\n…"}
+            
+
+ +

+ + {h.supportedAppsLabel} + + {h.supportedApps.map((app) => ( + + {app} + + ))} +

+

+ {h.comingSoonHint} +

+
+
+
+ ); +} diff --git a/src/components/skill-marketplace.tsx b/src/components/skill-marketplace.tsx new file mode 100644 index 0000000..154766a --- /dev/null +++ b/src/components/skill-marketplace.tsx @@ -0,0 +1,191 @@ +"use client"; + +import { useCallback, useEffect, useMemo, useState } from "react"; +import Link from "next/link"; +import { site } from "@/content/site"; +import { ScrollRevealItem } from "@/components/scroll-reveal"; +import { SkillExampleContent } from "@/components/skill-example-content"; + +type Skill = (typeof site.skillMarket.skills)[number]; +type Report = (typeof site.intelligentReport.reports)[number]; + +export function SkillMarketplace() { + const m = site.skillMarket; + const reports = site.intelligentReport.reports; + + const [activeCategory, setActiveCategory] = useState("all"); + const [query, setQuery] = useState(""); + const [activeExample, setActiveExample] = useState(null); + + const exampleById = useMemo(() => { + const map: Record = {}; + for (const r of reports) map[r.id] = r; + return map; + }, [reports]); + + const counts = useMemo(() => { + const c: Record = { all: m.skills.length }; + for (const cat of m.categories) { + if (cat.id === "all") continue; + c[cat.id] = m.skills.filter((s) => s.category === cat.id).length; + } + return c; + }, [m.skills, m.categories]); + + const filtered = useMemo(() => { + const q = query.trim().toLowerCase(); + return m.skills.filter((s) => { + if (activeCategory !== "all" && s.category !== activeCategory) return false; + if (!q) return true; + return ( + s.title.toLowerCase().includes(q) || + s.body.toLowerCase().includes(q) || + s.slug.toLowerCase().includes(q) + ); + }); + }, [m.skills, activeCategory, query]); + + const openExample = useCallback( + (skill: Skill) => { + if (!("exampleId" in skill) || !skill.exampleId) return; + const ex = exampleById[skill.exampleId]; + if (ex) setActiveExample(ex); + }, + [exampleById] + ); + + const closeExample = useCallback(() => setActiveExample(null), []); + + useEffect(() => { + if (!activeExample) return; + const prev = document.body.style.overflow; + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = prev; + }; + }, [activeExample]); + + return ( + <> +
+
+
+ {m.categories.map((cat) => { + const isActive = activeCategory === cat.id; + return ( + + ); + })} +
+ +
+ setQuery(e.target.value)} + placeholder="搜索 SKILL..." + aria-label="搜索 SKILL" + className="skill-search-input" + /> +
+
+ + {filtered.length === 0 ? ( +

+ 没有匹配的 SKILL,换个关键词试试。 +

+ ) : ( +
+ {filtered.map((skill, index) => { + const exampleId = + "exampleId" in skill ? skill.exampleId : undefined; + const hasExample = Boolean(exampleId && exampleById[exampleId]); + return ( + +
+
+

+ + {skill.title} + +

+ {skill.slug} +
+

+ {skill.body} +

+
+ + {m.runLabel} + + + {m.installLabel} + + +
+
+
+ ); + })} +
+ )} +
+ + {activeExample ? ( +
{ + if (e.target === e.currentTarget) closeExample(); + }} + onKeyDown={(e) => { + if (e.key === "Escape") closeExample(); + }} + > +
+
+ + +
+
+
+ ) : null} + + ); +} diff --git a/src/content/site.ts b/src/content/site.ts index 45863b1..e80a279 100644 --- a/src/content/site.ts +++ b/src/content/site.ts @@ -188,6 +188,196 @@ export const site = { }, ], }, + skillMarket: { + eyebrow: "产品", + title: "SKILL 广场", + lead: "27 个经过真实场景验证的 SKILL,覆盖银行、投资、法务、采购四大客群。配置海宇数科 MCP,3 分钟跑通首份尽调报告。", + howToUse: { + eyebrow: "使用指南", + title: "怎么使用 SKILL ?", + subtitle: "三步完成,在 AI 对话框里直接操作,不需要写代码", + steps: [ + { + no: "01", + title: "配置海宇数科 MCP", + desc: "注册账号、获取 API Key、粘贴配置到你的 AI 工具。只需做一次,所有 SKILL 共用。", + }, + { + no: "02", + title: "发送给 AI 加载", + desc: "把下方配置原样发给你的 AI,AI 会自动加载对应客群的 SKILL 技能包。", + }, + { + no: "03", + title: "像聊天一样下指令", + desc: "AI 确认加载后,像平时聊天一样说出需求,例如「帮我做一份某科技公司的 KYB 核验」。", + }, + ], + configLabel: "MCP 配置", + configIntro: "安装海宇数科 MCP Server,即可在对话中调用全部企业核验与尽调 SKILL。", + configJson: { + mcpServers: { + haiyushuke: { + command: "npx", + args: ["-y", "@haiyushuke/mcp-server"], + env: { + HAIYUSHUKE_API_KEY: "your_api_key_here", + }, + }, + }, + }, + supportedAppsLabel: "已接入", + supportedApps: ["小龙虾", "OpenClaw", "Cursor", "Claude Code", "更多智能体"], + comingSoonHint: "一键安装通道即将开放,当前可手动按上方配置接入。", + expandLabel: "展开", + collapseLabel: "收起", + copyLabel: "复制", + copiedLabel: "已复制", + }, + categories: [ + { id: "all", label: "全部", emoji: "✨" }, + { id: "bank", label: "银行", emoji: "🏦" }, + { id: "invest", label: "投资", emoji: "📊" }, + { id: "legal", label: "法务", emoji: "⚖️" }, + { id: "supply", label: "供应链", emoji: "🔗" }, + ], + skills: [ + { + id: "kyb-verification", + category: "bank", + title: "KYB 企业核验", + slug: "/kyb-verification", + body: "银行开户和授信审批前的核心合规工具。输入企业名称,AI 自动完成主体真实性核验、股权结构穿透、34 类司法与经营风险扫描,3 分钟输出符合 FATF / PBOC / CBIRC 标准的合规报告,替代传统客户经理 2-3 小时人工尽调流程。", + exampleId: "kyb", + }, + { + id: "ic-memo", + category: "invest", + title: "IC Memo 投资备忘录", + slug: "/ic-memo", + body: "PE/VC 投资决策的核心尽调工具。一次对话并行完成目标公司工商登记、多层股权穿透、司法风险、知识产权全维度扫描,直接输出符合投委会要求的标准格式备忘录,传统分析师 2-3 小时工作量压缩至约 3 分钟。", + exampleId: "ic-memo", + }, + { + id: "contract-review", + category: "legal", + title: "合同核验", + slug: "/contract-review", + body: "合同审核与主体核验一体化工具。AI 审阅合同文本的同时,自动核验合同双方的工商登记状态、34 类司法与经营风险,营业执照注销、被执行、股权冻结等异常立即生成高风险批注,防范与问题主体签约导致合同无效或履约失败。", + exampleId: "contract", + }, + { + id: "new-supplier-screening", + category: "supply", + title: "新供应商快速筛选", + slug: "/new-supplier-screening", + body: "招投标评审阶段的候选供应商快速筛查工具。对多家候选供应商同步核查工商合规性、失信被执行黑名单状态与资质证件真实性,3 分钟批量出具筛查结论,在评审会召开前帮助采购团队快速排除存在硬性风险的候选方。", + exampleId: "supplier", + }, + { + id: "history-evolution", + category: "invest", + title: "企业历史沿革与发展历程", + slug: "/history-evolution", + body: "PE/VC 投前立项与拓客调研的主体背调工具。一次对话并行整合目标企业工商登记、股权历史、董监高沿革、知识产权与荣誉资质,生成涵盖发展里程碑、股权演变、关联方网络与资质荣誉的历史沿革报告,小微/融资/上市三档自适应,3 分钟替代分析师 4-6 小时手工梳理。", + }, + { + id: "contract-party-check", + category: "legal", + title: "合同相对方主体核验", + slug: "/contract-party-check", + body: "合同签署前的主体快速核验工具。自动查验合同相对方的工商登记状态、法定代表人真实性与经营异常情况,在签字盖章前识别已注销、吊销或存在重大风险的问题主体,防范合同无效与履约失败风险。", + }, + { + id: "vendor-assess", + category: "supply", + title: "供应商准入评估", + slug: "/vendor-assess", + body: "采购准入阶段的供应商深度尽调工具。输入供应商名称,AI 自动完成多维度风险评估,覆盖 34 类中国特有风险信号,包括司法执行、经营异常、税务违规、破产风险等,输出结构化准入报告,辅助采购评审委员会做出有据可查的准入决策。", + }, + { + id: "fundraising-tracker", + category: "invest", + title: "融资历史追踪", + slug: "/fundraising-tracker", + body: "早期投资研究的融资历史溯源工具。系统追溯目标企业历次融资记录,结合各轮次股权变更情况推演估值区间变化趋势,帮助 VC 和 FA 在接触企业前快速建立对其融资历史的完整认知,为估值谈判提供数据参考。", + }, + { + id: "ubo-screening", + category: "bank", + title: "受益所有人识别", + slug: "/ubo-screening", + body: "对公开户、高净值客户尽调与反洗钱日常筛查场景下的受益所有人识别工具。3 分钟自动锁定企业最终受益人,按央行 3 号令 25% 阈值穿透股权链路、查明实控人画像与关联企业网络,输出可直接归档 KYC 系统的合规档案。", + }, + { + id: "equity-structure", + category: "invest", + title: "股权结构穿透分析", + slug: "/equity-structure", + body: "投资决策前的控制权核查工具。自动完成多层股权穿透并生成可视化结构图,识别实际控制人、一致行动人及隐性关联关系,帮助投资团队在 DD 阶段快速厘清目标公司的实际控制架构,识别潜在的关联交易风险。", + }, + { + id: "executive-background", + category: "bank", + title: "高管背景核查", + slug: "/executive-background", + body: "高管背景核查工具,默认对全体董监高(含董事/监事/独立董事/职工代表董事)+ 实际控制人 + 主要高管做完整画像。覆盖失信、限高、限出境、股权冻结、税收违法、控制企业、在外任职、历史合作等维度。", + }, + { + id: "credit-due-diligence", + category: "bank", + title: "授信尽调报告", + slug: "/credit-due-diligence", + body: "信贷审批流程中的企业全维度尽调工具。授信前自动汇总目标企业工商登记、实控人信息、经营异常记录与司法风险状况,输出标准化授信尽调底稿,帮助信贷审批人在放款前全面掌握企业风险画像,支撑有据可查的授信决策。", + }, + { + id: "credit-monitoring", + category: "bank", + title: "信贷风险定期监控", + slug: "/credit-monitoring", + body: "贷后管理中对存量借款客户的持续风险监控工具。定期对贷款企业进行风险体检,失信被执行、经营异常登记、法定代表人变更等关键风险信号一旦发生变化即时推送预警,帮助客户经理在风险暴露初期及时采取措施。", + }, + { + id: "litigation-analysis", + category: "legal", + title: "诉讼风险评估", + slug: "/litigation-analysis", + body: "立案前的对手方诉讼风险评估工具。以「企业当前 × 企业历史 × 核心人员」三层穿透扫描对手方,整合裁判文书、立案信息、执行风险、限高/限出境与法代/实控人个人涉诉档案,基于 5-10 年历史数据做时间序列趋势分析,支撑立案、授信、投资决策。", + }, + { + id: "counterparty-risk", + category: "bank", + title: "交易对手风险评估", + slug: "/counterparty-risk", + body: "贸易融资业务中对交易对手的多维风险评估工具。整合企业进出口信用等级、行政处罚记录、失信被执行情况与经营异常信息,生成综合风险评分,帮助贸易金融团队在开证、议付等关键节点识别潜在违约风险。", + }, + { + id: "strip-profile", + category: "invest", + title: "企业画像速览", + slug: "/strip-profile", + body: "投资前快速了解目标公司的轻量尽调工具。3 分钟生成一页纸企业画像,整合工商登记、核心风险信号与知识产权资产,以结构化方式呈现企业基本面,适用于 LP 推介前、项目初步筛选和内部立项汇报场景。", + }, + { + id: "ip-asset-inventory", + category: "invest", + title: "知产资产清单", + slug: "/ip-asset-inventory", + body: "科技型企业投资与并购前的知产资产核查工具。全量输出目标企业专利、商标、软件著作权的数量与有效性状态,评估技术壁垒深度与核心资产的法律稳定性,帮助投资团队在 DD 阶段准确判断企业的知产护城河。", + }, + { + id: "supplier-annual-check", + category: "supply", + title: "供应商年度健康体检", + slug: "/supplier-annual-check", + body: "供应商年度评审的标准化核查工具。对核心供应商进行全面年度体检,一次性输出经营状态变化、资质证件到期情况、信用记录变更的结构化报告,帮助采购团队在年度供应商评审和续签决策中有充分的数据支撑。", + }, + ], + runLabel: "在线运行", + installLabel: "安装方式", + exampleLabel: "示例报告", + exampleMissingHint: "示例报告整理中", + }, reportAgent: { eyebrow: "Skill", title: "智能体技能一键安装", diff --git a/src/content/skills.ts b/src/content/skills.ts new file mode 100644 index 0000000..727abdc --- /dev/null +++ b/src/content/skills.ts @@ -0,0 +1,289 @@ +/** + * SKILL 技能数据 — 列表页与详情页共用同一数据源。 + * + * id: 列表卡片与路由的唯一标识(用作 /reports/[skill] 的动态段) + * category: 分类 id,对应 skillMarket.categories + * title: 技能名 + * slug: 短命令标识(展示用) + * body: 一句话描述(列表卡片用) + * exampleId: 关联 intelligentReport.reports 的 id,没有则留空 + * detail: 详情页专属内容 + */ + +export type SkillCategory = "bank" | "invest" | "legal" | "supply"; + +export type SkillDetail = { + /** 在线运行 tab:副标题 */ + runSubtitle: string; + /** 在线运行 tab:输入框提示 */ + runPlaceholder: string; + /** 在线运行 tab:企业名称默认示例 */ + runDefaultCompany: string; + /** 在线运行 tab:运行按钮文案 */ + runButton: string; + /** 安装方式 tab:加载本技能给 AI 的提示词(Markdown 片段) */ + installPrompt: string; + /** 示例报告 tab:空状态提示(无 exampleId 时显示) */ + exampleMissingHint: string; +}; + +export type Skill = { + id: string; + category: SkillCategory; + title: string; + slug: string; + body: string; + exampleId?: string; + detail: SkillDetail; +}; + +export const categoryMeta: Record< + SkillCategory, + { slug: string; label: string } +> = { + bank: { slug: "banking", label: "银行 · 合规风控" }, + invest: { slug: "invest", label: "投资 · 尽调" }, + legal: { slug: "legal", label: "法务 · 合规" }, + supply: { slug: "supply", label: "供应链 · 采购" }, +}; + +const sharedRun = { + runPlaceholder: "支持企业完整登记名称或 18 位统一社会信用代码", + runDefaultCompany: "示例:某科技有限公司", + runButton: "打开在线运行", + exampleMissingHint: "该 SKILL 的示例报告正在整理中,敬请期待。", +}; + +const sharedInstall = (title: string, slug: string) => + `将下面这段配置发给你的 AI,AI 会自动加载「${title}」技能:\n\n\`\`\`\n@haiyushuke/mcp ${slug}\n\`\`\`\n\nAI 确认加载后,像平时聊天一样说出你的需求即可。`; + +export const skills: Skill[] = [ + { + id: "kyb-verification", + category: "bank", + title: "KYB 企业核验", + slug: "/kyb-verification", + body: "银行开户和授信审批前的核心合规工具。输入企业名称,AI 自动完成主体真实性核验、股权结构穿透、34 类司法与经营风险扫描,3 分钟输出符合 FATF / PBOC / CBIRC 标准的合规报告,替代传统客户经理 2-3 小时人工尽调流程。", + exampleId: "kyb", + detail: { + runSubtitle: "输入企业名称,AI 自动完成 KYB 企业核验", + ...sharedRun, + installPrompt: sharedInstall("KYB 企业核验", "/kyb-verification"), + }, + }, + { + id: "ic-memo", + category: "invest", + title: "IC Memo 投资备忘录", + slug: "/ic-memo", + body: "PE/VC 投资决策的核心尽调工具。一次对话并行完成目标公司工商登记、多层股权穿透、司法风险、知识产权全维度扫描,直接输出符合投委会要求的标准格式备忘录,传统分析师 2-3 小时工作量压缩至约 3 分钟。", + exampleId: "ic-memo", + detail: { + runSubtitle: "输入目标公司,AI 自动生成 IC Memo 投资备忘录", + ...sharedRun, + installPrompt: sharedInstall("IC Memo 投资备忘录", "/ic-memo"), + }, + }, + { + id: "contract-review", + category: "legal", + title: "合同核验", + slug: "/contract-review", + body: "合同审核与主体核验一体化工具。AI 审阅合同文本的同时,自动核验合同双方的工商登记状态、34 类司法与经营风险,营业执照注销、被执行、股权冻结等异常立即生成高风险批注,防范与问题主体签约导致合同无效或履约失败。", + exampleId: "contract", + detail: { + runSubtitle: "上传合同或输入双方名称,AI 自动核验并批注风险", + ...sharedRun, + installPrompt: sharedInstall("合同核验", "/contract-review"), + }, + }, + { + id: "new-supplier-screening", + category: "supply", + title: "新供应商快速筛选", + slug: "/new-supplier-screening", + body: "招投标评审阶段的候选供应商快速筛查工具。对多家候选供应商同步核查工商合规性、失信被执行黑名单状态与资质证件真实性,3 分钟批量出具筛查结论,在评审会召开前帮助采购团队快速排除存在硬性风险的候选方。", + exampleId: "supplier", + detail: { + runSubtitle: "批量输入候选供应商,AI 同步筛查并给出结论", + ...sharedRun, + installPrompt: sharedInstall("新供应商快速筛选", "/new-supplier-screening"), + }, + }, + { + id: "history-evolution", + category: "invest", + title: "企业历史沿革与发展历程", + slug: "/history-evolution", + body: "PE/VC 投前立项与拓客调研的主体背调工具。一次对话并行整合目标企业工商登记、股权历史、董监高沿革、知识产权与荣誉资质,生成涵盖发展里程碑、股权演变、关联方网络与资质荣誉的历史沿革报告,小微/融资/上市三档自适应,3 分钟替代分析师 4-6 小时手工梳理。", + detail: { + runSubtitle: "输入目标企业,AI 自动生成历史沿革与发展历程报告", + ...sharedRun, + installPrompt: sharedInstall("企业历史沿革与发展历程", "/history-evolution"), + }, + }, + { + id: "contract-party-check", + category: "legal", + title: "合同相对方主体核验", + slug: "/contract-party-check", + body: "合同签署前的主体快速核验工具。自动查验合同相对方的工商登记状态、法定代表人真实性与经营异常情况,在签字盖章前识别已注销、吊销或存在重大风险的问题主体,防范合同无效与履约失败风险。", + detail: { + runSubtitle: "输入合同相对方名称,AI 快速核验主体真实性", + ...sharedRun, + installPrompt: sharedInstall("合同相对方主体核验", "/contract-party-check"), + }, + }, + { + id: "vendor-assess", + category: "supply", + title: "供应商准入评估", + slug: "/vendor-assess", + body: "采购准入阶段的供应商深度尽调工具。输入供应商名称,AI 自动完成多维度风险评估,覆盖 34 类中国特有风险信号,包括司法执行、经营异常、税务违规、破产风险等,输出结构化准入报告,辅助采购评审委员会做出有据可查的准入决策。", + detail: { + runSubtitle: "输入供应商名称,AI 输出多维度风险评估与准入结论", + ...sharedRun, + installPrompt: sharedInstall("供应商准入评估", "/vendor-assess"), + }, + }, + { + id: "fundraising-tracker", + category: "invest", + title: "融资历史追踪", + slug: "/fundraising-tracker", + body: "早期投资研究的融资历史溯源工具。系统追溯目标企业历次融资记录,结合各轮次股权变更情况推演估值区间变化趋势,帮助 VC 和 FA 在接触企业前快速建立对其融资历史的完整认知,为估值谈判提供数据参考。", + detail: { + runSubtitle: "输入目标企业,AI 追溯历次融资并推演估值趋势", + ...sharedRun, + installPrompt: sharedInstall("融资历史追踪", "/fundraising-tracker"), + }, + }, + { + id: "ubo-screening", + category: "bank", + title: "受益所有人识别", + slug: "/ubo-screening", + body: "对公开户、高净值客户尽调与反洗钱日常筛查场景下的受益所有人识别工具。3 分钟自动锁定企业最终受益人,按央行 3 号令 25% 阈值穿透股权链路、查明实控人画像与关联企业网络,输出可直接归档 KYC 系统的合规档案。", + detail: { + runSubtitle: "输入企业名称,AI 按央行 3 号令识别最终受益人", + ...sharedRun, + installPrompt: sharedInstall("受益所有人识别", "/ubo-screening"), + }, + }, + { + id: "equity-structure", + category: "invest", + title: "股权结构穿透分析", + slug: "/equity-structure", + body: "投资决策前的控制权核查工具。自动完成多层股权穿透并生成可视化结构图,识别实际控制人、一致行动人及隐性关联关系,帮助投资团队在 DD 阶段快速厘清目标公司的实际控制架构,识别潜在的关联交易风险。", + detail: { + runSubtitle: "输入目标公司,AI 多层穿透股权并生成结构图", + ...sharedRun, + installPrompt: sharedInstall("股权结构穿透分析", "/equity-structure"), + }, + }, + { + id: "executive-background", + category: "bank", + title: "高管背景核查", + slug: "/executive-background", + body: "高管背景核查工具,默认对全体董监高(含董事/监事/独立董事/职工代表董事)+ 实际控制人 + 主要高管做完整画像。覆盖失信、限高、限出境、股权冻结、税收违法、控制企业、在外任职、历史合作等维度。", + detail: { + runSubtitle: "输入企业名称,AI 对全体董监高与实控人做完整画像", + ...sharedRun, + installPrompt: sharedInstall("高管背景核查", "/executive-background"), + }, + }, + { + id: "credit-due-diligence", + category: "bank", + title: "授信尽调报告", + slug: "/credit-due-diligence", + body: "信贷审批流程中的企业全维度尽调工具。授信前自动汇总目标企业工商登记、实控人信息、经营异常记录与司法风险状况,输出标准化授信尽调底稿,帮助信贷审批人在放款前全面掌握企业风险画像,支撑有据可查的授信决策。", + detail: { + runSubtitle: "输入借款企业,AI 汇总风险并输出授信尽调底稿", + ...sharedRun, + installPrompt: sharedInstall("授信尽调报告", "/credit-due-diligence"), + }, + }, + { + id: "credit-monitoring", + category: "bank", + title: "信贷风险定期监控", + slug: "/credit-monitoring", + body: "贷后管理中对存量借款客户的持续风险监控工具。定期对贷款企业进行风险体检,失信被执行、经营异常登记、法定代表人变更等关键风险信号一旦发生变化即时推送预警,帮助客户经理在风险暴露初期及时采取措施。", + detail: { + runSubtitle: "输入存量借款企业,AI 定期体检并推送风险预警", + ...sharedRun, + installPrompt: sharedInstall("信贷风险定期监控", "/credit-monitoring"), + }, + }, + { + id: "litigation-analysis", + category: "legal", + title: "诉讼风险评估", + slug: "/litigation-analysis", + body: "立案前的对手方诉讼风险评估工具。以「企业当前 × 企业历史 × 核心人员」三层穿透扫描对手方,整合裁判文书、立案信息、执行风险、限高/限出境与法代/实控人个人涉诉档案,基于 5-10 年历史数据做时间序列趋势分析,支撑立案、授信、投资决策。", + detail: { + runSubtitle: "输入对手方企业,AI 三层穿透扫描并研判诉讼风险", + ...sharedRun, + installPrompt: sharedInstall("诉讼风险评估", "/litigation-analysis"), + }, + }, + { + id: "counterparty-risk", + category: "bank", + title: "交易对手风险评估", + slug: "/counterparty-risk", + body: "贸易融资业务中对交易对手的多维风险评估工具。整合企业进出口信用等级、行政处罚记录、失信被执行情况与经营异常信息,生成综合风险评分,帮助贸易金融团队在开证、议付等关键节点识别潜在违约风险。", + detail: { + runSubtitle: "输入交易对手,AI 生成综合风险评分", + ...sharedRun, + installPrompt: sharedInstall("交易对手风险评估", "/counterparty-risk"), + }, + }, + { + id: "strip-profile", + category: "invest", + title: "企业画像速览", + slug: "/strip-profile", + body: "投资前快速了解目标公司的轻量尽调工具。3 分钟生成一页纸企业画像,整合工商登记、核心风险信号与知识产权资产,以结构化方式呈现企业基本面,适用于 LP 推介前、项目初步筛选和内部立项汇报场景。", + detail: { + runSubtitle: "输入目标公司,AI 3 分钟生成一页纸企业画像", + ...sharedRun, + installPrompt: sharedInstall("企业画像速览", "/strip-profile"), + }, + }, + { + id: "ip-asset-inventory", + category: "invest", + title: "知产资产清单", + slug: "/ip-asset-inventory", + body: "科技型企业投资与并购前的知产资产核查工具。全量输出目标企业专利、商标、软件著作权的数量与有效性状态,评估技术壁垒深度与核心资产的法律稳定性,帮助投资团队在 DD 阶段准确判断企业的知产护城河。", + detail: { + runSubtitle: "输入目标企业,AI 全量核查知产资产并评估有效性", + ...sharedRun, + installPrompt: sharedInstall("知产资产清单", "/ip-asset-inventory"), + }, + }, + { + id: "supplier-annual-check", + category: "supply", + title: "供应商年度健康体检", + slug: "/supplier-annual-check", + body: "供应商年度评审的标准化核查工具。对核心供应商进行全面年度体检,一次性输出经营状态变化、资质证件到期情况、信用记录变更的结构化报告,帮助采购团队在年度供应商评审和续签决策中有充分的数据支撑。", + detail: { + runSubtitle: "输入核心供应商,AI 输出年度健康体检报告", + ...sharedRun, + installPrompt: sharedInstall("供应商年度健康体检", "/supplier-annual-check"), + }, + }, +]; + +export function getSkillById(id: string): Skill | undefined { + return skills.find((s) => s.id === id); +} + +export function getAllSkillIds(): string[] { + return skills.map((s) => s.id); +} diff --git a/src/styles/components.css b/src/styles/components.css index 895baab..52921d6 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -1647,4 +1647,753 @@ border-top: none; box-shadow: inset 0 1px 0 var(--color-hairline); } + + .btn-sm { + min-height: 34px; + padding: 7px var(--spacing-lg); + font-size: 13px; + border-radius: var(--radius-sm); + } + + .btn-outline:disabled { + color: var(--color-body-light); + background-color: var(--color-canvas-muted); + box-shadow: none; + cursor: not-allowed; + opacity: 0.7; + } + + .btn-outline:disabled:hover { + transform: none; + background-color: var(--color-canvas-muted); + box-shadow: none; + } + + .skill-marketplace { + display: flex; + flex-direction: column; + gap: var(--spacing-3xl); + } + + .skill-marketplace__toolbar { + display: flex; + flex-direction: column; + gap: var(--spacing-xl); + align-items: flex-start; + } + + @media (width >= 768px) { + .skill-marketplace__toolbar { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + } + + .skill-marketplace__categories { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); + } + + .skill-category-chip { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + font-family: var(--font-display); + font-size: 13px; + font-weight: 500; + line-height: 1; + color: var(--color-body); + background-color: var(--color-canvas); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-full); + padding: 8px var(--spacing-lg); + cursor: pointer; + transition: + color 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease; + } + + .skill-category-chip:hover { + color: var(--color-accent-ink); + border-color: var(--color-accent-border); + } + + .skill-category-chip--active { + color: var(--color-on-primary); + background-color: var(--color-ink); + border-color: var(--color-ink); + } + + .skill-category-chip--active:hover { + color: var(--color-on-primary); + } + + .skill-category-chip__emoji { + font-size: 14px; + line-height: 1; + } + + .skill-category-chip__count { + font-family: var(--font-mono-caps); + font-size: 11px; + font-weight: 500; + color: inherit; + opacity: 0.7; + margin-left: var(--spacing-xxs); + } + + .skill-marketplace__search { + width: 100%; + max-width: 280px; + } + + @media (width < 768px) { + .skill-marketplace__search { + max-width: 100%; + } + } + + .skill-search-input { + width: 100%; + font-family: var(--font-display); + font-size: 14px; + color: var(--color-ink); + background-color: var(--color-canvas); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-md); + padding: 9px var(--spacing-lg); + min-height: 38px; + box-shadow: var(--shadow-field); + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; + } + + .skill-search-input::placeholder { + color: var(--color-body-light); + } + + .skill-search-input:focus { + outline: none; + border-color: var(--color-accent-border); + box-shadow: var(--shadow-field-focus); + } + + .skill-marketplace__grid { + display: grid; + gap: var(--spacing-xl); + grid-template-columns: 1fr; + } + + @media (width >= 768px) { + .skill-marketplace__grid { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (width >= 1024px) { + .skill-marketplace__grid { + grid-template-columns: repeat(3, 1fr); + } + } + + .skill-marketplace__empty { + text-align: center; + padding: var(--spacing-5xl) var(--spacing-lg); + } + + .skill-card { + position: relative; + display: flex; + flex-direction: column; + gap: var(--spacing-lg); + height: 100%; + border-radius: var(--radius-md); + padding: var(--spacing-2xl); + background-color: var(--color-canvas); + color: var(--color-ink); + border: 1px solid var(--color-hairline); + box-shadow: var(--shadow-surface); + transition: box-shadow 0.2s ease, transform 0.2s ease; + } + + .skill-card:hover { + box-shadow: var(--shadow-surface-hover); + transform: translateY(-2px); + } + + @media (prefers-reduced-motion: reduce) { + .skill-card:hover { + transform: none; + } + } + + .skill-card__head { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + } + + .skill-card__title { + font-size: 18px; + line-height: 1.35; + } + + .skill-card__slug { + font-family: var(--font-mono-caps); + font-size: 11px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.04em; + color: var(--color-accent-soft); + background-color: var(--color-accent-wash); + border-radius: var(--radius-xs); + padding: 4px var(--spacing-sm); + align-self: flex-start; + } + + .skill-card__body { + font-size: 13.5px; + line-height: 1.65; + flex: 1; + display: -webkit-box; + -webkit-line-clamp: 4; + line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .skill-card__cta { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); + margin-top: auto; + padding-top: var(--spacing-sm); + } + + /* ===== 怎么使用 SKILL ===== */ + .skill-howto { + display: flex; + flex-direction: column; + gap: var(--spacing-3xl); + } + + .skill-howto__head { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + max-width: 720px; + } + + .skill-howto__title { + font-size: clamp(24px, 3vw, 34px); + line-height: 1.2; + } + + .skill-howto__subtitle { + color: var(--color-body); + } + + .skill-howto__layout { + display: grid; + gap: var(--spacing-3xl); + grid-template-columns: 1fr; + align-items: start; + } + + @media (width >= 960px) { + .skill-howto__layout { + grid-template-columns: 1fr 1fr; + gap: var(--spacing-4xl); + } + } + + .skill-howto__steps { + display: flex; + flex-direction: column; + gap: var(--spacing-2xl); + margin: 0; + padding: 0; + list-style: none; + } + + .skill-howto-step { + display: flex; + gap: var(--spacing-lg); + } + + .skill-howto-step__no { + font-family: var(--font-mono-caps); + font-size: 12px; + font-weight: 500; + line-height: 1; + letter-spacing: 0.06em; + color: var(--color-accent-ink); + background-color: var(--color-accent-wash); + border-radius: var(--radius-full); + width: 32px; + height: 32px; + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + } + + .skill-howto-step__copy { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + padding-top: 3px; + } + + .skill-howto-step__title { + font-size: 15px; + } + + .skill-howto-step__desc { + font-size: 13.5px; + line-height: 1.65; + } + + .skill-howto-config { + display: flex; + flex-direction: column; + gap: var(--spacing-lg); + } + + .skill-howto-config__intro { + font-size: 13.5px; + line-height: 1.65; + margin: 0; + } + + /* ===== 可折叠代码面板(仿企查查) ===== */ + .skill-code-block { + border: 1px solid var(--color-hairline); + border-radius: var(--radius-md); + background-color: var(--color-canvas-muted); + overflow: hidden; + box-shadow: var(--shadow-field); + } + + .skill-code-block__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: var(--spacing-sm) var(--spacing-md); + background-color: var(--color-canvas); + border-bottom: 1px solid var(--color-hairline); + } + + .skill-code-block__filename { + display: inline-flex; + align-items: center; + gap: var(--spacing-sm); + font-family: var(--font-mono-caps); + font-size: 12px; + font-weight: 500; + letter-spacing: 0.04em; + color: var(--color-ink-soft); + } + + .skill-code-block__dot { + width: 8px; + height: 8px; + border-radius: var(--radius-full); + background-color: var(--color-accent-ink); + flex-shrink: 0; + } + + .skill-code-block__actions { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + } + + .skill-code-block__btn { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + font-family: var(--font-display); + font-size: 12px; + font-weight: 500; + line-height: 1; + color: var(--color-body); + background-color: transparent; + border: 1px solid transparent; + border-radius: var(--radius-sm); + padding: 6px var(--spacing-sm); + cursor: pointer; + transition: + color 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease; + } + + .skill-code-block__btn:hover { + color: var(--color-accent-ink); + background-color: var(--color-accent-wash); + } + + .skill-code-block__btn--toggle { + color: var(--color-accent-ink); + } + + .skill-code-block__chevron { + transition: transform 0.2s ease; + } + + .skill-code-block[data-open="true"] .skill-code-block__chevron { + transform: rotate(180deg); + } + + .skill-code-block__pre { + margin: 0; + padding: var(--spacing-lg) var(--spacing-md); + overflow-x: auto; + font-family: var(--font-mono-caps); + font-size: 12.5px; + line-height: 1.75; + color: var(--color-ink-soft); + } + + .skill-code-block__pre code { + font-family: inherit; + white-space: pre; + } + + .skill-howto-config__apps { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--spacing-sm); + margin: 0; + } + + .skill-howto-config__apps-label { + font-family: var(--font-mono-caps); + font-size: 11px; + letter-spacing: 0.06em; + color: var(--color-body-light); + } + + .skill-howto-config__app { + font-size: 12px; + color: var(--color-ink-soft); + background-color: var(--color-canvas); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-full); + padding: 4px var(--spacing-md); + } + + .skill-howto-config__soon { + margin: 0; + } + + /* ===== SKILL 详情页 ===== */ + + .skill-detail { + display: flex; + flex-direction: column; + gap: var(--spacing-2xl); + max-width: 880px; + margin: 0 auto; + width: 100%; + } + + .skill-detail__crumbs ol { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--spacing-xs); + list-style: none; + margin: 0; + padding: 0; + font-family: var(--font-mono-caps); + font-size: 12px; + letter-spacing: 0.08em; + color: var(--color-body-light); + text-transform: uppercase; + } + + .skill-detail__crumbs a { + color: var(--color-body-light); + text-decoration: none; + transition: color 0.2s ease; + } + + .skill-detail__crumbs a:hover { + color: var(--color-accent-ink); + } + + .skill-detail__crumbs li[aria-current="page"] { + color: var(--color-ink); + } + + .skill-detail__head { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + } + + .skill-detail__title { + margin: 0; + } + + .skill-detail__lead { + margin: 0; + } + + .skill-detail__tabs { + display: flex; + gap: var(--spacing-xs); + border-bottom: 1px solid var(--color-hairline); + padding: 0; + } + + .skill-detail__tab { + font-family: var(--font-display); + font-size: 15px; + font-weight: 500; + color: var(--color-body-light); + background: transparent; + border: none; + border-bottom: 2px solid transparent; + padding: var(--spacing-sm) var(--spacing-lg); + margin-bottom: -1px; + cursor: pointer; + transition: + color 0.2s ease, + border-color 0.2s ease; + } + + .skill-detail__tab:hover { + color: var(--color-ink-soft); + } + + .skill-detail__tab--active { + color: var(--color-accent-ink); + border-bottom-color: var(--color-accent-ink); + } + + .skill-detail__panel { + min-height: 320px; + } + + /* 在线运行 */ + .skill-run { + display: flex; + flex-direction: column; + gap: var(--spacing-xl); + } + + .skill-run__head { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + } + + .skill-run__title { + margin: 0; + } + + .skill-run__hint { + margin: 0; + } + + .skill-run__field { + position: relative; + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + } + + .skill-run__label { + margin: 0; + } + + .skill-run__input { + font-family: var(--font-display); + font-size: 15px; + color: var(--color-ink); + background-color: var(--color-canvas); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-md); + padding: 14px var(--spacing-xl); + padding-right: 44px; + box-shadow: var(--shadow-field); + transition: + border-color 0.2s ease, + box-shadow 0.2s ease; + outline: none; + width: 100%; + } + + .skill-run__input:focus { + border-color: var(--color-accent-ink); + box-shadow: var(--shadow-field-focus); + } + + .skill-run__clear { + position: absolute; + right: var(--spacing-sm); + bottom: 9px; + width: 28px; + height: 28px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 18px; + line-height: 1; + color: var(--color-body-light); + background: transparent; + border: none; + border-radius: 50%; + cursor: pointer; + transition: color 0.2s ease, background-color 0.2s ease; + } + + .skill-run__clear:hover { + color: var(--color-ink); + background-color: var(--color-canvas-muted); + } + + .skill-run__go { + align-self: flex-start; + } + + .skill-run__note { + margin: 0; + } + + /* 安装方式 */ + .skill-install { + display: flex; + flex-direction: column; + gap: var(--spacing-3xl); + } + + .skill-install__step { + display: flex; + flex-direction: column; + gap: var(--spacing-md); + } + + .skill-install__title, + .skill-install__subtitle { + margin: 0; + } + + .skill-install__desc { + margin: 0; + } + + .skill-code-block__pre--text { + white-space: pre-wrap; + word-break: break-word; + } + + /* 示例报告 */ + .skill-example { + display: flex; + flex-direction: column; + gap: var(--spacing-xl); + } + + .skill-example__content { + display: flex; + flex-direction: column; + gap: var(--spacing-2xl); + } + + .skill-example__head { + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + } + + .skill-example__title { + margin: 0; + } + + .skill-example__sections { + display: flex; + flex-direction: column; + gap: var(--spacing-xl); + } + + .skill-example__section { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); + padding-bottom: var(--spacing-xl); + border-bottom: 1px solid var(--color-hairline); + } + + .skill-example__section:last-child { + border-bottom: none; + padding-bottom: 0; + } + + .skill-example__section-title { + margin: 0; + } + + .skill-example__bullets { + margin: 0; + padding-left: var(--spacing-xl); + display: flex; + flex-direction: column; + gap: var(--spacing-xs); + } + + .skill-example__bullets li { + color: var(--color-body); + font-size: 15px; + line-height: 1.6; + } + + .skill-example__empty { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: var(--spacing-lg); + padding: var(--spacing-2xl); + background-color: var(--color-canvas-muted); + border-radius: var(--radius-md); + } + + .skill-example__empty p { + margin: 0; + } + + /* 卡片标题链接 */ + .skill-card__title-link { + color: inherit; + text-decoration: none; + transition: color 0.2s ease; + } + + .skill-card__title-link:hover { + color: var(--color-accent-ink); + } + + @media (max-width: 640px) { + .skill-detail { + gap: var(--spacing-xl); + } + + .skill-detail__tab { + padding: var(--spacing-sm) var(--spacing-md); + font-size: 14px; + } + + .skill-install { + gap: var(--spacing-2xl); + } + } }