Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
2026-06-02 23:28:39 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import '../../models/deck.dart';
|
|
|
|
|
import '../../models/settings.dart';
|
|
|
|
|
import '../../models/slide.dart';
|
|
|
|
|
import '../../services/export_service.dart';
|
|
|
|
|
import '../../services/slide_rasterizer.dart';
|
2026-06-04 02:30:03 +02:00
|
|
|
import '../../l10n/app_localizations.dart';
|
2026-06-02 23:28:39 +02:00
|
|
|
|
|
|
|
|
/// Exports the deck by rendering the on-screen slide previews to images and
|
|
|
|
|
/// packing them into a PDF or PPTX (WYSIWYG — the export matches the preview).
|
|
|
|
|
class ExportDialog extends StatefulWidget {
|
|
|
|
|
final String deckPath;
|
|
|
|
|
final List<Slide> slides;
|
|
|
|
|
final ThemeProfile themeProfile;
|
|
|
|
|
final String? projectPath;
|
|
|
|
|
final ExportService exportService;
|
|
|
|
|
final TlpLevel tlp;
|
|
|
|
|
|
2026-06-03 15:03:27 +02:00
|
|
|
/// Folder all exports are written to. Null = next to the source deck.
|
|
|
|
|
final String? exportDirectory;
|
|
|
|
|
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
/// The deck's Marp Markdown, used for the self-contained HTML export.
|
|
|
|
|
final String markdown;
|
|
|
|
|
|
2026-06-02 23:28:39 +02:00
|
|
|
const ExportDialog({
|
|
|
|
|
super.key,
|
|
|
|
|
required this.deckPath,
|
|
|
|
|
required this.slides,
|
|
|
|
|
required this.themeProfile,
|
|
|
|
|
required this.projectPath,
|
|
|
|
|
required this.exportService,
|
|
|
|
|
this.tlp = TlpLevel.none,
|
2026-06-03 15:03:27 +02:00
|
|
|
this.exportDirectory,
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
this.markdown = '',
|
2026-06-02 23:28:39 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
static Future<void> show(
|
|
|
|
|
BuildContext context, {
|
|
|
|
|
required String deckPath,
|
|
|
|
|
required List<Slide> slides,
|
|
|
|
|
required ThemeProfile themeProfile,
|
|
|
|
|
required String? projectPath,
|
|
|
|
|
required ExportService exportService,
|
|
|
|
|
TlpLevel tlp = TlpLevel.none,
|
2026-06-03 15:03:27 +02:00
|
|
|
String? exportDirectory,
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
String markdown = '',
|
2026-06-02 23:28:39 +02:00
|
|
|
}) {
|
|
|
|
|
return showDialog(
|
|
|
|
|
context: context,
|
|
|
|
|
barrierDismissible: false,
|
|
|
|
|
builder: (_) => ExportDialog(
|
|
|
|
|
deckPath: deckPath,
|
|
|
|
|
slides: slides,
|
|
|
|
|
themeProfile: themeProfile,
|
|
|
|
|
projectPath: projectPath,
|
|
|
|
|
exportService: exportService,
|
|
|
|
|
tlp: tlp,
|
2026-06-03 15:03:27 +02:00
|
|
|
exportDirectory: exportDirectory,
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
markdown: markdown,
|
2026-06-02 23:28:39 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<ExportDialog> createState() => _ExportDialogState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _ExportDialogState extends State<ExportDialog> {
|
|
|
|
|
bool _loading = false;
|
|
|
|
|
String? _result;
|
|
|
|
|
bool _success = false;
|
|
|
|
|
String _phase = '';
|
|
|
|
|
int _done = 0;
|
|
|
|
|
int _total = 0;
|
|
|
|
|
|
2026-06-03 15:03:27 +02:00
|
|
|
/// Image quality for PDF export: false = full-resolution PNG, true = a smaller
|
|
|
|
|
/// downscaled JPEG handout.
|
|
|
|
|
bool _compress = false;
|
|
|
|
|
|
|
|
|
|
Future<void> _export(ExportFormat format, {bool compress = false}) async {
|
2026-06-04 02:30:03 +02:00
|
|
|
final l10n = context.l10n;
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
// HTML renders from Markdown in the browser, so it needs no slide raster.
|
|
|
|
|
final needsRaster = format != ExportFormat.html;
|
2026-06-02 23:28:39 +02:00
|
|
|
setState(() {
|
|
|
|
|
_loading = true;
|
|
|
|
|
_result = null;
|
2026-06-04 02:30:03 +02:00
|
|
|
_phase = needsRaster ? l10n.t('renderingSlides') : l10n.t('buildingHtml');
|
2026-06-02 23:28:39 +02:00
|
|
|
_done = 0;
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
_total = needsRaster ? widget.slides.length : 0;
|
2026-06-02 23:28:39 +02:00
|
|
|
});
|
|
|
|
|
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
final images = needsRaster
|
|
|
|
|
? await SlideRasterizer.rasterize(
|
|
|
|
|
context: context,
|
|
|
|
|
slides: widget.slides,
|
|
|
|
|
themeProfile: widget.themeProfile,
|
|
|
|
|
projectPath: widget.projectPath,
|
|
|
|
|
tlp: widget.tlp,
|
|
|
|
|
onProgress: (done, total) {
|
|
|
|
|
if (mounted) setState(() => _done = done);
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
: const <Uint8List>[];
|
2026-06-02 23:28:39 +02:00
|
|
|
|
|
|
|
|
if (!mounted) return;
|
2026-06-04 02:30:03 +02:00
|
|
|
setState(() => _phase = '${format.label} ${l10n.t('buildingExport')}');
|
2026-06-02 23:28:39 +02:00
|
|
|
|
|
|
|
|
final r = await widget.exportService.export(
|
|
|
|
|
widget.deckPath,
|
|
|
|
|
format,
|
|
|
|
|
images,
|
2026-06-03 15:03:27 +02:00
|
|
|
compress: compress,
|
|
|
|
|
outputDirectory: widget.exportDirectory,
|
2026-06-04 00:51:09 +02:00
|
|
|
// Speaker notes travel 1:1 with the rendered slides (PPTX notes pane).
|
|
|
|
|
notes: [for (final s in widget.slides) s.notes],
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
markdown: widget.markdown,
|
2026-06-04 02:30:03 +02:00
|
|
|
themeProfile: widget.themeProfile,
|
2026-06-02 23:28:39 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
setState(() {
|
|
|
|
|
_loading = false;
|
|
|
|
|
_success = r.success;
|
2026-06-04 02:30:03 +02:00
|
|
|
_result = r.success
|
|
|
|
|
? '${l10n.t('exportedTo')}\n${r.outputPath}'
|
|
|
|
|
: r.error;
|
2026-06-02 23:28:39 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2026-06-04 02:30:03 +02:00
|
|
|
final l10n = context.l10n;
|
2026-06-02 23:28:39 +02:00
|
|
|
return AlertDialog(
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
scrollable: true,
|
2026-06-04 02:30:03 +02:00
|
|
|
title: Text(l10n.t('exportDialogTitle')),
|
2026-06-02 23:28:39 +02:00
|
|
|
content: SizedBox(width: 380, child: _content()),
|
|
|
|
|
actions: [
|
|
|
|
|
if (_result != null && _success)
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: () => setState(() => _result = null),
|
2026-06-04 02:30:03 +02:00
|
|
|
child: Text(l10n.t('exportAgain')),
|
2026-06-02 23:28:39 +02:00
|
|
|
),
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: _loading ? null : () => Navigator.pop(context),
|
2026-06-04 02:30:03 +02:00
|
|
|
child: Text(l10n.t('close')),
|
2026-06-02 23:28:39 +02:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Widget _content() {
|
2026-06-04 02:30:03 +02:00
|
|
|
final l10n = context.l10n;
|
2026-06-02 23:28:39 +02:00
|
|
|
if (_loading) {
|
|
|
|
|
final fraction = _total == 0 ? null : _done / _total;
|
|
|
|
|
return Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
_phase,
|
|
|
|
|
style: const TextStyle(fontSize: 13, color: Color(0xFF334155)),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
ClipRRect(
|
|
|
|
|
borderRadius: BorderRadius.circular(4),
|
|
|
|
|
child: LinearProgressIndicator(value: fraction, minHeight: 6),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
Text(
|
2026-06-04 02:30:03 +02:00
|
|
|
_total == 0
|
|
|
|
|
? ''
|
|
|
|
|
: '${l10n.t('slideOf')} $_done ${l10n.t('of')} $_total',
|
2026-06-02 23:28:39 +02:00
|
|
|
style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8)),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_result != null) {
|
|
|
|
|
return Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
Icon(
|
|
|
|
|
_success ? Icons.check_circle : Icons.error_outline,
|
|
|
|
|
color: _success ? Colors.green : Colors.red,
|
|
|
|
|
size: 36,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
Text(
|
|
|
|
|
_result!,
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 13,
|
|
|
|
|
color: _success ? const Color(0xFF166534) : Colors.red[800],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
|
children: [
|
2026-06-04 02:30:03 +02:00
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(bottom: 8),
|
2026-06-02 23:28:39 +02:00
|
|
|
child: Text(
|
2026-06-04 02:30:03 +02:00
|
|
|
l10n.t('exportIntro'),
|
|
|
|
|
style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)),
|
2026-06-02 23:28:39 +02:00
|
|
|
),
|
|
|
|
|
),
|
2026-06-04 02:30:03 +02:00
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(bottom: 6),
|
2026-06-03 15:03:27 +02:00
|
|
|
child: Text(
|
2026-06-04 02:30:03 +02:00
|
|
|
l10n.t('imageQualityPdf'),
|
|
|
|
|
style: const TextStyle(
|
2026-06-03 15:03:27 +02:00
|
|
|
fontSize: 11,
|
|
|
|
|
fontWeight: FontWeight.w600,
|
|
|
|
|
color: Color(0xFF475569),
|
2026-06-02 23:28:39 +02:00
|
|
|
),
|
2026-06-03 15:03:27 +02:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
SegmentedButton<bool>(
|
2026-06-04 02:30:03 +02:00
|
|
|
segments: [
|
2026-06-03 15:03:27 +02:00
|
|
|
ButtonSegment(
|
|
|
|
|
value: false,
|
2026-06-04 02:30:03 +02:00
|
|
|
icon: const Icon(Icons.image_outlined),
|
|
|
|
|
label: Text(l10n.t('normal')),
|
2026-06-03 15:03:27 +02:00
|
|
|
),
|
|
|
|
|
ButtonSegment(
|
|
|
|
|
value: true,
|
2026-06-04 02:30:03 +02:00
|
|
|
icon: const Icon(Icons.compress),
|
|
|
|
|
label: Text(l10n.t('compressed')),
|
2026-06-03 15:03:27 +02:00
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
selected: {_compress},
|
|
|
|
|
onSelectionChanged: (s) => setState(() => _compress = s.first),
|
|
|
|
|
showSelectedIcon: false,
|
|
|
|
|
style: const ButtonStyle(visualDensity: VisualDensity.compact),
|
|
|
|
|
),
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(top: 4, bottom: 8),
|
|
|
|
|
child: Text(
|
2026-06-04 02:30:03 +02:00
|
|
|
_compress ? l10n.t('compressedHelp') : l10n.t('losslessHelp'),
|
2026-06-03 15:03:27 +02:00
|
|
|
style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8)),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
_exportButton(
|
|
|
|
|
icon: _formatIcon(ExportFormat.pdf),
|
2026-06-04 02:30:03 +02:00
|
|
|
label: l10n.t('exportAsPdf'),
|
2026-06-03 15:03:27 +02:00
|
|
|
onPressed: () => _export(ExportFormat.pdf, compress: _compress),
|
|
|
|
|
),
|
|
|
|
|
_exportButton(
|
|
|
|
|
icon: _formatIcon(ExportFormat.pptx),
|
2026-06-04 02:30:03 +02:00
|
|
|
label: l10n.t('exportAsPptx'),
|
2026-06-03 15:03:27 +02:00
|
|
|
onPressed: () => _export(ExportFormat.pptx),
|
|
|
|
|
),
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
_exportButton(
|
|
|
|
|
icon: _formatIcon(ExportFormat.html),
|
2026-06-04 02:30:03 +02:00
|
|
|
label: l10n.t('exportAsHtml'),
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
onPressed: () => _export(ExportFormat.html),
|
|
|
|
|
),
|
|
|
|
|
const Padding(
|
|
|
|
|
padding: EdgeInsets.only(top: 4),
|
|
|
|
|
child: Text(
|
|
|
|
|
'HTML opent in elke browser zonder internet en rendert codeblokken, '
|
|
|
|
|
'wiskunde en mermaid-diagrammen.',
|
|
|
|
|
style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8)),
|
|
|
|
|
),
|
|
|
|
|
),
|
2026-06-02 23:28:39 +02:00
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 15:03:27 +02:00
|
|
|
Widget _exportButton({
|
|
|
|
|
required IconData icon,
|
|
|
|
|
required String label,
|
|
|
|
|
required VoidCallback onPressed,
|
|
|
|
|
}) {
|
|
|
|
|
return Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
|
|
|
child: OutlinedButton.icon(
|
|
|
|
|
onPressed: onPressed,
|
|
|
|
|
icon: Icon(icon),
|
|
|
|
|
label: Text(label),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 23:28:39 +02:00
|
|
|
IconData _formatIcon(ExportFormat f) {
|
|
|
|
|
switch (f) {
|
|
|
|
|
case ExportFormat.pdf:
|
|
|
|
|
return Icons.picture_as_pdf_outlined;
|
|
|
|
|
case ExportFormat.pptx:
|
|
|
|
|
return Icons.slideshow_outlined;
|
Add self-contained Marp HTML export
New export target: a single offline .html rendered from the deck's Marp
Markdown. Bundles (inlines) marked, highlight.js, MathJax (tex-svg, no font
files) and mermaid, so code highlighting, LaTeX math and mermaid diagrams all
render in any browser with no network access.
- MarpHtmlService splits the deck on `---`, strips front-matter, and inlines
the vendored libraries (assets/web_export/) with a </script> breakout guard.
The asset loader is injectable for testing.
- ExportFormat.html wired through ExportService (no rasterization needed),
the export dialog (new button, skips slide rendering) and app_shell
(passes the generated Markdown). Export dialog is now scrollable.
Note: rendered with marked, not Marp Core, so theme fidelity differs from the
in-app preview / PDF / PPTX; the win is a portable, dependency-free deck.
Tests: slide splitting, library inlining, breakout escaping, and an
end-to-end .html export.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 01:37:46 +02:00
|
|
|
case ExportFormat.html:
|
|
|
|
|
return Icons.public_outlined;
|
2026-06-02 23:28:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|