diff --git a/web/src/components/MemoEditor/components/EditorToolbar.tsx b/web/src/components/MemoEditor/components/EditorToolbar.tsx index 15e8db194816a..098fcd206325e 100644 --- a/web/src/components/MemoEditor/components/EditorToolbar.tsx +++ b/web/src/components/MemoEditor/components/EditorToolbar.tsx @@ -1,3 +1,4 @@ +import { Eye, EyeOff } from "lucide-react"; import type { FC } from "react"; import { Button } from "@/components/ui/button"; import { validationService } from "../services"; @@ -11,6 +12,7 @@ export const EditorToolbar: FC = ({ onSave, onCancel, memoNa const { valid } = validationService.canSave(state); const isSaving = state.ui.isLoading.saving; + const is_preview_mode = state.ui.isPreviewMode; const handleLocationChange = (location: typeof state.metadata.location) => { dispatch(actions.setMetadata({ location })); @@ -20,13 +22,17 @@ export const EditorToolbar: FC = ({ onSave, onCancel, memoNa dispatch(actions.toggleFocusMode()); }; + const handleTogglePreviewMode = () => { + dispatch(actions.togglePreviewMode()); + }; + const handleVisibilityChange = (visibility: typeof state.metadata.visibility) => { dispatch(actions.setMetadata({ visibility })); }; return (
-
+
= ({ onSave, onCancel, memoNa onToggleFocusMode={handleToggleFocusMode} memoName={memoName} /> +
diff --git a/web/src/components/MemoEditor/hooks/useKeyboard.ts b/web/src/components/MemoEditor/hooks/useKeyboard.ts index e90537503eb5c..4e3664e235fd1 100644 --- a/web/src/components/MemoEditor/hooks/useKeyboard.ts +++ b/web/src/components/MemoEditor/hooks/useKeyboard.ts @@ -3,15 +3,22 @@ import type { EditorRefActions } from "../Editor"; interface UseKeyboardOptions { onSave: () => void; + onTogglePreview?: () => void; } export const useKeyboard = (_editorRef: React.RefObject, options: UseKeyboardOptions) => { useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { + // Cmd/Ctrl + Enter to save if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { event.preventDefault(); options.onSave(); } + // Cmd/Ctrl + Shift + P to toggle preview + if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === "p") { + event.preventDefault(); + options.onTogglePreview?.(); + } }; window.addEventListener("keydown", handleKeyDown); diff --git a/web/src/components/MemoEditor/index.tsx b/web/src/components/MemoEditor/index.tsx index 2d0b0169cf50b..becc8c24c24f9 100644 --- a/web/src/components/MemoEditor/index.tsx +++ b/web/src/components/MemoEditor/index.tsx @@ -1,6 +1,8 @@ import { useQueryClient } from "@tanstack/react-query"; import { useRef } from "react"; import { toast } from "react-hot-toast"; +import MemoContent from "@/components/MemoContent"; +import { MemoViewContext } from "@/components/MemoView/MemoViewContext"; import useCurrentUser from "@/hooks/useCurrentUser"; import { memoKeys } from "@/hooks/useMemoQueries"; import { userKeys } from "@/hooks/useUserQueries"; @@ -62,7 +64,11 @@ const MemoEditorImpl: React.FC = ({ dispatch(actions.toggleFocusMode()); }; - useKeyboard(editorRef, { onSave: handleSave }); + const handleTogglePreviewMode = () => { + dispatch(actions.togglePreviewMode()); + }; + + useKeyboard(editorRef, { onSave: handleSave, onTogglePreview: handleTogglePreviewMode }); async function handleSave() { // Validate before saving @@ -136,7 +142,26 @@ const MemoEditorImpl: React.FC = ({ {/* Editor content grows to fill available space in focus mode */} - + {state.ui.isPreviewMode ? ( +
+ + + +
+ ) : ( + + )} {/* Metadata and toolbar grouped together at bottom */}
diff --git a/web/src/components/MemoEditor/services/memoService.ts b/web/src/components/MemoEditor/services/memoService.ts index 53b553293ccc7..76dc9282323e6 100644 --- a/web/src/components/MemoEditor/services/memoService.ts +++ b/web/src/components/MemoEditor/services/memoService.ts @@ -135,6 +135,7 @@ export const memoService = { }, ui: { isFocusMode: false, + isPreviewMode: false, isLoading: { saving: false, uploading: false, diff --git a/web/src/components/MemoEditor/state/actions.ts b/web/src/components/MemoEditor/state/actions.ts index ec46bd05a62fa..79939d9b46b7e 100644 --- a/web/src/components/MemoEditor/state/actions.ts +++ b/web/src/components/MemoEditor/state/actions.ts @@ -57,6 +57,10 @@ export const editorActions = { type: "TOGGLE_FOCUS_MODE", }), + togglePreviewMode: (): EditorAction => ({ + type: "TOGGLE_PREVIEW_MODE", + }), + setLoading: (key: LoadingKey, value: boolean): EditorAction => ({ type: "SET_LOADING", payload: { key, value }, diff --git a/web/src/components/MemoEditor/state/reducer.ts b/web/src/components/MemoEditor/state/reducer.ts index cc935f2bf4979..715b47bee4a82 100644 --- a/web/src/components/MemoEditor/state/reducer.ts +++ b/web/src/components/MemoEditor/state/reducer.ts @@ -89,6 +89,15 @@ export function editorReducer(state: EditorState, action: EditorAction): EditorS }, }; + case "TOGGLE_PREVIEW_MODE": + return { + ...state, + ui: { + ...state.ui, + isPreviewMode: !state.ui.isPreviewMode, + }, + }; + case "SET_LOADING": return { ...state, diff --git a/web/src/components/MemoEditor/state/types.ts b/web/src/components/MemoEditor/state/types.ts index 48289b210245d..1cd40e4f00553 100644 --- a/web/src/components/MemoEditor/state/types.ts +++ b/web/src/components/MemoEditor/state/types.ts @@ -15,6 +15,7 @@ export interface EditorState { }; ui: { isFocusMode: boolean; + isPreviewMode: boolean; isLoading: { saving: boolean; uploading: boolean; @@ -42,6 +43,7 @@ export type EditorAction = | { type: "REMOVE_LOCAL_FILE"; payload: string } | { type: "CLEAR_LOCAL_FILES" } | { type: "TOGGLE_FOCUS_MODE" } + | { type: "TOGGLE_PREVIEW_MODE" } | { type: "SET_LOADING"; payload: { key: LoadingKey; value: boolean } } | { type: "SET_DRAGGING"; payload: boolean } | { type: "SET_COMPOSING"; payload: boolean } @@ -57,6 +59,7 @@ export const initialState: EditorState = { }, ui: { isFocusMode: false, + isPreviewMode: false, isLoading: { saving: false, uploading: false,