// node.el — Node type definitions and color/radius mapping. // // Node types mirror the Engram knowledge graph node_type field. // Colors are chosen for dark-background (Studio) legibility. // ── Node type constants ────────────────────────────────────────────────────── fn node_type_memory() -> String { "Memory" } fn node_type_backlog() -> String { "BacklogItem" } fn node_type_knowledge() -> String { "Knowledge" } fn node_type_entity() -> String { "Entity" } fn node_type_default() -> String { "Node" } // ── Color map ──────────────────────────────────────────────────────────────── fn node_color(node_type: String) -> String { if str_eq(node_type, "Memory") { return "#58A6FF" } if str_eq(node_type, "BacklogItem") { return "#C9A84C" } if str_eq(node_type, "Knowledge") { return "#2ecc71" } if str_eq(node_type, "Entity") { return "#e74c3c" } if str_eq(node_type, "WorkContext") { return "#9b59b6" } if str_eq(node_type, "Artifact") { return "#1abc9c" } if str_eq(node_type, "Process") { return "#e67e22" } "#7a8ba8" } // ── Radius ─────────────────────────────────────────────────────────────────── // // Clamp salience (0.0–1.0) to radius range [6, 18]. fn node_radius(salience: Float) -> Float { let min_r: Float = int_to_float(6) let max_r: Float = int_to_float(18) let range: Float = max_r - min_r let clamped: Float = if salience < int_to_float(0) { int_to_float(0) } else { if salience > int_to_float(1) { int_to_float(1) } else { salience } } min_r + range * clamped } fn node_radius_int(salience: Float) -> Int { float_to_int(node_radius(salience)) } // ── Label truncation ───────────────────────────────────────────────────────── fn node_label_truncate(label: String) -> String { let max_len: Int = 30 let l: Int = str_len(label) if l <= max_len { return label } str_slice(label, 0, max_len) + "..." } // ── Node JSON accessors ────────────────────────────────────────────────────── // // Nodes are passed as JSON objects: { id, label, node_type, salience, ... } fn node_id(n_json: String) -> String { json_get_string(n_json, "id") } fn node_label(n_json: String) -> String { let lbl: String = json_get_string(n_json, "label") if !str_eq(lbl, "") { return lbl } // Fall back to first 40 chars of content let c: String = json_get_string(n_json, "content") if str_len(c) > 40 { return str_slice(c, 0, 40) } c } fn node_type_field(n_json: String) -> String { let t: String = json_get_string(n_json, "node_type") if str_eq(t, "") { return node_type_default() } t } fn node_salience(n_json: String) -> Float { json_get_float(n_json, "salience") }