A Fun Little Markdown Editor Using Flet And Python

I got bored and made a little markdown editor in Flet. Enjoy

#!/usr/bin/env python3
import flet as ft
import markdown2
import os

def main(page: ft.Page):
    
    # *** Page setup. ***
    page.title = "Flet Markdown Example"
    page.padding=4
    
    # *** Add floating button for saving. ***
    page.floating_action_button = ft.FloatingActionButton(
        icon=ft.icons.SAVE_ALT_ROUNDED,  
        bgcolor=ft.colors.PINK_900,
        tooltip="Export as PDF?",
        width=35,
        height=35,
        on_click=lambda _: export_dialog.save_file(allowed_extensions=["md","html"],file_name="Untitled.md"),
    )
    
    # *** Add file save dialog and actions. ***
    def export_file(e: ft.FilePickerResultEvent):
        if len(MarkDownOutput.value) > 0:
            if '.' not in e.path:
                page.snack_bar = ft.SnackBar(ft.Text(f"Please provide a proper filename. You can save as .md or .html."),
                                            bgcolor=ft.colors.PINK_100)
                page.snack_bar.open = True
            else:
                if 'md' in os.path.splitext(e.path)[-1].lower():
                    data = MarkDownOutput.value
                elif 'html' in os.path.splitext(e.path)[-1].lower():
                    data = markdown2.markdown(MarkDownOutput.value)
                    
                try:
                    with open(e.path, 'w') as export:
                        export.write(data)
                    page.snack_bar = ft.SnackBar(ft.Text(f"File saved."),
                                                bgcolor=ft.colors.PINK_100)
                    page.snack_bar.open = True
                except Exception as err:
                    page.snack_bar = ft.SnackBar(ft.Text(str(err)),
                                                bgcolor=ft.colors.PINK_100)
                    page.snack_bar.open = True
        else:
            page.snack_bar = ft.SnackBar(ft.Text("Nothing to save."),
                                        bgcolor=ft.colors.PINK_100)
            page.snack_bar.open = True
            
        page.update()
        
    export_dialog = ft.FilePicker(on_result=export_file)
    page.overlay.append(export_dialog)
    
    # *** Add test and markdown controls and action. ***
    def update_markdown(e):
        MarkDownOutput.value = MarkDownInput.value
        MarkDownOutput.update()
        
    MarkDownInput = ft.TextField(label="MarkDown", 
                                 multiline=True, 
                                 autofocus=True, 
                                 border=ft.InputBorder.NONE, 
                                 filled=True, 
                                 on_change=update_markdown)
    
    MarkDownOutput = ft.Markdown(MarkDownInput.value,
            selectable=True,
            code_theme="tomorrow-night-eighties",
            code_style=ft.TextStyle(font_family="Roboto Mono"),
            extension_set=ft.MarkdownExtensionSet.GITHUB_FLAVORED,
            on_tap_link=lambda e: page.launch_url(e.data),)
    

    # *** Build the page. ***
    page.add(
        ft.Row(
            [
                ft.Container(
                    MarkDownInput,
                    alignment=ft.alignment.top_left,
                    expand=True,
                    padding=4
                ),
                ft.VerticalDivider(width=12, thickness=4),
                ft.Column(
                    controls=[ft.Container(
                        MarkDownOutput,
                        padding=4,
                    )],
                    scroll=ft.ScrollMode.AUTO,
                    alignment=ft.CrossAxisAlignment.START,
                    expand=True,
                )
            ],
            spacing=0,
            expand=True,
            alignment=ft.MainAxisAlignment.START,
            vertical_alignment=ft.CrossAxisAlignment.START
        )
    )
# Main 
if __name__ == "__main__":
    ft.app(target=main, assets_dir="assets")

This one employs a Yes/No dialog to keep it from overwriting the user’s files.

#!/usr/bin/env python3
import flet as ft
import markdown2
import os

class FileAlert(ft.UserControl):
    
    file_path = None
    markdown = None
    
    def __init__(self, page):
        super().__init__()
        
        self.page = page
        
        self.dlg_modal = ft.AlertDialog(
            modal=True,
            title=ft.Text("Please confirm"),
            content=ft.Text("Overwrite?"),
            actions=[
                ft.TextButton("No", on_click=self.close_dlg),
                ft.TextButton("Yes", on_click=self.yes_click),
            ],
            actions_alignment=ft.MainAxisAlignment.END,
            on_dismiss=lambda e: print("closed"),
        )
        
    # *** Add popup.   
    def open(self):
        self.page.dialog = self.dlg_modal
        self.dlg_modal.open = True
        self.page.update()
                
    def close_dlg(self, e):
        self.dlg_modal.open = False
        self.page.update()
    
    def yes_click(self, e):
            
        save_markup(self.page, self.file_path, self.markdown)
            
        self.dlg_modal.open = False
        self.page.update()
        
def save_markup(page, save_path, content):
    if 'md' in os.path.splitext(save_path)[-1].lower():
        data = content
    elif 'html' in os.path.splitext(save_path)[-1].lower():
        data = markdown2.markdown(content)
        
    try:
        with open(save_path, 'w') as export:
            export.write(data)
        page.snack_bar = ft.SnackBar(ft.Text(f"File saved."),
                                    bgcolor=ft.colors.PINK_100)
        page.snack_bar.open = True
    except Exception as err:
        page.snack_bar = ft.SnackBar(ft.Text(str(err)),
                                    bgcolor=ft.colors.PINK_100)
        page.snack_bar.open = True

def main(page: ft.Page):
    
    # *** Page setup. ***
    page.title = "Flet Markdown Example"
    page.padding=4
    
    # *** Add floating button for saving. ***
    page.floating_action_button = ft.FloatingActionButton(
        icon=ft.icons.SAVE_ALT_ROUNDED,  
        bgcolor=ft.colors.PINK_900,
        tooltip="Save Markdown?",
        width=35,
        height=35,
        on_click=lambda _: export_dialog.save_file(allowed_extensions=["md","html"],file_name="Untitled.md"),
    )
    
    # *** Add file save dialog and actions. ***
    def export_file(e: ft.FilePickerResultEvent):
        save_path = e.path
        if len(MarkDownOutput.value) > 0:
            if '.' not in save_path:
                page.snack_bar = ft.SnackBar(ft.Text(f"Please provide a proper filename. You can save as .md or .html."),
                                            bgcolor=ft.colors.PINK_100)
                page.snack_bar.open = True
            else:
                if os.path.exists(save_path):
                    
                    OWAlert = FileAlert(page)
                    OWAlert.markdown = MarkDownOutput.value
                    OWAlert.file_path = save_path
                    OWAlert.open()
                    
                else:
                    
                    save_markup(page, save_path, MarkDownOutput.value)
                    
        else:
            page.snack_bar = ft.SnackBar(ft.Text("Nothing to save."),
                                        bgcolor=ft.colors.PINK_100)
            page.snack_bar.open = True
            
        page.update()
        
    export_dialog = ft.FilePicker(on_result=export_file)
    page.overlay.append(export_dialog)
    
    # *** Add test and markdown controls and action. ***
    def update_markdown(e):
        MarkDownOutput.value = MarkDownInput.value
        MarkDownOutput.update()
        
    MarkDownInput = ft.TextField(label="MarkDown", 
                                 multiline=True, 
                                 autofocus=True, 
                                 border=ft.InputBorder.NONE, 
                                 filled=True, 
                                 on_change=update_markdown)
    
    MarkDownOutput = ft.Markdown(MarkDownInput.value,
            selectable=True,
            code_theme="tomorrow-night-eighties",
            code_style=ft.TextStyle(font_family="Roboto Mono"),
            extension_set=ft.MarkdownExtensionSet.GITHUB_FLAVORED,
            on_tap_link=lambda e: page.launch_url(e.data),)
    

    # *** Build the page. ***
    page.add(
        ft.Row(
            [ 
                ft.Container(
                    MarkDownInput,
                    alignment=ft.alignment.top_left,
                    expand=True,
                    padding=4
                ),
                ft.VerticalDivider(width=12, thickness=4),
                ft.Column(
                    controls=[ft.Container(
                        MarkDownOutput,
                        padding=4,
                    )],
                    scroll=ft.ScrollMode.AUTO,
                    alignment=ft.CrossAxisAlignment.START,
                    expand=True,
                )
            ],
            spacing=0,
            expand=True,
            alignment=ft.MainAxisAlignment.START,
            vertical_alignment=ft.CrossAxisAlignment.START
        ),
    )
 
# MAIN : Run with assets folder set.    
if __name__ == "__main__":
    ft.app(target=main, assets_dir="assets")