From a25acbec7c1d9f933f8ebe78326f10efe79bf742 Mon Sep 17 00:00:00 2001 From: Anso Date: Thu, 30 Apr 2026 22:01:17 -0400 Subject: [PATCH] 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 --- docs/features/editor.mdx | 17 +++ .../compose-diff-preview/diff-modal.png | Bin 0 -> 48720 bytes .../compose-diff-preview/settings-toggle.png | Bin 0 -> 27266 bytes docs/reference/settings.mdx | 22 ++++ .../components/ComposeDiffPreviewDialog.tsx | 103 ++++++++++++++++++ frontend/src/components/EditorLayout.tsx | 79 +++++++++++++- .../components/settings/AppearanceSection.tsx | 21 ++++ .../hooks/use-compose-diff-preview-enabled.ts | 46 ++++++++ .../src/hooks/use-deploy-feedback-enabled.ts | 7 +- 9 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 docs/images/compose-diff-preview/diff-modal.png create mode 100644 docs/images/compose-diff-preview/settings-toggle.png create mode 100644 frontend/src/components/ComposeDiffPreviewDialog.tsx create mode 100644 frontend/src/hooks/use-compose-diff-preview-enabled.ts diff --git a/docs/features/editor.mdx b/docs/features/editor.mdx index b32033d4..c403f725 100644 --- a/docs/features/editor.mdx +++ b/docs/features/editor.mdx @@ -41,6 +41,23 @@ The `.env` editor has the same save/discard controls as the compose editor. Chan Sencho reads the `env_file:` paths from your `compose.yaml` to discover available env files. If no `env_file:` is declared, a default `.env` in the stack directory is used. +## Diff preview before save + +When the **Diff preview before save** toggle is enabled in **Settings → Appearance**, clicking **Save & Deploy** or **Save Only** opens a side-by-side diff modal before anything is written to disk. The left pane shows the current on-disk content; the right pane shows your unsaved edits, with additions highlighted in green. + + + Diff preview modal showing side-by-side YAML diff with a new comment line highlighted in green on the right + + +Review the diff, then: + +- Click **Save & deploy** (or **Save**) to confirm and write the changes to disk. +- Click **Cancel** to return to the editor without saving. + +If there are no unsaved changes the modal is skipped and the save proceeds directly. + +The toggle is off by default and saved per browser. Each device remembers its own setting independently. Enable it in **Settings → Appearance → Diff preview before save**. + ## Container panel The left column lists all containers that belong to the selected stack under the **CONTAINERS** heading. Each container shows: diff --git a/docs/images/compose-diff-preview/diff-modal.png b/docs/images/compose-diff-preview/diff-modal.png new file mode 100644 index 0000000000000000000000000000000000000000..88679ba8699c929805b097f8582a8824e2ddcbe8 GIT binary patch literal 48720 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 bV=$l^6Sl9j_;8lMCm;zCS>a+qU7!B~UY>+q literal 0 HcmV?d00001 diff --git a/docs/images/compose-diff-preview/settings-toggle.png b/docs/images/compose-diff-preview/settings-toggle.png new file mode 100644 index 0000000000000000000000000000000000000000..788dbcbd63febcef4c11d707db8e56796ad00846 GIT binary patch literal 27266 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

Bin*W__KS6ROi{$<`1m&U`*eRF%G_ctXXUQsmy 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 Zja-Hv-Je|=G2lNpWF-|OieA3;{y#}~)TjUe literal 0 HcmV?d00001 diff --git a/docs/reference/settings.mdx b/docs/reference/settings.mdx index b913df38..62586f05 100644 --- a/docs/reference/settings.mdx +++ b/docs/reference/settings.mdx @@ -71,6 +71,28 @@ Control how much information Sencho packs on screen. Each browser you sign in fr Density affects the dashboard stack table, the resource gauge strip, the Settings Hub sidebar, the Schedules and Audit Log tables, and every other data table in Sencho. Typography, color, and layout structure stay the same; only vertical padding compresses. +### Deploy progress modal + +When enabled, Sencho streams live output in a modal whenever you deploy, restart, update, install, or run a Git operation. The modal closes automatically on success or stays open on failure so you can read the error output. + +| Value | Behavior | +|-------|----------| +| **Enabled** | A progress modal opens for every long-running operation | +| **Disabled** (default) | Operations run silently; results surface via toast notifications only | + +### Diff preview before save + +When enabled, clicking **Save & Deploy** or **Save Only** in the compose or env editor opens a side-by-side diff modal before writing anything to disk. The left pane shows the current on-disk content; the right pane shows your unsaved edits, with additions highlighted green and removals highlighted red. + +| Value | Behavior | +|-------|----------| +| **Enabled** | Diff modal opens on every save that has unsaved changes | +| **Disabled** (default) | File is saved directly without a review step | + +If there are no unsaved changes the modal is skipped and the save proceeds immediately. + +See [Diff preview before save](/features/editor#diff-preview-before-save) in the Editor guide for screenshots and the full workflow. + --- ## License diff --git a/frontend/src/components/ComposeDiffPreviewDialog.tsx b/frontend/src/components/ComposeDiffPreviewDialog.tsx new file mode 100644 index 00000000..8e632ad6 --- /dev/null +++ b/frontend/src/components/ComposeDiffPreviewDialog.tsx @@ -0,0 +1,103 @@ +import { Suspense } from 'react'; +import { DiffEditor } from '@/lib/monacoLoader'; +import { FileDiff, Loader2 } from 'lucide-react'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; + +export interface ComposeDiffPreviewDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + stackName: string; + fileName: string; + language: 'yaml' | 'ini'; + original: string; + modified: string; + actionLabel: 'Save' | 'Save & deploy'; + confirming: boolean; + isDarkMode: boolean; + onConfirm: () => void | Promise; +} + +export function ComposeDiffPreviewDialog({ + open, + onOpenChange, + stackName, + fileName, + language, + original, + modified, + actionLabel, + confirming, + isDarkMode, + onConfirm, +}: ComposeDiffPreviewDialogProps) { + return ( + + + + + + Review changes to {stackName} + {fileName} + + + Review the diff between on-disk content and unsaved editor changes before saving. + + + +
+
+ }> + + +
+
+ + + ON DISK → UNSAVED +
+ + +
+
+
+
+ ); +} diff --git a/frontend/src/components/EditorLayout.tsx b/frontend/src/components/EditorLayout.tsx index 628c2941..4df6b8b1 100644 --- a/frontend/src/components/EditorLayout.tsx +++ b/frontend/src/components/EditorLayout.tsx @@ -78,6 +78,8 @@ import type { FilterChip, StackMenuCtx } from '@/components/sidebar/sidebar-type import { useBulkStackActions, type BulkAction } from '@/hooks/useBulkStackActions'; import { isInputFocused, isPaletteOpen } from '@/lib/keyboard-guards'; import { StackFileExplorer } from '@/components/files/StackFileExplorer'; +import { useComposeDiffPreviewEnabled } from '@/hooks/use-compose-diff-preview-enabled'; +import { ComposeDiffPreviewDialog } from '@/components/ComposeDiffPreviewDialog'; interface ContainerInfo { Id: string; @@ -328,6 +330,7 @@ export default function EditorLayout() { window.matchMedia('(prefers-color-scheme: dark)').matches ); const isDarkMode = theme === 'dark' || (theme === 'auto' && systemDark); + const [diffPreviewEnabled] = useComposeDiffPreviewEnabled(); const [activeView, setActiveView] = useState<'dashboard' | 'editor' | 'host-console' | 'resources' | 'templates' | 'global-observability' | 'fleet' | 'audit-log' | 'scheduled-ops' | 'auto-updates' | 'settings'>('dashboard'); const [settingsSection, setSettingsSection] = useState('appearance'); const [securityHistoryOpen, setSecurityHistoryOpen] = useState(false); @@ -335,6 +338,14 @@ export default function EditorLayout() { const [schedulePrefill, setSchedulePrefill] = useState(null); const handlePrefillConsumed = useCallback(() => setSchedulePrefill(null), []); const [isEditing, setIsEditing] = useState(false); + const [diffPreview, setDiffPreview] = useState<{ + mode: 'save' | 'save-and-deploy'; + language: 'yaml' | 'ini'; + original: string; + modified: string; + fileName: string; + } | null>(null); + const [diffPreviewConfirming, setDiffPreviewConfirming] = useState(false); const [editingCompose, setEditingCompose] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [stackStatuses, setStackStatuses] = useState({}); @@ -1260,6 +1271,40 @@ export default function EditorLayout() { } }; + const requestSave = () => { + const isCompose = activeTab === 'compose'; + const orig = isCompose ? originalContent : originalEnvContent; + const curr = isCompose ? content : envContent; + if (diffPreviewEnabled && activeTab !== 'files' && curr !== orig) { + setDiffPreview({ + mode: 'save', + language: isCompose ? 'yaml' : 'ini', + original: orig, + modified: curr, + fileName: isCompose ? 'compose.yaml' : (selectedEnvFile || '.env'), + }); + } else { + void saveFile(); + } + }; + + const requestSaveAndDeploy = (e: React.MouseEvent) => { + const isCompose = activeTab === 'compose'; + const orig = isCompose ? originalContent : originalEnvContent; + const curr = isCompose ? content : envContent; + if (diffPreviewEnabled && activeTab !== 'files' && curr !== orig) { + setDiffPreview({ + mode: 'save-and-deploy', + language: isCompose ? 'yaml' : 'ini', + original: orig, + modified: curr, + fileName: isCompose ? 'compose.yaml' : (selectedEnvFile || '.env'), + }); + } else { + void handleSaveAndDeploy(e); + } + }; + const rollbackStack = async () => { if (!selectedFile || isStackBusy(selectedFile)) return; const stackFile = selectedFile; @@ -2866,7 +2911,7 @@ export default function EditorLayout() { ) : (
- @@ -2877,7 +2922,7 @@ export default function EditorLayout() { - + Save Only @@ -3199,6 +3244,36 @@ export default function EditorLayout() { onClose={() => setStackMisconfigScanId(null)} /> + {/* Compose diff preview */} + { if (!open && !diffPreviewConfirming) setDiffPreview(null); }} + stackName={selectedFile ? selectedFile.replace(/\.(yml|yaml)$/, '') : ''} + fileName={diffPreview?.fileName ?? ''} + language={diffPreview?.language ?? 'yaml'} + original={diffPreview?.original ?? ''} + modified={diffPreview?.modified ?? ''} + actionLabel={diffPreview?.mode === 'save-and-deploy' ? 'Save & deploy' : 'Save'} + confirming={diffPreviewConfirming} + isDarkMode={isDarkMode} + onConfirm={async () => { + const snapshot = diffPreview; + setDiffPreviewConfirming(true); + try { + if (snapshot?.mode === 'save-and-deploy') { + await saveFile(); + // e.preventDefault/stopPropagation are no-ops here; no browser event is in flight + await deployStack({ preventDefault() {}, stopPropagation() {} } as unknown as React.MouseEvent); + } else { + await saveFile(); + } + } finally { + setDiffPreviewConfirming(false); + setDiffPreview(null); + } + }} + /> + {/* Scan history overlay */} = { export function AppearanceSection() { const [density, setDensity] = useDensity(); const [isEnabled, setEnabled] = useDeployFeedbackEnabled(); + const [diffPreviewEnabled, setDiffPreviewEnabled] = useComposeDiffPreviewEnabled(); return (
@@ -55,6 +57,25 @@ export function AppearanceSection() {
+ + +
+ setDiffPreviewEnabled(v === true)} + /> + +
+

diff --git a/frontend/src/hooks/use-compose-diff-preview-enabled.ts b/frontend/src/hooks/use-compose-diff-preview-enabled.ts new file mode 100644 index 00000000..c6658599 --- /dev/null +++ b/frontend/src/hooks/use-compose-diff-preview-enabled.ts @@ -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(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]; +} diff --git a/frontend/src/hooks/use-deploy-feedback-enabled.ts b/frontend/src/hooks/use-deploy-feedback-enabled.ts index 40c8af3c..74ca9b06 100644 --- a/frontend/src/hooks/use-deploy-feedback-enabled.ts +++ b/frontend/src/hooks/use-deploy-feedback-enabled.ts @@ -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];