mirror of
https://git.srhtnin.garden/sarah/SaraView.git
synced 2026-10-15 02:43:17 +02:00
Client: links and images open, line breaks like the web, directory notes setting
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
55ee8e2f23
commit
0513d94c6a
8 files changed
+134
-23
No files matched your search
+42
-7
@@ -18,17 +18,18 @@ EditorView/ImageView idea, with SaraView's View/Edit/Save toolbar).
|
||||
- SaraTag tags (from the server's Books) show as chips under the toolbar."""
|
||||
|
||||
import re
|
||||
from urllib.parse import unquote
|
||||
from typing import Callable, Optional
|
||||
|
||||
import flet as ft
|
||||
|
||||
from App import App
|
||||
from Core import ChipButton, CodeStyleSheet, HumanSize, MarkdownCodeTheme, MarkdownSheet, Palette
|
||||
from Core import ChipButton, CodeStyleSheet, HardLineBreaks, HumanSize, MarkdownCodeTheme, MarkdownSheet, Palette
|
||||
from Diagrams import MaybeDiagram, WithDiagrams
|
||||
from Properties import ParseProperties, PropertiesView, ToggleLine, WithCheckboxes
|
||||
from Variables import FindSpans, Substitute
|
||||
from LiveMarkdown import LiveMarkdown
|
||||
from Sources import Entry, SourceError, TextFileTypes
|
||||
from Sources import Entry, NormalizeRelativePath, ParentPath, SourceError, TextFileTypes
|
||||
|
||||
LanguageByExtension = {
|
||||
".json": "json", ".toml": "toml", ".yaml": "yaml", ".yml": "yaml", ".cfg": "ini", ".ini": "ini",
|
||||
@@ -58,6 +59,7 @@ class FileView(ft.Container):
|
||||
OnOpenExternally: Callable[[], None],
|
||||
OnDownload: Optional[Callable[[], None]],
|
||||
OnFocusChange: Callable[[bool], None],
|
||||
OnOpenLink: Optional[Callable[[str, str], None]] = None,
|
||||
):
|
||||
self.TheApp = TheApp
|
||||
self.Root = Root
|
||||
@@ -66,6 +68,8 @@ class FileView(ft.Container):
|
||||
self.FileType = Item.FileType or "unknown"
|
||||
self.OnClose = OnClose
|
||||
self.OnFocusChange = OnFocusChange
|
||||
self.OnOpenLink = OnOpenLink # (Root, RelativePath): a link to another note, or an image, clicked
|
||||
self.Pictures: dict[str, Optional[bytes]] = {} # images by path, read once
|
||||
self.IsText = self.FileType in TextFileTypes
|
||||
self.IsLive = (self.FileType == "markdown" and TheApp.Config.MarkdownEditing == "Live"
|
||||
and TheApp.Source.CanWrite)
|
||||
@@ -130,7 +134,8 @@ class FileView(ft.Container):
|
||||
self.Live = LiveMarkdown(self.Content, self.TheApp.Config.FontSize, self._OnTextChanged,
|
||||
self.OnFocusChange, self._OnTapLink,
|
||||
MakeDiagram=lambda Text, Size: MaybeDiagram(self.TheApp, Text, Size),
|
||||
GetSpans=lambda Document: FindSpans(self.TheApp, self.Root, self.RelativePath, Document))
|
||||
GetSpans=lambda Document: FindSpans(self.TheApp, self.Root, self.RelativePath, Document),
|
||||
MakeImage=self._Image)
|
||||
return self.Live
|
||||
return self._RenderedView()
|
||||
if self.FileType == "image":
|
||||
@@ -152,7 +157,7 @@ class FileView(ft.Container):
|
||||
|
||||
def MakeMarkdown(Part: str) -> ft.Control:
|
||||
return ft.Markdown(
|
||||
value=Part,
|
||||
value=HardLineBreaks(Part),
|
||||
selectable=True,
|
||||
extension_set=ft.MarkdownExtensionSet.GITHUB_FLAVORED,
|
||||
md_style_sheet=MarkdownSheet(FontSize),
|
||||
@@ -164,7 +169,7 @@ class FileView(ft.Container):
|
||||
def MakeWithCheckboxes(Part: str, Start: int) -> ft.Control:
|
||||
FirstLine = Text[:BodyStart + Start].count("\n")
|
||||
Shown, LineMap = Substitute(Part, BodyStart + Start, Spans) # variables as their results
|
||||
Parts = WithCheckboxes(Shown, FirstLine, MakeMarkdown, Toggle, FontSize, LineMap)
|
||||
Parts = WithCheckboxes(Shown, FirstLine, MakeMarkdown, Toggle, FontSize, LineMap, self._Image)
|
||||
return Parts[0] if len(Parts) == 1 else ft.Column(Parts, spacing=0, tight=True)
|
||||
|
||||
Views = WithDiagrams(self.TheApp, Text[BodyStart:], FontSize, MakeWithCheckboxes)
|
||||
@@ -220,9 +225,39 @@ class FileView(ft.Container):
|
||||
return ft.Row(Chips, wrap=True, spacing=4, run_spacing=4)
|
||||
|
||||
def _OnTapLink(self, e) -> None:
|
||||
Target = str(e.data or "")
|
||||
if Target.startswith(("http://", "https://", "mailto:")):
|
||||
self.OpenTarget(str(e.data or ""))
|
||||
|
||||
def OpenTarget(self, Target: str) -> None:
|
||||
"""A clicked link or image: web addresses in the browser, a note (or
|
||||
directory, or image) relative to this one in SaraView."""
|
||||
if not Target or Target.startswith("#"):
|
||||
return
|
||||
if re.match(r"^[a-z][a-z0-9+.-]*:", Target, re.IGNORECASE):
|
||||
self.TheApp.Page.run_task(ft.UrlLauncher().launch_url, Target)
|
||||
elif self.OnOpenLink is not None:
|
||||
Path = NormalizeRelativePath(ParentPath(self.RelativePath), unquote(Target.split("#", 1)[0]))
|
||||
self.OnOpenLink(self.Root, Path)
|
||||
|
||||
def _Image(self, Src: str, Alt: str) -> Optional[ft.Control]:
|
||||
"""A line that's only an image (Properties.WithCheckboxes): the image,
|
||||
opened when clicked. None if it can't be read: then it shows as written."""
|
||||
if re.match(r"^https?://", Src):
|
||||
Picture: ft.Image = ft.Image(src=Src, fit=ft.BoxFit.CONTAIN, tooltip=Alt or None)
|
||||
else:
|
||||
Path = NormalizeRelativePath(ParentPath(self.RelativePath), unquote(Src))
|
||||
if Path not in self.Pictures:
|
||||
try:
|
||||
self.Pictures[Path] = self.TheApp.Source.ReadBytes(self.Root, Path)
|
||||
except SourceError:
|
||||
self.Pictures[Path] = None
|
||||
if self.Pictures[Path] is None:
|
||||
return None
|
||||
Picture = ft.Image(src=self.Pictures[Path], fit=ft.BoxFit.CONTAIN, tooltip=Alt or None)
|
||||
return ft.Container(
|
||||
ft.GestureDetector(content=Picture, on_tap=lambda e: self.OpenTarget(Src), mouse_cursor=ft.MouseCursor.CLICK),
|
||||
alignment=ft.Alignment.CENTER_LEFT,
|
||||
padding=ft.Padding.symmetric(vertical=4),
|
||||
)
|
||||
|
||||
def _ToggleAndSave(self, Line: int) -> None:
|
||||
"""A checkbox ticked in View: flip its line and save, like the web view."""
|
||||
|
||||
Reference in new issue
Block a user