diff --git a/assets/icons/osnap/apparent.svg b/assets/icons/osnap/apparent.svg
new file mode 100644
index 000000000..66e8c4646
--- /dev/null
+++ b/assets/icons/osnap/apparent.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/center.svg b/assets/icons/osnap/center.svg
new file mode 100644
index 000000000..4c45bba7b
--- /dev/null
+++ b/assets/icons/osnap/center.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/endpoint.svg b/assets/icons/osnap/endpoint.svg
new file mode 100644
index 000000000..5610ff59f
--- /dev/null
+++ b/assets/icons/osnap/endpoint.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/extension.svg b/assets/icons/osnap/extension.svg
new file mode 100644
index 000000000..9fd1934e3
--- /dev/null
+++ b/assets/icons/osnap/extension.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/grid.svg b/assets/icons/osnap/grid.svg
new file mode 100644
index 000000000..731603ffb
--- /dev/null
+++ b/assets/icons/osnap/grid.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/insertion.svg b/assets/icons/osnap/insertion.svg
new file mode 100644
index 000000000..b7da7b610
--- /dev/null
+++ b/assets/icons/osnap/insertion.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/intersection.svg b/assets/icons/osnap/intersection.svg
new file mode 100644
index 000000000..a8eccb0a2
--- /dev/null
+++ b/assets/icons/osnap/intersection.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/midpoint.svg b/assets/icons/osnap/midpoint.svg
new file mode 100644
index 000000000..eb278968c
--- /dev/null
+++ b/assets/icons/osnap/midpoint.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/nearest.svg b/assets/icons/osnap/nearest.svg
new file mode 100644
index 000000000..96996fa7a
--- /dev/null
+++ b/assets/icons/osnap/nearest.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/node.svg b/assets/icons/osnap/node.svg
new file mode 100644
index 000000000..0c5a3eaa2
--- /dev/null
+++ b/assets/icons/osnap/node.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/parallel.svg b/assets/icons/osnap/parallel.svg
new file mode 100644
index 000000000..7c887b848
--- /dev/null
+++ b/assets/icons/osnap/parallel.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/perpendicular.svg b/assets/icons/osnap/perpendicular.svg
new file mode 100644
index 000000000..1db15039f
--- /dev/null
+++ b/assets/icons/osnap/perpendicular.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/quadrant.svg b/assets/icons/osnap/quadrant.svg
new file mode 100644
index 000000000..070c6f9f8
--- /dev/null
+++ b/assets/icons/osnap/quadrant.svg
@@ -0,0 +1 @@
+
diff --git a/assets/icons/osnap/tangent.svg b/assets/icons/osnap/tangent.svg
new file mode 100644
index 000000000..ef1e0cca6
--- /dev/null
+++ b/assets/icons/osnap/tangent.svg
@@ -0,0 +1 @@
+
diff --git a/src/ui/icons.rs b/src/ui/icons.rs
index 45a6f3fc9..5a95628c5 100644
--- a/src/ui/icons.rs
+++ b/src/ui/icons.rs
@@ -15,6 +15,25 @@ const TRI_RIGHT: &[u8] = include_bytes!("../../assets/icons/ui/tri_right.svg");
const UNDO: &[u8] = include_bytes!("../../assets/icons/ui/undo.svg");
const REDO: &[u8] = include_bytes!("../../assets/icons/ui/redo.svg");
+// OSNAP marker symbols. Rendered as SVG (not Unicode glyphs) so the snap menu
+// shows the right shapes on the web build, whose bundled Fira Sans lacks the
+// geometric glyphs and rendered them as tofu boxes. (#138)
+const OSNAP_ENDPOINT: &[u8] = include_bytes!("../../assets/icons/osnap/endpoint.svg");
+const OSNAP_MIDPOINT: &[u8] = include_bytes!("../../assets/icons/osnap/midpoint.svg");
+const OSNAP_CENTER: &[u8] = include_bytes!("../../assets/icons/osnap/center.svg");
+const OSNAP_NODE: &[u8] = include_bytes!("../../assets/icons/osnap/node.svg");
+const OSNAP_QUADRANT: &[u8] = include_bytes!("../../assets/icons/osnap/quadrant.svg");
+const OSNAP_INTERSECTION: &[u8] = include_bytes!("../../assets/icons/osnap/intersection.svg");
+const OSNAP_EXTENSION: &[u8] = include_bytes!("../../assets/icons/osnap/extension.svg");
+const OSNAP_INSERTION: &[u8] = include_bytes!("../../assets/icons/osnap/insertion.svg");
+const OSNAP_PERPENDICULAR: &[u8] =
+ include_bytes!("../../assets/icons/osnap/perpendicular.svg");
+const OSNAP_TANGENT: &[u8] = include_bytes!("../../assets/icons/osnap/tangent.svg");
+const OSNAP_NEAREST: &[u8] = include_bytes!("../../assets/icons/osnap/nearest.svg");
+const OSNAP_APPARENT: &[u8] = include_bytes!("../../assets/icons/osnap/apparent.svg");
+const OSNAP_PARALLEL: &[u8] = include_bytes!("../../assets/icons/osnap/parallel.svg");
+const OSNAP_GRID: &[u8] = include_bytes!("../../assets/icons/osnap/grid.svg");
+
const LAY_ON: &[u8] = include_bytes!("../../assets/icons/layers/layon.svg");
const LAY_OFF: &[u8] = include_bytes!("../../assets/icons/layers/layoff.svg");
const LAY_FRZ: &[u8] = include_bytes!("../../assets/icons/layers/layfrz.svg");
@@ -82,6 +101,29 @@ pub fn raw<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> {
.into()
}
+/// SVG bytes for an OSNAP mode's marker symbol, for the snap menu. (#138)
+pub fn osnap(snap: crate::snap::SnapType) -> &'static [u8] {
+ use crate::snap::SnapType as S;
+ match snap {
+ S::Endpoint => OSNAP_ENDPOINT,
+ S::Midpoint => OSNAP_MIDPOINT,
+ S::Center => OSNAP_CENTER,
+ S::Node => OSNAP_NODE,
+ S::Quadrant => OSNAP_QUADRANT,
+ S::Intersection => OSNAP_INTERSECTION,
+ S::Extension => OSNAP_EXTENSION,
+ S::Insertion => OSNAP_INSERTION,
+ S::Perpendicular => OSNAP_PERPENDICULAR,
+ S::Tangent => OSNAP_TANGENT,
+ S::Nearest => OSNAP_NEAREST,
+ S::ApparentIntersection => OSNAP_APPARENT,
+ S::Parallel => OSNAP_PARALLEL,
+ S::Grid => OSNAP_GRID,
+ // Not shown in the snap menu; fall back to a neutral marker.
+ S::ObjectPick => OSNAP_NEAREST,
+ }
+}
+
/// Layer visibility icon bytes (on / off).
pub fn layer_visible(visible: bool) -> &'static [u8] {
if visible {
diff --git a/src/ui/snap_popup.rs b/src/ui/snap_popup.rs
index d048010fa..700a8eef3 100644
--- a/src/ui/snap_popup.rs
+++ b/src/ui/snap_popup.rs
@@ -36,8 +36,8 @@ pub fn snap_popup_overlay<'a>(snapper: &'a Snapper, right_offset: f32) -> Elemen
// Snap mode rows
let mut rows: Vec> = Vec::new();
- for &(snap_type, icon, label) in ALL_SNAP_MODES {
- rows.push(snap_row(snap_type, icon, label, snapper.is_on(snap_type)));
+ for &(snap_type, _glyph, label) in ALL_SNAP_MODES {
+ rows.push(snap_row(snap_type, label, snapper.is_on(snap_type)));
}
// "Object Snap Settings…" footer
@@ -101,18 +101,18 @@ pub fn snap_popup_overlay<'a>(snapper: &'a Snapper, right_offset: f32) -> Elemen
// ── Individual snap row ───────────────────────────────────────────────────
-fn snap_row<'a>(
- snap_type: SnapType,
- icon: &'a str,
- label: &'a str,
- active: bool,
-) -> Element<'a, Message> {
+fn snap_row<'a>(snap_type: SnapType, label: &'a str, active: bool) -> Element<'a, Message> {
let checkmark = crate::ui::icons::check_cell(active, CHECK_COLOR);
- let icon_el = text(icon)
- .size(11)
- .color(ICON_COLOR)
- .width(Length::Fixed(16.0));
+ // SVG marker (not a Unicode glyph) so the symbols render on the web build,
+ // whose bundled font lacks them and showed tofu boxes. (#138)
+ let icon_el = container(crate::ui::icons::tinted::(
+ crate::ui::icons::osnap(snap_type),
+ 13.0,
+ ICON_COLOR,
+ ))
+ .width(Length::Fixed(16.0))
+ .align_x(iced::Center);
let label_el = text(label)
.size(11)