From cf48a006ded96e5180e750d7dd2b89cf6b4948da Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Thu, 14 May 2026 15:50:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(shell-core/figma):=20kind=E2=86=92NodeKind?= =?UTF-8?q?=20mapper=20(clipboard=20JSON=20one=20step=20from=20real=20impo?= =?UTF-8?q?rt)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `FigmaClipboardNode::to_node_kind()` maps the 8 most-common Figma type strings to the closest `crate::document::NodeKind`: RECTANGLE → Rect ELLIPSE → Ellipse LINE → Line POLYGON / REGULAR_POLY → Polygon VECTOR → Path TEXT → Text FRAME / SECTION → Frame GROUP → Group → Other() Unknown kinds round-trip via `NodeKind::Other(...)` so the layer panel still renders something honest instead of dropping the node. Future commits add per-kind specialised geometry mappers (fills / strokes / vectors / text / layout) ported from `pen-figma`. Tests (2 new): - 10-case table of every supported Figma kind → expected NodeKind - Unknown `BOOLEAN_OPERATION` → `Other("BOOLEAN_OPERATION")` round-trip #9 Figma now ~50% — file kind detection + clipboard JSON walker + per-child kind + name + NodeKind mapping. Remaining: per-kind geometry/style mapper functions (the `pen-figma` figma-fill-mapper / stroke-mapper / text-mapper / vector-decoder / etc port). Tests total: 245 shell-core (+2). Wasm32 build clean. --- crates/openpencil-shell-core/src/figma.rs | 67 +++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/crates/openpencil-shell-core/src/figma.rs b/crates/openpencil-shell-core/src/figma.rs index 21e381199..87dbcf7dc 100644 --- a/crates/openpencil-shell-core/src/figma.rs +++ b/crates/openpencil-shell-core/src/figma.rs @@ -222,6 +222,36 @@ pub struct FigmaClipboardNode { pub name: String, } +impl FigmaClipboardNode { + /// Map this clipboard node's Figma kind string onto the + /// closest `NodeKind` in the shell-core document model. + /// Unknown kinds round-trip as `NodeKind::Other(kind)` so the + /// shell still has a layer-panel entry to display. + /// Covers the 8 most-common Figma kinds — the remaining + /// specialised types (BOOLEAN_OPERATION, SLICE, COMPONENT, + /// INSTANCE, STAR, ...) ship in follow-up commits as the + /// converters need them. + pub fn to_node_kind(&self) -> crate::document::NodeKind { + use crate::document::NodeKind; + match self.kind.as_str() { + "RECTANGLE" => NodeKind::Rect, + "ELLIPSE" => NodeKind::Ellipse, + "LINE" => NodeKind::Line, + "POLYGON" | "REGULAR_POLYGON" => NodeKind::Polygon, + "VECTOR" => NodeKind::Path, + "TEXT" => NodeKind::Text, + "FRAME" | "GROUP" | "SECTION" => { + if self.kind == "GROUP" { + NodeKind::Group + } else { + NodeKind::Frame + } + } + _ => NodeKind::Other(self.kind.clone()), + } + } +} + /// Successful parse result. Carries the file kind, top-level /// children count, and (for clipboard JSON) a minimal-fields /// extraction of each top-level child. Placeholder until the full @@ -327,6 +357,43 @@ mod tests { assert_eq!(parsed.clipboard_nodes[1].name, ""); } + #[test] + fn figma_kind_maps_to_node_kind_for_common_types() { + use crate::document::NodeKind; + let cases = [ + ("RECTANGLE", NodeKind::Rect), + ("ELLIPSE", NodeKind::Ellipse), + ("LINE", NodeKind::Line), + ("POLYGON", NodeKind::Polygon), + ("REGULAR_POLYGON", NodeKind::Polygon), + ("VECTOR", NodeKind::Path), + ("TEXT", NodeKind::Text), + ("FRAME", NodeKind::Frame), + ("GROUP", NodeKind::Group), + ("SECTION", NodeKind::Frame), + ]; + for (input, expected) in cases { + let n = FigmaClipboardNode { + kind: input.into(), + name: String::new(), + }; + assert_eq!(n.to_node_kind(), expected, "{input}"); + } + } + + #[test] + fn figma_kind_unknown_round_trips_via_other() { + use crate::document::NodeKind; + let n = FigmaClipboardNode { + kind: "BOOLEAN_OPERATION".into(), + name: String::new(), + }; + assert_eq!( + n.to_node_kind(), + NodeKind::Other("BOOLEAN_OPERATION".into()) + ); + } + #[test] fn parse_fig_returns_unknown_for_random_bytes() { assert_eq!(parse_fig(b"random"), Err(FigParseError::UnknownFormat));