refactor: remove "use client" directive from multiple files and update project structure to align with Vite and React Router
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
import type { CanvasAgentOp } from "@/lib/canvas/canvas-agent-ops";
|
||||
|
||||
export type AgentChatRole = "user" | "assistant" | "system" | "tool" | "error";
|
||||
export type AgentAttachment = { id: string; name: string; type: string; size: number; url: string; dataUrl: string };
|
||||
export type AgentChatItem = { id: string; role: AgentChatRole; title?: string; text: string; meta?: string; detail?: unknown; attachments?: AgentAttachment[]; streamId?: string };
|
||||
export type AgentEventLog = { id: string; time: string; title: string; text: string; raw?: unknown };
|
||||
export type AgentPendingToolCall = { requestId: string; name: string; input?: { ops?: CanvasAgentOp[] } };
|
||||
export type AgentThreadSummary = { id: string; preview: string; name?: string | null; cwd?: string; status?: string; source?: unknown; createdAt?: number; updatedAt?: number };
|
||||
export type AgentPanelTab = "chat" | "setup" | "history" | "log";
|
||||
|
||||
type CanvasAgentStore = {
|
||||
width: number;
|
||||
url: string;
|
||||
token: string;
|
||||
connected: boolean;
|
||||
enabled: boolean;
|
||||
prompt: string;
|
||||
attachments: AgentAttachment[];
|
||||
sending: boolean;
|
||||
waiting: boolean;
|
||||
messages: AgentChatItem[];
|
||||
eventLogs: AgentEventLog[];
|
||||
threads: AgentThreadSummary[];
|
||||
activeThreadId: string;
|
||||
workspacePath: string;
|
||||
loadingThreads: boolean;
|
||||
activeTab: AgentPanelTab;
|
||||
confirmTools: boolean;
|
||||
activity: string;
|
||||
connectError: string;
|
||||
pendingTool: AgentPendingToolCall | null;
|
||||
setAgentState: (patch: Partial<Omit<CanvasAgentStore, "setAgentState" | "addMessage" | "addEventLog" | "clearEventLogs">>) => void;
|
||||
addMessage: (item: AgentChatItem) => void;
|
||||
addEventLog: (item: AgentEventLog) => void;
|
||||
clearEventLogs: () => void;
|
||||
};
|
||||
|
||||
export const useCanvasAgentStore = create<CanvasAgentStore>((set) => ({
|
||||
width: typeof window === "undefined" ? 440 : Number(localStorage.getItem("canvas-agent-panel-width")) || 440,
|
||||
url: typeof window === "undefined" ? "http://127.0.0.1:17371" : localStorage.getItem("canvas-agent-url") || "http://127.0.0.1:17371",
|
||||
token: typeof window === "undefined" ? "" : localStorage.getItem("canvas-agent-token") || "",
|
||||
connected: false,
|
||||
enabled: false,
|
||||
prompt: "",
|
||||
attachments: [],
|
||||
sending: false,
|
||||
waiting: false,
|
||||
messages: [],
|
||||
eventLogs: [],
|
||||
threads: [],
|
||||
activeThreadId: "",
|
||||
workspacePath: "",
|
||||
loadingThreads: false,
|
||||
activeTab: "setup",
|
||||
confirmTools: true,
|
||||
activity: "就绪",
|
||||
connectError: "",
|
||||
pendingTool: null,
|
||||
setAgentState: (patch) => set(patch),
|
||||
addMessage: (item) => set((state) => ({ messages: [...state.messages.slice(-120), item] })),
|
||||
addEventLog: (item) => set((state) => ({ eventLogs: [...state.eventLogs.slice(-160), item] })),
|
||||
clearEventLogs: () => set({ eventLogs: [] }),
|
||||
}));
|
||||
@@ -0,0 +1,134 @@
|
||||
import { create } from "zustand";
|
||||
import { persist, type PersistStorage, type StorageValue } from "zustand/middleware";
|
||||
|
||||
import { nanoid } from "nanoid";
|
||||
import { localForageStorage } from "@/lib/localforage-storage";
|
||||
import type { CanvasBackgroundMode } from "@/lib/canvas-theme";
|
||||
import type { CanvasAssistantSession, CanvasConnection, CanvasNodeData, ViewportTransform } from "@/types/canvas";
|
||||
|
||||
export type CanvasProject = {
|
||||
id: string;
|
||||
title: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
nodes: CanvasNodeData[];
|
||||
connections: CanvasConnection[];
|
||||
chatSessions: CanvasAssistantSession[];
|
||||
activeChatId: string | null;
|
||||
backgroundMode: CanvasBackgroundMode;
|
||||
showImageInfo: boolean;
|
||||
viewport: ViewportTransform;
|
||||
};
|
||||
|
||||
type CanvasStore = {
|
||||
hydrated: boolean;
|
||||
projects: CanvasProject[];
|
||||
createProject: (title?: string) => string;
|
||||
importProject: (project: Partial<CanvasProject>) => string;
|
||||
openProject: (id: string) => CanvasProject | null;
|
||||
renameProject: (id: string, title: string) => void;
|
||||
deleteProjects: (ids: string[]) => void;
|
||||
replaceProjects: (projects: CanvasProject[]) => void;
|
||||
updateProject: (id: string, patch: Partial<Pick<CanvasProject, "nodes" | "connections" | "chatSessions" | "activeChatId" | "backgroundMode" | "showImageInfo" | "viewport">>) => void;
|
||||
};
|
||||
|
||||
const initialViewport: ViewportTransform = { x: 0, y: 0, k: 1 };
|
||||
const CANVAS_STORE_KEY = "infinite-canvas:canvas_store";
|
||||
type PersistedCanvasState = Pick<CanvasStore, "projects">;
|
||||
let saveTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let queuedPersistState: PersistedCanvasState | null = null;
|
||||
|
||||
const canvasStorage: PersistStorage<CanvasStore> = {
|
||||
getItem: async (name) => {
|
||||
const value = await localForageStorage.getItem(name);
|
||||
if (!value) return null;
|
||||
const parsed = JSON.parse(value) as StorageValue<CanvasStore>;
|
||||
queuedPersistState = parsed.state as PersistedCanvasState;
|
||||
return parsed;
|
||||
},
|
||||
setItem: (name, value) => {
|
||||
const nextState = value.state as PersistedCanvasState;
|
||||
if (queuedPersistState && queuedPersistState.projects === nextState.projects) return;
|
||||
queuedPersistState = nextState;
|
||||
if (saveTimer) clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(() => {
|
||||
saveTimer = null;
|
||||
void localForageStorage.setItem(name, JSON.stringify(value));
|
||||
}, 400);
|
||||
},
|
||||
removeItem: (name) => localForageStorage.removeItem(name),
|
||||
};
|
||||
|
||||
export const useCanvasStore = create<CanvasStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
hydrated: false,
|
||||
projects: [],
|
||||
createProject: (title = "未命名画布") => {
|
||||
const now = new Date().toISOString();
|
||||
const id = nanoid();
|
||||
const project: CanvasProject = {
|
||||
id,
|
||||
title,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
nodes: [],
|
||||
connections: [],
|
||||
chatSessions: [],
|
||||
activeChatId: null,
|
||||
backgroundMode: "lines",
|
||||
showImageInfo: false,
|
||||
viewport: initialViewport,
|
||||
};
|
||||
set((state) => ({ projects: [project, ...state.projects] }));
|
||||
return id;
|
||||
},
|
||||
importProject: (source) => {
|
||||
const now = new Date().toISOString();
|
||||
const project: CanvasProject = {
|
||||
id: nanoid(),
|
||||
title: source.title || "导入画布",
|
||||
createdAt: source.createdAt || now,
|
||||
updatedAt: now,
|
||||
nodes: source.nodes || [],
|
||||
connections: source.connections || [],
|
||||
chatSessions: source.chatSessions || [],
|
||||
activeChatId: source.activeChatId || null,
|
||||
backgroundMode: source.backgroundMode || "lines",
|
||||
showImageInfo: source.showImageInfo || false,
|
||||
viewport: source.viewport || initialViewport,
|
||||
};
|
||||
set((state) => ({ projects: [project, ...state.projects] }));
|
||||
return project.id;
|
||||
},
|
||||
openProject: (id) => {
|
||||
return get().projects.find((item) => item.id === id) || null;
|
||||
},
|
||||
renameProject: (id, title) =>
|
||||
set((state) => ({
|
||||
projects: state.projects.map((project) => (project.id === id ? { ...project, title: title.trim() || project.title, updatedAt: new Date().toISOString() } : project)),
|
||||
})),
|
||||
deleteProjects: (ids) =>
|
||||
set((state) => {
|
||||
const projects = state.projects.filter((project) => !ids.includes(project.id));
|
||||
return { projects };
|
||||
}),
|
||||
replaceProjects: (projects) => set({ projects }),
|
||||
updateProject: (id, patch) =>
|
||||
set((state) => ({
|
||||
projects: state.projects.map((project) => (project.id === id ? { ...project, ...patch, updatedAt: new Date().toISOString() } : project)),
|
||||
})),
|
||||
}),
|
||||
{
|
||||
name: CANVAS_STORE_KEY,
|
||||
storage: canvasStorage,
|
||||
partialize: (state) =>
|
||||
({
|
||||
projects: state.projects,
|
||||
}) as StorageValue<CanvasStore>["state"],
|
||||
onRehydrateStorage: () => () => {
|
||||
useCanvasStore.setState({ hydrated: true });
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
import { create } from "zustand";
|
||||
|
||||
type CanvasUiStore = {
|
||||
editingProjectId: string | null;
|
||||
editingProjectTitle: string;
|
||||
selectedProjectIds: string[];
|
||||
deleteProjectIds: string[];
|
||||
startEditingProject: (id: string, title: string) => void;
|
||||
setEditingProjectTitle: (title: string) => void;
|
||||
stopEditingProject: () => void;
|
||||
toggleSelectedProjectId: (id: string, selected: boolean) => void;
|
||||
setDeleteProjectIds: (ids: string[]) => void;
|
||||
removeSelectedProjectIds: (ids: string[]) => void;
|
||||
};
|
||||
|
||||
export const useCanvasUiStore = create<CanvasUiStore>((set) => ({
|
||||
editingProjectId: null,
|
||||
editingProjectTitle: "",
|
||||
selectedProjectIds: [],
|
||||
deleteProjectIds: [],
|
||||
startEditingProject: (editingProjectId, editingProjectTitle) => set({ editingProjectId, editingProjectTitle }),
|
||||
setEditingProjectTitle: (editingProjectTitle) => set({ editingProjectTitle }),
|
||||
stopEditingProject: () => set({ editingProjectId: null }),
|
||||
toggleSelectedProjectId: (id, selected) => set((state) => ({ selectedProjectIds: selected ? [...new Set([...state.selectedProjectIds, id])] : state.selectedProjectIds.filter((item) => item !== id) })),
|
||||
setDeleteProjectIds: (deleteProjectIds) => set({ deleteProjectIds }),
|
||||
removeSelectedProjectIds: (ids) => set((state) => ({ selectedProjectIds: state.selectedProjectIds.filter((id) => !ids.includes(id)) })),
|
||||
}));
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
import { persist, type PersistStorage, type StorageValue } from "zustand/middleware";
|
||||
|
||||
@@ -89,7 +87,7 @@ export const useAssetStore = create<AssetStore>()(
|
||||
replaceAssets: (assets) => set({ assets }),
|
||||
cleanupImages: (extra) => {
|
||||
window.setTimeout(async () => {
|
||||
const { useCanvasStore } = await import("@/app/(user)/canvas/stores/use-canvas-store");
|
||||
const { useCanvasStore } = await import("@/stores/canvas/use-canvas-store");
|
||||
await cleanupUnusedImages({ assets: get().assets, projects: useCanvasStore.getState().projects, extra });
|
||||
await cleanupUnusedMedia({ assets: get().assets, projects: useCanvasStore.getState().projects, extra });
|
||||
}, 0);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
export type LocalUser = {
|
||||
|
||||
Reference in New Issue
Block a user