From 382ce3cf63bba05622733ac1e01b3083e5c048d0 Mon Sep 17 00:00:00 2001 From: Brent Yi Date: Mon, 3 Feb 2025 10:40:50 -0800 Subject: [PATCH] Introduce `gui.add_html()` (#387) * Introduce `gui.add_html()` * Docs * add Html.tsx --- docs/source/gui_handles.md | 2 + src/viser/__init__.py | 1 + src/viser/_gui_api.py | 39 +++++++++++++++++++ src/viser/_gui_handles.py | 5 +++ src/viser/_messages.py | 16 ++++++++ .../client/src/ControlPanel/Generated.tsx | 3 ++ src/viser/client/src/WebsocketMessages.ts | 13 +++++++ src/viser/client/src/components/Html.tsx | 8 ++++ 8 files changed, 87 insertions(+) create mode 100644 src/viser/client/src/components/Html.tsx diff --git a/docs/source/gui_handles.md b/docs/source/gui_handles.md index 9cd30e41e..e79fff603 100644 --- a/docs/source/gui_handles.md +++ b/docs/source/gui_handles.md @@ -14,6 +14,8 @@ .. autoclass:: viser.GuiMarkdownHandle() +.. autoclass:: viser.GuiHtmlHandle() + .. autoclass:: viser.GuiPlotlyHandle() .. autoclass:: viser.GuiTabGroupHandle() diff --git a/src/viser/__init__.py b/src/viser/__init__.py index e7c48ae6f..7ce5a1ad5 100644 --- a/src/viser/__init__.py +++ b/src/viser/__init__.py @@ -5,6 +5,7 @@ from ._gui_handles import GuiDropdownHandle as GuiDropdownHandle from ._gui_handles import GuiEvent as GuiEvent from ._gui_handles import GuiFolderHandle as GuiFolderHandle +from ._gui_handles import GuiHtmlHandle as GuiHtmlHandle from ._gui_handles import GuiImageHandle as GuiImageHandle from ._gui_handles import GuiInputHandle as GuiInputHandle from ._gui_handles import GuiMarkdownHandle as GuiMarkdownHandle diff --git a/src/viser/_gui_api.py b/src/viser/_gui_api.py index e29e9848d..261b7679e 100644 --- a/src/viser/_gui_api.py +++ b/src/viser/_gui_api.py @@ -41,6 +41,7 @@ GuiDropdownHandle, GuiEvent, GuiFolderHandle, + GuiHtmlHandle, GuiImageHandle, GuiMarkdownHandle, GuiModalHandle, @@ -631,6 +632,44 @@ def add_markdown( handle.content = content return handle + def add_html( + self, + content: str, + order: float | None = None, + visible: bool = True, + ) -> GuiHtmlHandle: + """Add HTML to the GUI. + + Args: + content: HTML content to display. + order: Optional ordering, smallest values will be displayed first. + visible: Whether the component is visible. + + Returns: + A handle that can be used to interact with the GUI element. + """ + message = _messages.GuiHtmlMessage( + uuid=_make_uuid(), + container_uuid=self._get_container_uuid(), + props=_messages.GuiHtmlProps( + order=_apply_default_order(order), + content=content, + visible=visible, + ), + ) + self._websock_interface.queue_message(message) + + handle = GuiHtmlHandle( + _GuiHandleState( + message.uuid, + self, + None, + props=message.props, + parent_container_id=message.container_uuid, + ), + ) + return handle + def add_image( self, image: np.ndarray, diff --git a/src/viser/_gui_handles.py b/src/viser/_gui_handles.py index 7276b92be..1efad94f1 100644 --- a/src/viser/_gui_handles.py +++ b/src/viser/_gui_handles.py @@ -38,6 +38,7 @@ GuiCloseModalMessage, GuiDropdownProps, GuiFolderProps, + GuiHtmlProps, GuiImageProps, GuiMarkdownProps, GuiMultiSliderProps, @@ -797,6 +798,10 @@ def content(self, content: str) -> None: self._markdown = _parse_markdown(content, self._image_root) +class GuiHtmlHandle(_GuiHandle[None], GuiHtmlProps): + """Handling for updating and removing HTML elements.""" + + class GuiPlotlyHandle(_GuiHandle[None], GuiPlotlyProps): """Handle for updating and removing Plotly figures.""" diff --git a/src/viser/_messages.py b/src/viser/_messages.py index 991c1e48b..db5a68722 100644 --- a/src/viser/_messages.py +++ b/src/viser/_messages.py @@ -866,6 +866,22 @@ class GuiMarkdownMessage(_CreateGuiComponentMessage): props: GuiMarkdownProps +@dataclasses.dataclass +class GuiHtmlProps: + order: float + """Order value for arranging GUI elements. Synchronized automatically when assigned.""" + content: str + """HTML content to be displayed. Synchronized automatically when assigned.""" + visible: bool + """Visibility state of the markdown element. Synchronized automatically when assigned.""" + + +@dataclasses.dataclass +class GuiHtmlMessage(_CreateGuiComponentMessage): + container_uuid: str + props: GuiHtmlProps + + @dataclasses.dataclass class GuiProgressBarProps: order: float diff --git a/src/viser/client/src/ControlPanel/Generated.tsx b/src/viser/client/src/ControlPanel/Generated.tsx index c1fdc67f9..3540ddb80 100644 --- a/src/viser/client/src/ControlPanel/Generated.tsx +++ b/src/viser/client/src/ControlPanel/Generated.tsx @@ -23,6 +23,7 @@ import MultiSliderComponent from "../components/MultiSlider"; import UploadButtonComponent from "../components/UploadButton"; import ProgressBarComponent from "../components/ProgressBar"; import ImageComponent from "../components/Image"; +import HtmlComponent from "../components/Html"; /** Root of generated inputs. */ export default function GeneratedGuiContainer({ @@ -101,6 +102,8 @@ function GeneratedInput(props: { guiUuid: string }) { return ; case "GuiMarkdownMessage": return ; + case "GuiHtmlMessage": + return ; case "GuiPlotlyMessage": return ; case "GuiImageMessage": diff --git a/src/viser/client/src/WebsocketMessages.ts b/src/viser/client/src/WebsocketMessages.ts index 4d37f34e2..6bb26f1d3 100644 --- a/src/viser/client/src/WebsocketMessages.ts +++ b/src/viser/client/src/WebsocketMessages.ts @@ -354,6 +354,16 @@ export interface GuiMarkdownMessage { container_uuid: string; props: { order: number; _markdown: string; visible: boolean }; } +/** GuiHtmlMessage(uuid: 'str', container_uuid: 'str', props: 'GuiHtmlProps') + * + * (automatically generated) + */ +export interface GuiHtmlMessage { + type: "GuiHtmlMessage"; + uuid: string; + container_uuid: string; + props: { order: number; content: string; visible: boolean }; +} /** GuiProgressBarMessage(uuid: 'str', value: 'float', container_uuid: 'str', props: 'GuiProgressBarProps') * * (automatically generated) @@ -1207,6 +1217,7 @@ export type Message = | RemoveSceneNodeMessage | GuiFolderMessage | GuiMarkdownMessage + | GuiHtmlMessage | GuiProgressBarMessage | GuiPlotlyMessage | GuiImageMessage @@ -1290,6 +1301,7 @@ export type SceneNodeMessage = export type GuiComponentMessage = | GuiFolderMessage | GuiMarkdownMessage + | GuiHtmlMessage | GuiProgressBarMessage | GuiPlotlyMessage | GuiImageMessage @@ -1339,6 +1351,7 @@ export function isSceneNodeMessage( const typeSetGuiComponentMessage = new Set([ "GuiFolderMessage", "GuiMarkdownMessage", + "GuiHtmlMessage", "GuiProgressBarMessage", "GuiPlotlyMessage", "GuiImageMessage", diff --git a/src/viser/client/src/components/Html.tsx b/src/viser/client/src/components/Html.tsx new file mode 100644 index 000000000..e03ba6658 --- /dev/null +++ b/src/viser/client/src/components/Html.tsx @@ -0,0 +1,8 @@ +import { GuiHtmlMessage } from "../WebsocketMessages"; + +function HtmlComponent({ props }: GuiHtmlMessage) { + if (!props.visible) return <>; + return
; +} + +export default HtmlComponent;