Add live Markdown editing, colours and files in the tree

This commit is contained in:
sarah committed 2026-10-09 09:03:23 +02:00
1 parent 0eecfba224
commit f851ecd820
15 files changed
+1025 -58

No files matched your search

+77
View File
@@ -61,6 +61,16 @@ DataHome = Home / ".local" / "share"
BundleDir = Path(getattr(sys, "_MEIPASS", Path(__file__).resolve().parent))
AssetsDir = BundleDir / "assets"
# Shared by the file list and the sidebar tree.
FileIcons = {
"markdown": ft.Icons.DESCRIPTION_OUTLINED,
"text": ft.Icons.ARTICLE_OUTLINED,
"config": ft.Icons.SETTINGS_OUTLINED,
"html": ft.Icons.CODE,
"image": ft.Icons.IMAGE_OUTLINED,
"video": ft.Icons.MOVIE_OUTLINED,
}
_ThemeRoleMap = {
"Background": "Base", "Surface": "Mantle", "SurfaceAlt": "Surface0", "Border": "Surface1",
"Text": "Text", "TextMuted": "Overlay0", "Black": "Crust", "White": "White",
@@ -151,7 +161,12 @@ class AppConfig:
ConfirmDelete: bool = True
ShowSidebar: bool = True
SidebarWidth: int = 240
ShowFilesInTree: bool = True # False: the sidebar tree shows directories only
FontSize: int = 14 # file list and viewer text
# "Separate": Markdown opens rendered, with Edit/View to switch to the raw
# text. "Live": rendered, and clicking a block shows its Markdown to edit
# in place (like Obsidian's live preview). Only affects Markdown.
MarkdownEditing: str = "Live"
# Double-click on a file: True opens it in SaraView's own viewer, False
# hands it to the system's default program. The menu always offers the other.
OpenFilesInApp: bool = True
@@ -360,6 +375,68 @@ def CodeStyleSheet() -> ft.MarkdownStyleSheet:
return ft.MarkdownStyleSheet(code_text_style=ft.TextStyle(font_family=ActiveFontFamily))
def MarkdownSheet(FontSize: int) -> ft.MarkdownStyleSheet:
"""Rendered Markdown's colours, the same roles as the web view's
.MarkdownBody (Frontend/src/Styles/Theme.css): rainbow headings, coloured
bold/italic/code, a Purple bar on quotes."""
def Heading(Colour: str, Extra: int) -> ft.TextStyle:
return ft.TextStyle(size=FontSize + Extra, color=Colour, weight=ft.FontWeight.BOLD)
return ft.MarkdownStyleSheet(
p_text_style=ft.TextStyle(size=FontSize, color=Palette.Text),
h1_text_style=Heading(Palette.Mauve, 10),
h2_text_style=Heading(Palette.Blue, 7),
h3_text_style=Heading(Palette.Green, 4),
h4_text_style=Heading(Palette.Yellow, 2),
h5_text_style=Heading(Palette.Peach, 1),
h6_text_style=Heading(Palette.Red, 0),
strong_text_style=ft.TextStyle(color=Palette.Peach, weight=ft.FontWeight.BOLD),
em_text_style=ft.TextStyle(color=Palette.Yellow, italic=True),
del_text_style=ft.TextStyle(color=Palette.Overlay0, decoration=ft.TextDecoration.LINE_THROUGH),
a_text_style=ft.TextStyle(color=Palette.Blue, decoration=ft.TextDecoration.UNDERLINE),
code_text_style=ft.TextStyle(font_family=ActiveFontFamily, size=FontSize, color=Palette.Green, bgcolor=Palette.Surface0),
blockquote_text_style=ft.TextStyle(size=FontSize, color=Palette.Overlay0),
blockquote_decoration=ft.BoxDecoration(
bgcolor=Palette.Mantle, border=ft.Border(left=ft.BorderSide(3, Palette.Mauve)),
),
blockquote_padding=ft.Padding.only(left=12, top=6, right=8, bottom=6),
list_bullet_text_style=ft.TextStyle(size=FontSize, color=Palette.Mauve),
checkbox_text_style=ft.TextStyle(size=FontSize, color=Palette.Mauve),
table_head_text_style=ft.TextStyle(size=FontSize, color=Palette.Mauve, weight=ft.FontWeight.BOLD),
table_body_text_style=ft.TextStyle(size=FontSize, color=Palette.Text),
table_cells_decoration=ft.BoxDecoration(bgcolor=Palette.Mantle),
codeblock_decoration=ft.BoxDecoration(bgcolor=Palette.Mantle, border_radius=4),
horizontal_rule_decoration=ft.BoxDecoration(border=ft.Border(top=ft.BorderSide(1, Palette.Surface1))),
)
def MarkdownCodeTheme() -> ft.MarkdownCustomCodeTheme:
"""Syntax colours for code blocks from the palette roles (Catppuccin's
own mapping), in place of a fixed highlight.js theme that ignored the
active SaraSuite theme."""
def Style(Colour: str, **Extra) -> ft.TextStyle:
return ft.TextStyle(color=Colour, **Extra)
return ft.MarkdownCustomCodeTheme(
root=ft.TextStyle(color=Palette.Text, bgcolor=Palette.Mantle, font_family=ActiveFontFamily),
keyword=Style(Palette.Mauve), meta_keyword=Style(Palette.Mauve), doctag=Style(Palette.Mauve),
section=Style(Palette.Mauve, weight=ft.FontWeight.BOLD), bullet=Style(Palette.Mauve), tag=Style(Palette.Mauve),
string=Style(Palette.Green), meta_string=Style(Palette.Green), addition=Style(Palette.Green),
number=Style(Palette.Peach), literal=Style(Palette.Peach), regexp=Style(Palette.Peach), meta=Style(Palette.Peach),
built_in=Style(Palette.Yellow), builtin_name=Style(Palette.Yellow), type=Style(Palette.Yellow),
class_name=Style(Palette.Yellow), attr=Style(Palette.Yellow), attribute=Style(Palette.Yellow),
selector_class=Style(Palette.Yellow),
function=Style(Palette.Blue), title=Style(Palette.Blue), name=Style(Palette.Blue), link=Style(Palette.Blue),
selector_tag=Style(Palette.Blue), selector_id=Style(Palette.Blue),
variable=Style(Palette.Red), template_variable=Style(Palette.Red), symbol=Style(Palette.Red),
deletion=Style(Palette.Red),
comment=Style(Palette.Overlay0, italic=True), quote=Style(Palette.Overlay0, italic=True),
emphasis=ft.TextStyle(italic=True), strong=ft.TextStyle(weight=ft.FontWeight.BOLD),
)
def ChipButton(Text: str, OnClick, Icon=None, Accent: str = "", Disabled: bool = False) -> ft.OutlinedButton:
return ft.OutlinedButton(content=Text, icon=Icon, style=ChipButtonStyle(Accent), on_click=OnClick, disabled=Disabled)