Skip to content

Cathet v5.0.0

Latest

Choose a tag to compare

@Crlyzd Crlyzd released this 17 Oct 20:38

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 EditorComponent from 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 against devicePixelRatio dynamically 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_size backend 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.ts so scrolling within the font dropdown menu no longer triggers ancestor scroll listeners, accompanied by overscroll-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) and box-sizing: border-box across .dualpane-empty-card and 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 EditorComponent bound to fixed DOM identifiers (#editor-wrap, #editor, #markdown-preview).
  • Solution & Technical Details:
    • Decoupled src-ui/src/components/Editor.ts by replacing hardcoded ID lookups with container-scoped class queries (.cathet-editor-wrap, .cathet-editor, .cathet-markdown-preview). Exposed modular accessors getWrapperEl() and focus() to support dynamic lifecycle attachment.
    • Implemented src-ui/src/components/dualpane/DualPaneView.ts (orchestrator) managing twin pane DOM slots (#dualpane-left-slot and #dualpane-right-slot), active pane tracking, and layout state.
    • Built src-ui/src/components/dualpane/DualPaneHeader.ts providing 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.ts providing an interactive divider with drag-based resize capabilities, min/max layout clamps, and an integrated sync lock badge.
    • Created src-ui/src/services/dualPaneService.ts managing 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.ts binding Ctrl+D to 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.ts at 229 lines, DualPaneSplitter.ts at 124 lines, DualPaneHeader.ts at 81 lines, dualPaneService.ts at 76 lines).

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.ts using normalized scroll ratios:
      ratio = scrollTop / (scrollHeight - clientHeight)
    • Ping-Pong Recursion Prevention: Applied a boolean isSyncing re-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.
    • 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-galaxy on 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.ts with cross-window event synchronization (cathet:snap-scroll-change) via Tauri IPC.

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.ts and src-ui/styles/editor.css to render simultaneous side-by-side drop targets (.drop-zone-left and .drop-zone-right).
    • Spatial Cursor Tracking: In src-ui/src/services/dragDropService.ts, captured cursor coordinates across both browser DOM dragover events and native Windows OLE drag events forwarded by Tauri.
    • Device Pixel Ratio Normalization: Physical screen coordinates from native OS events are normalized via window.devicePixelRatio against dualPaneView.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) with backdrop-filter: blur(16px)).
    • Anti-Ghosting State: Toggled is-dragging class on document.body during drag operations. In dualpane.css, applied rules to hide the underlying secondary placeholder island during file dragging, preventing visual collisions between placeholder text and drop zone cards.

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.json apply globally and could not adapt to single-pane versus dual-pane layouts.
  • Solution & Technical Details:
    • Configured baseline window constraints in src-tauri/tauri.conf.json with "minWidth": 200 and "minHeight": 300, guaranteeing a minimum usable vertical editing area below the topbar.
    • Implemented set_window_min_size in src-tauri/src/commands/window.rs, querying the active window via app_handle and invoking window.set_min_size(Some(LogicalSize::new(width, height))).
    • Auto-Expansion Logic: If current window dimensions are smaller than the requested minimum, set_window_min_size automatically expands the window to the threshold to prevent immediate clipping.
    • Registered command in src-tauri/src/lib.rs and added frontend service wrapper setMinSize in src-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).

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.
  • Solution & Technical Details:
    • Baseline Dirty Tracking: Added savedContent baseline tracking to EditorComponent with isModified(): boolean and markSaved(): void. When loaded or saved, the baseline is captured; comparisons ignore trivial whitespace variances.
    • Disabled Save on Up-to-Date Documents: In mainMenuItems.ts, wired disabled: ctx.isUpToDate. Styled .popup-menu-item.disabled in popup.css with dimmed opacity (0.42) and pointer-events: none. Prevented saveDocument from executing redundant writes when isModified() is false.
    • Windows Read-Only Metadata Inspection: Implemented is_file_readonly command in src-tauri/src/commands/file.rs using std::fs::metadata(path)?.permissions().readonly().
    • Proactive Read-Only Warning: Integrated isFileReadonly check in src-ui/src/main.ts prior 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."

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 MenuItem interface in src-ui/src/components/PopupMenu.ts with optional isActive?: boolean property.
    • In src-ui/src/utils/mainMenuItems.ts, set isActive: ctx.isDualMode on the "Exit Dual Pane" action and isActive: ctx.isPreviewMode on "Markdown: Preview".
    • Styled .popup-menu-item.active in src-ui/styles/popup.css with a 2px accent inset border, translucent highlight background tint, and high-contrast text.
    • Retained strict modularity with PopupMenu.ts at 89 lines and mainMenuItems.ts at 48 lines.

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.ts detected 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, updated scrollCloseHandler to inspect e.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-menu max-height from 120px to 152px, comfortably displaying 5 to 6 items without cramped borders.
      • Added overscroll-behavior: contain to 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.

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-box on 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-card in src-ui/styles/dualpane.css so all internal child elements clip cleanly within the border-radius boundary.
    • Responsive Title Ellipsis: Configured .dualpane-empty-title with box-sizing: border-box; overflow: hidden; text-overflow: ellipsis; text-align: center; and enforced white-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; and min-width: 0; to .dualpane-empty-btn and .dualpane-empty-actions, ensuring width: 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 Document and compact New) in DualPaneEmptyDropzone.ts.
    • Removed duplicate card close button, leaving pane closing to the dedicated header close button.

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 utilities
  • 44efddd — feat(ui): implement frosted glass Dual Pane components and layout
  • 2f0a36c — feat(app): wire Dual Pane lifecycle, spatial drag-and-drop, and log history
  • cae7142 — style(ui): add monochrome icons and responsive layout styles
  • e18edde — feat(scroll): implement offset-based scroll sync and snap-scroll state
  • 1ed87f3 — feat(settings): add Dual Pane Snap Scroll setting toggle
  • e8d1e05 — fix(dualpane): refine headers, sync icons, and fix initial preview state
  • a1a4a8d — docs(history): log Dual Pane UI refinements, monochrome icons, and offset scroll sync
  • 4c626d5 — feat(backend): implement dynamic window min-size command and default window constraints
  • ff03614 — fix(settings): fix font dropdown scroll collapse, enable overscroll contain, and add micro-scrollbar
  • 26d2e22 — feat(drop-overlay): implement simultaneous dual drop zones, dynamic hover highlight, and frosted glass backdrop
  • 97eba3d — feat(dualpane): add secondary island New Document action, 350x350 window floor, and drag transparency
  • 967003f — docs(history): log dual drop targets, 350x350 window floor, island actions, and font scroll fixes
  • 7e232e3 — feat(backend): implement is_file_readonly command and register invoke handler
  • 21bb853 — feat(editor): implement baseline dirty tracking and read-only save guard
  • 4c47aa6 — feat(menu): add active mode highlights and disabled state for up-to-date save
  • 6681f09 — feat(dualpane): add blue galaxy snap sync glow, secondary card masking, and symmetric button spacing
  • ab40676 — docs(history): log menu highlights, blue galaxy sync, save guards, and secondary island refinements
  • 8f57372 — bump version to 5.0