Browse Source

#617 automatically strip indentation from markdown content

Falko Schindler 2 years ago
parent
commit
be6d5cd0d2
3 changed files with 23 additions and 27 deletions
  1. 12 1
      nicegui/elements/markdown.py
  2. 9 9
      website/documentation.py
  3. 2 17
      website/documentation_tools.py

+ 12 - 1
nicegui/elements/markdown.py

@@ -33,7 +33,7 @@ class Markdown(ContentElement):
 
 @lru_cache(maxsize=int(os.environ.get('MARKDOWN_CONTENT_CACHE_SIZE', '1000')))
 def prepare_content(content: str, extras: str) -> str:
-    html = markdown2.markdown(content, extras=extras.split())
+    html = markdown2.markdown(remove_indentation(content), extras=extras.split())
     return apply_tailwind(html)  # we need explicit Markdown styling because tailwind CSS removes all default styles
 
 
@@ -52,3 +52,14 @@ def apply_tailwind(html: str) -> str:
     }
     pattern = re.compile('|'.join(rep.keys()))
     return pattern.sub(lambda m: rep[re.escape(m.group(0))], html)
+
+
+def remove_indentation(text: str) -> str:
+    """Remove indentation from a multi-line string based on the indentation of the first non-empty line."""
+    lines = text.splitlines()
+    while lines and not lines[0].strip():
+        lines.pop(0)
+    if not lines:
+        return ''
+    indentation = len(lines[0]) - len(lines[0].lstrip())
+    return '\n'.join(line[indentation:] for line in lines)

+ 9 - 9
website/documentation.py

@@ -3,7 +3,7 @@ import uuid
 from nicegui import app, ui
 
 from .demo import bash_window, python_window
-from .documentation_tools import element_demo, heading, intro_demo, load_demo, markdown, subheading, text_demo
+from .documentation_tools import element_demo, heading, intro_demo, load_demo, subheading, text_demo
 
 CONSTANT_UUID = str(uuid.uuid4())
 
@@ -570,7 +570,7 @@ def create_full(menu: ui.element) -> None:
 
     subheading('Server Hosting')
 
-    markdown('''
+    ui.markdown('''
         To deploy your NiceGUI app on a server, you will need to execute your `main.py` (or whichever file contains your `ui.run(...)`) on your cloud infrastructure.
         You can, for example, just install the [NiceGUI python package via pip](https://pypi.org/project/nicegui/) and use systemd or similar service to start the main script.
         In most cases, you will set the port to 80 (or 443 if you want to use HTTPS) with the `ui.run` command to make it easily accessible from the outside.
@@ -579,19 +579,19 @@ def create_full(menu: ui.element) -> None:
         With this command you can launch the script `main.py` in the current directory on the public port 80:
     ''')
     with bash_window(classes='max-w-lg w-full h-52'):
-        markdown('''
+        ui.markdown('''
             ```bash
             docker run -p 80:8080 -v $(pwd)/:/app/ \\
                 -d --restart always zauberzeug/nicegui:latest
             ```
         ''')
-    markdown('''
+    ui.markdown('''
         The demo assumes `main.py` uses the port 8080 in the `ui.run` command (which is the default).
         The `-d` tells docker to run in background and `--restart always` makes sure the container is restarted if the app crashes or the server reboots.
         Of course this can also be written in a Docker compose file:
     ''')
     with python_window('docker-compose.yml', classes='max-w-lg w-full h-52'):
-        markdown('''
+        ui.markdown('''
             ```yaml
             app:
                 image: zauberzeug/nicegui:latest
@@ -603,7 +603,7 @@ def create_full(menu: ui.element) -> None:
             ```
         ''')
 
-    markdown('''
+    ui.markdown('''
         You can provide SSL certificates directly using [FastAPI](https://fastapi.tiangolo.com/deployment/https/).
         In production we also like using reverse proxies like [Traefik](https://doc.traefik.io/traefik/) or [NGINX](https://www.nginx.com/) to handle these details for us.
         See our [docker-compose.yml](https://github.com/zauberzeug/nicegui/blob/main/docker-compose.yml) as an example.
@@ -615,7 +615,7 @@ def create_full(menu: ui.element) -> None:
 
     subheading('Package for Installation')
 
-    markdown('''
+    ui.markdown('''
         NiceGUI apps can also be bundled into an executable with [PyInstaller](https://www.pyinstaller.org/).
         This allows you to distribute your app as a single file that can be executed on any computer.
 
@@ -625,7 +625,7 @@ def create_full(menu: ui.element) -> None:
 
     with ui.row().classes('w-full items-stretch'):
         with python_window(classes='max-w-lg w-full'):
-            markdown('''
+            ui.markdown('''
                 ```python
                 from nicegui import ui
 
@@ -635,7 +635,7 @@ def create_full(menu: ui.element) -> None:
                 ```
             ''')
         with python_window('build.py', classes='max-w-lg w-full'):
-            markdown('''
+            ui.markdown('''
                 ```python
                 import os
                 import subprocess

+ 2 - 17
website/documentation_tools.py

@@ -7,24 +7,13 @@ import docutils.core
 
 from nicegui import globals, ui
 from nicegui.binding import BindableProperty
-from nicegui.elements.markdown import apply_tailwind
+from nicegui.elements.markdown import apply_tailwind, remove_indentation
 
 from .demo import demo
 
 SPECIAL_CHARACTERS = re.compile('[^(a-z)(A-Z)(0-9)-]')
 
 
-def remove_indentation(text: str) -> str:
-    """Remove indentation from a multi-line string based on the indentation of the first line."""
-    lines = text.splitlines()
-    while lines and not lines[0].strip():
-        lines.pop(0)
-    if not lines:
-        return ''
-    indentation = len(lines[0]) - len(lines[0].lstrip())
-    return '\n'.join(line[indentation:] for line in lines)
-
-
 def pascal_to_snake(name: str) -> str:
     return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
 
@@ -64,10 +53,6 @@ def subheading(text: str, *, make_menu_entry: bool = True, more_link: Optional[s
             ui.link(text, target=f'#{name}').props('data-close-overlay').on('click', click)
 
 
-def markdown(text: str) -> ui.markdown:
-    return ui.markdown(remove_indentation(text))
-
-
 def render_docstring(doc: str, with_params: bool = True) -> ui.html:
     doc = remove_indentation(doc)
     doc = doc.replace('param ', '')
@@ -87,7 +72,7 @@ class text_demo:
 
     def __call__(self, f: Callable) -> Callable:
         subheading(self.title, make_menu_entry=self.make_menu_entry)
-        markdown(self.explanation)
+        ui.markdown(self.explanation)
         return demo()(f)