Files
clide/lib/builtin/markdown/src/extension.dart
T
jpmschweitzerandClaude Opus 4.8 095c45a023 feat(markdown): live-sync read-mirror of the open editor buffer (T-36)
D-50 behavior 4: opening a renderable .md in the editor now auto-reveals the
context-panel reader, which mirrors the buffer read-only and re-renders as the
user types — rather than a one-shot disk read.

The extension reveals the tab on editor.opened (renderable only — D-50 behavior
5 leaves non-.md files alone). The viewer owns the mirror: on mount it picks up
the active buffer (editor.read, no id), enters mirror mode on editor.opened /
active-changed for a renderable file, re-reads the in-memory buffer on
editor.edited, and drops the mirror (with its edit affordance back) on a disk
load or a switch to a non-renderable buffer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:08:31 +02:00

60 lines
2.0 KiB
Dart

import 'dart:async';
import 'package:clide/builtin/markdown/src/markdown_viewer.dart';
import 'package:clide/extension/extension.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
class MarkdownExtension extends ClideExtension {
@override
String get id => 'builtin.markdown';
@override
String get title => 'Markdown';
@override
String get version => '0.3.0';
@override
List<String> get dependsOn => const [];
StreamSubscription<Message>? _sub;
StreamSubscription<DaemonEvent>? _editorSub;
@override
List<ContributionPoint> get contributions => [
TabContribution(
id: 'markdown.viewer',
slot: Slots.contextPanel,
title: 'Markdown',
icon: PhosphorIcons.byName('file-text'),
build: (_) => const MarkdownViewer(),
),
];
@override
Future<void> activate(ClideExtensionContext ctx) async {
// Ensure the retained nav exists so it records selections + emits
// loads whether or not the viewer is mounted (T-196). This handler
// only reveals the tab; the nav owns load + history.
ctx.readerNav.navFor(id, dataKey: 'path');
_sub = ctx.messages.subscribe(publisher: id, channel: 'selection').listen((msg) {
if (msg.data['path'] is! String) return;
ctx.panels.activateTab(Slots.contextPanel, 'markdown.viewer');
});
// Live-sync read-mirror (T-36, D-50 behavior 4): opening a renderable .md in
// the editor auto-reveals the reader, which then mirrors the buffer live.
// Non-renderable files reveal nothing (D-50 behavior 5).
_editorSub = ctx.events.on<DaemonEvent>().listen((e) {
if (e.subsystem != 'editor' || e.kind != 'editor.opened') return;
final path = e.data['path'] as String?;
if (path != null && isRenderableMarkdownPath(path)) {
ctx.panels.activateTab(Slots.contextPanel, 'markdown.viewer');
}
});
}
@override
Future<void> deactivate() async {
await _sub?.cancel();
await _editorSub?.cancel();
}
}