Repository navigation
Cathet v5.0.0 Release Notes
1. Title
Cathet v5.0.0 — Dual Pane Side-by-Side Architecture, Proportional and Offset Scroll Synchronization, Spatial Dual Drop Targets, Dynamic OS Window Constraints, Read-Only Save Protection, and UI Refinements
2. Target of the Change
The primary targets of this release are:
- Introducing Dual Pane mode to enable simultaneous side-by-side editing, viewing, and comparison of two independent documents within a single native window instance.
- Providing high-precision, bidirectional proportional scroll synchronization between panes with user-configurable relative offset preservation and 1:1 ratio snap modes.
- Implementing simultaneous dual-zone drag-and-drop spatial targeting with live cursor tracking across DOM and native Windows OLE events.
- Enforcing hardware-level Win32 Desktop Window Manager (DWM) minimum window constraints (200x300 single-pane default, automatically elevated to 350x350 in Dual Mode) to permanently prevent UI squishing and control clipping.
- Implementing document baseline dirty tracking to disable redundant saves on unmodified files and intercepting save attempts on Windows files bearing the Read-Only attribute with a safe redirection dialog.
- Improving top-bar menu telemetry with active mode indicators, and resolving the Settings typography dropdown scroll collapse defect.
- Hardening the secondary document placeholder island against horizontal card bleeding, title text wrapping, and button margin clipping under compact viewports.
3. Summary of the Change
Cathet version 5.0.0 introduces a major architectural evolution, expanding the application from a single-document scratchpad into a dual-document workstation while preserving its strict anti-monolith modularity standards (each source module kept under 250 lines) and ultra-lightweight binary profile.
Key advancements include:
- Container-Scoped Multi-Instance Editor Engine: Decoupled
EditorComponentfrom global document element IDs into modular container-scoped DOM instances. Dual Pane mode mounts two fully isolated editor instances with per-pane state management, independent lossless Markdown Preview toggles, and unified window title telemetry. - Proportional Scroll Synchronization Engine (
SyncScrollController): Built an event-driven scroll coordinator utilizing normalized scroll ratios (0.0 to 1.0) with an atomic recursion guard preventing ping-pong event reverberation. Supports continuous offset-based synchronization (preserving relative scroll displacements when locked) alongside a user-selectable "Dual Pane Snap Scroll" mode. - Dual-Zone Spatial Drop Targets (
DropOverlay): Replaced the single-document overlay curtain with simultaneous side-by-side frosted glass drop zones. Live cursor coordinates normalized againstdevicePixelRatiodynamically illuminate the hovered pane with radiant accent feedback while keeping the opposite pane visible as an idle target. - Dynamic OS DWM Window Size Constraints: Added the
set_window_min_sizebackend command. Cathet enforces a default floor of 200x300 in single-pane mode, dynamically enforces a 350x350 floor upon entering Dual Pane mode (auto-expanding the window if needed), and cleanly restores single-pane bounds upon exiting. - Baseline Dirty Tracking and Windows Read-Only Guard: Integrated baseline text tracking to disable the "Save" menu action when files are up-to-date, preventing unnecessary disk writes. Implemented backend Win32 metadata inspection (
is_file_readonly) to catch read-only files before write execution and prompt users via a frosted glass dialog to use "Save As...". - Typography Menu Scroll Repair: Resolved wheel event propagation in
FontSelect.tsso scrolling within the font dropdown menu no longer triggers ancestor scroll listeners, accompanied byoverscroll-behavior: contain, auto-scroll to the selected item, and an integrated 3px micro-scrollbar thumb. - Responsive Placeholder Island Hardening: Enforced container boundary masking (
overflow: hidden) andbox-sizing: border-boxacross.dualpane-empty-cardand its action buttons, preventing border collisions and title bleed on narrow viewports.
4. Detailed Breakdown
4.1 Dual Pane Side-by-Side Editing Architecture & Lifecycle
- Problem: Users handling comparative note-taking, translation, documentation editing, or side-by-side reference required running multiple OS window instances or manually juggling window positioning. Prior to v5.0.0, Cathet's UI architecture assumed a singleton
EditorComponentbound to fixed DOM identifiers (#editor-wrap,#editor,#markdown-preview). - Solution & Technical Details:
- Decoupled
src-ui/src/components/Editor.tsby replacing hardcoded ID lookups with container-scoped class queries (.cathet-editor-wrap,.cathet-editor,.cathet-markdown-preview). Exposed modular accessorsgetWrapperEl()andfocus()to support dynamic lifecycle attachment. - Implemented
src-ui/src/components/dualpane/DualPaneView.ts(orchestrator) managing twin pane DOM slots (#dualpane-left-slotand#dualpane-right-slot), active pane tracking, and layout state. - Built
src-ui/src/components/dualpane/DualPaneHeader.tsproviding a per-pane frosted glass titlebar strip featuring the active filename, a theme-adaptive monochrome SVG folder icon, a lossless Markdown Preview mode toggle, a file open trigger, and a pane dismiss button. - Built
src-ui/src/components/dualpane/DualPaneSplitter.tsproviding an interactive divider with drag-based resize capabilities, min/max layout clamps, and an integrated sync lock badge. - Created
src-ui/src/services/dualPaneService.tsmanaging dual-pane state persistence, formatted window title generation (doc1.md | doc2.md - Cathet), and synchronization event dispatching. - Implemented keyboard shortcut handler in
src-ui/src/utils/shortcuts.tsbindingCtrl+Dto toggle Dual Pane mode. - Strict Modularity Compliance: All new modules were designed with isolated responsibilities, keeping every file strictly under the 250-line threshold (
DualPaneView.tsat 229 lines,DualPaneSplitter.tsat 124 lines,DualPaneHeader.tsat 81 lines,dualPaneService.tsat 76 lines).
- Decoupled
4.2 Proportional & Offset-Preserving Scroll Synchronization Engine
- Problem: Synchronizing two independent scroll containers often leads to infinite event feedback loops (ping-pong recursion). Furthermore, naive synchronization forces both documents to the exact same percentage offset, disorienting users who intentionally align document sections located at different proportional depths.
- Solution & Technical Details:
- Built
src-ui/src/utils/syncScrollController.tsusing normalized scroll ratios:
ratio = scrollTop / (scrollHeight - clientHeight) - Ping-Pong Recursion Prevention: Applied a boolean
isSyncingre-entrancy lock and targeted event listener coordination so driver container events do not trigger reciprocal updates from the follower container. - Dual Synchronization Modes:
- Offset-Preserved Synchronization (Snap Scroll OFF, Default): When the user locks the sync badge, the controller calculates the instantaneous ratio delta between panes:
ratioOffset = followerRatio - driverRatio
During subsequent scrolling, the follower pane tracks the driver while preserving this calculated relative offset:
targetRatio = clamp(driverRatio + ratioOffset, 0, 1) - Snap-to-Ratio Synchronization (Snap Scroll ON): When enabled via Settings, locking the sync badge immediately snaps the follower pane to match the driver's exact ratio (
targetRatio = driverRatio), ensuring 1:1 proportional alignment.
- Offset-Preserved Synchronization (Snap Scroll OFF, Default): When the user locks the sync badge, the controller calculates the instantaneous ratio delta between panes:
- Visual Telemetry on Splitter Badge:
- Anchored the sync badge squircle to the bottom of the splitter divider to avoid visual collision with document headers.
- Default Locked State: Renders a cosmic magenta/violet galaxy nebula gradient with radiant magenta aura.
- Snap Scroll Locked State: Dynamically toggles
.blue-galaxyon the sync badge, rendering an electric sapphire/cyan gradient with a radiant blue aura to indicate snap mode is active.
- Added "Dual Pane Snap Scroll" toggle in
SettingsPane.tswith cross-window event synchronization (cathet:snap-scroll-change) via Tauri IPC.
- Built
4.3 Simultaneous Dual Drop Zones & Spatial Drag-and-Drop Targeting
- Problem: In Dual Pane mode, dragging a file from Windows Explorer over the window previously displayed either a single full-window overlay or ambiguous drop boundaries, making it impossible to predict which pane would receive the file before releasing the mouse button.
- Solution & Technical Details:
- Overhauled
src-ui/src/components/DropOverlay.tsandsrc-ui/styles/editor.cssto render simultaneous side-by-side drop targets (.drop-zone-leftand.drop-zone-right). - Spatial Cursor Tracking: In
src-ui/src/services/dragDropService.ts, captured cursor coordinates across both browser DOMdragoverevents and native Windows OLE drag events forwarded by Tauri. - Device Pixel Ratio Normalization: Physical screen coordinates from native OS events are normalized via
window.devicePixelRatioagainstdualPaneView.getSplitterPosition(). - Dynamic State Toggling: The hovered target receives
.active-target(illuminated with a radiant accent border and glowing frosted glass backdrop), while the non-hovered pane receives.idle-target(translucent styling indicating availability). - Background Translucency & Clean Masking: Replaced opaque drop curtains with translucent frosted glass (
rgba(..., 0.45-0.55)withbackdrop-filter: blur(16px)). - Anti-Ghosting State: Toggled
is-draggingclass ondocument.bodyduring drag operations. Indualpane.css, applied rules to hide the underlying secondary placeholder island during file dragging, preventing visual collisions between placeholder text and drop zone cards.
- Overhauled
4.4 Dynamic OS-Level Window Minimum Constraints
- Problem: Shrinking the Cathet window to extreme dimensions (e.g. 150x150) caused the dual-pane headers, splitter controls, and text areas to overlap and become unclickable. Furthermore, fixed minimum dimensions in
tauri.conf.jsonapply globally and could not adapt to single-pane versus dual-pane layouts. - Solution & Technical Details:
- Configured baseline window constraints in
src-tauri/tauri.conf.jsonwith"minWidth": 200and"minHeight": 300, guaranteeing a minimum usable vertical editing area below the topbar. - Implemented
set_window_min_sizeinsrc-tauri/src/commands/window.rs, querying the active window viaapp_handleand invokingwindow.set_min_size(Some(LogicalSize::new(width, height))). - Auto-Expansion Logic: If current window dimensions are smaller than the requested minimum,
set_window_min_sizeautomatically expands the window to the threshold to prevent immediate clipping. - Registered command in
src-tauri/src/lib.rsand added frontend service wrappersetMinSizeinsrc-ui/src/services/windowService.ts. - Dual Mode Adaptive Enforcement: On entering Dual Pane mode, Cathet calls
setMinSize(350, 350). On exiting Dual Pane mode, it cleanly restores the single-pane floor of(200, 300).
- Configured baseline window constraints in
4.5 Baseline Document Dirty Tracking & Windows Read-Only Save Guard
- Problem:
- Selecting "Save" (
Ctrl+S) on an unmodified document triggered redundant disk write operations. - Attempting to save edits to a file flagged with the Windows Read-Only file attribute resulted in unhandled operating system permission rejections, creating a confusing user experience.
- Selecting "Save" (
- Solution & Technical Details:
- Baseline Dirty Tracking: Added
savedContentbaseline tracking toEditorComponentwithisModified(): booleanandmarkSaved(): void. When loaded or saved, the baseline is captured; comparisons ignore trivial whitespace variances. - Disabled Save on Up-to-Date Documents: In
mainMenuItems.ts, wireddisabled: ctx.isUpToDate. Styled.popup-menu-item.disabledinpopup.csswith dimmed opacity (0.42) andpointer-events: none. PreventedsaveDocumentfrom executing redundant writes whenisModified()is false. - Windows Read-Only Metadata Inspection: Implemented
is_file_readonlycommand insrc-tauri/src/commands/file.rsusingstd::fs::metadata(path)?.permissions().readonly(). - Proactive Read-Only Warning: Integrated
isFileReadonlycheck insrc-ui/src/main.tsprior to write execution. If read-only, Cathet aborts the overwrite and displays a frosted glass warning dialog (GlassDialog.ts) prompting the user: "This file is marked as read-only. Please save a copy using Save As to keep your changes."
- Baseline Dirty Tracking: Added
4.6 Active Dropdown Menu Highlights & UI Feedback
- Problem: The main window top-bar dropdown menu (
●) gave no visual indication of whether toggled features (such as Markdown Preview or Dual Pane mode) were currently active, requiring users to inspect document state or open settings to confirm mode status. - Solution & Technical Details:
- Extended
MenuIteminterface insrc-ui/src/components/PopupMenu.tswith optionalisActive?: booleanproperty. - In
src-ui/src/utils/mainMenuItems.ts, setisActive: ctx.isDualModeon the "Exit Dual Pane" action andisActive: ctx.isPreviewModeon "Markdown: Preview". - Styled
.popup-menu-item.activeinsrc-ui/styles/popup.csswith a 2px accent inset border, translucent highlight background tint, and high-contrast text. - Retained strict modularity with
PopupMenu.tsat 89 lines andmainMenuItems.tsat 48 lines.
- Extended
4.7 Settings Typography Font Dropdown Scrolling & Micro-Scrollbar
- Problem: Opening the font family dropdown in the Settings window and attempting to scroll down to view lower fonts caused the dropdown menu to instantly close. The ancestor scroll listener in
FontSelect.tsdetected mouse wheel events inside the menu and interpreted them as window scrolling, prematurely collapsing the popup. - Solution & Technical Details:
- Internal Scroll Event Isolation: In
src-ui/src/components/settings/FontSelect.ts, updatedscrollCloseHandlerto inspecte.target. If the target is contained within#font-select-menu, the close event is ignored. - Auto-Scroll to Active Option: Added logic upon opening to calculate the offset of the currently selected font item and execute
scrollIntoView({ block: "nearest" }). - CSS Enhancements (
src-ui/styles/settings.css):- Increased
#font-select-menumax-heightfrom 120px to 152px, comfortably displaying 5 to 6 items without cramped borders. - Added
overscroll-behavior: containto prevent wheel scroll chaining from bubbling up to the Settings card container. - Implemented a minimalist 3px micro-scrollbar thumb with theme-adaptive contrast and rounded corners.
- Increased
- Internal Scroll Event Isolation: In
4.8 Secondary Document Placeholder Layout Hardening & Button Symmetry
- Problem: In Dual Pane mode when the window was narrowed horizontally, the secondary pane placeholder island (
.dualpane-empty-card) suffered from multiple layout defects:- The title "Secondary Document" wrapped onto two lines and bled past the right dashed border.
- Default CSS
box-sizing: content-boxon action buttons added horizontal padding beyond 100% width, causing buttons to touch the right card border while leaving 10px margins on the left. - The secondary card featured an unnecessary
✕close button that duplicated the header close action.
- Solution & Technical Details:
- Boundary Masking: Added
overflow: hidden;to.dualpane-empty-cardinsrc-ui/styles/dualpane.cssso all internal child elements clip cleanly within the border-radius boundary. - Responsive Title Ellipsis: Configured
.dualpane-empty-titlewithbox-sizing: border-box; overflow: hidden; text-overflow: ellipsis; text-align: center;and enforcedwhite-space: nowrap;under container query breakpoints (@container (max-width: 175px)and@container (max-width: 140px)). Titles truncate cleanly (Secondary...) without line wrapping. - Symmetric Button Spacing: Applied
box-sizing: border-box;andmin-width: 0;to.dualpane-empty-btnand.dualpane-empty-actions, ensuringwidth: 100%calculates precisely against the card's inner content box, restoring symmetric 10px spacing on both left and right sides. - Typography Cleanup: Removed the redundant
+prefix symbol from action buttons (New Documentand compactNew) inDualPaneEmptyDropzone.ts. - Removed duplicate card close button, leaving pane closing to the dedicated header close button.
- Boundary Masking: Added
4.9 Commits Covered
The following 19 commits are covered in this release note:
60a9e6b— feat(dualpane): add sync scroll engine, dual-pane service, and modular utilities44efddd— feat(ui): implement frosted glass Dual Pane components and layout2f0a36c— feat(app): wire Dual Pane lifecycle, spatial drag-and-drop, and log historycae7142— style(ui): add monochrome icons and responsive layout stylese18edde— feat(scroll): implement offset-based scroll sync and snap-scroll state1ed87f3— feat(settings): add Dual Pane Snap Scroll setting togglee8d1e05— fix(dualpane): refine headers, sync icons, and fix initial preview statea1a4a8d— docs(history): log Dual Pane UI refinements, monochrome icons, and offset scroll sync4c626d5— feat(backend): implement dynamic window min-size command and default window constraintsff03614— fix(settings): fix font dropdown scroll collapse, enable overscroll contain, and add micro-scrollbar26d2e22— feat(drop-overlay): implement simultaneous dual drop zones, dynamic hover highlight, and frosted glass backdrop97eba3d— feat(dualpane): add secondary island New Document action, 350x350 window floor, and drag transparency967003f— docs(history): log dual drop targets, 350x350 window floor, island actions, and font scroll fixes7e232e3— feat(backend): implement is_file_readonly command and register invoke handler21bb853— feat(editor): implement baseline dirty tracking and read-only save guard4c47aa6— feat(menu): add active mode highlights and disabled state for up-to-date save6681f09— feat(dualpane): add blue galaxy snap sync glow, secondary card masking, and symmetric button spacingab40676— docs(history): log menu highlights, blue galaxy sync, save guards, and secondary island refinements8f57372— bump version to 5.0