feat(canvas): open .canvas files in a workspace pane (T-322)
Makes the canvas foundation (parser/renderer/view) reachable. The extension owns an app-scoped MultitabController (the diff/T-233 pattern) so open documents survive the pane being rebuilt; each document is a real sub-tab per the refinement decision, kept alive across switches. Routing goes through the existing seams instead of the dead TabContribution.fileGlobs field: openWorkspaceFile gains a .canvas branch mirroring .md, and ui.open gains a canvas reader for D-6 parity (clide ui open canvas <path>). Also corrects the json_canvas doc header that claimed SVG-lowering — the interactive pane paints the model directly (D-103 live-widget exception). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/// openWorkspaceFile (T-51/T-187) — the single dispatch point for file
|
||||
/// activations: `.md` → markdown reader, `.canvas` → canvas pane (T-322),
|
||||
/// everything else → the editor via IPC. Also records recents.
|
||||
library;
|
||||
|
||||
import 'package:clide/clide.dart';
|
||||
import 'package:clide/kernel/kernel.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../helpers/kernel_fixture.dart';
|
||||
|
||||
void main() {
|
||||
late KernelFixture f;
|
||||
late List<Message> published;
|
||||
late List<Object?> editorOpens;
|
||||
|
||||
setUp(() async {
|
||||
f = await KernelFixture.create();
|
||||
published = [];
|
||||
editorOpens = [];
|
||||
f.services.messages.subscribe().listen(published.add);
|
||||
f.ipc.stub('editor.open', (args) async {
|
||||
editorOpens.add(args['path']);
|
||||
return IpcResponse.ok(id: '1', data: const {});
|
||||
});
|
||||
});
|
||||
tearDown(() => f.dispose());
|
||||
|
||||
Future<void> deliver() => Future<void>.delayed(Duration.zero);
|
||||
|
||||
test('.md routes to the markdown reader via the bus', () async {
|
||||
openWorkspaceFile(f.services, 'docs/plan.md');
|
||||
await deliver();
|
||||
expect(published, hasLength(1));
|
||||
expect(published.single.publisher, 'builtin.markdown');
|
||||
expect(published.single.channel, 'selection');
|
||||
expect(published.single.data, {'path': 'docs/plan.md'});
|
||||
expect(editorOpens, isEmpty);
|
||||
});
|
||||
|
||||
test('.canvas routes to the canvas pane via the bus (T-322)', () async {
|
||||
openWorkspaceFile(f.services, 'notes/board.canvas');
|
||||
await deliver();
|
||||
expect(published, hasLength(1));
|
||||
expect(published.single.publisher, 'builtin.canvas');
|
||||
expect(published.single.channel, 'selection');
|
||||
expect(published.single.data, {'path': 'notes/board.canvas'});
|
||||
expect(editorOpens, isEmpty);
|
||||
});
|
||||
|
||||
test('extension match is case-insensitive', () async {
|
||||
openWorkspaceFile(f.services, 'BOARD.CANVAS');
|
||||
await deliver();
|
||||
expect(published.single.publisher, 'builtin.canvas');
|
||||
});
|
||||
|
||||
test('everything else opens in the editor over IPC', () async {
|
||||
openWorkspaceFile(f.services, 'lib/main.dart');
|
||||
await deliver();
|
||||
expect(published, isEmpty);
|
||||
expect(editorOpens, ['lib/main.dart']);
|
||||
});
|
||||
|
||||
test('records the open in recent files; empty path is a no-op', () async {
|
||||
openWorkspaceFile(f.services, 'notes/board.canvas');
|
||||
openWorkspaceFile(f.services, '');
|
||||
await deliver();
|
||||
expect(f.services.recentFiles.paths, ['notes/board.canvas']);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user