diff --git a/src/viser/_gui_handles.py b/src/viser/_gui_handles.py index 5138b48f..93aabf70 100644 --- a/src/viser/_gui_handles.py +++ b/src/viser/_gui_handles.py @@ -117,6 +117,9 @@ def __setattr__(self, name: str, value: Any) -> None: handle = cast(_GuiInputHandle, self) # Get the value of the T TypeVar. if name in self._prop_hints: + if getattr(handle._impl.props, name) == value: + # Do nothing. Assumes equality is defined for the prop value. + return setattr(handle._impl.props, name, value) handle._impl.gui_api._websock_interface.queue_message( _messages.GuiUpdateMessage(handle._impl.id, {name: value}) @@ -489,7 +492,7 @@ def add_tab(self, label: str, icon: IconName | None = None) -> GuiTabHandle: self._tab_handles.append(out) self._tab_labels = self._tab_labels + (label,) - self._icons_html = self._icons_html + ( + self._tab_icons_html = self._tab_icons_html + ( None if icon is None else svg_from_icon(icon), ) self._tab_container_ids = tuple(handle._id for handle in self._tab_handles) @@ -551,9 +554,9 @@ def remove(self) -> None: self._parent._tab_labels[:found_index] + self._parent._tab_labels[found_index + 1 :] ) - self._parent._icons_html = ( - self._parent._icons_html[:found_index] - + self._parent._icons_html[found_index + 1 :] + self._parent._tab_icons_html = ( + self._parent._tab_icons_html[:found_index] + + self._parent._tab_icons_html[found_index + 1 :] ) self._parent._tab_handles = ( self._parent._tab_handles[:found_index] diff --git a/src/viser/_messages.py b/src/viser/_messages.py index 0057c7f0..77748a44 100644 --- a/src/viser/_messages.py +++ b/src/viser/_messages.py @@ -14,7 +14,7 @@ from . import infra, theme -@dataclasses.dataclass +@dataclasses.dataclass(frozen=True) class GuiSliderMark: value: float label: Optional[str] diff --git a/src/viser/_scene_handles.py b/src/viser/_scene_handles.py index 87c8b990..ec62e562 100644 --- a/src/viser/_scene_handles.py +++ b/src/viser/_scene_handles.py @@ -57,6 +57,10 @@ def __setattr__(self, name: str, value: Any) -> None: elif hint == onpt.NDArray[np.uint8] and "color" in name: value = colors_to_uint8(value) + if getattr(handle._impl.props, name) == value: + # Do nothing. Assumes equality is defined for the prop value. + return + setattr(handle._impl.props, name, value) handle._impl.api._websock_interface.queue_message( _messages.SceneNodeUpdateMessage(handle.name, {name: value}) diff --git a/src/viser/client/src/ThreeAssets.tsx b/src/viser/client/src/ThreeAssets.tsx index 5ce5717d..2d45dae1 100644 --- a/src/viser/client/src/ThreeAssets.tsx +++ b/src/viser/client/src/ThreeAssets.tsx @@ -601,24 +601,18 @@ export const ViserMesh = React.forwardRef< const state = viewer.skinnedMeshState.current[message.name]; const bones = bonesRef.current; if (bones !== undefined) { - bones.forEach((bone, i) => { - if (!state.initialized) { + if (!state.initialized) { + bones.forEach((bone) => { parentNode.add(bone); - } - const wxyz = state.initialized - ? state.poses[i].wxyz - : message.props.bone_wxyzs[i]; - const position = state.initialized - ? state.poses[i].position - : message.props.bone_positions[i]; - + }); + state.initialized = true; + } + bones.forEach((bone, i) => { + const wxyz = state.poses[i].wxyz; + const position = state.poses[i].position; bone.quaternion.set(wxyz[1], wxyz[2], wxyz[3], wxyz[0]); bone.position.set(position[0], position[1], position[2]); }); - - if (!state.initialized) { - state.initialized = true; - } } }); diff --git a/src/viser/infra/_infra.py b/src/viser/infra/_infra.py index e3f1e1b4..25d12956 100644 --- a/src/viser/infra/_infra.py +++ b/src/viser/infra/_infra.py @@ -442,13 +442,36 @@ async def viser_http_server( use_gzip = "gzip" in request_headers.get("Accept-Encoding", "") - mime_type = mimetypes.guess_type(relpath)[0] + # First, try some known MIME types. Using guess_type() can cause + # problems for Javascript on some Windows machines. + # + # Some references: + # https://github.com/nerfstudio-project/viser/issues/256#issuecomment-2369684252 + # https://bugs.python.org/issue43975 + # https://github.com/golang/go/issues/32350#issuecomment-525111557 + # + # We're assuming UTF-8, this is mostly reasonable but might want to revisit. + mime_type = { + ".css": "text/css; charset=utf-8", + ".gif": "image/gif", + ".htm": "text/html; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".jpg": "image/jpeg", + ".js": "application/javascript", + ".wasm": "application/wasm", + ".pdf": "application/pdf", + ".png": "image/png", + ".svg": "image/svg+xml", + ".xml": "text/xml; charset=utf-8", + }.get(Path(path).suffix.lower(), None) + if mime_type is None: + mime_type = mimetypes.guess_type(relpath)[0] if mime_type is None: mime_type = "application/octet-stream" + response_headers = { "Content-Type": mime_type, } - if source_path not in file_cache: file_cache[source_path] = source_path.read_bytes() if use_gzip: