react-docs-ui 0.6.14 → 0.6.16

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.
Files changed (68) hide show
  1. package/README.md +13 -0
  2. package/dist/AIChatDialog-W60DtiEW.js +418 -0
  3. package/dist/AIProvider-C1_b1iKH.js +1066 -0
  4. package/dist/AISelectionTrigger-MaBGhxTE.js +86 -0
  5. package/dist/AISettingsPanel-aiMnncnQ.js +524 -0
  6. package/dist/DocsApp-CUrnKwOU.js +23560 -0
  7. package/dist/GlobalContextMenu-DG6x5Y5w.js +278 -0
  8. package/dist/MdxContent-CMB8ZbXo.js +850 -0
  9. package/dist/MdxContent.lazy-C4rcCv4v.js +1768 -0
  10. package/dist/SearchDialog-tUSKKsxW.js +403 -0
  11. package/dist/SearchRuntime-5fXpxDsi.js +23 -0
  12. package/dist/context-menu-CB7erSoV.js +73 -0
  13. package/dist/dialog-DQ6nkP0y.js +97 -0
  14. package/dist/docs-app.es.d.ts +1 -0
  15. package/dist/docs-app.es.js +7 -9
  16. package/dist/react-docs-ui.css +2 -1
  17. package/dist/react-docs-ui.es.js +66 -195
  18. package/dist/shiki-highlighter-BmQSSJpS.js +7194 -0
  19. package/dist/theme-provider-fNhx9xK0.js +37 -0
  20. package/dist/types/components/DocsLayout.d.ts +1 -31
  21. package/dist/types/components/DocsLayout.d.ts.map +1 -1
  22. package/dist/types/components/Footer.d.ts +3 -28
  23. package/dist/types/components/Footer.d.ts.map +1 -1
  24. package/dist/types/components/HeaderNav.d.ts.map +1 -1
  25. package/dist/types/components/MdxContent.d.ts.map +1 -1
  26. package/dist/types/components/MobileSidebar.d.ts +1 -1
  27. package/dist/types/components/MobileSidebar.d.ts.map +1 -1
  28. package/dist/types/components/PageNavigation.d.ts +1 -1
  29. package/dist/types/components/PageNavigation.d.ts.map +1 -1
  30. package/dist/types/components/SidebarNav.d.ts +1 -1
  31. package/dist/types/components/SidebarNav.d.ts.map +1 -1
  32. package/dist/types/components/TableOfContents.d.ts +1 -1
  33. package/dist/types/components/TableOfContents.d.ts.map +1 -1
  34. package/dist/types/components/ai/AIChatDialog.d.ts +1 -1
  35. package/dist/types/components/ai/AIChatDialog.d.ts.map +1 -1
  36. package/dist/types/components/ai/AISelectionTrigger.d.ts +1 -1
  37. package/dist/types/components/ai/AISelectionTrigger.d.ts.map +1 -1
  38. package/dist/types/components/ai/AISettingsPanel.d.ts +1 -1
  39. package/dist/types/components/ai/AISettingsPanel.d.ts.map +1 -1
  40. package/dist/types/components/search/SearchItem.d.ts +1 -2
  41. package/dist/types/components/search/SearchItem.d.ts.map +1 -1
  42. package/dist/types/components/ui/button.d.ts +4 -4
  43. package/dist/types/components/ui/button.d.ts.map +1 -1
  44. package/dist/types/components/ui/toast.d.ts +3 -3
  45. package/dist/types/components/ui/toast.d.ts.map +1 -1
  46. package/dist/types/external-modules.d.ts +38 -0
  47. package/dist/types/lib/ai/crypto.d.ts.map +1 -1
  48. package/dist/types/lib/component-scanner.d.ts.map +1 -1
  49. package/dist/types/lib/config.d.ts +0 -4
  50. package/dist/types/lib/config.d.ts.map +1 -1
  51. package/dist/types/lib/export-utils.d.ts.map +1 -1
  52. package/dist/types/lib/frontmatter.d.ts +6 -0
  53. package/dist/types/lib/frontmatter.d.ts.map +1 -0
  54. package/dist/types/lib/rehype-toc.d.ts.map +1 -1
  55. package/dist/types/lib/search/search-index-plugin.d.ts.map +1 -1
  56. package/dist/types/mdast.d.ts +53 -0
  57. package/dist/use-toast-d9VPBjMn.js +98 -0
  58. package/dist/utils-Ct96Mtjw.js +8 -0
  59. package/package.json +23 -24
  60. package/dist/AISettingsPanel-Cvu2lFDx.js +0 -1171
  61. package/dist/DocsApp-CodPVxvb.js +0 -35173
  62. package/dist/GlobalContextMenu-B-Yx5Wqe.js +0 -210
  63. package/dist/MdxContent-BdKn0Oni.js +0 -812
  64. package/dist/MdxContent.lazy-BrPJ1yjA.js +0 -1512
  65. package/dist/SearchDialog-Cyz5zTp6.js +0 -406
  66. package/dist/SearchRuntime-ClG0KzkU.js +0 -31
  67. package/dist/context-menu-C46tlmGf.js +0 -140
  68. package/dist/index-B8So8dyN.js +0 -12
@@ -0,0 +1,1066 @@
1
+ import { createContext as e, useCallback as t, useContext as n, useEffect as r, useRef as i, useState as a } from "react";
2
+ import { jsx as o } from "react/jsx-runtime";
3
+ //#region src/lib/ai/crypto.ts
4
+ var s = "ai-device-key";
5
+ function c(e) {
6
+ return e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength);
7
+ }
8
+ async function l() {
9
+ let e = [];
10
+ e.push(navigator.userAgent), e.push(navigator.language), e.push(navigator.platform), e.push(`${screen.width}x${screen.height}x${screen.colorDepth}`), e.push(Intl.DateTimeFormat().resolvedOptions().timeZone), e.push(String(navigator.hardwareConcurrency || 0));
11
+ let t = navigator;
12
+ return t.deviceMemory && e.push(String(t.deviceMemory)), e.push(String(navigator.maxTouchPoints || 0)), e.join("|");
13
+ }
14
+ async function u(e, t) {
15
+ let n = new TextEncoder(), r = await crypto.subtle.importKey("raw", n.encode(e), "PBKDF2", !1, ["deriveBits", "deriveKey"]);
16
+ return crypto.subtle.deriveKey({
17
+ name: "PBKDF2",
18
+ salt: c(t),
19
+ iterations: 1e5,
20
+ hash: "SHA-256"
21
+ }, r, {
22
+ name: "AES-GCM",
23
+ length: 256
24
+ }, !1, ["encrypt", "decrypt"]);
25
+ }
26
+ async function d() {
27
+ let e;
28
+ try {
29
+ e = localStorage.getItem(s) || "";
30
+ } catch {
31
+ e = "";
32
+ }
33
+ if (!e) {
34
+ e = `${await l()}:${Array.from(crypto.getRandomValues(new Uint8Array(16))).map((e) => e.toString(16).padStart(2, "0")).join("")}`;
35
+ try {
36
+ localStorage.setItem(s, e);
37
+ } catch {}
38
+ }
39
+ return e;
40
+ }
41
+ async function f(e) {
42
+ if (!e) return "";
43
+ try {
44
+ let t = await d(), n = crypto.getRandomValues(new Uint8Array(16)), r = crypto.getRandomValues(new Uint8Array(12)), i = await u(t, n), a = new TextEncoder(), o = await crypto.subtle.encrypt({
45
+ name: "AES-GCM",
46
+ iv: r
47
+ }, i, a.encode(e)), s = new Uint8Array(n.length + r.length + o.byteLength);
48
+ return s.set(n, 0), s.set(r, n.length), s.set(new Uint8Array(o), n.length + r.length), btoa(String.fromCharCode(...s));
49
+ } catch (e) {
50
+ throw console.error("Failed to encrypt API key:", e), Error("加密失败");
51
+ }
52
+ }
53
+ async function p(e) {
54
+ if (!e) return "";
55
+ try {
56
+ let t = await d(), n = new Uint8Array(atob(e).split("").map((e) => e.charCodeAt(0))), r = n.slice(0, 16), i = n.slice(16, 28), a = n.slice(28), o = await u(t, r), s = await crypto.subtle.decrypt({
57
+ name: "AES-GCM",
58
+ iv: i
59
+ }, o, a);
60
+ return new TextDecoder().decode(s);
61
+ } catch (e) {
62
+ return console.error("Failed to decrypt API key:", e), "";
63
+ }
64
+ }
65
+ function m() {
66
+ return typeof crypto < "u" && crypto.subtle !== void 0;
67
+ }
68
+ function h(e) {
69
+ if (!e) return "";
70
+ let t = [];
71
+ for (let n = 0; n < e.length; n++) t.push(e.charCodeAt(n) ^ "react-docs-ui-ai-key".charCodeAt(n % 20));
72
+ return btoa(String.fromCharCode(...t));
73
+ }
74
+ function g(e) {
75
+ if (!e) return "";
76
+ try {
77
+ let t = atob(e), n = [];
78
+ for (let e = 0; e < t.length; e++) n.push(String.fromCharCode(t.charCodeAt(e) ^ "react-docs-ui-ai-key".charCodeAt(e % 20)));
79
+ return n.join("");
80
+ } catch {
81
+ return "";
82
+ }
83
+ }
84
+ //#endregion
85
+ //#region src/lib/ai/config.ts
86
+ var _ = "ai-config", v = [
87
+ "openai",
88
+ "claude",
89
+ "gemini"
90
+ ], y = (e) => v.includes(e);
91
+ function b(e) {
92
+ return {
93
+ openai: {
94
+ modelId: "gpt-4o-mini",
95
+ apiKey: "",
96
+ baseUrl: "https://api.openai.com/v1",
97
+ maxTokens: 4096,
98
+ temperature: .7,
99
+ enabled: !0
100
+ },
101
+ claude: {
102
+ modelId: "claude-3-5-sonnet-20241022",
103
+ apiKey: "",
104
+ baseUrl: "https://api.anthropic.com",
105
+ maxTokens: 4096,
106
+ temperature: .7,
107
+ enabled: !0
108
+ },
109
+ gemini: {
110
+ modelId: "gemini-1.5-flash",
111
+ apiKey: "",
112
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta",
113
+ maxTokens: 4096,
114
+ temperature: .7,
115
+ enabled: !0
116
+ }
117
+ }[e] || {
118
+ modelId: "",
119
+ apiKey: "",
120
+ baseUrl: "",
121
+ maxTokens: 4096,
122
+ temperature: .7,
123
+ enabled: !0
124
+ };
125
+ }
126
+ function x() {
127
+ return {
128
+ enabled: !0,
129
+ provider: "openai",
130
+ systemPrompt: "你是一个专业的文档助手,请根据用户的问题,提供准确、有帮助的回答。",
131
+ models: {
132
+ openai: b("openai"),
133
+ claude: b("claude"),
134
+ gemini: b("gemini")
135
+ },
136
+ features: {
137
+ chatAssistant: !0,
138
+ documentSummary: !0,
139
+ codeExplanation: !0,
140
+ searchEnhancement: !1
141
+ },
142
+ ui: {
143
+ position: "bottom-right",
144
+ theme: "auto",
145
+ size: "medium"
146
+ }
147
+ };
148
+ }
149
+ async function S(e) {
150
+ if (!e.apiKey) return e;
151
+ let t = m() ? await f(e.apiKey) : h(e.apiKey);
152
+ return {
153
+ ...e,
154
+ apiKey: t
155
+ };
156
+ }
157
+ async function C(e) {
158
+ if (!e.apiKey) return e;
159
+ let t = m() ? await p(e.apiKey) : g(e.apiKey);
160
+ return {
161
+ ...e,
162
+ apiKey: t
163
+ };
164
+ }
165
+ async function w(e) {
166
+ try {
167
+ let t = Object.entries(e.models), n = await Promise.all(t.map(async ([e, t]) => [e, await S(t)])), r = Object.fromEntries(n), i = {
168
+ ...e,
169
+ models: r
170
+ };
171
+ localStorage.setItem(_, JSON.stringify(i));
172
+ } catch (e) {
173
+ throw console.error("Failed to save AI config:", e), Error("保存配置失败");
174
+ }
175
+ }
176
+ async function T() {
177
+ try {
178
+ let e = localStorage.getItem(_);
179
+ if (!e) return null;
180
+ let t = JSON.parse(e), n = Object.entries(t.models), r = await Promise.all(n.map(async ([e, t]) => [e, await C(t)])), i = Object.fromEntries(r), a = {
181
+ enabled: t.enabled !== !1,
182
+ provider: t.provider || "openai",
183
+ systemPrompt: t.systemPrompt || "",
184
+ models: i,
185
+ features: t.features || {
186
+ chatAssistant: !0,
187
+ documentSummary: !0,
188
+ codeExplanation: !0,
189
+ searchEnhancement: !1
190
+ },
191
+ ui: t.ui || {
192
+ position: "bottom-right",
193
+ theme: "auto",
194
+ size: "medium"
195
+ }
196
+ };
197
+ if (!a.models[a.provider]?.apiKey) {
198
+ let e = Object.keys(a.models).find((e) => a.models[e]?.apiKey);
199
+ e && (a.provider = e, await w(a));
200
+ }
201
+ return a;
202
+ } catch (e) {
203
+ return console.error("Failed to read AI config:", e), null;
204
+ }
205
+ }
206
+ async function E() {
207
+ let e = await T();
208
+ if (!e || !e.enabled) return !1;
209
+ let t = e.models[e.provider];
210
+ return !!(t?.apiKey && t?.modelId);
211
+ }
212
+ async function D(e) {
213
+ let t = e || await T();
214
+ return t && t.models[t.provider] || null;
215
+ }
216
+ async function O(e, t) {
217
+ let n = await T() || x();
218
+ n.models[e] = {
219
+ ...n.models[e],
220
+ ...t
221
+ }, await w(n);
222
+ }
223
+ async function k(e) {
224
+ let t = await T();
225
+ if (!t) return;
226
+ if (y(e)) throw Error("不能删除预定义的 Provider");
227
+ let n = { ...t.models };
228
+ delete n[e];
229
+ let r = t.provider === e ? "openai" : t.provider;
230
+ await w({
231
+ ...t,
232
+ models: n,
233
+ provider: r
234
+ });
235
+ }
236
+ function A() {
237
+ try {
238
+ localStorage.removeItem(_);
239
+ } catch (e) {
240
+ console.error("Failed to clear AI config:", e);
241
+ }
242
+ }
243
+ function j(e) {
244
+ if (!e.apiKey) return {
245
+ valid: !1,
246
+ message: "API密钥不能为空"
247
+ };
248
+ if (!e.modelId) return {
249
+ valid: !1,
250
+ message: "模型ID不能为空"
251
+ };
252
+ if (!e.baseUrl) return {
253
+ valid: !1,
254
+ message: "API地址不能为空"
255
+ };
256
+ try {
257
+ new URL(e.baseUrl);
258
+ } catch {
259
+ return {
260
+ valid: !1,
261
+ message: "API地址格式不正确"
262
+ };
263
+ }
264
+ return {
265
+ valid: !0,
266
+ message: ""
267
+ };
268
+ }
269
+ async function M() {
270
+ let e = await T();
271
+ if (!e) return [...v];
272
+ let t = Object.keys(e.models).filter((e) => !y(e));
273
+ return [...v, ...t];
274
+ }
275
+ //#endregion
276
+ //#region src/lib/ai/providers/base.ts
277
+ var N = class {
278
+ config;
279
+ constructor(e) {
280
+ this.config = e;
281
+ }
282
+ validateConfig() {
283
+ return !!(this.config.apiKey && this.config.baseUrl && this.config.modelId);
284
+ }
285
+ updateConfig(e) {
286
+ this.config = {
287
+ ...this.config,
288
+ ...e
289
+ };
290
+ }
291
+ generateId() {
292
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
293
+ }
294
+ formatMessages(e) {
295
+ return e.filter((e) => e.role !== "system").map((e) => ({
296
+ role: e.role,
297
+ content: e.content
298
+ }));
299
+ }
300
+ async makeRequest(e, t, n, r) {
301
+ let i = await fetch(e, {
302
+ method: "POST",
303
+ headers: n,
304
+ body: JSON.stringify(t),
305
+ signal: r
306
+ });
307
+ if (!i.ok) {
308
+ let e = await i.text(), t = `HTTP ${i.status}`;
309
+ try {
310
+ let n = JSON.parse(e);
311
+ t = n.error?.message || n.message || t;
312
+ } catch {}
313
+ throw Error(t);
314
+ }
315
+ return i;
316
+ }
317
+ async readStream(e, t, n) {
318
+ let r = e.body?.getReader();
319
+ if (!r) throw Error("无法读取响应流");
320
+ let i = new TextDecoder(), a = "";
321
+ try {
322
+ for (;;) {
323
+ let { done: e, value: t } = await r.read();
324
+ if (e) {
325
+ a.trim() && n(a);
326
+ break;
327
+ }
328
+ a += i.decode(t, { stream: !0 });
329
+ let o = a.split("\n");
330
+ a = o.pop() || "";
331
+ for (let e of o) e.trim() && n(e);
332
+ }
333
+ } finally {
334
+ r.releaseLock();
335
+ }
336
+ }
337
+ }, P = class extends N {
338
+ async chat(e, t, n) {
339
+ let r = `${this.config.baseUrl}/chat/completions`, i = this.buildHeaders(), a = this.buildRequestBody(e);
340
+ t({ type: "start" });
341
+ try {
342
+ let e = await this.makeRequest(r, a, i, n);
343
+ await this.readStream(e, t, (e) => {
344
+ this.parseStreamChunk(e, t);
345
+ }), t({ type: "done" });
346
+ } catch (e) {
347
+ e instanceof Error && e.name === "AbortError" ? t({
348
+ type: "error",
349
+ error: "请求已取消"
350
+ }) : t({
351
+ type: "error",
352
+ error: e instanceof Error ? e.message : "未知错误"
353
+ });
354
+ }
355
+ }
356
+ async testConnection() {
357
+ try {
358
+ let e = `${this.config.baseUrl}/models`, t = this.buildHeaders(), n = await fetch(e, {
359
+ method: "GET",
360
+ headers: t
361
+ });
362
+ if (!n.ok) {
363
+ let e = await n.text(), t = `HTTP ${n.status}`;
364
+ try {
365
+ let n = JSON.parse(e);
366
+ t = n.error?.message || n.message || t;
367
+ } catch {}
368
+ return {
369
+ success: !1,
370
+ message: t
371
+ };
372
+ }
373
+ let r = await n.json(), i = (r.data || r.models || []).find((e) => e.id === this.config.modelId || e.id?.includes(this.config.modelId));
374
+ return {
375
+ success: !0,
376
+ message: "连接成功",
377
+ modelInfo: {
378
+ id: this.config.modelId,
379
+ name: i?.id || this.config.modelId
380
+ }
381
+ };
382
+ } catch (e) {
383
+ return {
384
+ success: !1,
385
+ message: e instanceof Error ? e.message : "连接失败"
386
+ };
387
+ }
388
+ }
389
+ buildHeaders() {
390
+ return {
391
+ "Content-Type": "application/json",
392
+ Authorization: `Bearer ${this.config.apiKey}`
393
+ };
394
+ }
395
+ buildRequestBody(e) {
396
+ let t = [];
397
+ this.config.systemPrompt && t.push({
398
+ role: "system",
399
+ content: this.config.systemPrompt
400
+ });
401
+ for (let n of e) n.role !== "system" && t.push({
402
+ role: n.role,
403
+ content: n.content
404
+ });
405
+ return {
406
+ model: this.config.modelId,
407
+ messages: t,
408
+ max_tokens: this.config.maxTokens,
409
+ temperature: this.config.temperature,
410
+ stream: !0
411
+ };
412
+ }
413
+ parseStreamChunk(e, t) {
414
+ let n = e.split("\n");
415
+ for (let e of n) {
416
+ let n = e.trim();
417
+ if (!n || !n.startsWith("data: ")) continue;
418
+ let r = n.slice(6);
419
+ if (r === "[DONE]") {
420
+ t({ type: "done" });
421
+ return;
422
+ }
423
+ try {
424
+ let e = JSON.parse(r), n = e.choices?.[0]?.delta?.content;
425
+ n && t({
426
+ type: "delta",
427
+ content: n
428
+ }), e.choices?.[0]?.finish_reason === "stop" && t({ type: "done" });
429
+ } catch {}
430
+ }
431
+ }
432
+ }, F = class extends N {
433
+ async chat(e, t, n) {
434
+ let r = `${this.config.baseUrl}/v1/messages`, i = this.buildHeaders(), a = this.buildRequestBody(e);
435
+ t({ type: "start" });
436
+ try {
437
+ let e = await this.makeRequest(r, a, i, n);
438
+ await this.readStream(e, t, (e) => {
439
+ this.parseStreamChunk(e, t);
440
+ }), t({ type: "done" });
441
+ } catch (e) {
442
+ e instanceof Error && e.name === "AbortError" ? t({
443
+ type: "error",
444
+ error: "请求已取消"
445
+ }) : t({
446
+ type: "error",
447
+ error: e instanceof Error ? e.message : "未知错误"
448
+ });
449
+ }
450
+ }
451
+ async testConnection() {
452
+ try {
453
+ let e = `${this.config.baseUrl}/v1/messages`, t = this.buildHeaders(), n = await fetch(e, {
454
+ method: "POST",
455
+ headers: t,
456
+ body: JSON.stringify({
457
+ model: this.config.modelId,
458
+ max_tokens: 1,
459
+ messages: [{
460
+ role: "user",
461
+ content: "hi"
462
+ }]
463
+ })
464
+ });
465
+ if (!n.ok) {
466
+ let e = await n.text(), t = `HTTP ${n.status}`;
467
+ try {
468
+ let n = JSON.parse(e);
469
+ t = n.error?.message || n.message || t;
470
+ } catch {}
471
+ return {
472
+ success: !1,
473
+ message: t
474
+ };
475
+ }
476
+ return {
477
+ success: !0,
478
+ message: "连接成功",
479
+ modelInfo: { id: this.config.modelId }
480
+ };
481
+ } catch (e) {
482
+ return {
483
+ success: !1,
484
+ message: e instanceof Error ? e.message : "连接失败"
485
+ };
486
+ }
487
+ }
488
+ buildHeaders() {
489
+ return {
490
+ "Content-Type": "application/json",
491
+ "x-api-key": this.config.apiKey,
492
+ "anthropic-version": "2023-06-01",
493
+ "anthropic-dangerous-direct-browser-access": "true"
494
+ };
495
+ }
496
+ buildRequestBody(e) {
497
+ let t = [];
498
+ for (let n of e) n.role !== "system" && t.push({
499
+ role: n.role,
500
+ content: n.content
501
+ });
502
+ (t.length === 0 || t[t.length - 1].role !== "user") && t.push({
503
+ role: "user",
504
+ content: "请继续"
505
+ });
506
+ let n = {
507
+ model: this.config.modelId,
508
+ messages: t,
509
+ max_tokens: this.config.maxTokens || 4096,
510
+ stream: !0
511
+ };
512
+ return this.config.systemPrompt && (n.system = this.config.systemPrompt), this.config.temperature !== void 0 && (n.temperature = this.config.temperature), n;
513
+ }
514
+ parseStreamChunk(e, t) {
515
+ let n = e.split("\n");
516
+ for (let e of n) {
517
+ let n = e.trim();
518
+ if (!n || !n.startsWith("data: ")) continue;
519
+ let r = n.slice(6);
520
+ try {
521
+ let e = JSON.parse(r);
522
+ switch (e.type) {
523
+ case "content_block_delta":
524
+ e.delta?.type === "text_delta" && e.delta.text && t({
525
+ type: "delta",
526
+ content: e.delta.text
527
+ });
528
+ break;
529
+ case "message_delta": break;
530
+ case "message_start": break;
531
+ case "message_stop":
532
+ t({ type: "done" });
533
+ break;
534
+ case "content_block_stop": break;
535
+ case "error":
536
+ e.error && t({
537
+ type: "error",
538
+ error: e.error.message || "API错误"
539
+ });
540
+ break;
541
+ case "ping": break;
542
+ }
543
+ } catch {}
544
+ }
545
+ }
546
+ }, I = class extends N {
547
+ async chat(e, t, n) {
548
+ let r = this.buildStreamUrl(), i = this.buildHeaders(), a = this.buildRequestBody(e);
549
+ t({ type: "start" });
550
+ try {
551
+ let e = await this.makeRequest(r, a, i, n);
552
+ await this.readStream(e, t, (e) => {
553
+ this.parseStreamChunk(e, t);
554
+ }), t({ type: "done" });
555
+ } catch (e) {
556
+ e instanceof Error && e.name === "AbortError" ? t({
557
+ type: "error",
558
+ error: "请求已取消"
559
+ }) : t({
560
+ type: "error",
561
+ error: e instanceof Error ? e.message : "未知错误"
562
+ });
563
+ }
564
+ }
565
+ async testConnection() {
566
+ try {
567
+ let e = this.buildGenerateContentUrl(), t = await fetch(e, {
568
+ method: "POST",
569
+ headers: this.buildHeaders(),
570
+ body: JSON.stringify({
571
+ contents: [{
572
+ parts: [{ text: "hi" }],
573
+ role: "user"
574
+ }],
575
+ generationConfig: { maxOutputTokens: 1 }
576
+ })
577
+ });
578
+ if (!t.ok) {
579
+ let e = await t.text(), n = `HTTP ${t.status}`;
580
+ try {
581
+ n = JSON.parse(e).error?.message || n;
582
+ } catch {}
583
+ return {
584
+ success: !1,
585
+ message: n
586
+ };
587
+ }
588
+ return {
589
+ success: !0,
590
+ message: "连接成功",
591
+ modelInfo: { id: this.config.modelId }
592
+ };
593
+ } catch (e) {
594
+ return {
595
+ success: !1,
596
+ message: e instanceof Error ? e.message : "连接失败"
597
+ };
598
+ }
599
+ }
600
+ buildStreamUrl() {
601
+ return `${this.config.baseUrl}/models/${this.config.modelId}:streamGenerateContent?alt=sse&key=${this.config.apiKey}`;
602
+ }
603
+ buildGenerateContentUrl() {
604
+ return `${this.config.baseUrl}/models/${this.config.modelId}:generateContent?key=${this.config.apiKey}`;
605
+ }
606
+ buildHeaders() {
607
+ return { "Content-Type": "application/json" };
608
+ }
609
+ buildRequestBody(e) {
610
+ let t = [];
611
+ for (let n of e) n.role !== "system" && t.push({
612
+ role: n.role === "assistant" ? "model" : "user",
613
+ parts: [{ text: n.content }]
614
+ });
615
+ let n = {
616
+ contents: t,
617
+ generationConfig: {
618
+ maxOutputTokens: this.config.maxTokens,
619
+ temperature: this.config.temperature
620
+ }
621
+ };
622
+ return this.config.systemPrompt && (n.systemInstruction = { parts: [{ text: this.config.systemPrompt }] }), n;
623
+ }
624
+ parseStreamChunk(e, t) {
625
+ let n = e.split("\n");
626
+ for (let e of n) {
627
+ let n = e.trim();
628
+ if (!n || !n.startsWith("data: ")) continue;
629
+ let r = n.slice(6);
630
+ try {
631
+ let e = JSON.parse(r);
632
+ if (e.error) {
633
+ t({
634
+ type: "error",
635
+ error: e.error.message || "API错误"
636
+ });
637
+ return;
638
+ }
639
+ if (e.candidates && e.candidates.length > 0) {
640
+ let n = e.candidates[0];
641
+ if (n.content?.parts) for (let e of n.content.parts) e.text && t({
642
+ type: "delta",
643
+ content: e.text
644
+ });
645
+ (n.finishReason === "STOP" || n.finishReason === "MAX_TOKENS") && t({ type: "done" });
646
+ }
647
+ } catch {}
648
+ }
649
+ }
650
+ }, L = [
651
+ "openai",
652
+ "claude",
653
+ "gemini"
654
+ ], R = (e) => L.includes(e);
655
+ function z(e, t) {
656
+ if (R(e)) switch (e) {
657
+ case "openai": return new P(t);
658
+ case "claude": return new F(t);
659
+ case "gemini": return new I(t);
660
+ }
661
+ return new P(t);
662
+ }
663
+ function B(e) {
664
+ if (!e) return "Unknown";
665
+ let t = {
666
+ openai: "OpenAI",
667
+ claude: "Claude (Anthropic)",
668
+ gemini: "Google Gemini"
669
+ };
670
+ return e in t ? t[e] : e.charAt(0).toUpperCase() + e.slice(1);
671
+ }
672
+ function V(e) {
673
+ if (!e) return [];
674
+ let t = {
675
+ openai: [
676
+ "gpt-4o",
677
+ "gpt-4o-mini",
678
+ "gpt-4-turbo",
679
+ "gpt-4",
680
+ "gpt-3.5-turbo"
681
+ ],
682
+ claude: [
683
+ "claude-3-5-sonnet-20241022",
684
+ "claude-3-5-haiku-20241022",
685
+ "claude-3-opus-20240229",
686
+ "claude-3-sonnet-20240229",
687
+ "claude-3-haiku-20240307"
688
+ ],
689
+ gemini: [
690
+ "gemini-1.5-pro",
691
+ "gemini-1.5-flash",
692
+ "gemini-1.5-flash-8b",
693
+ "gemini-1.0-pro"
694
+ ]
695
+ };
696
+ return e in t ? t[e] : [];
697
+ }
698
+ function H(e) {
699
+ if (!e) return {
700
+ apiKeyUrl: "",
701
+ docsUrl: ""
702
+ };
703
+ let t = {
704
+ openai: {
705
+ apiKeyUrl: "https://platform.openai.com/api-keys",
706
+ docsUrl: "https://platform.openai.com/docs"
707
+ },
708
+ claude: {
709
+ apiKeyUrl: "https://console.anthropic.com/settings/keys",
710
+ docsUrl: "https://docs.anthropic.com"
711
+ },
712
+ gemini: {
713
+ apiKeyUrl: "https://aistudio.google.com/apikey",
714
+ docsUrl: "https://ai.google.dev/docs"
715
+ }
716
+ };
717
+ return e in t ? t[e] : {
718
+ apiKeyUrl: "",
719
+ docsUrl: ""
720
+ };
721
+ }
722
+ //#endregion
723
+ //#region src/lib/ai/chat.ts
724
+ var U = 6e4, W = 3, G = 1e3;
725
+ async function ee(e) {
726
+ return new Promise((t) => setTimeout(t, e));
727
+ }
728
+ function te(e) {
729
+ let t = [
730
+ "network",
731
+ "timeout",
732
+ "ECONNREFUSED",
733
+ "ETIMEDOUT",
734
+ "ENOTFOUND",
735
+ "rate limit",
736
+ "429",
737
+ "503",
738
+ "502",
739
+ "500"
740
+ ], n = e.message.toLowerCase();
741
+ return t.some((e) => n.includes(e.toLowerCase()));
742
+ }
743
+ function ne(e, t) {
744
+ if (!t) return e;
745
+ let n = [];
746
+ return t.selectedText && n.push(`【选中的内容】\n${t.selectedText}\n`), t.pageTitle && n.push(`【当前页面】${t.pageTitle}`), n.push(e), n.join("\n");
747
+ }
748
+ function re(e, t) {
749
+ if (!t) return e;
750
+ let n = [e];
751
+ return t.selectedText && n.push("\n用户选中了文章中的部分内容,请基于选中的内容回答问题。"), t.extraContext && n.push(`\n${t.extraContext}`), n.join("");
752
+ }
753
+ async function K(e, t, n, r, i, a) {
754
+ let o = a?.timeout ?? U, s = a?.retryCount ?? W, c = a?.retryDelay ?? G, l = n || await T();
755
+ if (!l || !l.enabled) {
756
+ t({
757
+ type: "error",
758
+ error: "AI功能未启用"
759
+ });
760
+ return;
761
+ }
762
+ let u = await D(l);
763
+ if (!u?.apiKey) {
764
+ t({
765
+ type: "error",
766
+ error: "请先配置API密钥"
767
+ });
768
+ return;
769
+ }
770
+ let d = {
771
+ type: l.provider,
772
+ apiKey: u.apiKey,
773
+ baseUrl: u.baseUrl,
774
+ modelId: u.modelId,
775
+ systemPrompt: re(l.systemPrompt, r),
776
+ maxTokens: u.maxTokens,
777
+ temperature: u.temperature
778
+ }, f = z(l.provider, d), p = null;
779
+ for (let n = 0; n < s; n++) {
780
+ if (i?.aborted) {
781
+ t({
782
+ type: "error",
783
+ error: "请求已取消"
784
+ });
785
+ return;
786
+ }
787
+ try {
788
+ let n = setTimeout(() => {
789
+ t({
790
+ type: "error",
791
+ error: `请求超时(${o / 1e3}秒)`
792
+ });
793
+ }, o);
794
+ await f.chat(e, t, i), clearTimeout(n);
795
+ return;
796
+ } catch (e) {
797
+ if (p = e instanceof Error ? e : /* @__PURE__ */ Error("未知错误"), i?.aborted) {
798
+ t({
799
+ type: "error",
800
+ error: "请求已取消"
801
+ });
802
+ return;
803
+ }
804
+ if (!te(p)) {
805
+ t({
806
+ type: "error",
807
+ error: p.message
808
+ });
809
+ return;
810
+ }
811
+ n < s - 1 && (t({
812
+ type: "error",
813
+ error: `连接失败,${c / 1e3}秒后重试 (${n + 1}/${s})...`
814
+ }), await ee(c * (n + 1)));
815
+ }
816
+ }
817
+ t({
818
+ type: "error",
819
+ error: p?.message || "请求失败,请稍后重试"
820
+ });
821
+ }
822
+ async function ie(e, t, n) {
823
+ return t.apiKey ? z(e, {
824
+ type: e,
825
+ apiKey: t.apiKey,
826
+ baseUrl: t.baseUrl,
827
+ modelId: t.modelId,
828
+ systemPrompt: n || "",
829
+ maxTokens: t.maxTokens,
830
+ temperature: t.temperature
831
+ }).testConnection() : {
832
+ success: !1,
833
+ message: "API密钥不能为空"
834
+ };
835
+ }
836
+ function q() {
837
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
838
+ }
839
+ function J(e) {
840
+ return {
841
+ id: q(),
842
+ role: "user",
843
+ content: e,
844
+ timestamp: Date.now()
845
+ };
846
+ }
847
+ function Y(e = "") {
848
+ return {
849
+ id: q(),
850
+ role: "assistant",
851
+ content: e,
852
+ timestamp: Date.now(),
853
+ isStreaming: !0
854
+ };
855
+ }
856
+ function X(e, t, n = !1, r) {
857
+ return {
858
+ ...e,
859
+ content: t,
860
+ isStreaming: n,
861
+ error: r
862
+ };
863
+ }
864
+ //#endregion
865
+ //#region src/components/ai/AIProvider.tsx
866
+ var Z = "ai-current-session", ae = 6e4, Q = e(null), $ = null;
867
+ async function oe() {
868
+ let e = Date.now();
869
+ if ($ && e - $.timestamp < ae) return $.config;
870
+ let t = await T();
871
+ return $ = {
872
+ config: t,
873
+ timestamp: e
874
+ }, t;
875
+ }
876
+ function se() {
877
+ $ = null;
878
+ }
879
+ function ce({ children: e }) {
880
+ let [n, s] = a(null), [c, l] = a(!1), [u, d] = a(!1), [f, p] = a(null), [m, h] = a(null), [g, _] = a([]), [v, y] = a(""), [b, x] = a(), [S, C] = a(!1), [T, D] = a(!1), O = i(null);
881
+ r(() => {
882
+ let e = !1;
883
+ return (async () => {
884
+ try {
885
+ let t = await oe();
886
+ if (e) return;
887
+ s(t);
888
+ let n = await E();
889
+ if (e) return;
890
+ l(n);
891
+ let r = localStorage.getItem(Z);
892
+ if (r && !e) try {
893
+ let e = JSON.parse(r);
894
+ h(e), _(e.messages);
895
+ } catch {
896
+ console.error("[AI] Failed to load session");
897
+ }
898
+ } catch (e) {
899
+ console.error("[AI] 加载 AI 配置失败:", e);
900
+ }
901
+ })(), () => {
902
+ e = !0;
903
+ };
904
+ }, []);
905
+ let k = t((e) => {
906
+ try {
907
+ let t = m || {
908
+ id: `session_${Date.now()}`,
909
+ title: "新对话",
910
+ messages: [],
911
+ createdAt: Date.now(),
912
+ updatedAt: Date.now()
913
+ }, n = {
914
+ ...t,
915
+ messages: e,
916
+ updatedAt: Date.now(),
917
+ title: e[0]?.content?.slice(0, 50) || t.title
918
+ };
919
+ h(n), localStorage.setItem(Z, JSON.stringify(n));
920
+ } catch {
921
+ console.error("[AI] Failed to save session");
922
+ }
923
+ }, [m]), A = t((e) => {
924
+ e && (x(e), y(e.selectedText || "")), C(!0);
925
+ }, []), j = t(() => {
926
+ C(!1);
927
+ }, []), M = t(() => {
928
+ D(!0);
929
+ }, []), N = t(() => {
930
+ D(!1);
931
+ }, []), P = t(async (e, t) => {
932
+ if (!n || u) return;
933
+ let r = Y();
934
+ _([...e, r]), d(!0), p(null), O.current = new AbortController();
935
+ let i = "";
936
+ try {
937
+ await K(e, (e) => {
938
+ switch (e.type) {
939
+ case "delta":
940
+ e.content && (i += e.content, _((e) => {
941
+ let t = [...e], n = t.length - 1;
942
+ return n >= 0 && t[n].role === "assistant" && (t[n] = X(t[n], i, !0)), t;
943
+ }));
944
+ break;
945
+ case "done":
946
+ _((e) => {
947
+ let t = [...e], n = t.length - 1;
948
+ return n >= 0 && t[n].role === "assistant" && (t[n] = X(t[n], i, !1)), k(t), t;
949
+ }), t?.();
950
+ break;
951
+ case "error":
952
+ e.error === "请求已取消" ? _((e) => {
953
+ let t = [...e], n = t.length - 1;
954
+ if (n >= 0 && t[n].role === "assistant") {
955
+ let e = t[n].content, r = e.match(/```/g);
956
+ r && r.length % 2 != 0 && (e += "\n```"), t[n] = X(t[n], e, !1);
957
+ }
958
+ return k(t), t;
959
+ }) : (p(e.error || "发生错误"), _((t) => {
960
+ let n = [...t], r = n.length - 1;
961
+ return r >= 0 && n[r].role === "assistant" && (n[r] = X(n[r], i, !1, e.error)), n;
962
+ }));
963
+ break;
964
+ }
965
+ }, n ?? void 0, b, O.current.signal);
966
+ } catch (e) {
967
+ p(e instanceof Error ? e.message : "发送失败");
968
+ } finally {
969
+ d(!1), O.current = null;
970
+ }
971
+ }, [
972
+ n,
973
+ u,
974
+ b,
975
+ k
976
+ ]), F = t(async (e) => {
977
+ if (!n || u) return;
978
+ let t = J(ne(e, b));
979
+ await P(g.concat(t));
980
+ }, [
981
+ n,
982
+ g,
983
+ u,
984
+ b,
985
+ P
986
+ ]), I = t(async () => {
987
+ if (g.length < 2 || u) return;
988
+ let e = g.slice(0, -1);
989
+ _(e), e.filter((e) => e.role === "user").pop() && await P(e);
990
+ }, [
991
+ g,
992
+ u,
993
+ P
994
+ ]), L = t(() => {
995
+ _([]), h(null), localStorage.removeItem(Z);
996
+ }, []), R = t(async (e) => {
997
+ let t = n ? {
998
+ ...n,
999
+ ...e
1000
+ } : { ...e };
1001
+ e.models && n?.models && (t.models = {
1002
+ ...n.models,
1003
+ ...e.models
1004
+ }), await w(t), se(), s(t), l(await E());
1005
+ }, [n]), z = t(() => {
1006
+ O.current &&= (O.current.abort(), null);
1007
+ }, []), B = {
1008
+ config: n,
1009
+ isConfigured: c,
1010
+ isLoading: u,
1011
+ error: f,
1012
+ currentSession: m,
1013
+ messages: g,
1014
+ selectedText: v,
1015
+ setSelectedText: y,
1016
+ isDialogOpen: S,
1017
+ openDialog: A,
1018
+ closeDialog: j,
1019
+ isSettingsOpen: T,
1020
+ openSettings: M,
1021
+ closeSettings: N,
1022
+ sendMessage: F,
1023
+ regenerateLast: I,
1024
+ clearHistory: L,
1025
+ updateConfig: R,
1026
+ switchProvider: t(async (e) => {
1027
+ await R({ provider: e });
1028
+ }, [R]),
1029
+ stopGeneration: z,
1030
+ editMessage: t((e, t) => {
1031
+ _((n) => {
1032
+ let r = n.findIndex((t) => t.id === e);
1033
+ if (r === -1) return n;
1034
+ let i = [...n];
1035
+ if (i[r] = {
1036
+ ...i[r],
1037
+ content: t,
1038
+ timestamp: Date.now()
1039
+ }, i[r].role === "user") {
1040
+ let e = i.slice(0, r + 1);
1041
+ return k(e), e;
1042
+ }
1043
+ return k(i), i;
1044
+ });
1045
+ }, [k]),
1046
+ deleteMessage: t((e) => {
1047
+ _((t) => {
1048
+ let n = t.findIndex((t) => t.id === e);
1049
+ if (n === -1) return t;
1050
+ let r = t.slice(0, n);
1051
+ return k(r), r;
1052
+ });
1053
+ }, [k])
1054
+ };
1055
+ return /* @__PURE__ */ o(Q.Provider, {
1056
+ value: B,
1057
+ children: e
1058
+ });
1059
+ }
1060
+ function le() {
1061
+ let e = n(Q);
1062
+ if (!e) throw Error("useAI must be used within an AIProvider");
1063
+ return e;
1064
+ }
1065
+ //#endregion
1066
+ export { j as S, x as _, q as a, w as b, z as c, H as d, A as f, D as g, M as h, J as i, V as l, T as m, le as n, K as o, k as p, Y as r, ie as s, ce as t, B as u, b as v, O as x, E as y };