0.4.0 convex及界面修改
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
export type ThemeMode = "system" | "light" | "dark";
|
||||
|
||||
type PersistedPreferences = {
|
||||
theme: ThemeMode;
|
||||
showStructure: boolean;
|
||||
spellCheck: boolean;
|
||||
flightMode: boolean;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = "mnote:preferences:v1";
|
||||
|
||||
const loadPersisted = (): PersistedPreferences | null => {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as Partial<PersistedPreferences>;
|
||||
const theme = parsed.theme === "light" || parsed.theme === "dark" || parsed.theme === "system" ? parsed.theme : "system";
|
||||
return {
|
||||
theme,
|
||||
showStructure: typeof parsed.showStructure === "boolean" ? parsed.showStructure : false,
|
||||
spellCheck: typeof parsed.spellCheck === "boolean" ? parsed.spellCheck : false,
|
||||
flightMode: typeof parsed.flightMode === "boolean" ? parsed.flightMode : false,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const persist = (value: PersistedPreferences) => {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
interface AppPreferencesState extends PersistedPreferences {
|
||||
hydrated: boolean;
|
||||
hydrate: () => void;
|
||||
setTheme: (theme: ThemeMode) => void;
|
||||
setShowStructure: (showStructure: boolean) => void;
|
||||
setSpellCheck: (spellCheck: boolean) => void;
|
||||
setFlightMode: (flightMode: boolean) => void;
|
||||
}
|
||||
|
||||
export const useAppPreferencesStore = create<AppPreferencesState>((set, get) => ({
|
||||
hydrated: false,
|
||||
theme: "system",
|
||||
showStructure: false,
|
||||
spellCheck: false,
|
||||
flightMode: false,
|
||||
hydrate: () => {
|
||||
const current = get();
|
||||
if (current.hydrated) return;
|
||||
const persisted = loadPersisted();
|
||||
if (persisted) {
|
||||
set({ ...persisted, hydrated: true });
|
||||
} else {
|
||||
set({ hydrated: true });
|
||||
}
|
||||
},
|
||||
setTheme: (theme) =>
|
||||
set((state) => {
|
||||
const next = { ...state, theme };
|
||||
persist({
|
||||
theme: next.theme,
|
||||
showStructure: next.showStructure,
|
||||
spellCheck: next.spellCheck,
|
||||
flightMode: next.flightMode,
|
||||
});
|
||||
return next;
|
||||
}),
|
||||
setShowStructure: (showStructure) =>
|
||||
set((state) => {
|
||||
const next = { ...state, showStructure };
|
||||
persist({
|
||||
theme: next.theme,
|
||||
showStructure: next.showStructure,
|
||||
spellCheck: next.spellCheck,
|
||||
flightMode: next.flightMode,
|
||||
});
|
||||
return next;
|
||||
}),
|
||||
setSpellCheck: (spellCheck) =>
|
||||
set((state) => {
|
||||
const next = { ...state, spellCheck };
|
||||
persist({
|
||||
theme: next.theme,
|
||||
showStructure: next.showStructure,
|
||||
spellCheck: next.spellCheck,
|
||||
flightMode: next.flightMode,
|
||||
});
|
||||
return next;
|
||||
}),
|
||||
setFlightMode: (flightMode) =>
|
||||
set((state) => {
|
||||
const next = { ...state, flightMode };
|
||||
persist({
|
||||
theme: next.theme,
|
||||
showStructure: next.showStructure,
|
||||
spellCheck: next.spellCheck,
|
||||
flightMode: next.flightMode,
|
||||
});
|
||||
return next;
|
||||
}),
|
||||
}));
|
||||
@@ -0,0 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import { create } from "zustand";
|
||||
|
||||
export type CommentTarget = {
|
||||
workspaceId: string;
|
||||
documentId: string;
|
||||
blockId: string | null;
|
||||
};
|
||||
|
||||
interface CommentsUiState {
|
||||
open: boolean;
|
||||
target: CommentTarget | null;
|
||||
openForPage: (args: { workspaceId: string; documentId: string }) => void;
|
||||
openForBlock: (args: { workspaceId: string; documentId: string; blockId: string }) => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export const useCommentsUiStore = create<CommentsUiState>((set) => ({
|
||||
open: false,
|
||||
target: null,
|
||||
openForPage: ({ workspaceId, documentId }) =>
|
||||
set({
|
||||
open: true,
|
||||
target: { workspaceId, documentId, blockId: null },
|
||||
}),
|
||||
openForBlock: ({ workspaceId, documentId, blockId }) =>
|
||||
set({
|
||||
open: true,
|
||||
target: { workspaceId, documentId, blockId },
|
||||
}),
|
||||
close: () => set({ open: false }),
|
||||
}));
|
||||
|
||||
@@ -12,11 +12,24 @@ export interface EditorReferenceBridgeResult {
|
||||
export interface EditorReferenceBridge {
|
||||
insertInlineReference: (target: ReferenceTarget, alias?: string) => EditorReferenceBridgeResult;
|
||||
insertEmbedReference: (target: ReferenceTarget) => EditorReferenceBridgeResult;
|
||||
undo?: () => void;
|
||||
redo?: () => void;
|
||||
getCursorBlockId?: () => string | null;
|
||||
/**
|
||||
* 向当前打开的文档插入一个“附件/媒体”块。
|
||||
* 主要用于侧边栏文件树拖拽上传后,把文件显示到主编辑区。
|
||||
*/
|
||||
insertMediaAsset?: (asset: MediaAsset) => void;
|
||||
/**
|
||||
* 向当前打开的文档插入一个“思维导图”块(使用 mindmapId 作为 block.id)。
|
||||
* 主要用于侧边栏垃圾桶“恢复思维导图”后,把导图重新显示到主编辑区。
|
||||
*/
|
||||
insertMindmapAsset?: (args: { documentId: string; mindmapId: string }) => void;
|
||||
/**
|
||||
* 向当前打开的文档插入一个“在线表格(luckysheet)”块。
|
||||
* 主要用于侧边栏垃圾桶“恢复表格”后,把表格重新显示到主编辑区。
|
||||
*/
|
||||
insertOnlineTableAsset?: (args: { documentId: string; tableId: string }) => void;
|
||||
replaceWithSnapshot: (blocks: Json) => void;
|
||||
openTableFullScreen?: (tableId: string) => void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user