@astro-minimax/ai 0.9.0 → 0.9.3
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/README.md +108 -18
- package/dist/cache/global-cache.d.ts +6 -2
- package/dist/cache/global-cache.d.ts.map +1 -1
- package/dist/cache/global-cache.js +24 -9
- package/dist/cache/index.d.ts +7 -6
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +12 -4
- package/dist/cache/injection-cache.d.ts +36 -0
- package/dist/cache/injection-cache.d.ts.map +1 -0
- package/dist/cache/injection-cache.js +90 -0
- package/dist/cache/kv-adapter.d.ts.map +1 -1
- package/dist/cache/kv-adapter.js +2 -1
- package/dist/cache/memory-adapter.d.ts.map +1 -1
- package/dist/cache/memory-adapter.js +2 -1
- package/dist/cache/response-cache.d.ts +10 -5
- package/dist/cache/response-cache.d.ts.map +1 -1
- package/dist/cache/response-cache.js +18 -6
- package/dist/components/AIChatContainer.d.ts +2 -2
- package/dist/components/AIChatContainer.d.ts.map +1 -1
- package/dist/components/AIChatContainer.js +8 -920
- package/dist/components/ChatInput.d.ts +15 -0
- package/dist/components/ChatInput.d.ts.map +1 -0
- package/dist/components/ChatInput.js +72 -0
- package/dist/components/ChatPanel.d.ts +1 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +210 -672
- package/dist/components/CodeBlock.d.ts +31 -0
- package/dist/components/CodeBlock.d.ts.map +1 -0
- package/dist/components/CodeBlock.js +143 -0
- package/dist/components/MarkmapBlock.d.ts +4 -0
- package/dist/components/MarkmapBlock.d.ts.map +1 -0
- package/dist/components/MarkmapBlock.js +180 -0
- package/dist/components/MermaidBlock.d.ts +4 -0
- package/dist/components/MermaidBlock.d.ts.map +1 -0
- package/dist/components/MermaidBlock.js +193 -0
- package/dist/components/MessageBubble.d.ts +21 -0
- package/dist/components/MessageBubble.d.ts.map +1 -0
- package/dist/components/MessageBubble.js +233 -0
- package/dist/components/ReasoningBlock.d.ts +6 -0
- package/dist/components/ReasoningBlock.d.ts.map +1 -0
- package/dist/components/ReasoningBlock.js +11 -0
- package/dist/components/RichText.d.ts +41 -0
- package/dist/components/RichText.d.ts.map +1 -0
- package/dist/components/RichText.js +202 -0
- package/dist/components/VizShared.d.ts +57 -0
- package/dist/components/VizShared.d.ts.map +1 -0
- package/dist/components/VizShared.js +233 -0
- package/dist/components/tool-auto-continue.d.ts +5 -0
- package/dist/components/tool-auto-continue.d.ts.map +1 -0
- package/dist/components/tool-auto-continue.js +33 -0
- package/dist/constants.d.ts +61 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +72 -0
- package/dist/data/index.d.ts +4 -3
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +4 -10
- package/dist/data/knowledge-types.d.ts +8 -0
- package/dist/data/knowledge-types.d.ts.map +1 -0
- package/dist/data/knowledge-types.js +14 -0
- package/dist/data/metadata-loader.d.ts +4 -28
- package/dist/data/metadata-loader.d.ts.map +1 -1
- package/dist/data/metadata-loader.js +11 -34
- package/dist/data/types.d.ts +17 -2
- package/dist/data/types.d.ts.map +1 -1
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +24 -0
- package/dist/extensions/injector.d.ts +14 -0
- package/dist/extensions/injector.d.ts.map +1 -0
- package/dist/extensions/injector.js +146 -0
- package/dist/extensions/loader.d.ts +5 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +45 -0
- package/dist/extensions/registry.d.ts +4 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +144 -0
- package/dist/extensions/types.d.ts +126 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +0 -0
- package/dist/fact-registry/prompt-injector.d.ts +1 -1
- package/dist/fact-registry/prompt-injector.d.ts.map +1 -1
- package/dist/fact-registry/prompt-injector.js +2 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/intelligence/citation-guard.d.ts +2 -13
- package/dist/intelligence/citation-guard.d.ts.map +1 -1
- package/dist/intelligence/citation-guard.js +52 -23
- package/dist/intelligence/evidence-analysis.d.ts +24 -16
- package/dist/intelligence/evidence-analysis.d.ts.map +1 -1
- package/dist/intelligence/evidence-analysis.js +118 -20
- package/dist/intelligence/evidence-budget.d.ts +13 -0
- package/dist/intelligence/evidence-budget.d.ts.map +1 -0
- package/dist/intelligence/evidence-budget.js +49 -0
- package/dist/intelligence/index.d.ts +10 -4
- package/dist/intelligence/index.d.ts.map +1 -1
- package/dist/intelligence/index.js +27 -3
- package/dist/intelligence/keyword-extract.d.ts +1 -1
- package/dist/intelligence/keyword-extract.d.ts.map +1 -1
- package/dist/intelligence/keyword-extract.js +5 -9
- package/dist/intelligence/request-interpretation.d.ts +40 -0
- package/dist/intelligence/request-interpretation.d.ts.map +1 -0
- package/dist/intelligence/request-interpretation.js +71 -0
- package/dist/intelligence/response-templates.d.ts +1 -0
- package/dist/intelligence/response-templates.d.ts.map +1 -1
- package/dist/intelligence/response-templates.js +13 -0
- package/dist/prompt/dynamic-layer.d.ts +1 -5
- package/dist/prompt/dynamic-layer.d.ts.map +1 -1
- package/dist/prompt/dynamic-layer.js +145 -9
- package/dist/prompt/prompt-builder.d.ts +1 -1
- package/dist/prompt/prompt-builder.d.ts.map +1 -1
- package/dist/prompt/prompt-builder.js +5 -1
- package/dist/prompt/semi-static-layer.d.ts +1 -1
- package/dist/prompt/semi-static-layer.d.ts.map +1 -1
- package/dist/prompt/semi-static-layer.js +22 -12
- package/dist/prompt/static-layer.d.ts.map +1 -1
- package/dist/prompt/static-layer.js +37 -4
- package/dist/prompt/types.d.ts +9 -4
- package/dist/prompt/types.d.ts.map +1 -1
- package/dist/provider-manager/base.d.ts +5 -1
- package/dist/provider-manager/base.d.ts.map +1 -1
- package/dist/provider-manager/base.js +22 -2
- package/dist/provider-manager/config.d.ts.map +1 -1
- package/dist/provider-manager/config.js +3 -2
- package/dist/provider-manager/index.d.ts +1 -1
- package/dist/provider-manager/index.d.ts.map +1 -1
- package/dist/provider-manager/index.js +1 -2
- package/dist/provider-manager/manager.d.ts +10 -1
- package/dist/provider-manager/manager.d.ts.map +1 -1
- package/dist/provider-manager/manager.js +26 -10
- package/dist/provider-manager/openai.d.ts +2 -2
- package/dist/provider-manager/openai.d.ts.map +1 -1
- package/dist/provider-manager/openai.js +19 -4
- package/dist/provider-manager/types.d.ts +18 -38
- package/dist/provider-manager/types.d.ts.map +1 -1
- package/dist/provider-manager/workers.d.ts +2 -2
- package/dist/provider-manager/workers.d.ts.map +1 -1
- package/dist/provider-manager/workers.js +15 -4
- package/dist/query/followup.d.ts +7 -0
- package/dist/query/followup.d.ts.map +1 -0
- package/dist/query/followup.js +46 -0
- package/dist/query/intent.d.ts +6 -0
- package/dist/query/intent.d.ts.map +1 -0
- package/dist/query/intent.js +137 -0
- package/dist/query/types.d.ts +8 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +0 -0
- package/dist/search/hybrid-search.d.ts +111 -0
- package/dist/search/hybrid-search.d.ts.map +1 -0
- package/dist/search/hybrid-search.js +326 -0
- package/dist/search/index.d.ts +11 -9
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +46 -10
- package/dist/search/scoring.d.ts +18 -0
- package/dist/search/scoring.d.ts.map +1 -0
- package/dist/search/{search-utils.js → scoring.js} +14 -27
- package/dist/search/search-api.d.ts +16 -1
- package/dist/search/search-api.d.ts.map +1 -1
- package/dist/search/search-api.js +118 -15
- package/dist/search/search-index.d.ts +2 -2
- package/dist/search/search-index.d.ts.map +1 -1
- package/dist/search/search-index.js +4 -2
- package/dist/search/session-cache.d.ts +4 -10
- package/dist/search/session-cache.d.ts.map +1 -1
- package/dist/search/session-cache.js +12 -45
- package/dist/search/types.d.ts +28 -0
- package/dist/search/types.d.ts.map +1 -1
- package/dist/search/vector-reranker.d.ts +3 -3
- package/dist/search/vector-reranker.d.ts.map +1 -1
- package/dist/search/vector-reranker.js +14 -2
- package/dist/server/chat-handler.d.ts +86 -1
- package/dist/server/chat-handler.d.ts.map +1 -1
- package/dist/server/chat-handler.js +835 -401
- package/dist/server/chat-message-utils.d.ts +6 -0
- package/dist/server/chat-message-utils.d.ts.map +1 -0
- package/dist/server/chat-message-utils.js +40 -0
- package/dist/server/chat-utils.d.ts +30 -0
- package/dist/server/chat-utils.d.ts.map +1 -0
- package/dist/server/chat-utils.js +88 -0
- package/dist/server/dev-server.js +238 -101
- package/dist/server/env-config.d.ts +22 -0
- package/dist/server/env-config.d.ts.map +1 -0
- package/dist/server/env-config.js +25 -0
- package/dist/server/errors.d.ts +1 -0
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +14 -7
- package/dist/server/index.d.ts +2 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -25
- package/dist/server/metadata-init.d.ts +10 -5
- package/dist/server/metadata-init.d.ts.map +1 -1
- package/dist/server/metadata-init.js +78 -34
- package/dist/server/notify.d.ts +12 -11
- package/dist/server/notify.d.ts.map +1 -1
- package/dist/server/notify.js +46 -48
- package/dist/server/prompt-runtime.d.ts +60 -0
- package/dist/server/prompt-runtime.d.ts.map +1 -0
- package/dist/server/prompt-runtime.js +284 -0
- package/dist/server/stream-helpers.d.ts +30 -16
- package/dist/server/stream-helpers.d.ts.map +1 -1
- package/dist/server/stream-helpers.js +152 -15
- package/dist/server/types.d.ts +47 -12
- package/dist/server/types.d.ts.map +1 -1
- package/dist/structured-output/generator.d.ts +6 -0
- package/dist/structured-output/generator.d.ts.map +1 -0
- package/dist/structured-output/generator.js +164 -0
- package/dist/structured-output/index.d.ts +4 -0
- package/dist/structured-output/index.d.ts.map +1 -0
- package/dist/structured-output/index.js +6 -0
- package/dist/structured-output/schemas/evidence.d.ts +88 -0
- package/dist/structured-output/schemas/evidence.d.ts.map +1 -0
- package/dist/structured-output/schemas/evidence.js +65 -0
- package/dist/structured-output/types.d.ts +69 -0
- package/dist/structured-output/types.d.ts.map +1 -0
- package/dist/structured-output/types.js +0 -0
- package/dist/tools/action-tools.d.ts +63 -0
- package/dist/tools/action-tools.d.ts.map +1 -0
- package/dist/tools/action-tools.js +158 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +30 -0
- package/dist/utils/i18n.d.ts +1 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +1 -1
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +36 -0
- package/dist/utils/text.d.ts +11 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +87 -0
- package/dist/utils/url.d.ts +19 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +13 -0
- package/package.json +46 -12
- package/dist/intelligence/intent-detect.d.ts +0 -40
- package/dist/intelligence/intent-detect.d.ts.map +0 -1
- package/dist/intelligence/intent-detect.js +0 -93
- package/dist/providers/index.d.ts +0 -2
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -5
- package/dist/search/search-utils.d.ts +0 -47
- package/dist/search/search-utils.d.ts.map +0 -1
- package/dist/stream/index.d.ts +0 -3
- package/dist/stream/index.d.ts.map +0 -1
- package/dist/stream/index.js +0 -8
- package/dist/stream/mock-stream.d.ts +0 -12
- package/dist/stream/mock-stream.d.ts.map +0 -1
- package/dist/stream/mock-stream.js +0 -26
- package/dist/stream/response.d.ts +0 -10
- package/dist/stream/response.d.ts.map +0 -1
- package/dist/stream/response.js +0 -21
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
2
|
+
import { h, Fragment } from 'preact';
|
|
3
|
+
|
|
4
|
+
import { RichText } from "./RichText.js";
|
|
5
|
+
import { ReasoningBlock } from "./ReasoningBlock.js";
|
|
6
|
+
import { generateFollowUpSuggestions, FollowUpSuggestions } from "./CodeBlock.js";
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null;
|
|
9
|
+
}
|
|
10
|
+
function getValidExternalUrl(value) {
|
|
11
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
12
|
+
try {
|
|
13
|
+
const url = new URL(value);
|
|
14
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url.href : null;
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getToolOutput(part) {
|
|
20
|
+
if (!isRecord(part.output)) return null;
|
|
21
|
+
return {
|
|
22
|
+
success: typeof part.output.success === "boolean" ? part.output.success : void 0,
|
|
23
|
+
confirmation: typeof part.output.confirmation === "string" ? part.output.confirmation : void 0
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const ACTION_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
27
|
+
"toggleTheme",
|
|
28
|
+
"navigateToArticle",
|
|
29
|
+
"scrollToSection",
|
|
30
|
+
"toggleReadingMode",
|
|
31
|
+
"highlightText",
|
|
32
|
+
"setPreference"
|
|
33
|
+
]);
|
|
34
|
+
function isToolPart(part) {
|
|
35
|
+
return typeof part === "object" && part !== null && "type" in part && typeof part.type === "string" && part.type.startsWith("tool-");
|
|
36
|
+
}
|
|
37
|
+
function getToolName(part) {
|
|
38
|
+
return part.type.slice("tool-".length);
|
|
39
|
+
}
|
|
40
|
+
function buildActionToolConfirmation(part, lang) {
|
|
41
|
+
const toolName = getToolName(part);
|
|
42
|
+
const output = getToolOutput(part);
|
|
43
|
+
if (!ACTION_TOOL_NAMES.has(toolName) || part.state !== "output-available" || !output?.success) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const input = isRecord(part.input) ? part.input : {};
|
|
47
|
+
switch (toolName) {
|
|
48
|
+
case "toggleTheme": {
|
|
49
|
+
const theme = input.theme;
|
|
50
|
+
if (theme === "dark") return lang === "en" ? "I switched to dark mode." : "\u5DF2\u4E3A\u4F60\u5207\u6362\u5230\u6697\u6A21\u5F0F\u3002";
|
|
51
|
+
if (theme === "light") return lang === "en" ? "I switched to light mode." : "\u5DF2\u4E3A\u4F60\u5207\u6362\u5230\u4EAE\u6A21\u5F0F\u3002";
|
|
52
|
+
return lang === "en" ? "I switched to system theme mode." : "\u5DF2\u4E3A\u4F60\u5207\u6362\u4E3A\u8DDF\u968F\u7CFB\u7EDF\u4E3B\u9898\u3002";
|
|
53
|
+
}
|
|
54
|
+
case "toggleReadingMode":
|
|
55
|
+
return input.enabled === false ? lang === "en" ? "I turned off reading mode." : "\u5DF2\u4E3A\u4F60\u5173\u95ED\u9605\u8BFB\u6A21\u5F0F\u3002" : lang === "en" ? "I turned on reading mode." : "\u5DF2\u4E3A\u4F60\u5F00\u542F\u9605\u8BFB\u6A21\u5F0F\u3002";
|
|
56
|
+
case "scrollToSection":
|
|
57
|
+
return lang === "en" ? "I jumped to the requested section." : "\u5DF2\u4E3A\u4F60\u8DF3\u8F6C\u5230\u5BF9\u5E94\u7AE0\u8282\u3002";
|
|
58
|
+
case "navigateToArticle":
|
|
59
|
+
return lang === "en" ? "I opened the requested article." : "\u5DF2\u4E3A\u4F60\u6253\u5F00\u76F8\u5173\u6587\u7AE0\u3002";
|
|
60
|
+
case "highlightText":
|
|
61
|
+
return lang === "en" ? "I highlighted the requested content." : "\u5DF2\u4E3A\u4F60\u9AD8\u4EAE\u76F8\u5173\u5185\u5BB9\u3002";
|
|
62
|
+
case "setPreference":
|
|
63
|
+
return lang === "en" ? "I updated that preference." : "\u5DF2\u4E3A\u4F60\u66F4\u65B0\u504F\u597D\u8BBE\u7F6E\u3002";
|
|
64
|
+
default:
|
|
65
|
+
return output.confirmation ?? null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function getActionToolConfirmations(message, lang) {
|
|
69
|
+
const confirmations = (message.parts ?? []).filter(isToolPart).map((part) => buildActionToolConfirmation(part, lang)).filter((text) => Boolean(text));
|
|
70
|
+
return Array.from(new Set(confirmations));
|
|
71
|
+
}
|
|
72
|
+
function shouldSuppressAssistantFallbackText(text, confirmations, lang) {
|
|
73
|
+
if (!text.trim() || confirmations.length === 0) return false;
|
|
74
|
+
const noOutputText = lang === "en" ? "Sorry, I could not generate a valid response. Please try rephrasing your question." : "\u62B1\u6B49\uFF0C\u6211\u65E0\u6CD5\u751F\u6210\u6709\u6548\u7684\u56DE\u7B54\u3002\u8BF7\u5C1D\u8BD5\u6362\u4E00\u79CD\u65B9\u5F0F\u63D0\u95EE\u3002";
|
|
75
|
+
const demoIntroText = lang === "en" ? "Thanks for asking! I'm in Demo mode and can recommend blog articles and external resources." : "\u611F\u8C22\u63D0\u95EE\uFF01\u6211\u76EE\u524D\u5728 Demo \u6A21\u5F0F\u4E0B\uFF0C\u53EF\u4EE5\u63A8\u8350\u535A\u5BA2\u6587\u7AE0\u548C\u5916\u90E8\u8D44\u6E90\u3002";
|
|
76
|
+
return text.includes(noOutputText) || text.includes(demoIntroText);
|
|
77
|
+
}
|
|
78
|
+
const TYPEWRITER_SPEED_MS = 25;
|
|
79
|
+
const TYPEWRITER_BATCH_SIZE = 1;
|
|
80
|
+
function useTypewriter(fullText, isStreaming) {
|
|
81
|
+
const [displayedLength, setDisplayedLength] = useState(0);
|
|
82
|
+
const prevFullTextRef = useRef(fullText);
|
|
83
|
+
const prevStreamingRef = useRef(isStreaming);
|
|
84
|
+
const animationRef = useRef(null);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (fullText !== prevFullTextRef.current && !fullText.startsWith(prevFullTextRef.current)) {
|
|
87
|
+
setDisplayedLength(0);
|
|
88
|
+
}
|
|
89
|
+
prevFullTextRef.current = fullText;
|
|
90
|
+
}, [fullText]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!isStreaming && prevStreamingRef.current) {
|
|
93
|
+
setDisplayedLength(fullText.length);
|
|
94
|
+
if (animationRef.current) {
|
|
95
|
+
cancelAnimationFrame(animationRef.current);
|
|
96
|
+
animationRef.current = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
prevStreamingRef.current = isStreaming;
|
|
100
|
+
}, [isStreaming, fullText.length]);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!isStreaming) return;
|
|
103
|
+
let lastTime = performance.now();
|
|
104
|
+
const animate = (currentTime) => {
|
|
105
|
+
const elapsed = currentTime - lastTime;
|
|
106
|
+
if (elapsed >= TYPEWRITER_SPEED_MS) {
|
|
107
|
+
setDisplayedLength((prev) => {
|
|
108
|
+
const targetLength = fullText.length;
|
|
109
|
+
if (prev >= targetLength) return prev;
|
|
110
|
+
const behind = targetLength - prev;
|
|
111
|
+
const speed = behind > 20 ? Math.min(behind, 5) : TYPEWRITER_BATCH_SIZE;
|
|
112
|
+
return Math.min(prev + speed, targetLength);
|
|
113
|
+
});
|
|
114
|
+
lastTime = currentTime;
|
|
115
|
+
}
|
|
116
|
+
animationRef.current = requestAnimationFrame(animate);
|
|
117
|
+
};
|
|
118
|
+
animationRef.current = requestAnimationFrame(animate);
|
|
119
|
+
return () => {
|
|
120
|
+
if (animationRef.current) {
|
|
121
|
+
cancelAnimationFrame(animationRef.current);
|
|
122
|
+
animationRef.current = null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}, [isStreaming, fullText]);
|
|
126
|
+
if (!isStreaming) return fullText;
|
|
127
|
+
if (displayedLength <= 0) return "";
|
|
128
|
+
if (displayedLength >= fullText.length) return fullText;
|
|
129
|
+
let end = displayedLength;
|
|
130
|
+
const fence = fullText.indexOf("```", Math.max(0, end - 2));
|
|
131
|
+
if (fence !== -1 && fence < end + 3) {
|
|
132
|
+
const fenceEnd = fence + 3;
|
|
133
|
+
const newline = fullText.indexOf("\n", fenceEnd);
|
|
134
|
+
end = newline !== -1 ? newline + 1 : fenceEnd;
|
|
135
|
+
}
|
|
136
|
+
return fullText.slice(0, end);
|
|
137
|
+
}
|
|
138
|
+
function getTextFromMessage(message) {
|
|
139
|
+
const parts = message.parts ?? [];
|
|
140
|
+
return Array.isArray(parts) ? parts.filter((p) => p.type === "text").map((p) => p.text).join("") : "";
|
|
141
|
+
}
|
|
142
|
+
function BotIcon({ class: cls }) {
|
|
143
|
+
return /* @__PURE__ */ h("svg", { class: cls, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, /* @__PURE__ */ h("path", { d: "M12 8V4H8" }), /* @__PURE__ */ h("rect", { width: "16", height: "12", x: "4", y: "8", rx: "2" }), /* @__PURE__ */ h("path", { d: "M2 14h2" }), /* @__PURE__ */ h("path", { d: "M20 14h2" }), /* @__PURE__ */ h("path", { d: "M15 13v2" }), /* @__PURE__ */ h("path", { d: "M9 13v2" }));
|
|
144
|
+
}
|
|
145
|
+
function BotAvatar() {
|
|
146
|
+
return /* @__PURE__ */ h("div", { class: "flex size-5.5 shrink-0 items-center justify-center rounded-full bg-accent/15 mt-0.5" }, /* @__PURE__ */ h(BotIcon, { class: "size-3 text-accent" }));
|
|
147
|
+
}
|
|
148
|
+
function TypingDots({ statusMessage }) {
|
|
149
|
+
return /* @__PURE__ */ h("div", { class: "flex items-center gap-2" }, /* @__PURE__ */ h("span", { class: "inline-flex gap-1" }, /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:0ms]" }), /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:150ms]" }), /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:300ms]" })), statusMessage && /* @__PURE__ */ h("span", { class: "text-[11px] text-foreground-soft" }, statusMessage));
|
|
150
|
+
}
|
|
151
|
+
function AssistantMessage({ message, isStreaming, lang = "zh", articleContext, onFollowUp }) {
|
|
152
|
+
const fullText = getTextFromMessage(message);
|
|
153
|
+
const actionConfirmations = useMemo(() => getActionToolConfirmations(message, lang), [message, lang]);
|
|
154
|
+
const shouldSuppressFallbackText = useMemo(
|
|
155
|
+
() => shouldSuppressAssistantFallbackText(fullText, actionConfirmations, lang),
|
|
156
|
+
[fullText, actionConfirmations, lang]
|
|
157
|
+
);
|
|
158
|
+
const effectiveText = shouldSuppressFallbackText ? actionConfirmations.join("\n") : fullText || actionConfirmations.join("\n");
|
|
159
|
+
const displayedText = useTypewriter(effectiveText, isStreaming ?? false);
|
|
160
|
+
const reasoningParts = message.parts.filter((p) => p.type === "reasoning");
|
|
161
|
+
const reasoningFullText = reasoningParts.map((p) => p.text).join("");
|
|
162
|
+
const reasoningDisplayed = useTypewriter(reasoningFullText, isStreaming ?? false);
|
|
163
|
+
const hasReasoning = reasoningFullText.length > 0;
|
|
164
|
+
const isWaitingForContent = isStreaming && !fullText && !reasoningFullText;
|
|
165
|
+
const sources = message.parts.filter((p) => p.type === "source-url" || p.type === "source-document");
|
|
166
|
+
const sourceSnippets = message.parts.filter(
|
|
167
|
+
(p) => p.type === "data-source-snippet" && isRecord(p.data)
|
|
168
|
+
);
|
|
169
|
+
const followUpSuggestions = useMemo(() => {
|
|
170
|
+
if (isStreaming || !effectiveText || !onFollowUp || shouldSuppressFallbackText) return [];
|
|
171
|
+
return generateFollowUpSuggestions(effectiveText, articleContext);
|
|
172
|
+
}, [isStreaming, effectiveText, articleContext, onFollowUp, shouldSuppressFallbackText]);
|
|
173
|
+
if (isWaitingForContent) {
|
|
174
|
+
return /* @__PURE__ */ h("div", { class: "space-y-1.5" }, /* @__PURE__ */ h(ReasoningBlock, { text: "", isStreaming: true, lang }));
|
|
175
|
+
}
|
|
176
|
+
if (!effectiveText && !hasReasoning) return null;
|
|
177
|
+
return /* @__PURE__ */ h("div", { class: "space-y-1.5" }, hasReasoning && /* @__PURE__ */ h(ReasoningBlock, { text: reasoningDisplayed, isStreaming, lang }), displayedText && /* @__PURE__ */ h(RichText, { text: displayedText, isStreaming }), !isStreaming && sourceSnippets.length > 0 && /* @__PURE__ */ h("div", { class: "mt-2 space-y-2" }, /* @__PURE__ */ h("div", { class: "text-[11px] font-medium text-foreground-soft" }, lang === "en" ? "Relevant snippets" : "\u76F8\u5173\u539F\u6587\u6BB5\u843D"), /* @__PURE__ */ h("div", { class: "space-y-2" }, sourceSnippets.map((part, i) => {
|
|
178
|
+
const data = part.data;
|
|
179
|
+
const sourceUrl = getValidExternalUrl(data.url);
|
|
180
|
+
const terms = Array.isArray(data.matchTerms) ? data.matchTerms.filter((t) => typeof t === "string" && t.length > 0) : [];
|
|
181
|
+
const content = /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h("div", { class: "flex items-start justify-between gap-2" }, /* @__PURE__ */ h("div", { class: "min-w-0" }, /* @__PURE__ */ h("div", { class: "truncate text-[11px] font-medium text-foreground" }, typeof data.title === "string" && data.title ? data.title : "Source"), typeof data.heading === "string" && data.heading && /* @__PURE__ */ h("div", { class: "truncate text-[10px] text-foreground-soft" }, data.heading)), terms.length > 0 && /* @__PURE__ */ h("div", { class: "hidden shrink-0 flex-wrap gap-1 sm:flex" }, terms.slice(0, 2).map((term) => /* @__PURE__ */ h("span", { class: "rounded bg-accent/10 px-1.5 py-0.5 text-[10px] text-accent" }, term)))), typeof data.snippet === "string" && data.snippet && /* @__PURE__ */ h("div", { class: "mt-1.5 text-[12px] leading-relaxed text-foreground-soft" }, data.snippet));
|
|
182
|
+
return sourceUrl ? /* @__PURE__ */ h(
|
|
183
|
+
"a",
|
|
184
|
+
{
|
|
185
|
+
key: `snippet-${i}`,
|
|
186
|
+
href: sourceUrl,
|
|
187
|
+
class: "block rounded-lg border border-border bg-muted/25 px-3 py-2 transition-colors hover:border-accent/30 hover:bg-accent/5",
|
|
188
|
+
target: "_blank",
|
|
189
|
+
rel: "noopener noreferrer"
|
|
190
|
+
},
|
|
191
|
+
content
|
|
192
|
+
) : /* @__PURE__ */ h(
|
|
193
|
+
"div",
|
|
194
|
+
{
|
|
195
|
+
key: `snippet-${i}`,
|
|
196
|
+
class: "block rounded-lg border border-border bg-muted/25 px-3 py-2"
|
|
197
|
+
},
|
|
198
|
+
content
|
|
199
|
+
);
|
|
200
|
+
}))), !isStreaming && sources.length > 0 && /* @__PURE__ */ h("div", { class: "mt-2 flex flex-wrap gap-1.5" }, sources.map((s, i) => {
|
|
201
|
+
const part = s;
|
|
202
|
+
const sourceUrl = getValidExternalUrl(part.url);
|
|
203
|
+
const content = /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h("svg", { class: "size-2.5 opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, /* @__PURE__ */ h("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }), /* @__PURE__ */ h("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })), part.title ?? "Source");
|
|
204
|
+
return sourceUrl ? /* @__PURE__ */ h(
|
|
205
|
+
"a",
|
|
206
|
+
{
|
|
207
|
+
key: i,
|
|
208
|
+
href: sourceUrl,
|
|
209
|
+
class: "inline-flex items-center gap-1 rounded-md border border-border bg-muted/30 px-2 py-0.5 text-[11px] text-foreground-soft transition-colors hover:border-accent/40 hover:text-foreground",
|
|
210
|
+
target: "_blank",
|
|
211
|
+
rel: "noopener noreferrer"
|
|
212
|
+
},
|
|
213
|
+
content
|
|
214
|
+
) : /* @__PURE__ */ h(
|
|
215
|
+
"span",
|
|
216
|
+
{
|
|
217
|
+
key: i,
|
|
218
|
+
class: "inline-flex items-center gap-1 rounded-md border border-border bg-muted/30 px-2 py-0.5 text-[11px] text-foreground-soft"
|
|
219
|
+
},
|
|
220
|
+
content
|
|
221
|
+
);
|
|
222
|
+
})), !isStreaming && followUpSuggestions.length > 0 && onFollowUp && /* @__PURE__ */ h(FollowUpSuggestions, { suggestions: followUpSuggestions, onSend: onFollowUp, lang }));
|
|
223
|
+
}
|
|
224
|
+
export {
|
|
225
|
+
AssistantMessage,
|
|
226
|
+
BotAvatar,
|
|
227
|
+
BotIcon,
|
|
228
|
+
TypingDots,
|
|
229
|
+
getActionToolConfirmations,
|
|
230
|
+
getTextFromMessage,
|
|
231
|
+
shouldSuppressAssistantFallbackText,
|
|
232
|
+
useTypewriter
|
|
233
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReasoningBlock.d.ts","sourceRoot":"","sources":["../../src/components/ReasoningBlock.tsx"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAW,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,gCAwCxH"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { t, getLang } from "../utils/i18n.js";
|
|
2
|
+
import { h } from 'preact';
|
|
3
|
+
|
|
4
|
+
function ReasoningBlock({ text, isStreaming, lang = "zh" }) {
|
|
5
|
+
const isEmpty = text.length === 0;
|
|
6
|
+
const l = getLang(lang);
|
|
7
|
+
return /* @__PURE__ */ h("details", { class: "group rounded-lg border border-border/50 bg-muted/30 overflow-hidden", open: isStreaming || !isEmpty }, /* @__PURE__ */ h("summary", { class: "flex cursor-pointer items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-medium text-foreground-soft transition-colors hover:bg-muted/50 hover:text-foreground" }, /* @__PURE__ */ h("svg", { class: "size-3.5 transition-transform group-open:rotate-90", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, /* @__PURE__ */ h("path", { d: "m9 18 6-6-6-6" })), /* @__PURE__ */ h("span", { class: "flex items-center gap-1" }, isStreaming ? /* @__PURE__ */ h("svg", { class: "size-3 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, /* @__PURE__ */ h("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })) : /* @__PURE__ */ h("svg", { class: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, /* @__PURE__ */ h("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ h("path", { d: "M12 16v-4" }), /* @__PURE__ */ h("path", { d: "M12 8h.01" })), isStreaming && isEmpty ? t("ai.reasoning.thinking", l) : isStreaming ? t("ai.reasoning.thinking", l).replace("...", "") : t("ai.reasoning.viewReasoning", l))), /* @__PURE__ */ h("div", { class: "border-t border-border/30 bg-background/50 px-2.5 py-2" }, isEmpty && isStreaming ? /* @__PURE__ */ h("div", { class: "flex items-center gap-2 text-[11px] text-foreground-soft" }, /* @__PURE__ */ h("span", { class: "inline-flex gap-1" }, /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:0ms]" }), /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:150ms]" }), /* @__PURE__ */ h("span", { class: "size-1.5 animate-bounce rounded-full bg-foreground-soft [animation-delay:300ms]" })), /* @__PURE__ */ h("span", null, t("ai.reasoning.waiting", l))) : /* @__PURE__ */ h("pre", { class: "whitespace-pre-wrap text-[11px] leading-relaxed text-foreground-soft font-mono" }, text)));
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
ReasoningBlock
|
|
11
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type InlinePart = {
|
|
2
|
+
type: 'text';
|
|
3
|
+
text: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: 'link';
|
|
6
|
+
label: string;
|
|
7
|
+
url: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'bold';
|
|
10
|
+
text: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'code';
|
|
13
|
+
text: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 更健壮的 Markdown 链接解析器
|
|
17
|
+
*
|
|
18
|
+
* 处理以下边界情况:
|
|
19
|
+
* 1. URL 中包含括号:[text](url(with)parentheses)
|
|
20
|
+
* 2. 标签中包含方括号:[text [with] brackets](url)
|
|
21
|
+
* 3. 未闭合的语法:[text](url 或 [text](url)
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseInlineMarkdownRobust(text: string): InlinePart[];
|
|
24
|
+
export declare function InlineRichText({ text }: {
|
|
25
|
+
text: string;
|
|
26
|
+
}): import("preact").JSX.Element;
|
|
27
|
+
interface BlockNode {
|
|
28
|
+
type: 'paragraph' | 'code-block' | 'blockquote' | 'list';
|
|
29
|
+
content: string;
|
|
30
|
+
lang?: string;
|
|
31
|
+
ordered?: boolean;
|
|
32
|
+
items?: string[];
|
|
33
|
+
unclosed?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function parseBlocks(text: string): BlockNode[];
|
|
36
|
+
export declare function RichText({ text, isStreaming }: {
|
|
37
|
+
text: string;
|
|
38
|
+
isStreaming?: boolean;
|
|
39
|
+
}): import("preact").JSX.Element;
|
|
40
|
+
export {};
|
|
41
|
+
//# sourceMappingURL=RichText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichText.d.ts","sourceRoot":"","sources":["../../src/components/RichText.tsx"],"names":[],"mappings":"AAUA,KAAK,UAAU,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,CAmDpE;AAgDD,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,gCAuBxD;AAID,UAAU,SAAS;IACjB,IAAI,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,MAAM,CAAC;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CA+ErD;AAED,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,gCA+CtF"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useMemo } from "preact/hooks";
|
|
2
|
+
import { h } from 'preact';
|
|
3
|
+
|
|
4
|
+
import { CodeBlock } from "./CodeBlock.js";
|
|
5
|
+
const SAFE_URL_RE = /^(?:https?|mailto):/i;
|
|
6
|
+
function sanitizeUrl(url) {
|
|
7
|
+
if (SAFE_URL_RE.test(url) || url.startsWith("/") || url.startsWith("#")) return url;
|
|
8
|
+
return "#";
|
|
9
|
+
}
|
|
10
|
+
function parseInlineMarkdownRobust(text) {
|
|
11
|
+
const parts = [];
|
|
12
|
+
let i = 0;
|
|
13
|
+
while (i < text.length) {
|
|
14
|
+
if (text[i] === "[") {
|
|
15
|
+
const linkResult = tryParseLink(text, i);
|
|
16
|
+
if (linkResult) {
|
|
17
|
+
parts.push({ type: "link", label: linkResult.label, url: linkResult.url });
|
|
18
|
+
i = linkResult.endIndex;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (text.slice(i, i + 2) === "**") {
|
|
23
|
+
const boldEnd = text.indexOf("**", i + 2);
|
|
24
|
+
if (boldEnd !== -1) {
|
|
25
|
+
parts.push({ type: "bold", text: text.slice(i + 2, boldEnd) });
|
|
26
|
+
i = boldEnd + 2;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (text[i] === "`" && text[i + 1] !== "`") {
|
|
31
|
+
const codeEnd = text.indexOf("`", i + 1);
|
|
32
|
+
if (codeEnd !== -1) {
|
|
33
|
+
parts.push({ type: "code", text: text.slice(i + 1, codeEnd) });
|
|
34
|
+
i = codeEnd + 1;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
let textEnd = i + 1;
|
|
39
|
+
while (textEnd < text.length && text[textEnd] !== "[" && text[textEnd] !== "*" && text[textEnd] !== "`") {
|
|
40
|
+
textEnd++;
|
|
41
|
+
}
|
|
42
|
+
if (textEnd > i) {
|
|
43
|
+
parts.push({ type: "text", text: text.slice(i, textEnd) });
|
|
44
|
+
}
|
|
45
|
+
i = textEnd;
|
|
46
|
+
}
|
|
47
|
+
return parts;
|
|
48
|
+
}
|
|
49
|
+
function tryParseLink(text, start) {
|
|
50
|
+
if (text[start] !== "[") return null;
|
|
51
|
+
let depth = 1;
|
|
52
|
+
let labelEnd = start + 1;
|
|
53
|
+
while (labelEnd < text.length && depth > 0) {
|
|
54
|
+
if (text[labelEnd] === "[") depth++;
|
|
55
|
+
else if (text[labelEnd] === "]") depth--;
|
|
56
|
+
else if (text[labelEnd] === "\\" && labelEnd + 1 < text.length) labelEnd++;
|
|
57
|
+
labelEnd++;
|
|
58
|
+
}
|
|
59
|
+
if (depth !== 0 || labelEnd >= text.length || text[labelEnd] !== "(") return null;
|
|
60
|
+
const label = text.slice(start + 1, labelEnd - 1).replace(/\\(.)/g, "$1");
|
|
61
|
+
const urlStart = labelEnd + 1;
|
|
62
|
+
depth = 1;
|
|
63
|
+
let urlEnd = urlStart;
|
|
64
|
+
while (urlEnd < text.length && depth > 0) {
|
|
65
|
+
if (text[urlEnd] === "(") depth++;
|
|
66
|
+
else if (text[urlEnd] === ")") depth--;
|
|
67
|
+
urlEnd++;
|
|
68
|
+
}
|
|
69
|
+
if (depth !== 0) return null;
|
|
70
|
+
const url = text.slice(urlStart, urlEnd - 1).trim();
|
|
71
|
+
return { label, url, endIndex: urlEnd };
|
|
72
|
+
}
|
|
73
|
+
function ExternalLinkIcon() {
|
|
74
|
+
return /* @__PURE__ */ h("svg", { class: "inline-block size-3 shrink-0 opacity-50", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, /* @__PURE__ */ h("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), /* @__PURE__ */ h("polyline", { points: "15 3 21 3 21 9" }), /* @__PURE__ */ h("line", { x1: "10", y1: "14", x2: "21", y2: "3" }));
|
|
75
|
+
}
|
|
76
|
+
function InlineRichText({ text }) {
|
|
77
|
+
const parts = useMemo(() => parseInlineMarkdownRobust(text), [text]);
|
|
78
|
+
return /* @__PURE__ */ h("span", null, parts.map((p, i) => {
|
|
79
|
+
if (p.type === "link") {
|
|
80
|
+
const safeUrl = sanitizeUrl(p.url);
|
|
81
|
+
const isExternal = safeUrl.startsWith("http");
|
|
82
|
+
return /* @__PURE__ */ h(
|
|
83
|
+
"a",
|
|
84
|
+
{
|
|
85
|
+
key: i,
|
|
86
|
+
href: safeUrl,
|
|
87
|
+
class: "inline-flex items-center gap-0.5 font-medium text-accent underline decoration-accent/30 underline-offset-2 transition-colors hover:decoration-accent",
|
|
88
|
+
target: isExternal ? "_blank" : void 0,
|
|
89
|
+
rel: isExternal ? "noopener noreferrer" : void 0
|
|
90
|
+
},
|
|
91
|
+
p.label,
|
|
92
|
+
isExternal && /* @__PURE__ */ h(ExternalLinkIcon, null)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (p.type === "bold") return /* @__PURE__ */ h("strong", { key: i, class: "font-semibold" }, p.text);
|
|
96
|
+
if (p.type === "code") return /* @__PURE__ */ h("code", { key: i, class: "rounded bg-muted/60 px-1 py-0.5 text-[13px] font-mono" }, p.text);
|
|
97
|
+
return /* @__PURE__ */ h("span", { key: i }, p.text);
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
function parseBlocks(text) {
|
|
101
|
+
const lines = text.split("\n");
|
|
102
|
+
const blocks = [];
|
|
103
|
+
let i = 0;
|
|
104
|
+
while (i < lines.length) {
|
|
105
|
+
const line = lines[i];
|
|
106
|
+
if (line.startsWith("```")) {
|
|
107
|
+
const lang = line.slice(3).trim();
|
|
108
|
+
const codeLines = [];
|
|
109
|
+
i++;
|
|
110
|
+
let closed = false;
|
|
111
|
+
while (i < lines.length) {
|
|
112
|
+
if (lines[i].startsWith("```")) {
|
|
113
|
+
closed = true;
|
|
114
|
+
i++;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
codeLines.push(lines[i]);
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
blocks.push({
|
|
121
|
+
type: "code-block",
|
|
122
|
+
content: codeLines.join("\n"),
|
|
123
|
+
lang: lang || void 0,
|
|
124
|
+
unclosed: !closed
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (line.startsWith("> ") || line === ">") {
|
|
129
|
+
const quoteLines = [];
|
|
130
|
+
while (i < lines.length && (lines[i].startsWith("> ") || lines[i] === ">")) {
|
|
131
|
+
quoteLines.push(lines[i].replace(/^>\s?/, ""));
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
blocks.push({ type: "blockquote", content: quoteLines.join("\n") });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (/^[-*]\s/.test(line)) {
|
|
138
|
+
const items = [];
|
|
139
|
+
while (i < lines.length && /^[-*]\s/.test(lines[i])) {
|
|
140
|
+
items.push(lines[i].replace(/^[-*]\s/, ""));
|
|
141
|
+
i++;
|
|
142
|
+
}
|
|
143
|
+
blocks.push({ type: "list", content: "", ordered: false, items });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (/^\d+\.\s/.test(line)) {
|
|
147
|
+
const items = [];
|
|
148
|
+
while (i < lines.length && /^\d+\.\s/.test(lines[i])) {
|
|
149
|
+
items.push(lines[i].replace(/^\d+\.\s/, ""));
|
|
150
|
+
i++;
|
|
151
|
+
}
|
|
152
|
+
blocks.push({ type: "list", content: "", ordered: true, items });
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (!line.trim()) {
|
|
156
|
+
i++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const paraLines = [];
|
|
160
|
+
while (i < lines.length && lines[i].trim() && !lines[i].startsWith("```") && !lines[i].startsWith("> ") && lines[i] !== ">" && !/^[-*]\s/.test(lines[i]) && !/^\d+\.\s/.test(lines[i])) {
|
|
161
|
+
paraLines.push(lines[i]);
|
|
162
|
+
i++;
|
|
163
|
+
}
|
|
164
|
+
if (paraLines.length) {
|
|
165
|
+
blocks.push({ type: "paragraph", content: paraLines.join("\n") });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return blocks;
|
|
169
|
+
}
|
|
170
|
+
function RichText({ text, isStreaming }) {
|
|
171
|
+
const blocks = useMemo(() => parseBlocks(text), [text]);
|
|
172
|
+
return /* @__PURE__ */ h("div", { class: "space-y-2" }, blocks.map((block, i) => {
|
|
173
|
+
switch (block.type) {
|
|
174
|
+
case "code-block":
|
|
175
|
+
return /* @__PURE__ */ h(
|
|
176
|
+
CodeBlock,
|
|
177
|
+
{
|
|
178
|
+
key: i,
|
|
179
|
+
code: block.content,
|
|
180
|
+
lang: block.lang,
|
|
181
|
+
isStreaming: isStreaming || block.unclosed
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
case "blockquote":
|
|
185
|
+
return /* @__PURE__ */ h("blockquote", { key: i, class: "border-l-2 border-accent/40 pl-3 text-foreground-soft italic" }, /* @__PURE__ */ h(InlineRichText, { text: block.content }));
|
|
186
|
+
case "list":
|
|
187
|
+
if (block.ordered) {
|
|
188
|
+
return /* @__PURE__ */ h("ol", { key: i, class: "list-decimal space-y-0.5 pl-5" }, block.items?.map((item, j) => /* @__PURE__ */ h("li", { key: j }, /* @__PURE__ */ h(InlineRichText, { text: item }))));
|
|
189
|
+
}
|
|
190
|
+
return /* @__PURE__ */ h("ul", { key: i, class: "list-disc space-y-0.5 pl-5" }, block.items?.map((item, j) => /* @__PURE__ */ h("li", { key: j }, /* @__PURE__ */ h(InlineRichText, { text: item }))));
|
|
191
|
+
case "paragraph":
|
|
192
|
+
default:
|
|
193
|
+
return /* @__PURE__ */ h("p", { key: i, class: "whitespace-pre-wrap" }, /* @__PURE__ */ h(InlineRichText, { text: block.content }));
|
|
194
|
+
}
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
InlineRichText,
|
|
199
|
+
RichText,
|
|
200
|
+
parseBlocks,
|
|
201
|
+
parseInlineMarkdownRobust
|
|
202
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
interface Size {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function useVizScaleControls(initialScale?: number): {
|
|
6
|
+
scale: number;
|
|
7
|
+
setScale: import("preact/hooks").Dispatch<import("preact/hooks").StateUpdater<number>>;
|
|
8
|
+
handleZoomIn: () => void;
|
|
9
|
+
handleZoomOut: () => void;
|
|
10
|
+
handleReset: () => void;
|
|
11
|
+
handleWheelZoom: (event: WheelEvent) => void;
|
|
12
|
+
zoomWithWheelDelta: (deltaY: number) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare function measureElementSize(element: HTMLElement): Size;
|
|
15
|
+
export declare function getScaledCanvasStyles(baseSize: Size, scale: number): {
|
|
16
|
+
scaledStyle: {
|
|
17
|
+
width: string;
|
|
18
|
+
height: string;
|
|
19
|
+
};
|
|
20
|
+
transformStyle: {
|
|
21
|
+
transform: string;
|
|
22
|
+
transformOrigin: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export declare function useScaledCanvas(ref: {
|
|
26
|
+
current: HTMLElement | null;
|
|
27
|
+
}, scale: number, deps?: readonly unknown[]): {
|
|
28
|
+
scaledStyle: {
|
|
29
|
+
width: string;
|
|
30
|
+
height: string;
|
|
31
|
+
};
|
|
32
|
+
transformStyle: {
|
|
33
|
+
transform: string;
|
|
34
|
+
transformOrigin: string;
|
|
35
|
+
};
|
|
36
|
+
baseSize: Size;
|
|
37
|
+
};
|
|
38
|
+
export declare function useDragPanScroll(ref: {
|
|
39
|
+
current: HTMLElement | null;
|
|
40
|
+
}, enabled: boolean, deps?: readonly unknown[]): void;
|
|
41
|
+
interface VizToolbarProps {
|
|
42
|
+
onZoomIn: () => void;
|
|
43
|
+
onZoomOut: () => void;
|
|
44
|
+
onReset: () => void;
|
|
45
|
+
onFullscreen: () => void;
|
|
46
|
+
onShowSource: () => void;
|
|
47
|
+
showSourceActive?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function VizToolbar({ onZoomIn, onZoomOut, onReset, onFullscreen, onShowSource, showSourceActive }: VizToolbarProps): import("preact").JSX.Element;
|
|
50
|
+
export declare function CopyButton({ code }: {
|
|
51
|
+
code: string;
|
|
52
|
+
}): import("preact").JSX.Element;
|
|
53
|
+
export declare function SkeletonLoader({ height }: {
|
|
54
|
+
height?: string;
|
|
55
|
+
}): import("preact").JSX.Element;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=VizShared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VizShared.d.ts","sourceRoot":"","sources":["../../src/components/VizShared.tsx"],"names":[],"mappings":"AAEA,UAAU,IAAI;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAUD,wBAAgB,mBAAmB,CAAC,YAAY,SAAI;;;;;;6BAoBN,UAAU;iCALN,MAAM;EAmBvD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAK7D;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM;;;;;;;;;EAWlE;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE;IAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,EACpC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,OAAO,EAAO;;;;;;;;;;EAoC9B;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE;IAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,EACpC,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,SAAS,OAAO,EAAO,QA8E9B;AAID,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,EAAE,eAAe,gCAkEzH;AAID,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,gCA4BpD;AAID,wBAAgB,cAAc,CAAC,EAAE,MAAgB,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,gCAevE"}
|