Skip to content

Commit 3684cf7

Browse files
committed
Markdown preview fixes: app does not crash between preview activation/deactivations, imges render properly within the preiview pane, preiew and edited fiel split is now 50/50 at activation
1 parent 6114079 commit 3684cf7

File tree

4 files changed

+83
-11
lines changed

4 files changed

+83
-11
lines changed

plugins/markdown-preview/markdown-preview.vala

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
2424
Scratch.HeaderBar? toolbar = null;
2525
Gtk.ToggleButton? preview_button = null;
2626

27+
// Signal handler IDs
28+
ulong hook_toolbar_id = 0;
29+
ulong hook_document_id = 0;
30+
2731
// Store preview state per document
2832
private Gee.HashMap<Scratch.Services.Document, PreviewState> preview_states;
2933

@@ -37,6 +41,7 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
3741
public Gtk.Paned? paned = null;
3842
public Gtk.ScrolledWindow? original_scroll = null;
3943
public bool visible = false;
44+
public ulong buffer_changed_id = 0;
4045

4146
public PreviewState () {
4247
web_view = new WebKit.WebView ();
@@ -73,6 +78,25 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
7378
}
7479
return false;
7580
});
81+
82+
// Handle navigation requests - Open links in external browser
83+
web_view.decide_policy.connect ((decision, type) => {
84+
if (type == WebKit.PolicyDecisionType.NAVIGATION_ACTION) {
85+
var nav_decision = (WebKit.NavigationPolicyDecision) decision;
86+
var action = nav_decision.navigation_action;
87+
88+
if (action.get_navigation_type () == WebKit.NavigationType.LINK_CLICKED) {
89+
try {
90+
Gtk.show_uri (null, action.get_request ().uri, Gdk.CURRENT_TIME);
91+
} catch (Error e) {
92+
warning ("Failed to open URI: %s", e.message);
93+
}
94+
decision.ignore ();
95+
return true;
96+
}
97+
}
98+
return false;
99+
});
76100
}
77101
}
78102

@@ -88,12 +112,12 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
88112
preview_button.no_show_all = true;
89113

90114
// Hook into toolbar
91-
plugins.hook_toolbar.connect ((t) => {
115+
hook_toolbar_id = plugins.hook_toolbar.connect ((t) => {
92116
toolbar = t;
93117
});
94118

95119
// Hook into document changes
96-
plugins.hook_document.connect ((doc) => {
120+
hook_document_id = plugins.hook_document.connect ((doc) => {
97121
if (current_source != null) {
98122
current_source.notify["language"].disconnect (on_language_changed);
99123
}
@@ -191,6 +215,10 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
191215
warning ("Could not find parent of scroll window");
192216
return;
193217
}
218+
219+
// Capture width BEFORE removing from parent
220+
int width = scroll.get_allocated_width ();
221+
if (width <= 0) width = 600; // Fallback width
194222

195223
// Store reference to original scroll window
196224
state.original_scroll = scroll;
@@ -206,7 +234,7 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
206234
state.paned.pack2 (state.scrolled_window, true, false);
207235

208236
// Set initial position to 50/50 split
209-
state.paned.position = scroll.get_allocated_width () / 2;
237+
state.paned.position = width / 2;
210238

211239
// Add the paned widget back to the parent
212240
if (scroll_parent is Gtk.Grid) {
@@ -222,9 +250,11 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
222250
update_preview (doc, state);
223251

224252
// Connect to buffer changes for live updates
225-
doc.source_view.buffer.changed.connect (() => {
226-
on_text_changed (doc);
227-
});
253+
if (state.buffer_changed_id == 0) {
254+
state.buffer_changed_id = doc.source_view.buffer.changed.connect (() => {
255+
on_text_changed (doc);
256+
});
257+
}
228258
}
229259

230260
private void hide_preview (Scratch.Services.Document doc) {
@@ -246,6 +276,10 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
246276
// Remove the scroll window from the paned
247277
state.paned.remove (state.original_scroll);
248278

279+
// CRITICAL: Remove the preview scrolled window too, otherwise it gets destroyed
280+
// when state.paned is removed/destroyed.
281+
state.paned.remove (state.scrolled_window);
282+
249283
// Remove the paned from its parent
250284
paned_parent.remove (state.paned);
251285

@@ -256,6 +290,12 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
256290
((Gtk.Box) paned_parent).pack_start (state.original_scroll, true, true, 0);
257291
}
258292

293+
// Disconnect signal
294+
if (state.buffer_changed_id > 0) {
295+
doc.source_view.buffer.disconnect (state.buffer_changed_id);
296+
state.buffer_changed_id = 0;
297+
}
298+
259299
state.visible = false;
260300
state.paned = null;
261301
}
@@ -290,7 +330,21 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
290330
var markdown_text = buffer.get_text (start, end, false);
291331

292332
var html = markdown_to_html (markdown_text);
293-
state.web_view.load_html (html, null);
333+
334+
// Fix relative paths by providing a base URI
335+
string? base_uri = null;
336+
if (doc.file != null) {
337+
var parent = doc.file.get_parent ();
338+
if (parent != null) {
339+
base_uri = parent.get_uri ();
340+
// Ensure URI ends with a slash so relative files resolve correctly
341+
if (!base_uri.has_suffix ("/")) {
342+
base_uri += "/";
343+
}
344+
}
345+
}
346+
347+
state.web_view.load_html (html, base_uri);
294348
}
295349

296350
private string markdown_to_html (string markdown) {
@@ -550,13 +604,13 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
550604
var code_regex = new Regex ("`(.+?)`");
551605
result = code_regex.replace (result, -1, 0, "<code>\\1</code>");
552606

607+
// Images ![alt](url) - MUST BE BEFORE LINKS
608+
var img_regex = new Regex ("!\\[(.+?)\\]\\((.+?)\\)");
609+
result = img_regex.replace (result, -1, 0, "<img src=\"\\2\" alt=\"\\1\">");
610+
553611
// Links [text](url)
554612
var link_regex = new Regex ("\\[(.+?)\\]\\((.+?)\\)");
555613
result = link_regex.replace (result, -1, 0, "<a href=\"\\2\">\\1</a>");
556-
557-
// Images ![alt](url)
558-
var img_regex = new Regex ("!\\[(.+?)\\]\\((.+?)\\)");
559-
result = img_regex.replace (result, -1, 0, "<img src=\"\\2\" alt=\"\\1\">");
560614
} catch (RegexError e) {
561615
warning ("Regex error: %s", e.message);
562616
}
@@ -573,6 +627,24 @@ public class Code.Plugins.MarkdownPreview : Peas.ExtensionBase, Scratch.Services
573627
}
574628
preview_states.clear ();
575629

630+
// Disconnect main signal handlers
631+
if (plugins != null) {
632+
if (hook_toolbar_id > 0) {
633+
plugins.disconnect (hook_toolbar_id);
634+
hook_toolbar_id = 0;
635+
}
636+
if (hook_document_id > 0) {
637+
plugins.disconnect (hook_document_id);
638+
hook_document_id = 0;
639+
}
640+
}
641+
642+
// Disconnect from current source
643+
if (current_source != null) {
644+
current_source.notify["language"].disconnect (on_language_changed);
645+
current_source = null;
646+
}
647+
576648
if (toolbar != null && preview_button != null && preview_button.parent != null) {
577649
toolbar.remove (preview_button);
578650
}

0 commit comments

Comments
 (0)