Files
clide/lib/builtin/markdown/src/markdown_viewer.dart
T
jpmschweitzerandClaude Opus 4.6 46329700d5 dissolve app/ into repo root (D-056)
Single Flutter package at the repo root. All code, tests, assets,
and platform directories moved from app/ to root. Package renamed
from clide_app to clide — all imports rewritten. Merged pubspec
combines core (ffi) and app (flutter, yaml, xterm) dependencies.
Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon
lifecycle. 317 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 00:37:20 +02:00

95 lines
2.6 KiB
Dart

import 'dart:async';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/widgets.dart';
class MarkdownViewer extends StatefulWidget {
const MarkdownViewer({super.key});
@override
State<MarkdownViewer> createState() => _MarkdownViewerState();
}
class _MarkdownViewerState extends State<MarkdownViewer> {
String? _path;
String? _content;
String? _error;
StreamSubscription<DaemonEvent>? _eventSub;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_eventSub != null) return;
final kernel = ClideKernel.of(context);
_eventSub = kernel.events.on<DaemonEvent>().listen((e) {
if (e.kind == 'editor.buffer_activated') {
final path = e.data['path'] as String?;
if (path != null && path.endsWith('.md')) {
_loadFile(path);
}
}
});
final activeTab = kernel.panels.activeTabIn(Slots.workspace);
if (activeTab == 'editor.active') {
unawaited(_loadActiveBuffer());
}
}
@override
void dispose() {
_eventSub?.cancel();
super.dispose();
}
Future<void> _loadActiveBuffer() async {
final kernel = ClideKernel.of(context);
final resp = await kernel.ipc.request('editor.active');
if (!mounted || !resp.ok) return;
final path = resp.data['path'] as String?;
if (path != null && path.endsWith('.md')) {
await _loadFile(path);
}
}
Future<void> _loadFile(String path) async {
final kernel = ClideKernel.of(context);
final resp = await kernel.ipc.request('files.read', args: {'path': path});
if (!mounted) return;
if (resp.ok) {
setState(() {
_path = path;
_content = resp.data['content'] as String? ?? '';
_error = null;
});
} else {
setState(() => _error = resp.error?.message);
}
}
@override
Widget build(BuildContext context) {
if (_error != null) {
return Padding(padding: const EdgeInsets.all(12), child: ClideText(_error!, muted: true));
}
if (_content == null) {
return const Padding(
padding: EdgeInsets.all(12),
child: ClideText('Open a .md file to preview it here.', muted: true),
);
}
final tokens = ClideTheme.of(context).surface;
return ClidePaneChrome(
title: _path ?? 'viewer',
subtitle: '${_content!.split('\n').length} lines',
child: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Text(
_content!,
style: TextStyle(color: tokens.globalForeground, fontSize: 13, fontFamily: clideMonoFamily, fontFamilyFallback: clideMonoFamilyFallback),
),
),
);
}
}