react-docs-ui 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AIChatDialog-C2mEAX2T.js +407 -0
- package/dist/{AISelectionTrigger-SLyb8Vep.js → AISelectionTrigger-CWTJRvHo.js} +1 -1
- package/dist/{AISettingsPanel-Bsd_wLP4.js → AISettingsPanel-C9Bb_QtE.js} +1 -1
- package/dist/{DocsApp-CCFHuucK.js → DocsApp-CTh7Pa9A.js} +3797 -3560
- package/dist/{GlobalContextMenu-BtVjQk0v.js → GlobalContextMenu-DAIKsl1l.js} +1 -1
- package/dist/{MdxContent-CJydH4kW.js → MdxContent-BAm4izu3.js} +305 -317
- package/dist/{MdxContent.lazy-ChT4eAlU.js → MdxContent.lazy-vS9NrZj4.js} +1 -1
- package/dist/{SearchDialog-Dh1RvEgs.js → SearchDialog-CLbtpf7I.js} +157 -114
- package/dist/{SearchRuntime-wdAGDBv4.js → SearchRuntime--tjedqS6.js} +1 -1
- package/dist/{context-menu-BIQsQup7.js → context-menu-DwILPvwC.js} +1 -1
- package/dist/{dialog-D8otbqQL.js → dialog-D68sEJBe.js} +1 -1
- package/dist/docs-app.es.js +1 -1
- package/dist/{mdx-components-BLq-Umts.js → mdx-components-6beJPZgM.js} +2 -2
- package/dist/react-docs-ui.css +1 -1
- package/dist/react-docs-ui.es.js +10 -10
- package/dist/types/components/Breadcrumb.d.ts +14 -0
- package/dist/types/components/Breadcrumb.d.ts.map +1 -0
- package/dist/types/components/DocsLayout.d.ts.map +1 -1
- package/dist/types/components/MdxContent.d.ts.map +1 -1
- package/dist/types/components/ReadingProgressBar.d.ts +4 -0
- package/dist/types/components/ReadingProgressBar.d.ts.map +1 -0
- package/dist/types/components/ReleaseMetaBar.d.ts +1 -1
- package/dist/types/components/ReleaseMetaBar.d.ts.map +1 -1
- package/dist/types/components/ai/AIChatMessage.d.ts.map +1 -1
- package/dist/types/components/search/SearchItem.d.ts.map +1 -1
- package/dist/types/hooks/useScrollPosition.d.ts.map +1 -1
- package/dist/types/lib/changelog.d.ts +1 -1
- package/dist/types/lib/changelog.d.ts.map +1 -1
- package/dist/types/lib/config.d.ts +20 -0
- package/dist/types/lib/config.d.ts.map +1 -1
- package/dist/types/lib/reading-time.d.ts +7 -0
- package/dist/types/lib/reading-time.d.ts.map +1 -0
- package/dist/types/lib/search/index.d.ts +2 -1
- package/dist/types/lib/search/index.d.ts.map +1 -1
- package/dist/types/lib/search/runtime/highlighter.d.ts +10 -0
- package/dist/types/lib/search/runtime/highlighter.d.ts.map +1 -1
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/lib/utils.d.ts.map +1 -1
- package/dist/utils-KFBtT4Mx.js +25 -0
- package/package.json +147 -143
- package/scripts/cli.mjs +2 -0
- package/scripts/generate-feed.mjs +122 -0
- package/scripts/generate-sitemap.mjs +186 -0
- package/dist/AIChatDialog-BnAX3dRn.js +0 -418
- package/dist/utils-Ct96Mtjw.js +0 -8
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from "node:fs/promises"
|
|
2
|
+
import path from "node:path"
|
|
3
|
+
import { glob } from "glob"
|
|
4
|
+
import yaml from "js-yaml"
|
|
5
|
+
|
|
6
|
+
const rootDir = process.cwd()
|
|
7
|
+
const publicDir = path.join(rootDir, "public")
|
|
8
|
+
|
|
9
|
+
function parseFrontmatter(source) {
|
|
10
|
+
if (!source.startsWith("---\n")) return { data: {} }
|
|
11
|
+
const endIndex = source.indexOf("\n---\n", 4)
|
|
12
|
+
if (endIndex === -1) return { data: {} }
|
|
13
|
+
const raw = source.slice(4, endIndex)
|
|
14
|
+
const data = {}
|
|
15
|
+
for (const line of raw.split("\n")) {
|
|
16
|
+
const index = line.indexOf(":")
|
|
17
|
+
if (index <= 0) continue
|
|
18
|
+
data[line.slice(0, index).trim()] = line.slice(index + 1).trim().replace(/^["']|["']$/g, "")
|
|
19
|
+
}
|
|
20
|
+
return { data }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function escapeXml(str) {
|
|
24
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function loadSiteUrl() {
|
|
28
|
+
const configPath = path.join(publicDir, "config", "site.yaml")
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(configPath, "utf8")
|
|
31
|
+
const config = yaml.load(content)
|
|
32
|
+
return config?.site?.url || ""
|
|
33
|
+
} catch {
|
|
34
|
+
return ""
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function loadSitemapConfig() {
|
|
39
|
+
const configPath = path.join(publicDir, "config", "site.yaml")
|
|
40
|
+
try {
|
|
41
|
+
const content = await fs.readFile(configPath, "utf8")
|
|
42
|
+
const config = yaml.load(content)
|
|
43
|
+
return config?.sitemap || {}
|
|
44
|
+
} catch {
|
|
45
|
+
return {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function collectDocPages(lang) {
|
|
50
|
+
const mdFiles = await glob(`public/docs/${lang}/**/*.md`, { cwd: rootDir, absolute: true, nodir: true })
|
|
51
|
+
const mdxFiles = await glob(`public/docs/${lang}/**/*.mdx`, { cwd: rootDir, absolute: true, nodir: true })
|
|
52
|
+
const files = [...mdFiles, ...mdxFiles]
|
|
53
|
+
const pages = []
|
|
54
|
+
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
const relativePath = path.relative(path.join(publicDir, "docs", lang), file)
|
|
57
|
+
const slug = relativePath.replace(/\\/g, "/").replace(/\.(md|mdx)$/i, "")
|
|
58
|
+
const source = await fs.readFile(file, "utf8")
|
|
59
|
+
const { data } = parseFrontmatter(source)
|
|
60
|
+
|
|
61
|
+
if (data.draft === "true" || data.noindex === "true") continue
|
|
62
|
+
|
|
63
|
+
pages.push({
|
|
64
|
+
path: `/${lang}/${slug}`,
|
|
65
|
+
lastmod: data.date || "",
|
|
66
|
+
changefreq: data.changefreq || "",
|
|
67
|
+
priority: data.priority ? Number(data.priority) : undefined,
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return pages
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function collectChangelogPages(lang) {
|
|
75
|
+
const mdFiles = await glob(`public/docs/${lang}/changelog/*.md`, { cwd: rootDir, absolute: true, nodir: true })
|
|
76
|
+
const mdxFiles = await glob(`public/docs/${lang}/changelog/*.mdx`, { cwd: rootDir, absolute: true, nodir: true })
|
|
77
|
+
const files = [...mdFiles, ...mdxFiles]
|
|
78
|
+
const pages = []
|
|
79
|
+
|
|
80
|
+
for (const file of files) {
|
|
81
|
+
const slug = path.basename(file).replace(/\.(md|mdx)$/i, "")
|
|
82
|
+
const source = await fs.readFile(file, "utf8")
|
|
83
|
+
const { data } = parseFrontmatter(source)
|
|
84
|
+
|
|
85
|
+
if (data.draft === "true") continue
|
|
86
|
+
|
|
87
|
+
pages.push({
|
|
88
|
+
path: `/${lang}/changelog/${slug}`,
|
|
89
|
+
lastmod: data.date || "",
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return pages
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function main() {
|
|
97
|
+
const siteUrl = await loadSiteUrl()
|
|
98
|
+
if (!siteUrl) {
|
|
99
|
+
console.warn("[sitemap] Skipping: site.url is not configured in site.yaml")
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const sitemapConfig = await loadSitemapConfig()
|
|
104
|
+
if (sitemapConfig.enabled === false) {
|
|
105
|
+
console.log("[sitemap] Disabled in config")
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const excludePatterns = sitemapConfig.exclude || []
|
|
110
|
+
const defaultChangefreq = sitemapConfig.changefreq || "weekly"
|
|
111
|
+
const defaultPriority = sitemapConfig.priority || 0.7
|
|
112
|
+
|
|
113
|
+
const docsRoot = path.join(publicDir, "docs")
|
|
114
|
+
const entries = await fs.readdir(docsRoot, { withFileTypes: true })
|
|
115
|
+
const langs = entries.filter(entry => entry.isDirectory()).map(entry => entry.name)
|
|
116
|
+
|
|
117
|
+
const allPages = []
|
|
118
|
+
|
|
119
|
+
for (const lang of langs) {
|
|
120
|
+
// 首页
|
|
121
|
+
allPages.push({ path: `/${lang}`, priority: 1.0, changefreq: "daily" })
|
|
122
|
+
|
|
123
|
+
// 文档页
|
|
124
|
+
const docPages = await collectDocPages(lang)
|
|
125
|
+
allPages.push(...docPages)
|
|
126
|
+
|
|
127
|
+
// Changelog 页
|
|
128
|
+
const changelogPages = await collectChangelogPages(lang)
|
|
129
|
+
if (changelogPages.length > 0) {
|
|
130
|
+
allPages.push({ path: `/${lang}/changelog`, priority: 0.6, changefreq: "daily" })
|
|
131
|
+
allPages.push(...changelogPages)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 排除指定路径
|
|
136
|
+
const filteredPages = allPages.filter(page => {
|
|
137
|
+
return !excludePatterns.some(pattern => {
|
|
138
|
+
if (pattern.endsWith("/*")) {
|
|
139
|
+
return page.path.startsWith(pattern.slice(0, -1))
|
|
140
|
+
}
|
|
141
|
+
if (pattern.startsWith("*")) {
|
|
142
|
+
return page.path.endsWith(pattern.slice(1))
|
|
143
|
+
}
|
|
144
|
+
return page.path === pattern
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// 去重
|
|
149
|
+
const seen = new Set()
|
|
150
|
+
const uniquePages = filteredPages.filter(page => {
|
|
151
|
+
if (seen.has(page.path)) return false
|
|
152
|
+
seen.add(page.path)
|
|
153
|
+
return true
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// 生成 XML
|
|
157
|
+
const today = new Date().toISOString().split("T")[0]
|
|
158
|
+
const urls = uniquePages.map(page => {
|
|
159
|
+
const loc = `${siteUrl.replace(/\/+$/, "")}${page.path}`
|
|
160
|
+
const changefreq = page.changefreq || defaultChangefreq
|
|
161
|
+
const priority = page.priority ?? defaultPriority
|
|
162
|
+
const lastmod = page.lastmod || today
|
|
163
|
+
|
|
164
|
+
return ` <url>
|
|
165
|
+
<loc>${escapeXml(loc)}</loc>
|
|
166
|
+
<lastmod>${escapeXml(lastmod)}</lastmod>
|
|
167
|
+
<changefreq>${changefreq}</changefreq>
|
|
168
|
+
<priority>${Number(priority).toFixed(1)}</priority>
|
|
169
|
+
</url>`
|
|
170
|
+
}).join("\n")
|
|
171
|
+
|
|
172
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
173
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
174
|
+
${urls}
|
|
175
|
+
</urlset>
|
|
176
|
+
`
|
|
177
|
+
|
|
178
|
+
const outputPath = path.join(publicDir, "sitemap.xml")
|
|
179
|
+
await fs.writeFile(outputPath, xml, "utf8")
|
|
180
|
+
console.log(`[sitemap] Generated ${uniquePages.length} URLs to sitemap.xml`)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main().catch((error) => {
|
|
184
|
+
console.error("[sitemap] Failed:", error)
|
|
185
|
+
process.exitCode = 1
|
|
186
|
+
})
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
import { r as e } from "./chunk-NBPlniwU.js";
|
|
2
|
-
import { t } from "./utils-Ct96Mtjw.js";
|
|
3
|
-
import { n } from "./AIProvider-niZNPSGi.js";
|
|
4
|
-
import r, { useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
|
|
5
|
-
import { AlertCircle as c, AlertTriangle as l, Bot as u, Check as d, CheckCheck as f, CheckCircle as p, Copy as m, GripVertical as h, Loader2 as g, Pencil as _, RefreshCw as v, Send as y, Settings as b, Square as x, Trash2 as S, User as C, X as w } from "lucide-react";
|
|
6
|
-
import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
7
|
-
import O from "remark-gfm";
|
|
8
|
-
import k from "react-markdown";
|
|
9
|
-
//#region src/components/ai/AIChatMessage.tsx
|
|
10
|
-
function A({ message: e, onRegenerate: n, onEdit: a, onDelete: o }) {
|
|
11
|
-
let [l, h] = s(!1), [y, b] = s(null), [x, A] = s(!1), [j, M] = s(e.content), N = e.role === "user", P = e.isStreaming, F = !!e.error, I = i(async (e) => {
|
|
12
|
-
if (navigator.clipboard && window.isSecureContext) await navigator.clipboard.writeText(e);
|
|
13
|
-
else {
|
|
14
|
-
let t = document.createElement("textarea");
|
|
15
|
-
t.value = e, t.style.position = "fixed", t.style.left = "-9999px", t.style.top = "-9999px", document.body.appendChild(t), t.focus(), t.select();
|
|
16
|
-
try {
|
|
17
|
-
document.execCommand("copy");
|
|
18
|
-
} finally {
|
|
19
|
-
document.body.removeChild(t);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}, []), L = i(async () => {
|
|
23
|
-
try {
|
|
24
|
-
await I(e.content), h(!0), setTimeout(() => h(!1), 2e3);
|
|
25
|
-
} catch {
|
|
26
|
-
console.error("Failed to copy message");
|
|
27
|
-
}
|
|
28
|
-
}, [e.content, I]), R = i(async (e, t) => {
|
|
29
|
-
t.stopPropagation();
|
|
30
|
-
try {
|
|
31
|
-
await I(e), b(e), setTimeout(() => b(null), 2e3);
|
|
32
|
-
} catch {
|
|
33
|
-
console.error("Failed to copy code");
|
|
34
|
-
}
|
|
35
|
-
}, [I]), z = i(() => {
|
|
36
|
-
j.trim() && j !== e.content && a?.(j.trim()), A(!1);
|
|
37
|
-
}, [
|
|
38
|
-
j,
|
|
39
|
-
e.content,
|
|
40
|
-
a
|
|
41
|
-
]), B = i(() => {
|
|
42
|
-
M(e.content), A(!1);
|
|
43
|
-
}, [e.content]);
|
|
44
|
-
return /* @__PURE__ */ D("div", {
|
|
45
|
-
className: t("flex gap-3 p-4 group", N ? "flex-row-reverse" : "flex-row"),
|
|
46
|
-
children: [/* @__PURE__ */ E("div", {
|
|
47
|
-
className: t("flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center", N ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"),
|
|
48
|
-
children: E(N ? C : u, { className: "w-4 h-4" })
|
|
49
|
-
}), /* @__PURE__ */ E("div", {
|
|
50
|
-
className: t("flex-1 min-w-0", N ? "text-right" : "text-left"),
|
|
51
|
-
children: x ? /* @__PURE__ */ D("div", {
|
|
52
|
-
className: "inline-block max-w-full",
|
|
53
|
-
children: [/* @__PURE__ */ E("textarea", {
|
|
54
|
-
value: j,
|
|
55
|
-
onChange: (e) => M(e.target.value),
|
|
56
|
-
className: t("w-full min-w-[200px] max-w-[400px] p-3 rounded-lg border", "bg-background text-sm resize-none", "focus:outline-none focus:ring-2 focus:ring-ring"),
|
|
57
|
-
rows: Math.min(10, j.split("\n").length + 1),
|
|
58
|
-
autoFocus: !0
|
|
59
|
-
}), /* @__PURE__ */ D("div", {
|
|
60
|
-
className: "flex justify-end gap-2 mt-2",
|
|
61
|
-
children: [/* @__PURE__ */ E("button", {
|
|
62
|
-
onClick: B,
|
|
63
|
-
className: "p-1.5 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
64
|
-
title: "取消",
|
|
65
|
-
children: /* @__PURE__ */ E(w, { className: "w-4 h-4" })
|
|
66
|
-
}), /* @__PURE__ */ E("button", {
|
|
67
|
-
onClick: z,
|
|
68
|
-
className: "p-1.5 rounded text-primary hover:bg-primary/10 transition-colors",
|
|
69
|
-
title: "保存",
|
|
70
|
-
children: /* @__PURE__ */ E(f, { className: "w-4 h-4" })
|
|
71
|
-
})]
|
|
72
|
-
})]
|
|
73
|
-
}) : /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ D("div", {
|
|
74
|
-
className: t("inline-block max-w-full rounded-lg px-4 py-2", N ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", F && "border border-destructive"),
|
|
75
|
-
children: [e.content ? /* @__PURE__ */ E(k, {
|
|
76
|
-
remarkPlugins: [O],
|
|
77
|
-
components: {
|
|
78
|
-
pre: ({ children: e }) => {
|
|
79
|
-
let t = (e) => {
|
|
80
|
-
if (typeof e == "string") return e;
|
|
81
|
-
if (Array.isArray(e)) return e.map(t).join("");
|
|
82
|
-
if (r.isValidElement(e)) {
|
|
83
|
-
let n = e.props;
|
|
84
|
-
if (n.children) return t(n.children);
|
|
85
|
-
}
|
|
86
|
-
return "";
|
|
87
|
-
}, n = r.Children.toArray(e).find((e) => r.isValidElement(e) && e.type === "code"), i = n && r.isValidElement(n) ? t(n.props.children).replace(/\n$/, "") : "";
|
|
88
|
-
return /* @__PURE__ */ D("div", {
|
|
89
|
-
className: "relative my-3 rounded-lg overflow-hidden border dark:border-zinc-700 border-zinc-200 dark:bg-[#141414] bg-[#fafafa]",
|
|
90
|
-
children: [/* @__PURE__ */ D("div", {
|
|
91
|
-
className: "flex items-center justify-between px-4 py-2 border-b dark:border-zinc-700 border-zinc-200 dark:bg-[#1a1a1a] bg-zinc-100",
|
|
92
|
-
children: [/* @__PURE__ */ E("span", {
|
|
93
|
-
className: "text-xs dark:text-zinc-400 text-zinc-500",
|
|
94
|
-
children: "代码"
|
|
95
|
-
}), i && /* @__PURE__ */ E("button", {
|
|
96
|
-
type: "button",
|
|
97
|
-
onClick: (e) => R(i, e),
|
|
98
|
-
className: "flex items-center gap-1 px-2 py-1 text-xs rounded dark:bg-zinc-700 dark:hover:bg-zinc-600 dark:text-zinc-300 bg-zinc-200 hover:bg-zinc-300 text-zinc-600 transition-colors",
|
|
99
|
-
children: y === i ? /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ E(p, { className: "w-3.5 h-3.5 text-green-500" }), "已复制"] }) : /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ E(m, { className: "w-3.5 h-3.5" }), "复制"] })
|
|
100
|
-
})]
|
|
101
|
-
}), /* @__PURE__ */ E("pre", {
|
|
102
|
-
className: "!m-0 p-4 overflow-x-auto text-sm hljs",
|
|
103
|
-
children: e
|
|
104
|
-
})]
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
code: ({ className: e, children: n, ...r }) => /language-(\w+)/.exec(e || "") || e?.includes("hljs") ? /* @__PURE__ */ E("code", {
|
|
108
|
-
className: t(e, "block"),
|
|
109
|
-
...r,
|
|
110
|
-
children: n
|
|
111
|
-
}) : !e && String(n).includes("\n") ? /* @__PURE__ */ E("code", {
|
|
112
|
-
className: "block",
|
|
113
|
-
...r,
|
|
114
|
-
children: n
|
|
115
|
-
}) : /* @__PURE__ */ E("code", {
|
|
116
|
-
className: "px-1.5 py-0.5 rounded dark:bg-zinc-700 bg-zinc-200 text-sm",
|
|
117
|
-
...r,
|
|
118
|
-
children: n
|
|
119
|
-
})
|
|
120
|
-
},
|
|
121
|
-
children: e.content
|
|
122
|
-
}) : P ? /* @__PURE__ */ D("div", {
|
|
123
|
-
className: "flex items-center gap-2 text-muted-foreground",
|
|
124
|
-
children: [/* @__PURE__ */ E(g, { className: "w-4 h-4 animate-spin" }), /* @__PURE__ */ E("span", { children: "正在思考..." })]
|
|
125
|
-
}) : null, F && /* @__PURE__ */ D("div", {
|
|
126
|
-
className: "flex items-center gap-2 text-destructive mt-2",
|
|
127
|
-
children: [/* @__PURE__ */ E(c, { className: "w-4 h-4" }), /* @__PURE__ */ E("span", {
|
|
128
|
-
className: "text-sm",
|
|
129
|
-
children: e.error
|
|
130
|
-
})]
|
|
131
|
-
})]
|
|
132
|
-
}), !P && /* @__PURE__ */ D("div", {
|
|
133
|
-
className: t("flex items-center gap-2 mt-2 opacity-0 group-hover:opacity-100 transition-opacity", N ? "justify-end" : "justify-start"),
|
|
134
|
-
children: [
|
|
135
|
-
!N && e.content && /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ E("button", {
|
|
136
|
-
onClick: L,
|
|
137
|
-
className: "p-1.5 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
138
|
-
title: "复制",
|
|
139
|
-
children: l ? /* @__PURE__ */ E(d, { className: "w-3.5 h-3.5 text-green-500" }) : /* @__PURE__ */ E(m, { className: "w-3.5 h-3.5" })
|
|
140
|
-
}), n && /* @__PURE__ */ E("button", {
|
|
141
|
-
onClick: n,
|
|
142
|
-
className: "p-1.5 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
143
|
-
title: "重新生成",
|
|
144
|
-
children: /* @__PURE__ */ E(v, { className: "w-3.5 h-3.5" })
|
|
145
|
-
})] }),
|
|
146
|
-
N && a && /* @__PURE__ */ E("button", {
|
|
147
|
-
onClick: () => A(!0),
|
|
148
|
-
className: "p-1.5 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
149
|
-
title: "编辑",
|
|
150
|
-
children: /* @__PURE__ */ E(_, { className: "w-3.5 h-3.5" })
|
|
151
|
-
}),
|
|
152
|
-
o && /* @__PURE__ */ E("button", {
|
|
153
|
-
onClick: o,
|
|
154
|
-
className: "p-1.5 rounded text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
155
|
-
title: "删除",
|
|
156
|
-
children: /* @__PURE__ */ E(S, { className: "w-3.5 h-3.5" })
|
|
157
|
-
})
|
|
158
|
-
]
|
|
159
|
-
})] })
|
|
160
|
-
})]
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
//#endregion
|
|
164
|
-
//#region src/components/ai/AIChatInput.tsx
|
|
165
|
-
function j({ onSend: e, onStop: n, isLoading: r = !1, disabled: i = !1, placeholder: c = "输入消息..." }) {
|
|
166
|
-
let [l, u] = s(""), d = o(null);
|
|
167
|
-
a(() => {
|
|
168
|
-
let e = d.current;
|
|
169
|
-
e && (e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, 150)}px`);
|
|
170
|
-
}, [l]);
|
|
171
|
-
let f = () => {
|
|
172
|
-
let t = l.trim();
|
|
173
|
-
!t || r || i || (e(t), u(""), d.current && (d.current.style.height = "auto"));
|
|
174
|
-
};
|
|
175
|
-
return /* @__PURE__ */ D("div", {
|
|
176
|
-
className: "flex items-end gap-2 p-4 border-t bg-background",
|
|
177
|
-
children: [/* @__PURE__ */ E("div", {
|
|
178
|
-
className: "flex-1 relative",
|
|
179
|
-
children: /* @__PURE__ */ E("textarea", {
|
|
180
|
-
ref: d,
|
|
181
|
-
value: l,
|
|
182
|
-
onChange: (e) => u(e.target.value),
|
|
183
|
-
onKeyDown: (e) => {
|
|
184
|
-
e.key === "Enter" && !e.shiftKey && (e.preventDefault(), f());
|
|
185
|
-
},
|
|
186
|
-
placeholder: c,
|
|
187
|
-
disabled: i,
|
|
188
|
-
rows: 1,
|
|
189
|
-
className: t("w-full resize-none rounded-lg border bg-background px-4 py-2.5", "text-sm placeholder:text-muted-foreground", "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "disabled:cursor-not-allowed disabled:opacity-50", "min-h-[40px] max-h-[150px]"),
|
|
190
|
-
style: { height: "40px" }
|
|
191
|
-
})
|
|
192
|
-
}), r ? /* @__PURE__ */ E("button", {
|
|
193
|
-
onClick: n,
|
|
194
|
-
className: t("flex-shrink-0 w-10 h-10 rounded-lg", "flex items-center justify-center", "bg-destructive text-destructive-foreground", "hover:bg-destructive/90 transition-colors"),
|
|
195
|
-
title: "停止生成",
|
|
196
|
-
children: /* @__PURE__ */ E(x, { className: "w-4 h-4" })
|
|
197
|
-
}) : /* @__PURE__ */ E("button", {
|
|
198
|
-
onClick: f,
|
|
199
|
-
disabled: !l.trim() || i,
|
|
200
|
-
className: t("flex-shrink-0 w-10 h-10 rounded-lg", "flex items-center justify-center", "bg-primary text-primary-foreground", "hover:bg-primary/90 transition-colors", "disabled:opacity-50 disabled:cursor-not-allowed"),
|
|
201
|
-
title: "发送消息",
|
|
202
|
-
children: /* @__PURE__ */ E(y, { className: "w-4 h-4" })
|
|
203
|
-
})]
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
//#endregion
|
|
207
|
-
//#region src/components/ai/AIChatDialog.tsx
|
|
208
|
-
var M = /* @__PURE__ */ e({ AIChatDialog: () => I }), N = 300, P = 700, F = 500;
|
|
209
|
-
function I() {
|
|
210
|
-
let { isDialogOpen: e, closeDialog: r, openSettings: c, messages: d, isLoading: f, error: p, isConfigured: m, selectedText: g, sendMessage: _, regenerateLast: v, clearHistory: y, stopGeneration: x, editMessage: C, deleteMessage: T } = n(), O = o(null), k = o(null), M = o(null), I = o(null), L = o(!0), [R, z] = s(F), [B, V] = s(!1), [H, U] = s({
|
|
211
|
-
x: 0,
|
|
212
|
-
y: 0
|
|
213
|
-
}), [W, G] = s(!1), K = o({
|
|
214
|
-
x: 0,
|
|
215
|
-
y: 0,
|
|
216
|
-
posX: 0,
|
|
217
|
-
posY: 0
|
|
218
|
-
}), q = i((e = "smooth") => {
|
|
219
|
-
O.current && O.current.scrollIntoView({ behavior: e });
|
|
220
|
-
}, []), J = i(() => {
|
|
221
|
-
let e = k.current;
|
|
222
|
-
if (!e) return;
|
|
223
|
-
let { scrollTop: t, scrollHeight: n, clientHeight: r } = e;
|
|
224
|
-
L.current = n - t - r < 100;
|
|
225
|
-
}, []);
|
|
226
|
-
a(() => {
|
|
227
|
-
if (L.current && O.current) {
|
|
228
|
-
let e = d[d.length - 1]?.isStreaming;
|
|
229
|
-
q(e ? "auto" : "smooth");
|
|
230
|
-
}
|
|
231
|
-
}, [d, q]), a(() => {
|
|
232
|
-
let t = (t) => {
|
|
233
|
-
t.key === "Escape" && e && r();
|
|
234
|
-
};
|
|
235
|
-
return document.addEventListener("keydown", t), () => document.removeEventListener("keydown", t);
|
|
236
|
-
}, [e, r]);
|
|
237
|
-
let Y = i((e) => {
|
|
238
|
-
e.preventDefault(), V(!0);
|
|
239
|
-
let t = e.clientY, n = R, r = (e) => {
|
|
240
|
-
let r = t - e.clientY;
|
|
241
|
-
z(Math.min(P, Math.max(N, n + r)));
|
|
242
|
-
}, i = () => {
|
|
243
|
-
V(!1), document.removeEventListener("mousemove", r), document.removeEventListener("mouseup", i);
|
|
244
|
-
};
|
|
245
|
-
document.addEventListener("mousemove", r), document.addEventListener("mouseup", i);
|
|
246
|
-
}, [R]), X = i((e) => {
|
|
247
|
-
if (e.target.closest("button")) return;
|
|
248
|
-
e.preventDefault(), G(!0), K.current = {
|
|
249
|
-
x: e.clientX,
|
|
250
|
-
y: e.clientY,
|
|
251
|
-
posX: H.x,
|
|
252
|
-
posY: H.y
|
|
253
|
-
};
|
|
254
|
-
let t = (e) => {
|
|
255
|
-
let t = e.clientX - K.current.x, n = e.clientY - K.current.y;
|
|
256
|
-
U({
|
|
257
|
-
x: K.current.posX + t,
|
|
258
|
-
y: K.current.posY + n
|
|
259
|
-
});
|
|
260
|
-
}, n = () => {
|
|
261
|
-
G(!1), document.removeEventListener("mousemove", t), document.removeEventListener("mouseup", n);
|
|
262
|
-
};
|
|
263
|
-
document.addEventListener("mousemove", t), document.addEventListener("mouseup", n);
|
|
264
|
-
}, [H]), Z = i(() => {
|
|
265
|
-
U({
|
|
266
|
-
x: 0,
|
|
267
|
-
y: 0
|
|
268
|
-
});
|
|
269
|
-
}, []);
|
|
270
|
-
return !m && e ? /* @__PURE__ */ E("div", {
|
|
271
|
-
className: "fixed inset-x-4 bottom-4 z-50 mx-auto max-w-2xl",
|
|
272
|
-
children: /* @__PURE__ */ D("div", {
|
|
273
|
-
className: t("bg-background border rounded-xl shadow-2xl", "animate-in slide-in-from-bottom-4 duration-300"),
|
|
274
|
-
children: [/* @__PURE__ */ D("div", {
|
|
275
|
-
className: "flex items-center justify-between p-4 border-b",
|
|
276
|
-
children: [/* @__PURE__ */ D("div", {
|
|
277
|
-
className: "flex items-center gap-2",
|
|
278
|
-
children: [/* @__PURE__ */ E(u, { className: "w-5 h-5 text-primary" }), /* @__PURE__ */ E("h3", {
|
|
279
|
-
className: "font-semibold",
|
|
280
|
-
children: "AI 助手"
|
|
281
|
-
})]
|
|
282
|
-
}), /* @__PURE__ */ E("button", {
|
|
283
|
-
onClick: r,
|
|
284
|
-
className: "p-1.5 rounded hover:bg-muted transition-colors",
|
|
285
|
-
children: /* @__PURE__ */ E(w, { className: "w-4 h-4" })
|
|
286
|
-
})]
|
|
287
|
-
}), /* @__PURE__ */ D("div", {
|
|
288
|
-
className: "p-6 text-center",
|
|
289
|
-
children: [
|
|
290
|
-
/* @__PURE__ */ E(l, { className: "w-12 h-12 mx-auto text-muted-foreground mb-4" }),
|
|
291
|
-
/* @__PURE__ */ E("h4", {
|
|
292
|
-
className: "text-lg font-medium mb-2",
|
|
293
|
-
children: "需要配置 AI"
|
|
294
|
-
}),
|
|
295
|
-
/* @__PURE__ */ E("p", {
|
|
296
|
-
className: "text-muted-foreground mb-4",
|
|
297
|
-
children: "请先配置 AI 提供商和 API 密钥才能使用此功能"
|
|
298
|
-
}),
|
|
299
|
-
/* @__PURE__ */ E("button", {
|
|
300
|
-
onClick: () => {
|
|
301
|
-
r(), c();
|
|
302
|
-
},
|
|
303
|
-
className: "px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors",
|
|
304
|
-
children: "前往设置"
|
|
305
|
-
})
|
|
306
|
-
]
|
|
307
|
-
})]
|
|
308
|
-
})
|
|
309
|
-
}) : e ? /* @__PURE__ */ E("div", {
|
|
310
|
-
className: "fixed inset-x-4 bottom-4 z-50 mx-auto max-w-2xl",
|
|
311
|
-
style: { transform: `translate(${H.x}px, ${H.y}px)` },
|
|
312
|
-
children: /* @__PURE__ */ D("div", {
|
|
313
|
-
ref: M,
|
|
314
|
-
className: t("bg-background border rounded-xl shadow-2xl overflow-hidden", "animate-in slide-in-from-bottom-4 duration-300", "flex flex-col", W && "cursor-grabbing", B && "cursor-ns-resize"),
|
|
315
|
-
style: {
|
|
316
|
-
height: `${R}px`,
|
|
317
|
-
maxHeight: "70vh"
|
|
318
|
-
},
|
|
319
|
-
children: [
|
|
320
|
-
/* @__PURE__ */ D("div", {
|
|
321
|
-
className: "flex items-center justify-between p-4 border-b shrink-0 cursor-grab active:cursor-grabbing",
|
|
322
|
-
onMouseDown: X,
|
|
323
|
-
children: [/* @__PURE__ */ D("div", {
|
|
324
|
-
className: "flex items-center gap-2",
|
|
325
|
-
children: [
|
|
326
|
-
/* @__PURE__ */ E(h, { className: "w-4 h-4 text-muted-foreground" }),
|
|
327
|
-
/* @__PURE__ */ E(u, { className: "w-5 h-5 text-primary" }),
|
|
328
|
-
/* @__PURE__ */ E("h3", {
|
|
329
|
-
className: "font-semibold",
|
|
330
|
-
children: "AI 助手"
|
|
331
|
-
})
|
|
332
|
-
]
|
|
333
|
-
}), /* @__PURE__ */ D("div", {
|
|
334
|
-
className: "flex items-center gap-1",
|
|
335
|
-
children: [
|
|
336
|
-
H.x !== 0 || H.y !== 0 ? /* @__PURE__ */ E("button", {
|
|
337
|
-
onClick: Z,
|
|
338
|
-
className: "p-1.5 rounded hover:bg-muted transition-colors text-muted-foreground hover:text-foreground",
|
|
339
|
-
title: "重置位置",
|
|
340
|
-
children: /* @__PURE__ */ E(w, { className: "w-3 h-3" })
|
|
341
|
-
}) : null,
|
|
342
|
-
d.length > 0 && /* @__PURE__ */ E("button", {
|
|
343
|
-
onClick: y,
|
|
344
|
-
className: "p-1.5 rounded hover:bg-muted transition-colors text-muted-foreground hover:text-foreground",
|
|
345
|
-
title: "清除对话",
|
|
346
|
-
children: /* @__PURE__ */ E(S, { className: "w-4 h-4" })
|
|
347
|
-
}),
|
|
348
|
-
/* @__PURE__ */ E("button", {
|
|
349
|
-
onClick: c,
|
|
350
|
-
className: "p-1.5 rounded hover:bg-muted transition-colors text-muted-foreground hover:text-foreground",
|
|
351
|
-
title: "设置",
|
|
352
|
-
children: /* @__PURE__ */ E(b, { className: "w-4 h-4" })
|
|
353
|
-
}),
|
|
354
|
-
/* @__PURE__ */ E("button", {
|
|
355
|
-
onClick: r,
|
|
356
|
-
className: "p-1.5 rounded hover:bg-muted transition-colors text-muted-foreground hover:text-foreground",
|
|
357
|
-
title: "关闭",
|
|
358
|
-
children: /* @__PURE__ */ E(w, { className: "w-4 h-4" })
|
|
359
|
-
})
|
|
360
|
-
]
|
|
361
|
-
})]
|
|
362
|
-
}),
|
|
363
|
-
g && /* @__PURE__ */ D("div", {
|
|
364
|
-
className: "px-4 py-2 bg-muted/50 border-b shrink-0",
|
|
365
|
-
children: [/* @__PURE__ */ E("div", {
|
|
366
|
-
className: "text-xs text-muted-foreground mb-1",
|
|
367
|
-
children: "选中的内容:"
|
|
368
|
-
}), /* @__PURE__ */ E("div", {
|
|
369
|
-
className: "text-sm line-clamp-2",
|
|
370
|
-
children: g
|
|
371
|
-
})]
|
|
372
|
-
}),
|
|
373
|
-
/* @__PURE__ */ E("div", {
|
|
374
|
-
ref: k,
|
|
375
|
-
className: "flex-1 overflow-y-auto",
|
|
376
|
-
onScroll: J,
|
|
377
|
-
children: d.length === 0 ? /* @__PURE__ */ D("div", {
|
|
378
|
-
className: "flex flex-col items-center justify-center h-full text-muted-foreground p-4",
|
|
379
|
-
children: [/* @__PURE__ */ E(u, { className: "w-12 h-12 mb-4 opacity-50" }), /* @__PURE__ */ D("p", {
|
|
380
|
-
className: "text-center",
|
|
381
|
-
children: [
|
|
382
|
-
"你好!我是 AI 助手。",
|
|
383
|
-
/* @__PURE__ */ E("br", {}),
|
|
384
|
-
"有什么我可以帮助你的吗?"
|
|
385
|
-
]
|
|
386
|
-
})]
|
|
387
|
-
}) : /* @__PURE__ */ D("div", {
|
|
388
|
-
className: "divide-y",
|
|
389
|
-
children: [d.map((e, t) => /* @__PURE__ */ E(A, {
|
|
390
|
-
message: e,
|
|
391
|
-
onRegenerate: e.role === "assistant" && t === d.length - 1 && !f ? v : void 0,
|
|
392
|
-
onEdit: e.role === "user" ? (t) => C(e.id, t) : void 0,
|
|
393
|
-
onDelete: f ? void 0 : () => T(e.id)
|
|
394
|
-
}, e.id)), /* @__PURE__ */ E("div", { ref: O })]
|
|
395
|
-
})
|
|
396
|
-
}),
|
|
397
|
-
p && /* @__PURE__ */ E("div", {
|
|
398
|
-
className: "px-4 py-2 bg-destructive/10 text-destructive text-sm border-t",
|
|
399
|
-
children: p
|
|
400
|
-
}),
|
|
401
|
-
/* @__PURE__ */ E(j, {
|
|
402
|
-
onSend: _,
|
|
403
|
-
onStop: x,
|
|
404
|
-
isLoading: f,
|
|
405
|
-
placeholder: g ? "询问关于选中内容的问题..." : "输入消息..."
|
|
406
|
-
}),
|
|
407
|
-
/* @__PURE__ */ E("div", {
|
|
408
|
-
ref: I,
|
|
409
|
-
className: "absolute top-0 left-1/2 -translate-x-1/2 w-20 h-2 cursor-ns-resize flex items-center justify-center",
|
|
410
|
-
onMouseDown: Y,
|
|
411
|
-
children: /* @__PURE__ */ E("div", { className: "w-8 h-1 rounded-full bg-muted-foreground/30" })
|
|
412
|
-
})
|
|
413
|
-
]
|
|
414
|
-
})
|
|
415
|
-
}) : null;
|
|
416
|
-
}
|
|
417
|
-
//#endregion
|
|
418
|
-
export { A as i, M as n, j as r, I as t };
|