mirror of
https://git.srhtnin.garden/sarah/SaraView.git
synced 2026-10-15 02:43:17 +02:00
Add live Markdown editing, colours and files in the tree
This commit is contained in:
1 parent
0eecfba224
commit
f851ecd820
15 files changed
+1025
-58
No files matched your search
+58
-6
@@ -4,6 +4,9 @@ EditorView/ImageView idea, with SaraView's View/Edit/Save toolbar).
|
||||
- Markdown is rendered in View; the other text types are shown as
|
||||
highlighted code. Edit is a plain monospace field with an explicit Save
|
||||
(Ctrl+S). On a server, saving records line blame for you, like the web view.
|
||||
- With Markdown editing set to Live (Settings > General), Markdown opens in
|
||||
LiveMarkdown instead: rendered, and click a block to edit it in place.
|
||||
Edit/View becomes Source/Live there.
|
||||
- Images are shown fitted to the window.
|
||||
- Video and other files get their details and Open Externally (a server
|
||||
file is downloaded to ~/.cache/SaraView first). Flet has no built-in
|
||||
@@ -16,7 +19,8 @@ from typing import Callable, Optional
|
||||
import flet as ft
|
||||
|
||||
from App import App
|
||||
from Core import ChipButton, CodeStyleSheet, HumanSize, Palette
|
||||
from Core import ChipButton, CodeStyleSheet, HumanSize, MarkdownCodeTheme, MarkdownSheet, Palette
|
||||
from LiveMarkdown import LiveMarkdown
|
||||
from Sources import Entry, SourceError, TextFileTypes
|
||||
|
||||
LanguageByExtension = {
|
||||
@@ -56,6 +60,9 @@ class FileView(ft.Container):
|
||||
self.OnClose = OnClose
|
||||
self.OnFocusChange = OnFocusChange
|
||||
self.IsText = self.FileType in TextFileTypes
|
||||
self.IsLive = (self.FileType == "markdown" and TheApp.Config.MarkdownEditing == "Live"
|
||||
and TheApp.Source.CanWrite)
|
||||
self.Live: Optional[LiveMarkdown] = None
|
||||
self.IsEditing = False
|
||||
self.IsDirty = False
|
||||
self.Content = ""
|
||||
@@ -63,6 +70,9 @@ class FileView(ft.Container):
|
||||
|
||||
self.StatusText = ft.Text("", color=Palette.Overlay0, size=12)
|
||||
self.ToggleButton = ChipButton("Edit", self._ToggleEdit, Icon=ft.Icons.EDIT_OUTLINED)
|
||||
if self.IsLive:
|
||||
self.ToggleButton.content = "Source"
|
||||
self.ToggleButton.icon = ft.Icons.CODE
|
||||
self.SaveButton = ChipButton("Save", lambda e: self.Save(), Icon=ft.Icons.SAVE_OUTLINED, Disabled=True)
|
||||
CanWrite = TheApp.Source.CanWrite
|
||||
if not CanWrite:
|
||||
@@ -109,6 +119,10 @@ class FileView(ft.Container):
|
||||
if self.IsText:
|
||||
self.Content = Source.ReadText(self.Root, self.RelativePath)
|
||||
self.TextField.value = self.Content
|
||||
if self.IsLive:
|
||||
self.Live = LiveMarkdown(self.Content, self.TheApp.Config.FontSize, self._OnTextChanged,
|
||||
self.OnFocusChange, self._OnTapLink)
|
||||
return self.Live
|
||||
return self._RenderedView()
|
||||
if self.FileType == "image":
|
||||
return ft.Image(src=Source.ReadBytes(self.Root, self.RelativePath), fit=ft.BoxFit.CONTAIN, expand=True)
|
||||
@@ -124,7 +138,8 @@ class FileView(ft.Container):
|
||||
value=Text,
|
||||
selectable=True,
|
||||
extension_set=ft.MarkdownExtensionSet.GITHUB_FLAVORED,
|
||||
code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK,
|
||||
md_style_sheet=MarkdownSheet(self.TheApp.Config.FontSize),
|
||||
code_theme=MarkdownCodeTheme(),
|
||||
code_style_sheet=CodeStyleSheet(),
|
||||
on_tap_link=self._OnTapLink,
|
||||
)
|
||||
@@ -134,7 +149,8 @@ class FileView(ft.Container):
|
||||
value=FencedBlock(Text, LanguageByExtension.get(Extension, "")),
|
||||
selectable=True,
|
||||
extension_set=ft.MarkdownExtensionSet.GITHUB_FLAVORED,
|
||||
code_theme=ft.MarkdownCodeTheme.ATOM_ONE_DARK,
|
||||
md_style_sheet=MarkdownSheet(self.TheApp.Config.FontSize),
|
||||
code_theme=MarkdownCodeTheme(),
|
||||
code_style_sheet=CodeStyleSheet(),
|
||||
)
|
||||
else:
|
||||
@@ -186,7 +202,7 @@ class FileView(ft.Container):
|
||||
def CanSave(self) -> bool:
|
||||
return self.IsText and self.IsDirty
|
||||
|
||||
def _OnTextChanged(self, e) -> None:
|
||||
def _OnTextChanged(self, e=None) -> None:
|
||||
if not self.IsDirty:
|
||||
self.IsDirty = True
|
||||
self.SaveButton.disabled = False
|
||||
@@ -194,7 +210,17 @@ class FileView(ft.Container):
|
||||
self.SaveButton.update()
|
||||
self.StatusText.update()
|
||||
|
||||
@property
|
||||
def Text(self) -> str:
|
||||
"""What's being edited, in whichever view has it."""
|
||||
if self.Live is not None and not self.IsEditing:
|
||||
return self.Live.Text
|
||||
return self.TextField.value or ""
|
||||
|
||||
def _ToggleEdit(self, e=None) -> None:
|
||||
if self.Live is not None:
|
||||
self._ToggleSource()
|
||||
return
|
||||
self.IsEditing = not self.IsEditing
|
||||
if self.IsEditing:
|
||||
self.Body.content = self.TextField
|
||||
@@ -210,6 +236,32 @@ class FileView(ft.Container):
|
||||
self.Body.update()
|
||||
self.ToggleButton.update()
|
||||
|
||||
def _ToggleSource(self) -> None:
|
||||
"""Live mode's toggle: the whole file as plain text, and back."""
|
||||
self.IsEditing = not self.IsEditing
|
||||
if self.IsEditing:
|
||||
self.TextField.value = self.Live.Text
|
||||
self.Live.EditingIndex = None # its open block's text is in the field now
|
||||
self.Body.content = self.TextField
|
||||
self.ToggleButton.content = "Live"
|
||||
self.ToggleButton.icon = ft.Icons.VISIBILITY_OUTLINED
|
||||
self.OnFocusChange(True)
|
||||
else:
|
||||
self.Live.SetText(self.TextField.value or "", Update=False) # not on the page yet
|
||||
self.Body.content = self.Live
|
||||
self.ToggleButton.content = "Source"
|
||||
self.ToggleButton.icon = ft.Icons.CODE
|
||||
self.OnFocusChange(False)
|
||||
self.Body.update()
|
||||
self.ToggleButton.update()
|
||||
|
||||
def HandleEscape(self) -> bool:
|
||||
"""Esc first closes an open block in live mode; True if it did."""
|
||||
if self.Live is not None and not self.IsEditing and self.Live.IsEditing:
|
||||
self.Live.Commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
def ToggleEdit(self) -> None:
|
||||
if self.IsText and not self.ToggleButton.disabled:
|
||||
self._ToggleEdit()
|
||||
@@ -218,11 +270,11 @@ class FileView(ft.Container):
|
||||
if not self.CanSave:
|
||||
return
|
||||
try:
|
||||
self.TheApp.Source.WriteText(self.Root, self.RelativePath, self.TextField.value or "")
|
||||
self.TheApp.Source.WriteText(self.Root, self.RelativePath, self.Text)
|
||||
except SourceError as Error:
|
||||
self.TheApp.HandleError(Error, f"Couldn't save {self.Item.Name}")
|
||||
return
|
||||
self.Content = self.TextField.value or ""
|
||||
self.Content = self.Text
|
||||
self.IsDirty = False
|
||||
self.SaveButton.disabled = True
|
||||
self.StatusText.value = "Saved."
|
||||
|
||||
Reference in new issue
Block a user