0.3.3 外网下载修复

This commit is contained in:
liaibo
2026-01-21 18:21:10 +08:00
parent f13de35321
commit 71de56850b
45 changed files with 2499 additions and 476 deletions
@@ -15,6 +15,7 @@ import {
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import { clamp, MIN_AGENT_STEPS, MAX_AGENT_STEPS } from "@/lib/constants";
type AgentMessage = { role: "user" | "assistant"; content: string };
@@ -79,8 +80,6 @@ type ChatSession = {
type PanelPage = "chat" | "tools" | "history" | "account" | "settings";
const clamp = (n: number, min: number, max: number) => Math.max(min, Math.min(max, n));
const generateId = () => {
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
return `sess_${Math.random().toString(16).slice(2, 10)}`;
@@ -194,8 +193,8 @@ export function DocumentAiAgentPanel({
const p = (window.localStorage.getItem("doc_ai_provider") || "").trim();
const m = window.localStorage.getItem("doc_ai_model") || "";
const parsed = Number(stepsRaw);
if (Number.isFinite(parsed) && parsed >= 1) {
setMaxSteps(Math.max(1, Math.min(24, Math.floor(parsed))));
if (Number.isFinite(parsed) && parsed >= MIN_AGENT_STEPS) {
setMaxSteps(clamp(Math.floor(parsed), MIN_AGENT_STEPS, MAX_AGENT_STEPS));
}
if (p === "local" || p === "online") setAiProvider(p);
if (typeof m === "string") setAiModel(m);
@@ -270,7 +269,7 @@ export function DocumentAiAgentPanel({
// ignore
}
// 只在 documentId 变化时读取一次
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [documentId]);
useEffect(() => {
@@ -688,14 +687,14 @@ export function DocumentAiAgentPanel({
<input
className="w-[72px] rounded border px-2 py-1 text-xs"
type="number"
min={1}
max={24}
min={MIN_AGENT_STEPS}
max={MAX_AGENT_STEPS}
step={1}
value={maxSteps}
onChange={(e) => {
const v = Number(e.target.value);
if (!Number.isFinite(v)) return;
setMaxSteps(Math.max(1, Math.min(24, Math.floor(v))));
setMaxSteps(clamp(Math.floor(v), MIN_AGENT_STEPS, MAX_AGENT_STEPS));
}}
disabled={loading}
/>
@@ -23,11 +23,12 @@ import { CustomSideMenu } from "./menus/CustomSideMenu";
import { CustomSlashMenu } from "./menus/CustomSlashMenu";
import { MoveEmbedPickerHost } from "@/components/documents/move-embed-picker-host";
import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
import { DocumentToc, type TocEntry } from "@/components/editor/document-toc";
import { DocumentToc, type TocEntry } from "@/components/editor/document-toc";
import { useSearchPaletteStore } from "@/store/search-palette";
import { useEditorBridgeStore } from "@/store/editor-bridge";
import type { ReferenceTarget } from "@/types/search";
import FullScreenTableEditor from "@/components/online-table/FullScreenTableEditor";
import { clamp, MIN_BLOCK_LEVEL, MAX_BLOCK_LEVEL } from "@/lib/constants";
import { ASSETS_CHANGED_EVENT, emitAssetsChanged } from "@/lib/events";
interface BlockNoteEditorProps {
@@ -64,7 +65,7 @@ const buildHeadingToc = (blocks: Block<CustomBlockSchema>[]): TocEntry[] => {
const walk = (targetBlocks: Block<CustomBlockSchema>[]) => {
targetBlocks.forEach((block) => {
if (block.type === "heading") {
const level = Math.min(5, Math.max(1, Number(block.props.level) || 1));
const level = clamp(Number(block.props.level) || 1, MIN_BLOCK_LEVEL, MAX_BLOCK_LEVEL);
counters[level - 1] += 1;
for (let i = level; i < counters.length; i += 1) {
counters[i] = 0;
@@ -656,9 +656,6 @@ const MediaBlockContent = ({ block, editor }: any) => {
>
</DropdownMenuItem>
{assetType === "file" && isOfficeDoc && (
<DropdownMenuItem onClick={() => void openWithOnlyOffice()}>使 ONLYOFFICE </DropdownMenuItem>
)}
<DropdownMenuItem
onClick={() => {
void downloadAsset();
@@ -273,7 +273,7 @@ export function MindmapAiAgentPanel({
} catch {
// ignore
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mindmapId]);
useEffect(() => {
@@ -2502,8 +2502,8 @@ const MindmapBlockView = ({
const handleImageConfirm = async () => {
const url = imageUrl.trim();
// 获取原始图片尺寸,如果没有则使用默认值
let width = Number(imageWidth) || 0;
let height = Number(imageHeight) || 0;
const width = Number(imageWidth) || 0;
const height = Number(imageHeight) || 0;
if (!url) {
window.alert("请输入图片链接");
@@ -14,6 +14,7 @@ import { useDebouncedCallback } from "@/hooks/use-debounced-callback";
import { ImagePickerProvider } from "@/components/media/image-picker-context";
import { useRouter } from "next/navigation";
import { DocumentAiAgentPanel } from "./DocumentAiAgentPanel";
import { CONTENT_LOADING_DELAY_MS } from "@/lib/constants";
const BlockNoteEditor = dynamic(
() => import("@/components/editor/blocknote-editor").then((mod) => mod.BlockNoteEditor),
@@ -96,7 +97,7 @@ export function DocumentContent({
}
}, [documentId, editorBridge, openTableId, router]);
/* eslint-disable react-hooks/set-state-in-effect */
useEffect(() => {
setPageTitle(title ?? "无标题");
}, [title]);
@@ -108,7 +109,7 @@ export function DocumentContent({
useEffect(() => {
setStats(initialStats ?? defaultStats);
}, [initialStats]);
/* eslint-enable react-hooks/set-state-in-effect */
useEffect(() => {
let canceled = false;
@@ -128,12 +129,12 @@ export function DocumentContent({
clearTimeout(contentLoadingTimerRef.current);
contentLoadingTimerRef.current = null;
}
// 避免秒闪的加载提示:只有当加载超过短阈值时才显示提示
// 避免"秒闪"的加载提示:只有当加载超过短阈值时才显示提示
contentLoadingTimerRef.current = setTimeout(() => {
if (!canceled) {
setShowContentLoadingIndicator(true);
}
}, 200);
}, CONTENT_LOADING_DELAY_MS);
try {
const response = await fetch(`/api/documents/content?documentId=${encodeURIComponent(documentId)}`, {