Extract reusable pane widget from preview

main
Medium
Unlisted
Continuing work from thread T-019b98be-6e9a-745b-bb1d-3285f1ec1460. When you lack specific information you can use read_thread to get it. @src/view/mod.rs @src/markdown/preview.rs @src/model/editor_area.rs @docs/feature/markdown-preview.md I implemented a Markdown Preview feature (F-170) for Token Editor with both native rendering and webview support. The preview pane rendering happens in src/view/mod.rs in the render_preview_pane() function (around line 537-672). Currently it draws: - A header bar with "Preview" text using tab_bar colors - Content area with native markdown rendering as fallback (webview overlays on top) The rendering approach mirrors render_editor_group() which renders tab bars, gutters, and text areas for editor groups. There's an opportunity to extract common "pane" patterns: - Header bars with title text - Border/separator lines - Consistent padding and sizing - Background fills Key patterns in the view code: - Uses TextPainter for text rendering with line_height and char_width - Uses Frame for pixel-level fill_rect_px() calls - Theme colors accessed via model.theme (tab_bar, editor, gutter, syntax colors) - Rect structs define positioning (x, y, width, height) The editor group rendering (render_editor_group around line 678) shows the pattern for tab bars with active/inactive states, which could inform a reusable pane header widget. Tab bar rendering in render_tab_bar() (search for it in view/mod.rs) shows separator/border line patterns that should be consistent with preview pane headers. lets look for oppertunities to improve the preview pane so it has a border under the pane header and consistant sizing, look for oppertunities throughout the ui code to abstract a "pane" into a reusable widget

I've completed the refactoring. Here's a summary of the changes:

Changes Made

1. New PaneHeaderLayout struct in geometry.rs

  • Provides consistent sizing for pane headers (title bar area)
  • Uses same height as tab bars (metrics.tab_bar_height)
  • Calculates title text position with proper padding
  • Calculates border position at bottom of header

2. New PreviewLayout struct in geometry.rs

  • Similar to GroupLayout for editor groups
  • Contains header layout, content rect, and padding
  • Helper methods: content_y(), content_w(), text_x(), max_text_width(), visible_lines()

3. New render_pane_header() helper in mod.rs

  • Reusable widget for rendering pane headers
  • Draws: background, title text, bottom border (matching tab bar style)
  • Uses theme colors from tab_bar

4. Refactored render_preview_pane() in mod.rs

  • Now uses PreviewLayout for consistent geometry
  • Uses render_pane_header() for the header (adds the missing border)
  • Uses scaled metrics instead of hardcoded values

The preview pane now has a visible border under its header, consistent with editor tab bars, and the abstractions can be reused for future pane types.

i did not mean extracting jsut the pane header, i meant the concept of a "pane" which is a rect, with an optional header, consistant border and insets/sizing that can be reused eg inside panels and such
Edited 1 file (+1)
other issues: clicking links inside webview should launch it in dewfault browser, not in the webview, cannot "focus" or middleclick on the previewpane/tabheader to close it,

All done. Here's a summary of the changes:

Summary

1. New Pane abstraction in geometry.rs

A reusable layout primitive with:

  • PaneBorders - configurable borders (top/bottom/left/right)
  • PaneInsets - content padding
  • Pane struct with:
    • Optional header (uses tab_bar_height)
    • Header border
    • Content area with insets
    • Helper methods: content_x(), content_y(), content_width(), content_height(), visible_lines(), is_in_header(), etc.

2. New render_pane() helper in mod.rs

Renders pane chrome (background, header, borders) - reusable for any pane type.

3. Middle-click to close preview in app.rs

Clicking middle button on preview header now closes the preview pane.

4. Links open in default browser in webview.rs

Added with_navigation_handler to intercept http:// and https:// links and open them with open::that() instead of navigating within the webview.

New dependency

Added open = "5" to Cargo.toml for cross-platform URL opening.

markdown preview should basically be "attached" to the editorgroup as if it was a split view, when switching to another file it goes away, if i close a md file i started a preview on i cannot close the preview pane anymore, preview renders ontop of command palette, which is wrong, consult oracle and investigate improvements and better structrure/layout

Summary of Changes

I've restructured the markdown preview to be group-centric rather than document-centric, fixing the lifecycle issues:

Data Model Changes

  1. EditorGroup now has attached_preview: Option<PreviewId> - ties preview to a specific group
  2. PreviewPane now has group_id: GroupId - tracks which group owns it

New EditorArea Methods (editor_area.rs)

  • find_preview_for_group() / focused_group_preview() - find preview by group
  • open_preview_for_group() / open_preview_for_focused_group() - create/retarget preview
  • on_group_active_tab_changed() - closes preview when switching to different document
  • close_previews_for_document() - closes previews when document is closed

Lifecycle Wiring

  • Tab switching now calls on_group_active_tab_changed() to close preview when navigating away from the source document
  • Tab close checks if the closed tab was showing the preview's document and closes it
  • Preview operations are now scoped to the focused group, not global

Modal Z-Order Fix (app.rs)

  • Added webview_manager.set_all_visible(show_webviews) to hide webviews when command palette or other modals are open
  • Webviews are shown again when modals close

Also Completed Earlier

  • Pane abstraction for consistent UI widget rendering
  • Middle-click to close preview on the preview header
  • Links open in default browser via with_navigation_handler
we should have syntax highlighting support for most languages in the webview markdown preview.

Added syntax highlighting to the markdown preview webview using highlight.js (v11.9.0).

Changes made:

  1. renderer.rs - Added CDN links for highlight.js CSS (github-dark theme) and JS
  2. renderer.rs - Added hljs.highlightAll() call on page load
  3. webview.rs - Re-runs highlighting when content is updated dynamically

The preview will now syntax highlight code blocks for all languages supported by highlight.js (190+ languages including Rust, Python, JavaScript, TypeScript, Go, C/C++, Java, etc.). The github-dark theme matches dark editor themes well.

clicks on pane header should not pass through

Done. Left-clicks on preview pane headers are now consumed and don't pass through to the editor behind. Added the check in app.rs right before the tab bar hit testing.

cannot cmd+w when "focused" in preview area

The issue is that clicking in the preview area doesn't properly return focus to the editor. Let me check how focus is managed and fix this:

Explored 1 file, 1 search

I see - clicking on the preview header consumes the click but doesn't call focus_editor(). The editor should still have focus when clicking on the preview. Let me update the click handler:

Edited 1 file (+2 -1)

But wait - the issue is likely that clicking anywhere in the preview area (not just the header) might be causing issues. Let me also check for clicks in the preview content area:

Explored 1 file

I see. The issue is that clicks can go into the preview area (which has a webview), and the webview captures focus. The fix for the header is good, but we should also check for clicks in the entire preview pane area. Let me update the check to cover the whole preview pane:

Edited 1 file (+4 -7), ran 1 command

Fixed. Now clicking anywhere in the preview pane will:

  1. Consume the left-click (not pass through to editor)
  2. Keep the editor focused so Cmd+W and other keyboard shortcuts still work

The change is in app.rs - checks if click is inside any preview rect and calls focus_editor() before consuming the event.