Skip to content

Commit

Permalink
Update interface to intake a image_root Path and replace relative pat…
Browse files Browse the repository at this point in the history
…hs in mdx

This gets around introducing custom syntax that isn't being processed by mdx
  • Loading branch information
jonahbedouch committed Aug 6, 2023
1 parent 4b4ec9e commit 0d240e0
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 41 deletions.
6 changes: 1 addition & 5 deletions examples/14_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import time
from pathlib import Path

import imageio.v3 as iio
import viser

server = viser.ViserServer()
Expand All @@ -17,10 +16,7 @@
def _(client: viser.ClientHandle) -> None:
with open("./assets/mdx_example.mdx", "r") as mkdn:
markdown = client.add_gui_markdown(
markdown=mkdn.read(),
images={
"cal_logo": iio.imread(Path(__file__).parent / "assets/Cal_logo.png")
},
markdown=mkdn.read(), image_root=Path(__file__).parent
)

button = client.add_gui_button("Remove Markdown")
Expand Down
29 changes: 15 additions & 14 deletions examples/assets/mdx_example.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ Anywhere where you can insert GUI elements, you can also insert `images`, `block

In inline code blocks, you can show off colors with color chips: `#FED363` `hsl(0, 0%, 82%)` `rgb(255, 255, 255)`

Adding images from a remote origin is simple. For remote images, you must specify the image in the `images` prop of markdown, and then reference it
in your markdown as follows:
Adding images from a remote origin is simple. For remote images, you should specify a `image_root` that corresponds to the `Path` object that you'd like
your relative images to be scoped to when initializing your markdown. If no such `image_root` is provided, the file system will be scoped to the directory
that Viser is installed in.

<section>
![Viser Logo](http://nerfstudio-project.github.io/viser/_static/viser.svg)
![Cal Logo](cal_logo)
![Cal Logo](../examples/assets/Cal_logo.png)
</section>

Tables follow the standard markdown spec:
Expand All @@ -37,24 +38,24 @@ Viser GUI has MDX 2 support (WIP)
import time
from pathlib import Path

import imageio.v3 as iio
import viser

server = viser.ViserServer()
server.world_axes.visible = True

with open("./assets/mdx_example.mdx", "r") as mkdn:
markdown = server.add_gui_markdown(
markdown=mkdn.read(),
images={"cal_logo": iio.imread(Path(__file__).parent / "assets/Cal_logo.png")},
)

button = server.add_gui_button("Remove Markdown")
@server.on_client_connect
def _(client: viser.ClientHandle) -> None:
with open("./assets/mdx_example.mdx", "r") as mkdn:
markdown = client.add_gui_markdown(
markdown=mkdn.read(), image_root=Path(__file__).parent
)

button = client.add_gui_button("Remove Markdown")

@button.on_click
def _(_):
markdown.remove()
@button.on_click
def _(_):
markdown.remove()


while True:
Expand All @@ -69,7 +70,7 @@ there is no guarantee that styling will look good once you stray from the standa

<figure>
<img
src="cal_logo"
src="./assets/Cal_logo.png"
alt="Cal Logo"
style={{ width: "100%", height: "auto" }}
/>
Expand Down
53 changes: 31 additions & 22 deletions viser/_gui_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
TypeVar,
overload,
)

from pathlib import Path

import re
import imageio.v3 as iio
import numpy as onp
from typing_extensions import LiteralString
import urllib.parse
Expand Down Expand Up @@ -78,32 +79,41 @@ def _compute_precision_digits(x: float) -> int:
return digits


def _get_repls(images: Dict[str, onp.ndarray]):
def _markdown_repl(match: re.Match[str]) -> str:
url = match.group(2)
def _get_repls(image_root: Optional[Path]):
root_selected = True
if image_root is None:
root_selected = False
image_root = Path(__file__).parent

def _get_uri(match: re.Match[str], group: int) -> str:
url = match.group(group)

if url in images:
data_uri = _encode_image_base64(images[url], "png")
if not url.startswith("http") and not root_selected:
warnings.warn(
"No image_root provided. All relative paths will be scoped to viser's installation path.",
stacklevel=2,
)
try:
image = iio.imread(image_root / url)
data_uri = _encode_image_base64(image, "png")
url = urllib.parse.quote(f"{data_uri[1]}")
return f"![{match.group(1)}](data:{data_uri[0]};base64,{url})"
else:
return match.group()
return f"data:{data_uri[0]};base64,{url}"
except (IOError, FileNotFoundError):
return match.group(group)

def _src_repl(match: re.Match[str]) -> str:
url = match.group(1)
def _markdown_repl(match: re.Match[str]) -> str:
uri = _get_uri(match, 2)
return f"![{match.group(1)}]({uri})"

if url in images:
data_uri = _encode_image_base64(images[url], "png")
url = urllib.parse.quote(f"{data_uri[1]}")
return f'src="data:{data_uri[0]};base64,{url}"'
else:
return match.group()
def _src_repl(match: re.Match[str]) -> str:
uri = _get_uri(match, 1)
return f'src="{uri}"'

return [_markdown_repl, _src_repl]


def _parse_markdown(markdown: str, images: Dict[str, onp.ndarray]) -> str:
repls = _get_repls(images)
def _parse_markdown(markdown: str, image_root: Optional[Path]) -> str:
repls = _get_repls(image_root)
phase1 = re.sub(r"\!\[([^]]*)\]\(([^]]*)\)", repls[0], markdown)
return re.sub(r'src="([^"]*)"', repls[1], phase1)

Expand Down Expand Up @@ -167,11 +177,10 @@ def add_gui_tab_group(self) -> GuiTabGroupHandle:
)

def add_gui_markdown(
self, markdown: str, images: Optional[Dict[str, onp.ndarray]]
self, markdown: str, image_root: Optional[Path]
) -> GuiMarkdownHandle:
"""Add markdown to the GUI."""
if images is not None and len(images):
markdown = _parse_markdown(markdown, images)
markdown = _parse_markdown(markdown, image_root)

markdown_id = _make_unique_id()
self._get_api()._queue(
Expand Down

0 comments on commit 0d240e0

Please sign in to comment.