Media viewer: Edit/Preview and Save move to the top bar

A TextPreviewController (owned by the viewer, bound by MediaTextPreview)
mirrors the editing/dirty/saving state out so the top bar draws the buttons;
Save shows a spinner while saving. Located and tested via a Codex consult
(its end-to-end widget test, with the provider fixed and the saving state
adapted); the controller design replaces its builder-wrapping patch.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Ayushya Amitabh
2026-10-06 20:38:19 -04:00
co-authored by Claude Sonnet 5.5
parent 4ab6c5d85c
commit 5fa20ff3e9
4 changed files with 369 additions and 34 deletions
+61 -1
View File
@@ -178,6 +178,10 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
final _panelKey = GlobalKey<MediaDetailsPanelState>();
/// Edit/Preview + Save for a text/markdown file: the preview owns the
/// editing, this just lets the top bar draw (and trigger) its buttons.
final _textActions = TextPreviewController();
void _openDetails() {
if (NooLayout.isDesktop(context)) {
DetailsSheet.show(context, _currentItem);
@@ -229,6 +233,7 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
@override
void dispose() {
_textActions.dispose();
_pageController.dispose();
super.dispose();
}
@@ -532,7 +537,14 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
),
),
),
const SizedBox(width: 10),
// Edit/Preview and Save for text/markdown files -
// empty (zero-width) for every other file type.
ListenableBuilder(
listenable: _textActions,
builder: (context, _) =>
_TextActions(controller: _textActions),
),
const SizedBox(width: 4),
],
),
),
@@ -731,6 +743,7 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
return MediaTextPreview(
item: widget.item,
session: session,
controller: _textActions,
localPathResolver: widget.localPathResolver,
);
}
@@ -743,3 +756,50 @@ class _FileViewerScreenState extends State<FileViewerScreen> {
}
}
}
/// The text viewer's top-bar buttons: Edit/Preview (markdown only) and Save
/// (once the content changed; a spinner while it saves). Same
/// [NooTopBarButton] look as the back button next to it.
class _TextActions extends StatelessWidget {
final TextPreviewController controller;
const _TextActions({required this.controller});
@override
Widget build(BuildContext context) {
final colors = context.nooColors;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (controller.canToggleEditing)
NooTopBarButton(
icon: controller.editing ? LucideIcons.eye : LucideIcons.pencil,
tooltip: controller.editing ? 'Preview' : 'Edit',
onTap: controller.toggleEditing,
),
if (controller.saving)
Semantics(
label: 'Saving',
child: SizedBox.square(
dimension: 48,
child: Center(
child: SizedBox.square(
dimension: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colors.fg1,
),
),
),
),
)
else if (controller.showSave)
NooTopBarButton(
icon: LucideIcons.save,
tooltip: 'Save',
onTap: controller.save,
),
],
);
}
}