mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-25 17:57:06 +00:00
feat(editor): opt-in diff preview before save (#855)
* feat(editor): add useComposeDiffPreviewEnabled hook * feat(editor): add ComposeDiffPreviewDialog component * fix(editor): replace HTML entity with Unicode arrow in ComposeDiffPreviewDialog * feat(editor): add diff preview toggle to Appearance settings Added a new 'Diff preview before save' toggle in the Display section of the Appearance settings panel. Users can now enable or disable the side-by-side diff view before compose and env file edits are saved to disk. * feat(editor): wire diff preview dialog into compose save flow * fix(editor): snapshot diff content at open time and fix event name - Fix useComposeDiffPreviewEnabled and useDeployFeedbackEnabled to use the canonical SENCHO_SETTINGS_CHANGED constant from @/lib/events instead of the hardcoded string literal (wrong value) - Snapshot language, original, modified, and fileName into diffPreview state at click time to prevent tab-switching from corrupting dialog content mid-review - Remove React.MouseEvent from diffPreview state; pass a no-op stub to deployStack in the confirm path (preventDefault/stopPropagation are no-ops on an already-settled event anyway) - Add diff-modal screenshot and document the feature in editor.mdx and settings.mdx * docs(editor): add settings-toggle screenshot for diff preview feature
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { SENCHO_SETTINGS_CHANGED } from '@/lib/events';
|
||||
|
||||
export const COMPOSE_DIFF_PREVIEW_KEY = 'sencho.compose-editor.diff-preview.enabled';
|
||||
|
||||
function readStored(): boolean {
|
||||
if (typeof window === 'undefined') return false;
|
||||
try {
|
||||
return window.localStorage.getItem(COMPOSE_DIFF_PREVIEW_KEY) === 'true';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function useComposeDiffPreviewEnabled(): [boolean, (next: boolean) => void] {
|
||||
const [enabled, setEnabledState] = useState<boolean>(readStored);
|
||||
|
||||
useEffect(() => {
|
||||
function onSettingsChanged() {
|
||||
setEnabledState(readStored());
|
||||
}
|
||||
window.addEventListener(SENCHO_SETTINGS_CHANGED, onSettingsChanged);
|
||||
return () => window.removeEventListener(SENCHO_SETTINGS_CHANGED, onSettingsChanged);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
function onStorage(event: StorageEvent) {
|
||||
if (event.key !== COMPOSE_DIFF_PREVIEW_KEY) return;
|
||||
setEnabledState(event.newValue === 'true');
|
||||
}
|
||||
window.addEventListener('storage', onStorage);
|
||||
return () => window.removeEventListener('storage', onStorage);
|
||||
}, []);
|
||||
|
||||
const setEnabled = useCallback((next: boolean) => {
|
||||
try {
|
||||
window.localStorage.setItem(COMPOSE_DIFF_PREVIEW_KEY, next ? 'true' : 'false');
|
||||
} catch {
|
||||
// ignore; localStorage may be unavailable (private mode, quota)
|
||||
}
|
||||
setEnabledState(next);
|
||||
window.dispatchEvent(new CustomEvent(SENCHO_SETTINGS_CHANGED));
|
||||
}, []);
|
||||
|
||||
return [enabled, setEnabled];
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { SENCHO_SETTINGS_CHANGED } from '@/lib/events';
|
||||
|
||||
export const DEPLOY_FEEDBACK_KEY = 'sencho.deploy-feedback.enabled';
|
||||
|
||||
@@ -18,8 +19,8 @@ export function useDeployFeedbackEnabled(): [boolean, (next: boolean) => void] {
|
||||
function onSettingsChanged() {
|
||||
setEnabledState(readStored());
|
||||
}
|
||||
window.addEventListener('SENCHO_SETTINGS_CHANGED', onSettingsChanged);
|
||||
return () => window.removeEventListener('SENCHO_SETTINGS_CHANGED', onSettingsChanged);
|
||||
window.addEventListener(SENCHO_SETTINGS_CHANGED, onSettingsChanged);
|
||||
return () => window.removeEventListener(SENCHO_SETTINGS_CHANGED, onSettingsChanged);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -38,7 +39,7 @@ export function useDeployFeedbackEnabled(): [boolean, (next: boolean) => void] {
|
||||
// ignore; localStorage may be unavailable (private mode, quota)
|
||||
}
|
||||
setEnabledState(next);
|
||||
window.dispatchEvent(new CustomEvent('SENCHO_SETTINGS_CHANGED'));
|
||||
window.dispatchEvent(new CustomEvent(SENCHO_SETTINGS_CHANGED));
|
||||
}, []);
|
||||
|
||||
return [enabled, setEnabled];
|
||||
|
||||
Reference in New Issue
Block a user