diff --git a/.envrc b/.envrc index e81df70429..c1f1f5236a 100644 --- a/.envrc +++ b/.envrc @@ -1,5 +1,8 @@ export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc +# Enable beta playground features in the standalone web app by default. +export NEXT_PUBLIC_FIDDLE_BETA_DEFAULT=true + # speed up building the Ruby client library, which I think defaults to the release build export RB_SYS_CARGO_PROFILE="dev" diff --git a/engine/Cargo.lock b/engine/Cargo.lock index eac2821b3c..251e720c1c 100644 --- a/engine/Cargo.lock +++ b/engine/Cargo.lock @@ -1048,6 +1048,7 @@ dependencies = [ "indoc", "internal-baml-core", "itertools 0.14.0", + "log", "pretty_assertions", "strip-ansi-escapes", ] @@ -3949,6 +3950,7 @@ dependencies = [ "baml-types", "bstd", "either", + "indexmap 2.9.0", "internal-baml-diagnostics", "log", "pest", diff --git a/engine/baml-compiler/src/codegen.rs b/engine/baml-compiler/src/codegen.rs index 4c532d96f9..905ee4d29c 100644 --- a/engine/baml-compiler/src/codegen.rs +++ b/engine/baml-compiler/src/codegen.rs @@ -496,8 +496,13 @@ impl<'g> HirCompiler<'g> { // Locals come after. names.resize_with(names.capacity(), String::new); + log::info!("There are {} locals loremipsum", locals.len()); + // Distribute locals to their respective indexes. for (name, index) in locals { + if *index >= names.len() { + break; + } names[*index] = name.to_string(); } diff --git a/engine/baml-lib/ast/Cargo.toml b/engine/baml-lib/ast/Cargo.toml index 9e879040f0..0f88658343 100644 --- a/engine/baml-lib/ast/Cargo.toml +++ b/engine/baml-lib/ast/Cargo.toml @@ -26,6 +26,7 @@ either.workspace = true test-log.workspace = true pretty = { workspace = true } regex.workspace = true +indexmap.workspace = true [dev-dependencies] pretty_assertions.workspace = true diff --git a/engine/baml-lib/ast/examples/generate_mermaid_headers.rs b/engine/baml-lib/ast/examples/generate_mermaid_headers.rs index b2fb92ddf7..fa7ccadae0 100644 --- a/engine/baml-lib/ast/examples/generate_mermaid_headers.rs +++ b/engine/baml-lib/ast/examples/generate_mermaid_headers.rs @@ -1,6 +1,6 @@ use std::path::Path; -use internal_baml_ast::{parse, BamlVisDiagramGenerator}; +use internal_baml_ast::{diagram_generator, parse}; use internal_baml_diagnostics::SourceFile; fn main() { @@ -33,6 +33,12 @@ fn main() { } // Nicely styled header graph - let mermaid = BamlVisDiagramGenerator::generate_with_styling(&ast, true); + let mermaid = diagram_generator::generate_with_styling( + diagram_generator::MermaidGeneratorContext { + use_fancy: true, + function_filter: None, + }, + &ast, + ); println!("{mermaid}"); } diff --git a/engine/baml-lib/ast/src/ast.rs b/engine/baml-lib/ast/src/ast.rs index 5823680dc2..0adc4452e5 100644 --- a/engine/baml-lib/ast/src/ast.rs +++ b/engine/baml-lib/ast/src/ast.rs @@ -22,14 +22,12 @@ mod type_builder_block; mod type_expression_block; mod value_expression_block; -pub mod baml_vis; -pub mod header_collector; -pub mod mermaid_debug; +mod baml_vis; pub use app::App; pub use argument::{Argument, ArgumentId, ArgumentsList}; pub use assignment::Assignment; pub use attribute::{Attribute, AttributeContainer, AttributeId}; -pub use baml_vis::BamlVisDiagramGenerator; +pub use baml_vis::{diagram_generator, mermaid_debug::MermaidDiagramGenerator}; pub use config::ConfigBlockProperty; pub use expr::{ExprFn, TopLevelAssignment}; pub use expression::{ @@ -37,11 +35,9 @@ pub use expression::{ RawString, UnaryOperator, }; pub use field::{Field, FieldArity, FieldType}; -pub use header_collector::{HeaderCollector, HeaderIndex, RenderableHeader, ScopeId}; pub use identifier::{Identifier, RefIdentifier}; pub use indentation_type::IndentationType; pub use internal_baml_diagnostics::Span; -pub use mermaid_debug::MermaidDiagramGenerator; pub use newline_type::NewlineType; pub use stmt::{ AssertStmt, AssignOp, AssignOpStmt, AssignStmt, CForLoopStmt, ExprStmt, ForLoopStmt, Header, diff --git a/engine/baml-lib/ast/src/ast/baml_vis.rs b/engine/baml-lib/ast/src/ast/baml_vis.rs index 4f3f6a48b1..578ded7888 100644 --- a/engine/baml-lib/ast/src/ast/baml_vis.rs +++ b/engine/baml-lib/ast/src/ast/baml_vis.rs @@ -1,695 +1,14 @@ +pub mod diagram_generator; +mod graph; +mod header_collector; +pub(super) mod mermaid_debug; + use std::collections::{HashMap, HashSet}; -/// Config: maximum number of direct children (markdown + nested) a non-branching -/// container may have to be flattened into a linear sequence instead of a subgraph. -/// -/// For example, with `1` (default), containers with 0 or 1 child are flattened. -/// Top-level scopes are never flattened if they have any children, regardless of this value. -const MAX_CHILDREN_TO_FLATTEN: usize = 1; // Toggle: render function call nodes (e.g., SummarizeVideo, CreatePR) alongside headers. const SHOW_CALL_NODES: bool = false; use baml_types::BamlMap; +use graph::{Cluster, ClusterId, Direction, Graph, Node, NodeId, NodeKind}; use internal_baml_diagnostics::SerializedSpan; use serde_json; - -use super::{ - header_collector::{HeaderLabelKind, Hid}, - Ast, HeaderCollector, HeaderIndex, RenderableHeader, ScopeId, -}; - -/// A Mermaid flowchart (LR) generator focused on headers and control-flow-like structure. -/// -/// It renders nested connections as Mermaid subgraphs: the header that owns a nested scope -/// becomes a subgraph container (titled with the header text) and the nested scope headers -/// are rendered inside the container. Sibling elements are connected linearly with `-->`. -/// Connections never cross container boundaries; containers themselves are the units that -/// connect to other elements. -#[derive(Debug, Default)] -pub struct BamlVisDiagramGenerator; - -impl BamlVisDiagramGenerator { - /// Generate a Mermaid flowchart (LR) showing headers as linear steps and - /// nested scopes as subgraphs. - pub fn generate_headers_flowchart(ast: &Ast) -> String { - let index = HeaderCollector::collect(ast); - let builder = GraphBuilder::new(&index, BuilderConfig::default()); - let (graph, span_map) = builder.build(); - MermaidRenderer::render(&graph, Direction::TD, false, span_map) - } - - /// Back-compat API used by the example. `use_fancy` toggles optional cosmetic styling. - pub fn generate_with_styling(ast: &Ast, use_fancy: bool) -> String { - let index = HeaderCollector::collect(ast); - let builder = GraphBuilder::new(&index, BuilderConfig::default()); - let (graph, span_map) = builder.build(); - MermaidRenderer::render(&graph, Direction::TD, use_fancy, span_map) - } -} - -// ===== Renderer-agnostic graph model and builder ===== - -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -enum Direction { - TD, - LR, -} - -#[derive(Debug, Clone)] -#[allow(dead_code)] -enum NodeKind { - Header(Hid, Option), - Decision(Hid, Option), - Call { header: Hid, callee: String }, -} - -#[derive(Debug, Clone)] -struct Node { - id: String, - label: String, - kind: NodeKind, - cluster: Option, -} - -#[derive(Debug, Clone)] -struct Edge { - from: String, - to: String, -} - -#[derive(Debug, Clone)] -struct Cluster { - id: String, - label: String, - parent: Option, -} - -#[derive(Debug, Default)] -struct Graph { - nodes: Vec, - edges: Vec, - clusters: Vec, -} - -#[derive(Debug, Clone, Copy)] -struct BuilderConfig { - show_call_nodes: bool, -} - -impl Default for BuilderConfig { - fn default() -> Self { - Self { - show_call_nodes: SHOW_CALL_NODES, - } - } -} - -struct GraphBuilder<'a> { - index: &'a HeaderIndex, - cfg: BuilderConfig, - graph: Graph, - next_node: u32, - next_cluster: u32, - by_hid: HashMap, - idstr_to_hid: HashMap<&'a str, Hid>, - md_children: HashMap>, - has_md_parent: HashSet, - nested_children: HashMap>, - scope_root: HashMap, - nested_targets: HashSet, - header_entry: HashMap, - header_exits: HashMap>, - // we're going to need stable iteration in snapshot tests. - span_map: BamlMap, - call_node_cache: HashMap<(Hid, String), String>, -} - -impl<'a> GraphBuilder<'a> { - fn new(index: &'a HeaderIndex, cfg: BuilderConfig) -> Self { - let mut b = Self { - index, - cfg, - graph: Graph::default(), - next_node: 0, - next_cluster: 0, - by_hid: HashMap::new(), - idstr_to_hid: HashMap::new(), - md_children: HashMap::new(), - has_md_parent: HashSet::new(), - nested_children: HashMap::new(), - scope_root: HashMap::new(), - nested_targets: HashSet::new(), - header_entry: HashMap::new(), - header_exits: HashMap::new(), - span_map: BamlMap::new(), - call_node_cache: HashMap::new(), - }; - b.precompute(); - b - } - - fn precompute(&mut self) { - for h in &self.index.headers { - self.by_hid.insert(h.hid, h); - self.idstr_to_hid.insert(&h.id, h.hid); - self.scope_root.entry(h.scope).or_insert(h.hid); - } - for h in &self.index.headers { - if let Some(pid) = &h.parent_id { - if let Some(&ph) = self.idstr_to_hid.get(pid.as_str()) { - if let Some(parent) = self.by_hid.get(&ph) { - if parent.scope == h.scope { - self.md_children.entry(ph).or_default().push(h.hid); - self.has_md_parent.insert(h.hid); - } - } - } - } - } - for (p, c) in self.index.nested_edges_hid_iter() { - if let (Some(ph), Some(ch)) = (self.index.get_by_hid(*p), self.index.get_by_hid(*c)) { - if ph.scope != ch.scope { - self.nested_children.entry(*p).or_default().push(*c); - self.nested_targets.insert(*c); - } - } - } - } - - // Compute a tuple position key for stable ordering comparisons - fn pos_tuple(&self, hid: Hid) -> (String, usize) { - let h = self.by_hid[&hid]; - ( - h.span.file.path_buf().to_string_lossy().into_owned(), - h.span.start, - ) - } - - // Merge two already-ordered lists by source position, preserving internal order - fn merge_by_pos(&self, md: &[Hid], nested: &[Hid]) -> Vec { - let mut i = 0; - let mut j = 0; - let mut out: Vec = Vec::with_capacity(md.len() + nested.len()); - while i < md.len() || j < nested.len() { - if j == nested.len() - || (i < md.len() && self.pos_tuple(md[i]) <= self.pos_tuple(nested[j])) - { - out.push(md[i]); - i += 1; - } else { - out.push(nested[j]); - j += 1; - } - } - out - } - - fn build(mut self) -> (Graph, BamlMap) { - let mut tops: Vec<(String, usize, usize, ScopeId)> = Vec::new(); - let mut seen_scopes: HashSet = HashSet::new(); - for h in &self.index.headers { - if seen_scopes.insert(h.scope) { - let root_hid = self.scope_root[&h.scope]; - if !self.nested_targets.contains(&root_hid) { - let root = self.by_hid[&root_hid]; - tops.push(( - root.span.file.path_buf().to_string_lossy().into_owned(), - root.span.start, - root.span.end, - h.scope, - )); - } - } - } - tops.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)).then(a.2.cmp(&b.2))); - - let mut visited_scopes: HashSet = HashSet::new(); - for (_, _, _, scope) in tops { - self.build_scope_sequence(scope, &mut visited_scopes, None); - } - (self.graph, self.span_map) - } - - fn build_scope_sequence( - &mut self, - scope: ScopeId, - visited_scopes: &mut HashSet, - parent_cluster: Option, - ) { - if !visited_scopes.insert(scope) { - return; - } - let items: Vec = self - .index - .headers_in_scope_iter(scope) - .filter(|h| !self.has_md_parent.contains(&h.hid)) - .map(|h| h.hid) - .collect(); - - let mut prev_exits: Option> = None; - for hid in items { - let (entry, exits) = self.build_header(hid, visited_scopes, parent_cluster.clone()); - if let Some(prev) = prev_exits.take() { - for e in prev { - self.graph.edges.push(Edge { - from: e, - to: entry.clone(), - }); - } - } - prev_exits = Some(exits); - } - } - - fn build_header( - &mut self, - hid: Hid, - visited_scopes: &mut HashSet, - parent_cluster: Option, - ) -> (String, Vec) { - if let Some(entry) = self.header_entry.get(&hid).cloned() { - let exits = self - .header_exits - .get(&hid) - .cloned() - .unwrap_or_else(|| vec![entry.clone()]); - return (entry, exits); - } - let header = self.by_hid[&hid]; - let md_children = self.md_children.get(&hid).cloned().unwrap_or_default(); - let nested_children = self.nested_children.get(&hid).cloned().unwrap_or_default(); - let has_md = !md_children.is_empty(); - let has_nested = !nested_children.is_empty(); - let is_branching = header.label_kind == HeaderLabelKind::If; - - if !has_md && !has_nested { - let node_id = self.new_node_id(); - let span = SerializedSpan::serialize(&header.span); - self.span_map.insert(node_id.clone(), span.clone()); - self.graph.nodes.push(Node { - id: node_id.clone(), - label: header.title.to_string(), - kind: NodeKind::Header(hid, Some(span)), - cluster: parent_cluster.clone(), - }); - self.header_entry.insert(hid, node_id.clone()); - self.header_exits.insert(hid, vec![node_id.clone()]); - if self.cfg.show_call_nodes { - self.render_calls_for_header(hid, &node_id, parent_cluster.clone()); - } - return (node_id.clone(), vec![node_id]); - } - - let total_children = md_children.len() + nested_children.len(); - let mut single_nested_child_has_multiple_items = false; - if nested_children.len() == 1 { - let child_root = self.by_hid[&nested_children[0]]; - let count = self.index.headers_in_scope_iter(child_root.scope).count(); - single_nested_child_has_multiple_items = count > 1; - } - let should_flatten = !is_branching - && total_children <= MAX_CHILDREN_TO_FLATTEN - && !(nested_children.len() == 1 && single_nested_child_has_multiple_items); - - if should_flatten { - let node_id = self.new_node_id(); - let span = SerializedSpan::serialize(&header.span); - self.span_map.insert(node_id.clone(), span.clone()); - let kind = if is_branching { - NodeKind::Decision(hid, Some(span.clone())) - } else { - NodeKind::Header(hid, Some(span.clone())) - }; - self.graph.nodes.push(Node { - id: node_id.clone(), - label: header.title.to_string(), - kind, - cluster: parent_cluster.clone(), - }); - self.header_entry.insert(hid, node_id.clone()); - let mut exits = vec![node_id.clone()]; - if md_children.len() == 1 { - let (c_entry, c_exits) = - self.build_header(md_children[0], visited_scopes, parent_cluster.clone()); - self.graph.edges.push(Edge { - from: node_id.clone(), - to: c_entry.clone(), - }); - exits = c_exits; - } else if nested_children.len() == 1 { - let child_root_hid = nested_children[0]; - let child_scope = self.by_hid[&child_root_hid].scope; - self.build_scope_sequence(child_scope, visited_scopes, parent_cluster.clone()); - let (c_entry, c_exits) = - self.build_header(child_root_hid, visited_scopes, parent_cluster.clone()); - self.graph.edges.push(Edge { - from: node_id.clone(), - to: c_entry.clone(), - }); - exits = c_exits; - } - self.header_exits.insert(hid, exits.clone()); - return (node_id, exits); - } - - if is_branching { - let cluster_id = self.new_cluster_id(); - self.graph.clusters.push(Cluster { - id: cluster_id.clone(), - label: header.title.to_string(), - parent: parent_cluster.clone(), - }); - - let decision_id = self.new_node_id(); - let span = SerializedSpan::serialize(&header.span); - self.span_map.insert(decision_id.clone(), span.clone()); - self.graph.nodes.push(Node { - id: decision_id.clone(), - label: header.title.to_string(), - kind: NodeKind::Decision(hid, Some(span)), - cluster: Some(cluster_id.clone()), - }); - self.header_entry.insert(hid, decision_id.clone()); - if self.cfg.show_call_nodes { - self.render_calls_for_header(hid, &decision_id, Some(cluster_id.clone())); - } - - let mut branch_exits: Vec = Vec::new(); - for child_root_hid in nested_children.iter() { - let child_scope = self.by_hid[child_root_hid].scope; - self.build_scope_sequence(child_scope, visited_scopes, Some(cluster_id.clone())); - let (entry, exits) = - self.build_header(*child_root_hid, visited_scopes, Some(cluster_id.clone())); - self.graph.edges.push(Edge { - from: decision_id.clone(), - to: entry.clone(), - }); - if self.cfg.show_call_nodes { - self.render_calls_for_header(*child_root_hid, &entry, Some(cluster_id.clone())); - } - branch_exits.extend(exits); - } - - let mut md_ids_only: Vec = Vec::with_capacity(md_children.len()); - for ch in md_children.iter() { - let (rep_id, _exits) = - self.build_header(*ch, visited_scopes, Some(cluster_id.clone())); - md_ids_only.push(rep_id); - } - if let Some(first_md) = md_ids_only.first().cloned() { - if !branch_exits.is_empty() { - for e in branch_exits.iter() { - self.graph.edges.push(Edge { - from: e.clone(), - to: first_md.clone(), - }); - } - } else { - self.graph.edges.push(Edge { - from: decision_id.clone(), - to: first_md.clone(), - }); - } - } - for win in md_ids_only.windows(2) { - let (a, b) = (&win[0], &win[1]); - self.graph.edges.push(Edge { - from: a.clone(), - to: b.clone(), - }); - } - let outward = if let Some(last) = md_ids_only.last().cloned() { - vec![last] - } else { - branch_exits - }; - self.header_exits.insert(hid, outward.clone()); - return (decision_id, outward); - } - - let cluster_id = self.new_cluster_id(); - self.graph.clusters.push(Cluster { - id: cluster_id.clone(), - label: header.title.to_string(), - parent: parent_cluster.clone(), - }); - - // Merge markdown children and direct nested roots, preserving each list's internal order - let items_merged: Vec = self.merge_by_pos(&md_children, &nested_children); - - let mut first_rep: Option = None; - let mut prev_exits: Option> = None; - for child_hid in items_merged.into_iter() { - // If this child is a direct nested root for the current container, prebuild its scope - // inside this container's cluster and use the scope's final exits. - let mut prebuilt_scope_last_exits: Option> = None; - if nested_children.contains(&child_hid) { - let child_scope = self.by_hid[&child_hid].scope; - self.build_scope_sequence(child_scope, visited_scopes, Some(cluster_id.clone())); - let scope_items: Vec = self - .index - .headers_in_scope_iter(child_scope) - .filter(|h| !self.has_md_parent.contains(&h.hid)) - .map(|h| h.hid) - .collect(); - if let Some(&last_in_scope) = scope_items.last() { - if let Some(ex) = self.header_exits.get(&last_in_scope).cloned() { - prebuilt_scope_last_exits = Some(ex); - } - } - } - - let (entry, mut exits) = - self.build_header(child_hid, visited_scopes, Some(cluster_id.clone())); - if let Some(scope_exits) = prebuilt_scope_last_exits.take() { - exits = scope_exits; - } - if first_rep.is_none() { - first_rep = Some(entry.clone()); - } - if let Some(prev) = prev_exits.take() { - for e in prev { - self.graph.edges.push(Edge { - from: e, - to: entry.clone(), - }); - } - } - prev_exits = Some(exits); - } - let entry = first_rep.unwrap_or_else(|| { - // Create a placeholder node if the container is empty. - let node_id = self.new_node_id(); - let span = SerializedSpan::serialize(&header.span); - self.span_map.insert(node_id.clone(), span.clone()); - self.graph.nodes.push(Node { - id: node_id.clone(), - label: header.title.to_string(), - kind: NodeKind::Header(hid, Some(span)), - cluster: Some(cluster_id.clone()), - }); - node_id - }); - let exits = prev_exits.unwrap_or_else(|| vec![entry.clone()]); - self.header_entry.insert(hid, entry.clone()); - self.header_exits.insert(hid, exits.clone()); - (entry, exits) - } - - fn render_calls_for_header(&mut self, hid: Hid, header_rep_id: &str, cluster: Option) { - if let Some(callees) = self.index.header_calls.get(&hid) { - for callee in callees { - if let Some(cached_id) = self.call_node_cache.get(&(hid, callee.clone())) { - self.graph.edges.push(Edge { - from: cached_id.clone(), - to: header_rep_id.to_string(), - }); - continue; - } - let call_node_id = self.new_node_id(); - self.graph.nodes.push(Node { - id: call_node_id.clone(), - label: callee.clone(), - kind: NodeKind::Call { - header: hid, - callee: callee.clone(), - }, - cluster: cluster.clone(), - }); - self.graph.edges.push(Edge { - from: call_node_id.clone(), - to: header_rep_id.to_string(), - }); - self.call_node_cache - .insert((hid, callee.clone()), call_node_id); - } - } - } - - fn new_node_id(&mut self) -> String { - let id = format!("n{}", self.next_node); - self.next_node += 1; - id - } - fn new_cluster_id(&mut self) -> String { - let id = format!("sg{}", self.next_cluster); - self.next_cluster += 1; - id - } -} - -struct MermaidRenderer; - -impl MermaidRenderer { - fn render( - graph: &Graph, - direction: Direction, - use_fancy: bool, - span_map: BamlMap, - ) -> String { - let mut out: Vec = Vec::new(); - // out.push("---".to_string()); - // out.push("config:".to_string()); - // out.push(" layout: elk".to_string()); - // out.push("---".to_string()); - // out.push("".to_string()); - match direction { - Direction::TD => out.push("flowchart TD".to_string()), - Direction::LR => out.push("flowchart LR".to_string()), - } - if use_fancy { - out.push("classDef loopContainer shape:processes,fill:#e0f7fa,stroke:#006064,stroke-width:2px,color:#000".to_string()); - out.push( - "classDef decisionNode fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#000" - .to_string(), - ); - if SHOW_CALL_NODES { - out.push("".to_string()); - out.push( - "classDef callNode fill:#fffde7,stroke:#f9a825,stroke-width:2px,color:#000" - .to_string(), - ); - } - } - - let mut children_by_parent: BamlMap, Vec<&Cluster>> = BamlMap::new(); - for c in &graph.clusters { - children_by_parent - .entry(c.parent.clone()) - .or_default() - .push(c); - } - let mut nodes_by_cluster: BamlMap, Vec<&Node>> = BamlMap::new(); - for n in &graph.nodes { - nodes_by_cluster - .entry(n.cluster.clone()) - .or_default() - .push(n); - } - - fn emit( - out: &mut Vec, - cluster: Option<&Cluster>, - children_by_parent: &BamlMap, Vec<&Cluster>>, - nodes_by_cluster: &BamlMap, Vec<&Node>>, - use_fancy: bool, - indent: usize, - ) { - let indent_str = " ".repeat(indent); - let key = cluster.map(|c| c.id.clone()); - let key_opt = key.clone(); - if let Some(c) = cluster { - out.push(format!( - "{}subgraph {}[\"{}\"]", - indent_str, - c.id, - escape_label(&c.label) - )); - out.push(format!("{indent_str} direction LR")); - } - if let Some(nodes) = nodes_by_cluster.get(&key_opt) { - for n in nodes { - match &n.kind { - NodeKind::Decision(_, _) => { - out.push(format!( - "{} {}{{\"{}\"}}", - indent_str, - n.id, - escape_label(&n.label) - )); - // Always emit decisionNode class line to match expected output - out.push(format!("{} class {} decisionNode;", indent_str, n.id)); - } - NodeKind::Call { .. } => { - out.push(format!( - "{} {}[\"{}\"]", - indent_str, - n.id, - escape_label(&n.label) - )); - if use_fancy && SHOW_CALL_NODES { - out.push(format!("{} class {} callNode;", indent_str, n.id)); - } - } - NodeKind::Header(_, _) => { - out.push(format!( - "{} {}[\"{}\"]", - indent_str, - n.id, - escape_label(&n.label) - )); - } - } - } - } - if let Some(children) = children_by_parent.get(&key_opt) { - for ch in children { - emit( - out, - Some(ch), - children_by_parent, - nodes_by_cluster, - use_fancy, - indent + 2, - ); - } - } - if cluster.is_some() { - out.push(format!("{indent_str}end")); - } - } - emit( - &mut out, - None, - &children_by_parent, - &nodes_by_cluster, - use_fancy, - 0, - ); - - let mut emitted: HashSet<(String, String)> = HashSet::new(); - for e in &graph.edges { - if emitted.insert((e.from.clone(), e.to.clone())) { - out.push(format!(" {} --> {}", e.from, e.to)); - } - } - // Click lines disabled for tests - if !span_map.is_empty() { - if let Ok(json) = serde_json::to_string(&span_map) { - out.push(format!("%%__BAML_SPANMAP__={json}")); - } - for rep_id in span_map.keys() { - out.push(format!( - " click {rep_id} call bamlMermaidNodeClick() \"Go to source\"" - )); - } - } - out.join("\n") - } -} - -#[inline] -fn escape_label(s: &str) -> String { - s.replace('"', """) -} diff --git a/engine/baml-lib/ast/src/ast/baml_vis/diagram_generator.rs b/engine/baml-lib/ast/src/ast/baml_vis/diagram_generator.rs new file mode 100644 index 0000000000..dbb1df0c56 --- /dev/null +++ b/engine/baml-lib/ast/src/ast/baml_vis/diagram_generator.rs @@ -0,0 +1,200 @@ +//! A Mermaid flowchart (LR) generator focused on headers and control-flow-like structure. +//! +//! It renders nested connections as Mermaid subgraphs: the header that owns a nested scope +//! becomes a subgraph container (titled with the header text) and the nested scope headers +//! are rendered inside the container. Sibling elements are connected linearly with `-->`. +//! Connections never cross container boundaries; containers themselves are the units that +//! connect to other elements. +use std::collections::HashSet; + +use baml_types::BamlMap; +use internal_baml_diagnostics::SerializedSpan; + +use crate::ast::{ + baml_vis::{ + graph::{ + self, BuilderConfig, Cluster, ClusterId, Direction, Graph, Node, NodeId, NodeKind, + }, + SHOW_CALL_NODES, + }, + Ast, +}; + +use super::header_collector::HeaderCollector; + +pub struct MermaidGeneratorContext { + /// Include custom styles for the diagram. + pub use_fancy: bool, + + /// If specified, only diagram the specified function. + pub function_filter: Option, +} + +/// Generate a Mermaid flowchart showing headers as linear steps and +/// nested scopes as subgraphs. +pub fn generate_with_styling(context: MermaidGeneratorContext, ast: &Ast) -> String { + let index = HeaderCollector::collect(ast, context.function_filter.as_deref()); + if index.headers.is_empty() { + if let Some(function_name) = context.function_filter.as_deref() { + return context.render_single_node_placeholder(function_name); + } + } + let (graph, span_map) = graph::build(&index, BuilderConfig::default()); + log::debug!( + "[diagram_generator] built graph: nodes={}, edges={}, clusters={}, span_map_entries={}", + graph.nodes.len(), + graph.edges.len(), + graph.clusters.len(), + span_map.len() + ); + log::info!("graph structure {graph:#?}"); + context.render_mermaid_graph(&graph, span_map) +} + +impl MermaidGeneratorContext { + fn render_single_node_placeholder(&self, label: &str) -> String { + let mut out = self.init_output(); + out.push(format!(" n0[\"{}\"]", escape_label(label))); + out.join("\n") + } + + fn init_output(&self) -> Vec { + let mut out: Vec = vec!["flowchart TD".to_string()]; + if self.use_fancy { + out.push("classDef loopContainer shape:processes,fill:#e0f7fa,stroke:#006064,stroke-width:2px,color:#000".to_string()); + out.push( + "classDef decisionNode fill:#fff3e0,stroke:#ef6c00,stroke-width:2px,color:#000" + .to_string(), + ); + if SHOW_CALL_NODES { + out.push("".to_string()); + out.push( + "classDef callNode fill:#fffde7,stroke:#f9a825,stroke-width:2px,color:#000" + .to_string(), + ); + } + } + out + } + + fn render_mermaid_graph( + &self, + graph: &Graph<'_>, + span_map: BamlMap, + ) -> String { + let mut out = self.init_output(); + + let mut children_by_parent: BamlMap<_, Vec<_>> = BamlMap::new(); + for c in &graph.clusters { + children_by_parent.entry(c.parent).or_default().push(c); + } + let mut nodes_by_cluster: BamlMap<_, Vec<_>> = BamlMap::new(); + for n in &graph.nodes { + nodes_by_cluster.entry(n.cluster).or_default().push(n); + } + + fn emit<'index>( + out: &mut Vec, + cluster: Option<&Cluster<'index>>, + children_by_parent: &BamlMap, Vec<&Cluster<'index>>>, + nodes_by_cluster: &BamlMap, Vec<&Node<'index>>>, + use_fancy: bool, + indent: usize, + ) { + let indent_str = " ".repeat(indent); + let key = cluster.map(|c| c.id); + let key_opt = key; + if let Some(c) = cluster { + out.push(format!( + "{}subgraph {}[\"{}\"]", + indent_str, + c.id, + escape_label(c.label) + )); + out.push(format!("{indent_str} direction TB")); + } + if let Some(nodes) = nodes_by_cluster.get(&key_opt) { + for n in nodes { + match &n.kind { + NodeKind::Decision(_, _) => { + out.push(format!( + "{} {}{{\"{}\"}}", + indent_str, + n.id, + escape_label(n.label) + )); + // Always emit decisionNode class line to match expected output + out.push(format!("{} class {} decisionNode;", indent_str, n.id)); + } + NodeKind::Call { .. } => { + out.push(format!( + "{} {}[\"{}\"]", + indent_str, + n.id, + escape_label(n.label) + )); + if use_fancy && SHOW_CALL_NODES { + out.push(format!("{} class {} callNode;", indent_str, n.id)); + } + } + NodeKind::Header(_, _) => { + out.push(format!( + "{} {}[\"{}\"]", + indent_str, + n.id, + escape_label(n.label) + )); + } + } + } + } + if let Some(children) = children_by_parent.get(&key_opt) { + for ch in children { + emit( + out, + Some(ch), + children_by_parent, + nodes_by_cluster, + use_fancy, + indent + 2, + ); + } + } + if cluster.is_some() { + out.push(format!("{indent_str}end")); + } + } + emit( + &mut out, + None, + &children_by_parent, + &nodes_by_cluster, + self.use_fancy, + 0, + ); + + let mut emitted = HashSet::new(); + for e in &graph.edges { + if emitted.insert((e.from, e.to)) { + out.push(format!(" {} --> {}", e.from, e.to)); + } + } + // Click lines disabled for tests + if !span_map.is_empty() { + if let Ok(json) = serde_json::to_string(&span_map) { + out.push(format!("%%__BAML_SPANMAP__={json}")); + } + for rep_id in span_map.keys() { + out.push(format!( + " click {rep_id} call bamlMermaidNodeClick() \"Go to source\"" + )); + } + } + out.join("\n") + } +} + +#[inline] +fn escape_label(s: &str) -> String { + s.replace('"', """) +} diff --git a/engine/baml-lib/ast/src/ast/baml_vis/graph.rs b/engine/baml-lib/ast/src/ast/baml_vis/graph.rs new file mode 100644 index 0000000000..5f6bd062c1 --- /dev/null +++ b/engine/baml-lib/ast/src/ast/baml_vis/graph.rs @@ -0,0 +1,858 @@ +/// Renderer-agnostic graph model and builder +use std::{ + collections::{HashMap, HashSet}, + path::Path, +}; + +use baml_types::BamlMap; +use indexmap::IndexMap; +use internal_baml_diagnostics::SerializedSpan; + +use super::header_collector::{HeaderIndex, HeaderLabelKind, Hid, RenderableHeader, ScopeId}; + +/// Config: maximum number of direct children (markdown + nested) a non-branching +/// container may have to be flattened into a linear sequence instead of a subgraph. +/// +/// With the current value (`0`), any container with visible children retains its own cluster. +/// Top-level scopes are never flattened if they have any children, regardless of this value. +const MAX_CHILDREN_TO_FLATTEN: usize = 0; + +pub fn build<'index>( + index: &'index HeaderIndex, + config: BuilderConfig, +) -> (Graph<'index>, BamlMap) { + let pre = Prelude::from_index(index); + let builder = GraphBuilder::new(index, &pre, config); + let (graph, span_map) = builder.build(); + (graph, span_map) +} + +use super::{graph, SHOW_CALL_NODES}; + +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub enum Direction { + TD, + LR, +} + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum NodeKind<'index> { + Header(Hid, Option), + Decision(Hid, Option), + Call { header: Hid, callee: &'index str }, +} + +#[derive(Debug, Clone)] +pub struct Node<'index> { + pub id: NodeId, + pub label: &'index str, + pub kind: NodeKind<'index>, + pub cluster: Option, +} + +#[derive(Debug, Clone)] +pub struct Edge { + pub from: NodeId, + pub to: NodeId, +} +#[derive(Debug, Clone)] +pub struct Cluster<'index> { + pub id: ClusterId, + pub label: &'index str, + pub parent: Option, +} + +#[derive(Debug, Default)] +pub struct Graph<'index> { + pub nodes: Vec>, + pub edges: Vec, + pub clusters: Vec>, +} + +impl<'index> Graph<'index> { + // NOTE: since the graph is currently a tree, there should be no need for having the node id + // before constructing it. + // Only reason we use a builder function is because `Nodes` store their own id. + // Since we grab `&mut self` anyway & don't give it to the function, it won't be able to + // add more nodes to the tree before it has inserted this one. + pub fn add_node(&mut self, make: impl FnOnce(NodeId) -> Node<'index>) -> NodeId { + let node_id = NodeId(self.nodes.len() as u32); + self.nodes.push(make(node_id)); + node_id + } + + pub fn add_cluster(&mut self, make: impl FnOnce(ClusterId) -> Cluster<'index>) -> ClusterId { + let id = ClusterId(self.clusters.len() as u32); + self.clusters.push(make(id)); + id + } +} + +#[derive(Debug, Clone, Copy)] +pub struct BuilderConfig { + show_call_nodes: bool, +} + +impl Default for BuilderConfig { + fn default() -> Self { + Self { + show_call_nodes: SHOW_CALL_NODES, + } + } +} + +struct GraphBuilder<'index, 'pre> { + index: &'index HeaderIndex, + cfg: BuilderConfig, + graph: Graph<'index>, + // TODO: add Prelude ref directly. + by_hid: &'pre HashMap, + md_children: &'pre HashMap>, + has_md_parent: &'pre HashSet, + nested_children: &'pre HashMap>, + nested_targets: &'pre HashSet, + + // TODO: check visit order + header_entry: HashMap, + header_exits: HashMap>, + // we're going to need stable iteration in snapshot tests. + span_map: BamlMap, +} + +// NOTE: this could be part of HeaderIndex +/// Cached, precomuted data used by graph builder. +struct Prelude<'index> { + // NOTE: this could be a Box<[&'index RenderableHeader]>. Doesn't matter much. + /// Map to reference by `Hid`, since [`HeaderIndex::headers`] has a different order. + by_hid: HashMap, + /// Nodes that have markdown header children, & their respective children. + md_children: HashMap>, + /// Set of nodes that have a markdown header parent. + has_md_parent: HashSet, + /// For nested edges, the ones that cross a code scope. + nested_children: HashMap>, + /// The set of all [`Hid`] that are nested children. + nested_targets: HashSet, +} + +impl<'index> Prelude<'index> { + pub fn from_index(index: &'index HeaderIndex) -> Self { + let mut by_hid = HashMap::new(); + + let mut idstr_to_hid = HashMap::new(); + for h in &index.headers { + by_hid.insert(h.hid, h); + idstr_to_hid.insert(h.id.as_str(), h.hid); + } + + let mut md_children: HashMap<_, Vec<_>> = HashMap::new(); + let mut has_md_parent = HashSet::new(); + for h in &index.headers { + if let Some(pid) = &h.parent_id { + if let Some(&ph) = idstr_to_hid.get(pid.as_str()) { + let parent = by_hid[&ph]; + if parent.scope == h.scope { + md_children.entry(ph).or_default().push(h.hid); + has_md_parent.insert(h.hid); + } + } + } + } + + let mut nested_children: HashMap<_, Vec<_>> = HashMap::new(); + let mut nested_targets: HashSet<_> = HashSet::new(); + + for (p, c) in nested_scope_edges(index, &by_hid) { + nested_children.entry(p).or_default().push(c); + nested_targets.insert(c); + } + + Self { + by_hid, + md_children, + has_md_parent, + nested_children, + nested_targets, + } + } +} + +impl<'index, 'pre> GraphBuilder<'index, 'pre> { + pub fn new(index: &'index HeaderIndex, pre: &'pre Prelude<'index>, cfg: BuilderConfig) -> Self { + Self { + index, + cfg, + graph: Graph::default(), + by_hid: &pre.by_hid, + md_children: &pre.md_children, + has_md_parent: &pre.has_md_parent, + nested_children: &pre.nested_children, + nested_targets: &pre.nested_targets, + header_entry: HashMap::new(), + header_exits: HashMap::new(), + span_map: BamlMap::new(), + } + } + + pub fn build(mut self) -> (Graph<'index>, BamlMap) { + let scope_root = build_scope_roots(self.index); + + let top_scopes = self.index.scopes().filter(|scope| { + let root_hid = &scope_root[scope]; + !self.nested_targets.contains(&root_hid) + }); + + let filtered_headers = self.classify_and_filter_headers(); + + dbg!(&self.index.headers); + dbg!(&filtered_headers); + + for scope in top_scopes { + self.build_scope_sequence(scope, None, &filtered_headers.actions); + } + + self.add_scope_edges(); + + if self.cfg.show_call_nodes { + self.add_header_calls(); + } + + (self.graph, self.span_map) + } + + /// Returns an iterator of (child, parent) pairs, relating each component of the scope to the + /// previous in the list. First component is related to `parent_hid`. + fn link_scope_children<'iter>( + &'iter self, + scope: ScopeId, + parent_hid: Hid, + ) -> impl Iterator + 'iter { + let mut non_md_headers = self + .index + .headers_in_scope_iter(scope) + .map(|h| h.hid) + .filter(|hid| !self.has_md_parent.contains(hid)); + + let first = non_md_headers.next(); + + first + .map(move |first| { + // we relate each subsequent header to the previous one in its scope + let paired_rest = non_md_headers.scan(first, |prev, next| { + Some((next, std::mem::replace(prev, next))) + }); + + [(first, parent_hid)].into_iter().chain(paired_rest) + }) + .into_iter() + .flatten() + } + + // NOTE: right now, algorithm switches between visiting scopes & visiting headers because + // headers are not linked to their parent headers via scopes. + + /// Returns a map of actions to take on filtered headers. Iterating the map will result ina + /// pre-order traversal on the headers. + fn classify_and_filter_headers(&self) -> FilteredHeaders { + let mut actions = IndexMap::new(); + + // for non-root & non-filtered nodes, their parent. + let mut parent = HashMap::new(); + + let classify_nonfiltered = |header: &RenderableHeader, parent: &mut HashMap| { + let mut add_all_children = || { + let md_children = self + .md_children + .get(&header.hid) + .into_iter() + .flatten() + .copied(); + let nested_children = self + .nested_children + .get(&header.hid) + .into_iter() + .flatten() + .copied(); + let parent_hid = header.hid; + let md_children = md_children.map(|child| (child, parent_hid)); + + let scope_entries = nested_children.flat_map(|child| { + let scope = self.by_hid[&child].scope; + + self.link_scope_children(scope, parent_hid) + }); + + parent.extend(md_children.chain(scope_entries)); + }; + + // If any of the children lists is Some(), then it is nonempty. So unwrap_or(&[]) does + // not remove information. + + let md_children = self + .md_children + .get(&header.hid) + .map(Vec::as_slice) + .unwrap_or(&[]); + let nested_children = self + .nested_children + .get(&header.hid) + .map(Vec::as_slice) + .unwrap_or(&[]); + + dbg!(md_children, nested_children); + + if matches!((md_children, nested_children), ([], [])) { + HeaderAction::Empty + } else if header.label_kind == HeaderLabelKind::If { + // mark children as visited. + add_all_children(); + HeaderAction::If + } else { + let single_nested_child_has_multiple_items = nested_children.len() == 1 && { + let child_root = self.by_hid[&nested_children[0]]; + let count = self.index.headers_in_scope_iter(child_root.scope).count(); + count > 1 + }; + + let total_children = md_children.len() + nested_children.len(); + + let should_flatten = total_children <= MAX_CHILDREN_TO_FLATTEN + && !single_nested_child_has_multiple_items; + + if should_flatten { + // implicitly make filtered removed by not adding a parent to them + + if md_children.len() == 1 { + parent.insert(md_children[0], header.hid); + } else if nested_children.len() == 1 { + let scope = self.by_hid[&nested_children[0]].scope; + parent.extend(self.link_scope_children(scope, header.hid)); + } + + HeaderAction::Flattened + } else { + add_all_children(); + HeaderAction::Nonempty + } + } + }; + + // forward action classification & filtering. + + for root in self.index.headers.iter().filter(|h| h.parent_id.is_none()) { + actions.insert(root.hid, classify_nonfiltered(root, &mut parent)); + } + + for header in &self.index.headers { + // no parent => filtered. + let Some(parent_id) = parent.get(&header.hid).copied() else { + continue; + }; + + // to allow for scope sequences within filtered context, if a visited header is the first of its scope, the entire scope is + // marked as visited. + + let mut scope_iter = self.index.headers_in_scope_iter(header.scope); + + let is_first_of_scope = scope_iter + .next() + .is_some_and(|first| first.hid == header.hid); + + if is_first_of_scope { + parent.extend(scope_iter.map(|header| (header.hid, parent_id))); + } + + actions.insert(header.hid, classify_nonfiltered(header, &mut parent)); + } + + FilteredHeaders { actions, parent } + } + + /// Links the items in each scope in source order, showing execution order. + fn add_scope_edges(&mut self) { + for scope in self.index.scopes() { + // TODO: I think we can remove collect()? + let mut items = self + .index + .headers_in_scope_iter(scope) + .filter(|h| !self.has_md_parent.contains(&h.hid)) + .map(|h| h.hid); + + let Some(first) = items.next() else { + continue; + }; + + let prev_pairs = + items.scan(first, |prev, cur| Some((cur, std::mem::replace(prev, cur)))); + + for (hid, prev_hid) in prev_pairs { + let entry = self.header_entry[&hid]; + let prev_exits = &self.header_exits[&prev_hid]; + // NOTE: `extend` on a loop. TL;DR: each exits vec is pretty small. + // More thorough explanation at the end of `build_header`. + self.graph.edges.extend( + prev_exits + .iter() + .copied() + .map(|e| Edge { from: e, to: entry }), + ); + } + } + } + + fn build_scope_sequence( + &mut self, + scope: ScopeId, + parent_cluster: Option, + filtered: &IndexMap, + ) { + let items = self + .index + .headers_in_scope_iter(scope) + .filter(|h| !self.has_md_parent.contains(&h.hid)) + .map(|h| h.hid); + + // post-order: build headers inside scope. <- parent_cluster + for hid in items { + self.build_header(hid, parent_cluster, filtered); + } + } + + fn build_header( + &mut self, + hid: Hid, + parent_cluster: Option, + filtered: &IndexMap, + ) { + // pre-order/post-order notation: + //
-order:  [[output] <- ]
+
+        let header = self.by_hid[&hid];
+
+        assert!(
+            !self.header_entry.contains_key(&hid),
+            "header graph is a tree - no cycles!"
+        );
+        let md_children = self.md_children.get(&hid).map(Vec::as_slice).unwrap_or(&[]);
+        let nested_children = self
+            .nested_children
+            .get(&hid)
+            .map(Vec::as_slice)
+            .unwrap_or(&[]);
+
+        match filtered[&hid] {
+            HeaderAction::Empty => {
+                let span = SerializedSpan::serialize(&header.span);
+                let node_id = self.graph.add_node(|node_id| Node {
+                    id: node_id,
+                    label: header.title.as_ref(),
+                    kind: NodeKind::Header(hid, Some(span.clone())),
+                    cluster: parent_cluster,
+                });
+                self.span_map.insert(node_id, span);
+                self.header_entry.insert(hid, node_id);
+                self.header_exits.insert(hid, vec![node_id]);
+            }
+            HeaderAction::If => {
+                // pre-order: assign cluster ids: cluster_id <- header, parent_cluster
+                let cluster_id = self.graph.add_cluster(|cluster_id| Cluster {
+                    id: cluster_id,
+                    label: header.title.as_ref(),
+                    parent: parent_cluster,
+                });
+
+                // pre-order: insert entry <- header, cluster_id
+                let span = SerializedSpan::serialize(&header.span);
+                let decision_id = self.graph.add_node(|decision_id| Node {
+                    id: decision_id,
+                    label: header.title.as_ref(),
+                    kind: NodeKind::Decision(hid, Some(span.clone())),
+                    cluster: Some(cluster_id),
+                });
+                self.span_map.insert(decision_id, span);
+                self.header_entry.insert(hid, decision_id);
+
+                // post-order: build scope sequence for scope & build header
+                // header_entry, header_exit <- cluster_id.
+                // For some reason it can't work without a visited_scopes? That's pre-order data.
+                for child_root_id in nested_children {
+                    let child_scope = self.by_hid[child_root_id].scope;
+                    self.build_scope_sequence(child_scope, Some(cluster_id), filtered);
+                }
+
+                // post-order: build header for each of the markdown children <- cluster_id.
+                // Same doubt wrt visited_scopes.
+                for child in md_children {
+                    self.build_header(*child, Some(cluster_id), filtered);
+                }
+
+                // unordered: add edges from decision id to children <- decision_id
+                self.graph
+                    .edges
+                    .extend(nested_children.iter().map(|child_root_id| Edge {
+                        from: decision_id,
+                        to: self.header_entry[child_root_id],
+                    }));
+
+                // post-order: collect branch exits <- header_exits
+                let branch_exits: Vec<_> = nested_children
+                    .iter()
+                    .flat_map(|child_hid| &self.header_exits[child_hid])
+                    .copied()
+                    .collect();
+
+                let md_ids_only: Vec<_> =
+                    md_children.iter().map(|ch| self.header_entry[ch]).collect();
+
+                if let Some(first_md) = md_ids_only.first().copied() {
+                    if !branch_exits.is_empty() {
+                        for e in branch_exits.iter() {
+                            self.graph.edges.push(Edge {
+                                from: *e,
+                                to: first_md,
+                            });
+                        }
+                    } else {
+                        self.graph.edges.push(Edge {
+                            from: decision_id,
+                            to: first_md,
+                        });
+                    }
+                }
+                self.graph.edges.extend(md_ids_only.windows(2).map(|win| {
+                    let from = win[0];
+                    let to = win[1];
+                    Edge { from, to }
+                }));
+                let outward = if let Some(last) = md_ids_only.last().copied() {
+                    vec![last]
+                } else {
+                    branch_exits
+                };
+                self.header_exits.insert(hid, outward.clone());
+            }
+            HeaderAction::Nonempty => {
+                // pre-order: assign cluster id cluster_id <- header, parent_cluster
+
+                let cluster_id = self.graph.add_cluster(|cluster_id| Cluster {
+                    id: cluster_id,
+                    label: header.title.as_ref(),
+                    // TODO: clone() on copy
+                    parent: parent_cluster,
+                });
+
+                // post-order: build scope sequence <- cluster_id, visited_scopes. Only for direct nested
+                // children.
+                for child_hid in nested_children {
+                    let child_scope = self.by_hid[&child_hid].scope;
+                    self.build_scope_sequence(child_scope, Some(cluster_id), filtered);
+                }
+
+                // post-order: build header for markdown children (scope sequence in nested already visits nested
+                // children)
+                // <- cluster_id
+                for &child_hid in md_children {
+                    self.build_header(child_hid, Some(cluster_id), filtered);
+                }
+
+                // Merge markdown children and direct nested roots, preserving each list's internal order
+                let items_merged: Vec<_> =
+                    merge_by_pos(self.by_hid, &md_children, &nested_children).collect();
+
+                // We should have at least one item, since empty children are handled separately.
+                let first_rep = self.header_entry[&items_merged[0]];
+
+                // unordered: create edges <- header_exits.
+                // Left scan for choosing exits, although choosing fn is not expensive so it can be
+                // executed twice.
+
+                let choose_exits_hid = |child_hid| {
+                    // NOTE: since nested children Hids are marked, can we use pre?
+                    let prebuilt_scope_last_exits = if nested_children.contains(&child_hid) {
+                        let child_scope = self.by_hid[&child_hid].scope;
+                        let maybe_last_in_scope = self
+                            .index
+                            .headers_in_scope_iter(child_scope)
+                            .rev()
+                            .map(|h| h.hid)
+                            .find(|h| !self.has_md_parent.contains(h));
+
+                        maybe_last_in_scope.filter(|l| self.header_exits.contains_key(l))
+                    } else {
+                        None
+                    };
+
+                    prebuilt_scope_last_exits.unwrap_or(child_hid)
+                };
+
+                // NOTE: using Hid instead of direct reference since otherwise we lock writes to
+                // `self.header_exits`. We know that invalidating the reference that we hold is not possible
+                // but we cannot communicate this to Rust.
+                let mut prev_exits_hid = choose_exits_hid(items_merged[0]);
+
+                for &child_hid in &items_merged[1..] {
+                    let entry = self.header_entry[&child_hid];
+
+                    let exits_hid = choose_exits_hid(child_hid);
+                    let prev = std::mem::replace(&mut prev_exits_hid, exits_hid);
+
+                    // NOTE: using `extend` in a loop. Since most nodes will only have 1 or 2 children,
+                    // impact is covered by exponential allocation.
+                    //
+                    // The full iterator is `FlatMap`, it doesn't implement `ExactSizedIterator`
+                    // and doesn't have a reliable size_hint, so the perf fix here is to precalculate
+                    // the total exit count with a .iter().map().sum().
+                    let edges_prev = self.header_exits[&prev].iter().copied().map(|exit| Edge {
+                        from: exit,
+                        to: entry,
+                    });
+
+                    self.graph.edges.extend(edges_prev);
+                }
+
+                let entry = first_rep;
+                self.header_entry.insert(hid, entry);
+                let exits = self.header_exits[&prev_exits_hid].to_owned();
+                self.header_exits.insert(hid, exits);
+            }
+            HeaderAction::Flattened => {
+                // pre-order add & assign node id to header
+                let span = SerializedSpan::serialize(&header.span);
+                let node_id = self.graph.add_node(|node_id| Node {
+                    id: node_id,
+                    label: header.title.as_ref(),
+                    kind: NodeKind::Header(hid, Some(span.clone())),
+                    cluster: parent_cluster,
+                });
+
+                self.span_map.insert(node_id, span.clone());
+                self.header_entry.insert(hid, node_id);
+
+                // TODO: classify child & what to do inside flattened, like scope sequence needed or
+                // not.
+
+                // post-order: build scope sequence for child scope, only when flatten is single child.
+                // This may be implicit if the child is marked as visited.
+
+                // post-order: run children headers
+                if md_children.len() == 1 {
+                    self.build_header(md_children[0], parent_cluster, filtered);
+                } else if nested_children.len() == 1 {
+                    let child_root_hid = nested_children[0];
+                    let child_scope = self.by_hid[&child_root_hid].scope;
+                    // build_scope_sequence already calls build_header for the entries inside the
+                    // scope, including the child_root_hid
+                    self.build_scope_sequence(child_scope, parent_cluster, filtered);
+                }
+
+                // unordered: add edges <- node_id, children id
+
+                if md_children.len() == 1 {
+                    let c_entry = self.header_entry[&md_children[0]];
+
+                    self.graph.edges.push(Edge {
+                        from: node_id,
+                        to: c_entry,
+                    });
+                } else if nested_children.len() == 1 {
+                    let c_entry = self.header_entry[&nested_children[0]];
+                    self.graph.edges.push(Edge {
+                        from: node_id,
+                        to: c_entry,
+                    });
+                }
+
+                // post-order: copy exits from children.
+                // NOTE: we could reference them, but the vecs are pretty small.
+                // TODO: If each tree node only has one preceding parent, we can extract them (move) instead.
+                let exits = if md_children.len() == 1 {
+                    self.header_exits[&md_children[0]].to_owned()
+                } else if nested_children.len() == 1 {
+                    self.header_exits[&nested_children[0]].to_owned()
+                } else {
+                    vec![node_id]
+                };
+
+                self.header_exits.insert(hid, exits);
+            }
+        }
+    }
+
+    /// Visits the headers that have an assigned node id, & for each inserts & links a call node
+    /// if the header is in [`HeaderIndex::header_calls`]
+    fn add_header_calls(&mut self) {
+        let entries_with_calls = self.header_entry.iter().filter_map(|(hid, rep_id)| {
+            self.index
+                .header_calls
+                .get(hid)
+                .map(|callees| (*hid, *rep_id, callees))
+        });
+
+        for (hid, rep_id, callees) in entries_with_calls {
+            let cluster = self.graph.nodes[rep_id.0 as usize].cluster;
+            for callee in callees {
+                let call_node_id = self.graph.add_node(|call_node_id| Node {
+                    id: call_node_id,
+                    label: callee.as_str(),
+                    kind: NodeKind::Call {
+                        header: hid,
+                        callee,
+                    },
+                    cluster,
+                });
+                self.graph.edges.push(Edge {
+                    from: call_node_id,
+                    to: rep_id,
+                });
+            }
+        }
+    }
+}
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ClusterId(u32);
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct NodeId(u32);
+
+impl serde::Serialize for NodeId {
+    fn serialize(&self, serializer: S) -> Result
+    where
+        S: serde::Serializer,
+    {
+        serializer.collect_str(self)
+    }
+}
+
+impl std::fmt::Debug for ClusterId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{self}")
+    }
+}
+impl std::fmt::Debug for NodeId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{self}")
+    }
+}
+
+impl std::fmt::Display for NodeId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "n{}", self.0)
+    }
+}
+
+impl std::fmt::Display for ClusterId {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "sg{}", self.0)
+    }
+}
+
+fn build_scope_roots(index: &HeaderIndex) -> HashMap {
+    // iterate the headers by scope order. The first one to appear is the scope root.
+    let mut scope_root = HashMap::new();
+    for h in &index.headers {
+        scope_root.entry(h.scope).or_insert(h.hid);
+    }
+    scope_root
+}
+
+/// Edges that cross a scope enter, i.e not just header changes.
+fn nested_scope_edges<'iter>(
+    index: &'iter HeaderIndex,
+    by_hid: &'iter HashMap,
+) -> impl Iterator + 'iter {
+    index
+        .nested_edges_hid_iter()
+        .filter(|(p, c)| by_hid[p].scope != by_hid[c].scope)
+        .copied()
+}
+
+/// Compute a tuple position key for stable ordering comparisons
+fn pos_tuple<'index>(
+    by_hid: &HashMap,
+    hid: Hid,
+) -> (&'index Path, usize) {
+    let h = by_hid[&hid];
+    (h.span.file.path_buf().as_ref(), h.span.start)
+}
+
+/// Merge two already-ordered lists by source position, preserving internal order
+fn merge_by_pos<'index, 'iter>(
+    by_hid: &'iter HashMap,
+    lhs: &'iter [Hid],
+    rhs: &'iter [Hid],
+) -> impl Iterator + 'iter
+where
+    'index: 'iter,
+{
+    return State { by_hid, lhs, rhs };
+
+    // implement iterator manually for size hint + exact size.
+    struct State<'index, 'iter> {
+        by_hid: &'iter HashMap,
+        lhs: &'iter [Hid],
+        rhs: &'iter [Hid],
+    }
+
+    impl ExactSizeIterator for State<'_, '_> {
+        fn len(&self) -> usize {
+            self.lhs.len() + self.rhs.len()
+        }
+    }
+
+    impl Iterator for State<'_, '_> {
+        type Item = Hid;
+
+        // exact sized
+        fn size_hint(&self) -> (usize, Option) {
+            let len = self.len();
+            (len, Some(len))
+        }
+
+        fn next(&mut self) -> Option {
+            match (self.lhs, self.rhs) {
+                ([l, lrest @ ..], [r, rrest @ ..]) => Some(
+                    if pos_tuple(self.by_hid, *l) <= pos_tuple(self.by_hid, *r) {
+                        self.lhs = lrest;
+                        *l
+                    } else {
+                        self.rhs = rrest;
+                        *r
+                    },
+                ),
+                ([l, rest @ ..], []) => {
+                    self.lhs = rest;
+                    Some(*l)
+                }
+                ([], [r, rest @ ..]) => {
+                    self.rhs = rest;
+                    Some(*r)
+                }
+                ([], []) => None,
+            }
+        }
+    }
+}
+
+/// An internal action to take on a header. This is stored because there are both pre-order &
+/// post-order visits that need this information.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum HeaderAction {
+    Empty,
+    Nonempty,
+    Flattened,
+    If,
+}
+
+#[derive(Debug)]
+struct FilteredHeaders {
+    /// Map of actions to take on filtered headers. Iterating the map will result ina
+    /// pre-order traversal on the headers.
+    actions: IndexMap,
+
+    #[allow(dead_code)] // FIXME: remove this
+    // Given a header, tracks its parent, even across scope boundaries.
+    parent: HashMap,
+}
diff --git a/engine/baml-lib/ast/src/ast/header_collector.rs b/engine/baml-lib/ast/src/ast/baml_vis/header_collector.rs
similarity index 86%
rename from engine/baml-lib/ast/src/ast/header_collector.rs
rename to engine/baml-lib/ast/src/ast/baml_vis/header_collector.rs
index c313940e77..f6a33607ba 100644
--- a/engine/baml-lib/ast/src/ast/header_collector.rs
+++ b/engine/baml-lib/ast/src/ast/baml_vis/header_collector.rs
@@ -14,9 +14,13 @@
 
 use std::{collections::HashMap, sync::Arc};
 
+use indexmap::IndexMap;
 use internal_baml_diagnostics::Span;
 
-use super::{Ast, Expression, ExpressionBlock, Field, Header, Stmt, Top, WithName, WithSpan};
+use crate::ast::{
+    traits::{WithIdentifier, WithName},
+    Ast, ClassConstructorField, Expression, ExpressionBlock, Field, Header, Stmt, Top,
+};
 
 /// Alias for external header identifiers for public consumption
 type HeaderId = String;
@@ -29,16 +33,6 @@ pub struct Hid(pub u32);
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ScopeId(pub u32);
 
-/// Classification of scope kinds for visualization semantics
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ScopeKind {
-    TopLevel,
-    ForBody,
-    IfThen,
-    IfElse,
-    Generic,
-}
-
 /// Classification of what kind of statement a header labels
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum HeaderLabelKind {
@@ -70,8 +64,12 @@ pub struct RenderableHeader {
 pub struct HeaderIndex {
     /// All headers in source order per scope, flattened
     pub headers: Vec,
-    // header indexes in source order per scope
-    by_scope: HashMap>,
+
+    /// Header indexes in source order per scope.
+    /// Uses [`IndexMap`] instead of BamlMap to always guarantee iteration order = insertion order
+    /// = source order.
+    /// See [`graph`](crate::ast::baml_vis::graph) module, where we add
+    by_scope: IndexMap>,
     /// Mapping of internal Hid -> names of functions called by the labeled expression
     pub header_calls: HashMap>, // hid -> [callee_name]
     hid_to_idx: Vec,
@@ -81,13 +79,22 @@ pub struct HeaderIndex {
 
 impl HeaderIndex {
     /// Iterate headers in a scope without allocation
-    pub fn headers_in_scope_iter(&self, scope: ScopeId) -> impl Iterator {
+    pub fn headers_in_scope_iter(
+        &self,
+        scope: ScopeId,
+    ) -> impl Iterator + DoubleEndedIterator {
         self.by_scope
             .get(&scope)
             .into_iter()
             .flat_map(|idxs| idxs.iter().map(|i| &self.headers[*i]))
     }
 
+    /// Iterates all scopes. Order of iteration is guaranteed to be
+    /// consistent with source order.
+    pub fn scopes<'iter>(&'iter self) -> impl Iterator + 'iter {
+        self.by_scope.keys().copied()
+    }
+
     /// O(1) access to a header by its Hid via internal index
     pub fn get_by_hid(&self, hid: Hid) -> Option<&RenderableHeader> {
         let idx = *self.hid_to_idx.get(hid.0 as usize)?;
@@ -104,12 +111,13 @@ impl HeaderIndex {
 }
 
 /// Internal collector to walk AST and build a HeaderIndex
-#[derive(Debug)]
+#[derive(Debug, Default)]
 pub struct HeaderCollector {
     scope_counter: u32,
     scope_stack: Vec,
-    // Raw headers by scope before normalization and in-scope parenting
-    raw_by_scope: HashMap>, // source order
+    /// Raw headers by scope before normalization and in-scope parenting.
+    /// [`IndexMap`] to maintain source order.
+    raw_by_scope: IndexMap>, // source order
     // Accumulated nested edges (Hid -> Hid)
     nested_edges_hid: Vec<(Hid, Hid)>,
     // Mapping during collection: header (by Hid) -> function names called by the labeled expression
@@ -117,6 +125,8 @@ pub struct HeaderCollector {
     next_hid: u32,
     // Track nearest header so far per active scope for fast parent lookup
     last_hdr_stack: Vec>, // parallel to scope_stack
+    /// Optional top-level function name filter.
+    function_filter: Option,
 }
 
 #[derive(Debug, Clone)]
@@ -130,15 +140,10 @@ struct RawHeader {
 }
 
 impl HeaderCollector {
-    pub fn collect(ast: &Ast) -> HeaderIndex {
+    pub fn collect(ast: &Ast, function_filter: Option<&str>) -> HeaderIndex {
         let mut c = Self {
-            scope_counter: 0,
-            scope_stack: Vec::new(),
-            raw_by_scope: HashMap::new(),
-            nested_edges_hid: Vec::new(),
-            header_fn_calls: HashMap::new(),
-            next_hid: 0,
-            last_hdr_stack: Vec::new(),
+            function_filter: function_filter.map(|s| s.to_string()),
+            ..Self::default()
         };
         c.visit_ast(ast);
         c.build_index()
@@ -276,13 +281,31 @@ impl HeaderCollector {
             self.header_fn_calls
                 .entry(*hid)
                 .or_default()
-                .extend(top_calls.clone());
+                .extend(top_calls.iter().cloned());
         }
     }
 
     fn visit_ast(&mut self, ast: &Ast) {
         for top in &ast.tops {
-            self.visit_top(top);
+            if self.should_visit_top(top) {
+                self.visit_top(top);
+            }
+        }
+    }
+
+    fn should_visit_top(&self, top: &Top) -> bool {
+        let Some(filter) = self.function_filter.as_deref() else {
+            return true;
+        };
+
+        match top {
+            Top::Function(block)
+            | Top::Client(block)
+            | Top::Generator(block)
+            | Top::TestCase(block)
+            | Top::RetryPolicy(block) => block.identifier().name() == filter,
+            Top::ExprFn(expr_fn) => expr_fn.name.name() == filter,
+            _ => false,
         }
     }
 
@@ -348,13 +371,25 @@ impl HeaderCollector {
             Expression::ClassConstructor(cons, _) => {
                 for f in &cons.fields {
                     match f {
-                        super::ClassConstructorField::Named(_, e) => self.visit_expression(e),
-                        super::ClassConstructorField::Spread(e) => self.visit_expression(e),
+                        ClassConstructorField::Named(_, e) => self.visit_expression(e),
+                        ClassConstructorField::Spread(e) => self.visit_expression(e),
                     }
                 }
             }
             Expression::UnaryOperation { expr, .. } => self.visit_expression(expr),
             _ => {}
+            // Expression::BoolValue(_, span) => todo!(),
+            // Expression::NumericValue(_, span) => todo!(),
+            // Expression::Identifier(identifier) => todo!(),
+            // Expression::StringValue(_, span) => todo!(),
+            // Expression::RawStringValue(raw_string) => todo!(),
+            // Expression::JinjaExpressionValue(jinja_expression, span) => todo!(),
+            // Expression::App(app) => todo!(),
+            // Expression::ArrayAccess(expression, expression1, span) => todo!(),
+            // Expression::FieldAccess(expression, identifier, span) => todo!(),
+            // Expression::MethodCall { receiver, method, args, type_args, span } => todo!(),
+            // Expression::BinaryOperation { left, operator, right, span } => todo!(),
+            // Expression::Paren(expression, span) => todo!(),
         }
     }
 
@@ -461,7 +496,7 @@ impl HeaderCollector {
     fn build_index(self) -> HeaderIndex {
         let mut index = HeaderIndex {
             headers: Vec::new(),
-            by_scope: HashMap::new(),
+            by_scope: IndexMap::new(),
             header_calls: HashMap::new(),
             hid_to_idx: Vec::new(),
             nested_edges_hid: Vec::new(),
@@ -540,6 +575,7 @@ impl HeaderCollector {
 /// Only captures the outermost call(s) that structurally represent the expression,
 /// ignoring nested calls within arguments or sub-expressions.
 fn collect_top_level_calls(expr: &Expression) -> Vec {
+    use crate::ast::WithName;
     match expr {
         // If the expression is a block, the top-level expression is inside it
         Expression::ExprBlock(block, _span) => {
diff --git a/engine/baml-lib/ast/src/ast/mermaid_debug.rs b/engine/baml-lib/ast/src/ast/baml_vis/mermaid_debug.rs
similarity index 99%
rename from engine/baml-lib/ast/src/ast/mermaid_debug.rs
rename to engine/baml-lib/ast/src/ast/baml_vis/mermaid_debug.rs
index c9fdf02043..92b7bc9fb8 100644
--- a/engine/baml-lib/ast/src/ast/mermaid_debug.rs
+++ b/engine/baml-lib/ast/src/ast/baml_vis/mermaid_debug.rs
@@ -1,12 +1,14 @@
 use std::collections::HashMap;
 
-use super::{
-    App, Argument, ArgumentsList, Assignment, Ast, Attribute, BlockArgs, ClassConstructor,
-    ClassConstructorField, ExprFn, Expression, ExpressionBlock, Field, FieldType, Header,
-    HeaderCollector, HeaderIndex, Identifier, RawString, Stmt, TemplateString, Top,
-    TopLevelAssignment, TypeExpressionBlock, ValueExprBlock, WithIdentifier, WithName, WithSpan,
+use crate::ast::{
+    traits::{WithIdentifier, WithName},
+    Argument, ArgumentsList, Assignment, Ast, Attribute, BlockArgs, ClassConstructor,
+    ClassConstructorField, ExprFn, Expression, ExpressionBlock, Field, FieldType, Header, Stmt,
+    TemplateString, Top, TopLevelAssignment, TypeExpressionBlock, ValueExprBlock,
 };
 
+use super::header_collector::{HeaderCollector, HeaderIndex};
+
 /// A debug utility for converting AST structures to Mermaid diagrams
 #[derive(Debug)]
 pub struct MermaidDiagramGenerator {
@@ -858,7 +860,7 @@ impl MermaidDiagramGenerator {
 
     /// Render the header-only diagram using simplified HeaderIndex
     fn render_headers_only(&mut self, ast: &Ast) {
-        let index: HeaderIndex = HeaderCollector::collect(ast);
+        let index: HeaderIndex = HeaderCollector::collect(ast, None);
 
         // Create all header nodes first
         let mut header_node_ids: HashMap = HashMap::new();
diff --git a/engine/baml-lib/ast/src/lib.rs b/engine/baml-lib/ast/src/lib.rs
index d2f699b307..ccbb7e60d8 100644
--- a/engine/baml-lib/ast/src/lib.rs
+++ b/engine/baml-lib/ast/src/lib.rs
@@ -12,7 +12,7 @@ pub mod ast;
 mod formatter;
 mod parser;
 
-pub use ast::{BamlVisDiagramGenerator, MermaidDiagramGenerator};
+pub use ast::{diagram_generator, MermaidDiagramGenerator};
 pub use formatter::{format_schema, FormatOptions};
 
 /// Transform the input string into a valid (quoted and escaped) PSL string literal.
diff --git a/engine/baml-lib/baml/Cargo.toml b/engine/baml-lib/baml/Cargo.toml
index 2b0a62242e..7702abc980 100644
--- a/engine/baml-lib/baml/Cargo.toml
+++ b/engine/baml-lib/baml/Cargo.toml
@@ -24,6 +24,7 @@ base64.workspace = true
 dissimilar.workspace = true
 expect-test.workspace = true
 env_logger.workspace = true
+log.workspace = true
 indoc.workspace = true
 either.workspace = true
 strip-ansi-escapes.workspace = true
diff --git a/engine/baml-lib/baml/tests/mermaid_graph_tests.rs b/engine/baml-lib/baml/tests/mermaid_graph_tests.rs
index 7bddd3357c..a83211bdeb 100644
--- a/engine/baml-lib/baml/tests/mermaid_graph_tests.rs
+++ b/engine/baml-lib/baml/tests/mermaid_graph_tests.rs
@@ -1,7 +1,7 @@
 use std::{fs, path::Path};
 
 use baml_lib::{
-    internal_baml_ast::{ast::BamlVisDiagramGenerator, parse},
+    internal_baml_ast::{ast::diagram_generator, parse},
     internal_baml_diagnostics::SourceFile,
 };
 
@@ -12,6 +12,12 @@ const ROOT: &str = concat!(
 
 #[test]
 fn headers_mermaid_snapshots() {
+    // Initialize logging at INFO level
+    let _ = env_logger::builder()
+        .filter_level(log::LevelFilter::Debug)
+        .is_test(true)
+        .try_init();
+
     let dir = Path::new(ROOT);
     if !dir.exists() {
         panic!("fixtures dir missing: {ROOT}");
@@ -36,6 +42,9 @@ fn headers_mermaid_snapshots() {
             .and_then(|n| n.to_str())
             .map(|s| s.to_string())
             .unwrap_or_else(|| path.to_string_lossy().to_string());
+        log::info!("================================================");
+        log::info!("Generating Mermaid graph for {rel_name:#?}");
+        log::info!("================================================");
         // Parse defensively; let panic message print so it is visible, but keep the test running
         let path_clone = path.clone();
         let res = std::panic::catch_unwind(|| {
@@ -105,7 +114,13 @@ fn headers_mermaid_snapshots() {
                     }
                 } else {
                     // No errors: compare Mermaid graph
-                    let got = BamlVisDiagramGenerator::generate_headers_flowchart(&ast);
+                    let got = diagram_generator::generate_with_styling(
+                        diagram_generator::MermaidGeneratorContext {
+                            use_fancy: false,
+                            function_filter: None,
+                        },
+                        &ast,
+                    );
                     let mut exp_path = path.clone();
                     exp_path.set_extension("mmd");
                     if std::env::var("UPDATE").ok().as_deref() == Some("1") {
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/ai_content_pipeline.mmd b/engine/baml-lib/baml/tests/validation_files/headers/ai_content_pipeline.mmd
index a847a00929..26b13d0c17 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/ai_content_pipeline.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/ai_content_pipeline.mmd
@@ -1,34 +1,34 @@
 flowchart TD
   subgraph sg0["AI Content Pipeline"]
-    direction LR
+    direction TB
     n12["Complete"]
     subgraph sg1["Fetch Resources"]
-      direction LR
+      direction TB
       n0["Retrieve Emails and Posts from Database"]
       n1["Process and Load Video"]
     end
     subgraph sg2["Generate Content"]
-      direction LR
+      direction TB
       subgraph sg3["Video Summarization"]
-        direction LR
+        direction TB
         n2{"Video Summarization"}
         class n2 decisionNode;
         n5["Save Summary"]
         n6["Skip Summarization"]
         subgraph sg4["Generate Summary"]
-          direction LR
+          direction TB
           n3["Create Summary Text"]
           n4["Label Summary Category"]
         end
       end
       subgraph sg5["Generate Drafts for Posts"]
-        direction LR
+        direction TB
         n7["Get Content Type and Generate Draft"]
         n8["Save Draft"]
       end
     end
     subgraph sg6["Prepare Pull Request"]
-      direction LR
+      direction TB
       n9{"Prepare Pull Request"}
       class n9 decisionNode;
       n10["Create GitHub PR"]
@@ -37,18 +37,18 @@ flowchart TD
   end
   n0 --> n1
   n3 --> n4
-  n4 --> n5
   n2 --> n3
   n2 --> n6
-  n7 --> n8
   n4 --> n7
   n6 --> n7
-  n1 --> n2
   n9 --> n10
   n9 --> n11
+  n1 --> n2
   n8 --> n9
   n10 --> n12
   n11 --> n12
+  n4 --> n5
+  n7 --> n8
 %%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":4,"start":4,"end_line":4,"end":48},"n1":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":8,"start":4,"end_line":8,"end":31},"n2":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":15,"start":4,"end_line":15,"end":28},"n3":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":19,"start":8,"end_line":19,"end":34},"n4":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":22,"start":8,"end_line":22,"end":37},"n5":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":25,"start":8,"end_line":25,"end":26},"n6":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":30,"start":8,"end_line":30,"end":32},"n7":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":38,"start":8,"end_line":38,"end":49},"n8":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":42,"start":8,"end_line":42,"end":24},"n9":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":48,"start":4,"end_line":48,"end":28},"n10":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":50,"start":8,"end_line":50,"end":30},"n11":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":54,"start":8,"end_line":54,"end":29},"n12":{"file_path":"tests/validation_files/headers/ai_content_pipeline.baml","start_line":58,"start":4,"end_line":58,"end":16}}
   click n0 call bamlMermaidNodeClick() "Go to source"
   click n1 call bamlMermaidNodeClick() "Go to source"
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/basic_if.mmd b/engine/baml-lib/baml/tests/validation_files/headers/basic_if.mmd
index b9469f08c5..c24ae0c887 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/basic_if.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/basic_if.mmd
@@ -1,17 +1,17 @@
 flowchart TD
-  n0["Basic test"]
-  subgraph sg0["This is a named statement"]
-    direction LR
-    n1{"This is a named statement"}
-    class n1 decisionNode;
-    n2["Then Statement"]
-    n3["Else Statement"]
+  subgraph sg0["Basic test"]
+    direction TB
+    subgraph sg1["This is a named statement"]
+      direction TB
+      n0{"This is a named statement"}
+      class n0 decisionNode;
+      n1["Then Statement"]
+      n2["Else Statement"]
+    end
   end
-  n1 --> n2
-  n1 --> n3
   n0 --> n1
-%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":1,"start":0,"end_line":1,"end":14},"n1":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":5,"start":4,"end_line":5,"end":33},"n2":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":7,"start":8,"end_line":7,"end":26},"n3":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":10,"start":8,"end_line":10,"end":26}}
+  n0 --> n2
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":5,"start":4,"end_line":5,"end":33},"n1":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":7,"start":8,"end_line":7,"end":26},"n2":{"file_path":"tests/validation_files/headers/basic_if.baml","start_line":10,"start":8,"end_line":10,"end":26}}
   click n0 call bamlMermaidNodeClick() "Go to source"
   click n1 call bamlMermaidNodeClick() "Go to source"
-  click n2 call bamlMermaidNodeClick() "Go to source"
-  click n3 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
+  click n2 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/basic_sections.mmd b/engine/baml-lib/baml/tests/validation_files/headers/basic_sections.mmd
index df35906ced..5bb99fe65a 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/basic_sections.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/basic_sections.mmd
@@ -1,9 +1,9 @@
 flowchart TD
   subgraph sg0["Main Processing"]
-    direction LR
+    direction TB
     n0["Generate Summary"]
     subgraph sg1["Make Space"]
-      direction LR
+      direction TB
       n1["Prepare Final Output"]
       n2["Final Output"]
     end
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/complex_headers_test.mmd b/engine/baml-lib/baml/tests/validation_files/headers/complex_headers_test.mmd
index 778246c27a..9568dfe478 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/complex_headers_test.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/complex_headers_test.mmd
@@ -1,52 +1,54 @@
 flowchart TD
   subgraph sg0["Main Function"]
-    direction LR
-    n0["Initialization"]
-    n8["Final Processing"]
-    n9["Return Result"]
-    subgraph sg1["Processing Loop"]
-      direction LR
-      subgraph sg2["Markdown Header tester"]
-        direction LR
-        n1["Item Analysis"]
-        n6["very useful header"]
-        n7["Result"]
-        subgraph sg3["Validation"]
-          direction LR
-          n2{"Validation"}
-          class n2 decisionNode;
-          n5["False other branch"]
-          subgraph sg4["Markdown Header tester in statement"]
-            direction LR
-            n3["another header in the statement"]
-            n4["True totally"]
+    direction TB
+    n6["Final Processing"]
+    n7["Return Result"]
+    subgraph sg1["Initialization"]
+      direction TB
+      subgraph sg2["Processing Loop"]
+        direction TB
+        subgraph sg3["Markdown Header tester"]
+          direction TB
+          n0["Item Analysis"]
+          subgraph sg4["Validation"]
+            direction TB
+            n1{"Validation"}
+            class n1 decisionNode;
+            n4["False other branch"]
+            subgraph sg5["Markdown Header tester in statement"]
+              direction TB
+              n2["another header in the statement"]
+              n3["True totally"]
+            end
+          end
+          subgraph sg6["very useful header"]
+            direction TB
+            n5["Result"]
           end
         end
       end
     end
   end
-  subgraph sg5["Loop Processing"]
-    direction LR
-    n12["Final Result"]
-    subgraph sg6["Main Loop"]
-      direction LR
-      n10["Item Processing"]
-      n11["Accumulation"]
+  subgraph sg7["Loop Processing"]
+    direction TB
+    n10["Final Result"]
+    subgraph sg8["Main Loop"]
+      direction TB
+      n8["Item Processing"]
+      n9["Accumulation"]
     end
   end
-  n3 --> n4
   n2 --> n3
-  n2 --> n5
   n1 --> n2
-  n6 --> n7
-  n4 --> n6
-  n5 --> n6
+  n1 --> n4
   n0 --> n1
-  n7 --> n8
+  n3 --> n5
+  n4 --> n5
+  n5 --> n6
+  n6 --> n7
+  n9 --> n10
   n8 --> n9
-  n10 --> n11
-  n11 --> n12
-%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":3,"start":4,"end_line":3,"end":22},"n1":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":13,"start":8,"end_line":13,"end":27},"n2":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":16,"start":8,"end_line":16,"end":24},"n3":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":22,"start":12,"end_line":22,"end":49},"n4":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":25,"start":12,"end_line":25,"end":30},"n5":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":28,"start":12,"end_line":28,"end":36},"n6":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":32,"start":8,"end_line":32,"end":31},"n7":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":35,"start":8,"end_line":35,"end":20},"n8":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":39,"start":4,"end_line":39,"end":24},"n9":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":42,"start":4,"end_line":42,"end":21},"n10":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":53,"start":8,"end_line":53,"end":29},"n11":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":56,"start":8,"end_line":56,"end":26},"n12":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":62,"start":4,"end_line":62,"end":21}}
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":13,"start":8,"end_line":13,"end":27},"n1":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":16,"start":8,"end_line":16,"end":24},"n2":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":22,"start":12,"end_line":22,"end":49},"n3":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":25,"start":12,"end_line":25,"end":30},"n4":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":28,"start":12,"end_line":28,"end":36},"n5":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":35,"start":8,"end_line":35,"end":20},"n6":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":39,"start":4,"end_line":39,"end":24},"n7":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":42,"start":4,"end_line":42,"end":21},"n8":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":53,"start":8,"end_line":53,"end":29},"n9":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":56,"start":8,"end_line":56,"end":26},"n10":{"file_path":"tests/validation_files/headers/complex_headers_test.baml","start_line":62,"start":4,"end_line":62,"end":21}}
   click n0 call bamlMermaidNodeClick() "Go to source"
   click n1 call bamlMermaidNodeClick() "Go to source"
   click n2 call bamlMermaidNodeClick() "Go to source"
@@ -57,6 +59,4 @@ flowchart TD
   click n7 call bamlMermaidNodeClick() "Go to source"
   click n8 call bamlMermaidNodeClick() "Go to source"
   click n9 call bamlMermaidNodeClick() "Go to source"
-  click n10 call bamlMermaidNodeClick() "Go to source"
-  click n11 call bamlMermaidNodeClick() "Go to source"
-  click n12 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
+  click n10 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/complex_workflow.mmd b/engine/baml-lib/baml/tests/validation_files/headers/complex_workflow.mmd
index 57af91f1ae..aa919fa806 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/complex_workflow.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/complex_workflow.mmd
@@ -1,10 +1,10 @@
 flowchart TD
   subgraph sg0["Main Processing Pipeline"]
-    direction LR
+    direction TB
     n2["Generate Summary"]
     n3["Create Final Output"]
     subgraph sg1["Test"]
-      direction LR
+      direction TB
       n0{"Test"}
       class n0 decisionNode;
       n1["Generate Title"]
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/duplicate_function_calls.mmd b/engine/baml-lib/baml/tests/validation_files/headers/duplicate_function_calls.mmd
index bdb5867d1c..3a039574f2 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/duplicate_function_calls.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/duplicate_function_calls.mmd
@@ -1,6 +1,6 @@
 flowchart TD
   subgraph sg0["Pipeline"]
-    direction LR
+    direction TB
     n0["Step A"]
     n1["Step B"]
     n2["Step C"]
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/edge_cases.mmd b/engine/baml-lib/baml/tests/validation_files/headers/edge_cases.mmd
index 565355e4a4..75ac7cc14a 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/edge_cases.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/edge_cases.mmd
@@ -1,125 +1,153 @@
 flowchart TD
-  n0["你好世界"]
-  n1["Émojis 🎉🎊"]
-  n2["Special «Characters» ™"]
-  n3["Level 1"]
-  n4["Level 2"]
-  n5["Level 3"]
-  n6["Level 4"]
-  n7["Level 5"]
-  n8["Level 6"]
-  n9["Level 7 - More than 6 levels"]
-  n10["Level 8"]
-  n11["Level 9"]
-  n12["Extra Spaces Before Title"]
-  n13["Mixed Tabs And Spaces"]
-  n14["All Tabs"]
-  n15["Trailing Spaces"]
-  n16["Multiple  Spaces  Between  Words"]
-  n17["Header Before Comment"]
-  n18["Header After Comment"]
-  n19["Header After Doc Comment"]
-  n26["Back to Function Level"]
-  n27["Header-With-Dashes"]
-  n28["Header_With_Underscores"]
-  n29["Header.With.Dots"]
-  n30["Header/With/Slashes"]
-  n31["Header\With\Backslashes"]
-  n32["Header|With|Pipes"]
-  n33["First Header"]
-  n34["Second Header"]
-  n45["Final Section"]
-  n46["This Is An Extremely Long Header Title That Goes On And On And On And Contains Many Words To Test How The Parser Handles Very Long Header Titles That Might Span Multiple Lines In Some Editors But Should Still Be Parsed As A Single Header"]
-  n47["Another Long Title With Special Characters !@#$%^&*()_+-=[]{}|;':",./<>? And Numbers 1234567890"]
-  n48["Just Headers"]
-  n49["No Code"]
-  n50["Only Documentation"]
-  n51["Pure Annotation"]
-  n52["No Expressions"]
-  n53["Until The End"]
-  subgraph sg0["Main Function"]
-    direction LR
-    n25["Back to Block Level"]
-    subgraph sg1["Inside Block Expression"]
-      direction LR
-      n20{"Inside Block Expression"}
-      class n20 decisionNode;
-      n21["Inside If True"]
-      n22["Very Deep Block"]
-      n23["Back to If Level"]
-      n24["Inside Else"]
+  n10["Back to Function Level"]
+  n12["First Header"]
+  n13["Second Header"]
+  n23["Final Section"]
+  subgraph sg0["你好世界"]
+    direction TB
+    subgraph sg1["Émojis 🎉🎊"]
+      direction TB
+      n0["Special «Characters» ™"]
     end
   end
-  subgraph sg2["Third Header"]
-    direction LR
-    n35["Subsection A"]
-    n36["Subsection B"]
-    n37["Subsection C"]
+  subgraph sg2["Level 1"]
+    direction TB
+    subgraph sg3["Level 2"]
+      direction TB
+      subgraph sg4["Level 3"]
+        direction TB
+        subgraph sg5["Level 4"]
+          direction TB
+          subgraph sg6["Level 5"]
+            direction TB
+            subgraph sg7["Level 6"]
+              direction TB
+              subgraph sg8["Level 7 - More than 6 levels"]
+                direction TB
+                subgraph sg9["Level 8"]
+                  direction TB
+                  n1["Level 9"]
+                end
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+  subgraph sg10["Extra Spaces Before Title"]
+    direction TB
+    subgraph sg11["Mixed Tabs And Spaces"]
+      direction TB
+      n2["All Tabs"]
+    end
+  end
+  subgraph sg12["Trailing Spaces"]
+    direction TB
+    n3["Multiple  Spaces  Between  Words"]
   end
-  subgraph sg3["Before Let"]
-    direction LR
-    n42["Before For"]
-    n43["Inside For"]
-    n44["After For"]
-    subgraph sg4["After Let"]
-      direction LR
-      n41["After If"]
-      subgraph sg5["Before If"]
-        direction LR
-        n38{"Before If"}
-        class n38 decisionNode;
-        n39["Inside If"]
-        n40["Inside Else"]
+  subgraph sg13["Header Before Comment"]
+    direction TB
+    subgraph sg14["Header After Comment"]
+      direction TB
+      n4["Header After Doc Comment"]
+    end
+  end
+  subgraph sg15["Main Function"]
+    direction TB
+    n9["Back to Block Level"]
+    subgraph sg16["Inside Block Expression"]
+      direction TB
+      n5{"Inside Block Expression"}
+      class n5 decisionNode;
+      n7["Back to If Level"]
+      n8["Inside Else"]
+      subgraph sg17["Inside If True"]
+        direction TB
+        n6["Very Deep Block"]
+      end
+    end
+  end
+  subgraph sg18["Header-With-Dashes"]
+    direction TB
+    subgraph sg19["Header_With_Underscores"]
+      direction TB
+      subgraph sg20["Header.With.Dots"]
+        direction TB
+        subgraph sg21["Header/With/Slashes"]
+          direction TB
+          subgraph sg22["Header\With\Backslashes"]
+            direction TB
+            n11["Header|With|Pipes"]
+          end
+        end
+      end
+    end
+  end
+  subgraph sg23["Third Header"]
+    direction TB
+    n14["Subsection A"]
+    n15["Subsection B"]
+    n16["Subsection C"]
+  end
+  subgraph sg24["Before Let"]
+    direction TB
+    n22["After For"]
+    subgraph sg25["After Let"]
+      direction TB
+      n20["After If"]
+      subgraph sg26["Before If"]
+        direction TB
+        n17{"Before If"}
+        class n17 decisionNode;
+        n18["Inside If"]
+        n19["Inside Else"]
+      end
+    end
+    subgraph sg27["Before For"]
+      direction TB
+      n21["Inside For"]
+    end
+  end
+  subgraph sg28["This Is An Extremely Long Header Title That Goes On And On And On And Contains Many Words To Test How The Parser Handles Very Long Header Titles That Might Span Multiple Lines In Some Editors But Should Still Be Parsed As A Single Header"]
+    direction TB
+    n24["Another Long Title With Special Characters !@#$%^&*()_+-=[]{}|;':",./<>? And Numbers 1234567890"]
+  end
+  subgraph sg29["Just Headers"]
+    direction TB
+    subgraph sg30["No Code"]
+      direction TB
+      subgraph sg31["Only Documentation"]
+        direction TB
+        subgraph sg32["Pure Annotation"]
+          direction TB
+          subgraph sg33["No Expressions"]
+            direction TB
+            n25["Until The End"]
+          end
+        end
       end
     end
   end
-  n1 --> n2
-  n0 --> n1
-  n10 --> n11
-  n9 --> n10
-  n8 --> n9
-  n7 --> n8
-  n6 --> n7
   n5 --> n6
-  n4 --> n5
-  n3 --> n4
-  n13 --> n14
-  n12 --> n13
-  n15 --> n16
+  n5 --> n8
   n14 --> n15
-  n18 --> n19
+  n15 --> n16
   n17 --> n18
+  n17 --> n19
+  n18 --> n20
+  n19 --> n20
+  n20 --> n21
   n21 --> n22
+  n2 --> n3
+  n9 --> n10
+  n6 --> n9
+  n8 --> n9
+  n6 --> n7
+  n12 --> n13
+  n13 --> n14
   n22 --> n23
-  n20 --> n21
-  n20 --> n24
-  n22 --> n25
-  n24 --> n25
-  n25 --> n26
-  n31 --> n32
-  n30 --> n31
-  n29 --> n30
-  n28 --> n29
-  n27 --> n28
-  n33 --> n34
-  n35 --> n36
-  n36 --> n37
-  n34 --> n35
-  n38 --> n39
-  n38 --> n40
-  n39 --> n41
-  n40 --> n41
-  n42 --> n43
-  n41 --> n42
-  n43 --> n44
-  n44 --> n45
-  n46 --> n47
-  n52 --> n53
-  n51 --> n52
-  n50 --> n51
-  n49 --> n50
-  n48 --> n49
-%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":3,"start":4,"end_line":3,"end":12},"n1":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":6,"start":4,"end_line":6,"end":18},"n2":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":9,"start":4,"end_line":9,"end":32},"n3":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":15,"start":4,"end_line":15,"end":15},"n4":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":16,"start":0,"end_line":16,"end":16},"n5":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":17,"start":0,"end_line":17,"end":17},"n6":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":18,"start":0,"end_line":18,"end":18},"n7":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":19,"start":0,"end_line":19,"end":19},"n8":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":20,"start":0,"end_line":20,"end":20},"n9":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":21,"start":0,"end_line":21,"end":42},"n10":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":22,"start":0,"end_line":22,"end":22},"n11":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":23,"start":0,"end_line":23,"end":23},"n12":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":29,"start":4,"end_line":29,"end":36},"n13":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":30,"start":0,"end_line":30,"end":32},"n14":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":31,"start":0,"end_line":31,"end":20},"n15":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":35,"start":4,"end_line":35,"end":23},"n16":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":36,"start":0,"end_line":36,"end":41},"n17":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":42,"start":4,"end_line":42,"end":29},"n18":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":48,"start":0,"end_line":48,"end":29},"n19":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":51,"start":0,"end_line":51,"end":34},"n20":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":60,"start":8,"end_line":60,"end":36},"n21":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":63,"start":12,"end_line":63,"end":32},"n22":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":66,"start":16,"end_line":66,"end":38},"n23":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":70,"start":12,"end_line":70,"end":34},"n24":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":73,"start":12,"end_line":73,"end":29},"n25":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":77,"start":8,"end_line":77,"end":32},"n26":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":81,"start":4,"end_line":81,"end":30},"n27":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":86,"start":4,"end_line":86,"end":26},"n28":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":87,"start":0,"end_line":87,"end":32},"n29":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":88,"start":0,"end_line":88,"end":26},"n30":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":89,"start":0,"end_line":89,"end":30},"n31":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":90,"start":0,"end_line":90,"end":35},"n32":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":91,"start":0,"end_line":91,"end":30},"n33":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":97,"start":4,"end_line":97,"end":20},"n34":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":98,"start":0,"end_line":98,"end":21},"n35":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":101,"start":4,"end_line":101,"end":21},"n36":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":102,"start":0,"end_line":102,"end":21},"n37":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":103,"start":0,"end_line":103,"end":21},"n38":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":113,"start":4,"end_line":113,"end":19},"n39":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":115,"start":8,"end_line":115,"end":24},"n40":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":118,"start":8,"end_line":118,"end":26},"n41":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":121,"start":4,"end_line":121,"end":18},"n42":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":123,"start":4,"end_line":123,"end":19},"n43":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":125,"start":8,"end_line":125,"end":24},"n44":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":128,"start":4,"end_line":128,"end":18},"n45":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":130,"start":4,"end_line":130,"end":21},"n46":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":135,"start":4,"end_line":135,"end":245},"n47":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":137,"start":4,"end_line":137,"end":104},"n48":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":143,"start":4,"end_line":143,"end":20},"n49":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":144,"start":0,"end_line":144,"end":16},"n50":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":145,"start":0,"end_line":145,"end":28},"n51":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":146,"start":0,"end_line":146,"end":26},"n52":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":147,"start":0,"end_line":147,"end":26},"n53":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":148,"start":0,"end_line":148,"end":26}}
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":9,"start":4,"end_line":9,"end":32},"n1":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":23,"start":0,"end_line":23,"end":23},"n2":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":31,"start":0,"end_line":31,"end":20},"n3":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":36,"start":0,"end_line":36,"end":41},"n4":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":51,"start":0,"end_line":51,"end":34},"n5":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":60,"start":8,"end_line":60,"end":36},"n6":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":66,"start":16,"end_line":66,"end":38},"n7":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":70,"start":12,"end_line":70,"end":34},"n8":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":73,"start":12,"end_line":73,"end":29},"n9":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":77,"start":8,"end_line":77,"end":32},"n10":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":81,"start":4,"end_line":81,"end":30},"n11":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":91,"start":0,"end_line":91,"end":30},"n12":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":97,"start":4,"end_line":97,"end":20},"n13":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":98,"start":0,"end_line":98,"end":21},"n14":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":101,"start":4,"end_line":101,"end":21},"n15":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":102,"start":0,"end_line":102,"end":21},"n16":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":103,"start":0,"end_line":103,"end":21},"n17":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":113,"start":4,"end_line":113,"end":19},"n18":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":115,"start":8,"end_line":115,"end":24},"n19":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":118,"start":8,"end_line":118,"end":26},"n20":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":121,"start":4,"end_line":121,"end":18},"n21":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":125,"start":8,"end_line":125,"end":24},"n22":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":128,"start":4,"end_line":128,"end":18},"n23":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":130,"start":4,"end_line":130,"end":21},"n24":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":137,"start":4,"end_line":137,"end":104},"n25":{"file_path":"tests/validation_files/headers/edge_cases.baml","start_line":148,"start":0,"end_line":148,"end":26}}
   click n0 call bamlMermaidNodeClick() "Go to source"
   click n1 call bamlMermaidNodeClick() "Go to source"
   click n2 call bamlMermaidNodeClick() "Go to source"
@@ -145,32 +173,4 @@ flowchart TD
   click n22 call bamlMermaidNodeClick() "Go to source"
   click n23 call bamlMermaidNodeClick() "Go to source"
   click n24 call bamlMermaidNodeClick() "Go to source"
-  click n25 call bamlMermaidNodeClick() "Go to source"
-  click n26 call bamlMermaidNodeClick() "Go to source"
-  click n27 call bamlMermaidNodeClick() "Go to source"
-  click n28 call bamlMermaidNodeClick() "Go to source"
-  click n29 call bamlMermaidNodeClick() "Go to source"
-  click n30 call bamlMermaidNodeClick() "Go to source"
-  click n31 call bamlMermaidNodeClick() "Go to source"
-  click n32 call bamlMermaidNodeClick() "Go to source"
-  click n33 call bamlMermaidNodeClick() "Go to source"
-  click n34 call bamlMermaidNodeClick() "Go to source"
-  click n35 call bamlMermaidNodeClick() "Go to source"
-  click n36 call bamlMermaidNodeClick() "Go to source"
-  click n37 call bamlMermaidNodeClick() "Go to source"
-  click n38 call bamlMermaidNodeClick() "Go to source"
-  click n39 call bamlMermaidNodeClick() "Go to source"
-  click n40 call bamlMermaidNodeClick() "Go to source"
-  click n41 call bamlMermaidNodeClick() "Go to source"
-  click n42 call bamlMermaidNodeClick() "Go to source"
-  click n43 call bamlMermaidNodeClick() "Go to source"
-  click n44 call bamlMermaidNodeClick() "Go to source"
-  click n45 call bamlMermaidNodeClick() "Go to source"
-  click n46 call bamlMermaidNodeClick() "Go to source"
-  click n47 call bamlMermaidNodeClick() "Go to source"
-  click n48 call bamlMermaidNodeClick() "Go to source"
-  click n49 call bamlMermaidNodeClick() "Go to source"
-  click n50 call bamlMermaidNodeClick() "Go to source"
-  click n51 call bamlMermaidNodeClick() "Go to source"
-  click n52 call bamlMermaidNodeClick() "Go to source"
-  click n53 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
+  click n25 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/for_loop_mermaid.mmd b/engine/baml-lib/baml/tests/validation_files/headers/for_loop_mermaid.mmd
index 8bf5c12dd1..40aa24b608 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/for_loop_mermaid.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/for_loop_mermaid.mmd
@@ -1,15 +1,15 @@
 flowchart TD
   subgraph sg0["Loop Processing"]
-    direction LR
+    direction TB
     n2["Final Result"]
     subgraph sg1["Main Loop"]
-      direction LR
+      direction TB
       n0["Item Processing"]
       n1["Accumulation"]
     end
   end
-  n0 --> n1
   n1 --> n2
+  n0 --> n1
 %%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/for_loop_mermaid.baml","start_line":7,"start":8,"end_line":7,"end":29},"n1":{"file_path":"tests/validation_files/headers/for_loop_mermaid.baml","start_line":10,"start":8,"end_line":10,"end":26},"n2":{"file_path":"tests/validation_files/headers/for_loop_mermaid.baml","start_line":16,"start":4,"end_line":16,"end":21}}
   click n0 call bamlMermaidNodeClick() "Go to source"
   click n1 call bamlMermaidNodeClick() "Go to source"
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/for_loops.mmd b/engine/baml-lib/baml/tests/validation_files/headers/for_loops.mmd
index ce4dd4878d..61cd7daaaf 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/for_loops.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/for_loops.mmd
@@ -1,16 +1,16 @@
 flowchart TD
   subgraph sg0["Loop Processing"]
-    direction LR
-    n2["Final Result"]
+    direction TB
+    n1["Final Result"]
     subgraph sg1["Main Loop"]
-      direction LR
-      n0["Item Processing"]
-      n1["Accumulation"]
+      direction TB
+      subgraph sg2["Item Processing"]
+        direction TB
+        n0["Accumulation"]
+      end
     end
   end
   n0 --> n1
-  n1 --> n2
-%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/for_loops.baml","start_line":7,"start":8,"end_line":7,"end":29},"n1":{"file_path":"tests/validation_files/headers/for_loops.baml","start_line":10,"start":8,"end_line":10,"end":27},"n2":{"file_path":"tests/validation_files/headers/for_loops.baml","start_line":16,"start":4,"end_line":16,"end":21}}
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/for_loops.baml","start_line":10,"start":8,"end_line":10,"end":27},"n1":{"file_path":"tests/validation_files/headers/for_loops.baml","start_line":16,"start":4,"end_line":16,"end":21}}
   click n0 call bamlMermaidNodeClick() "Go to source"
-  click n1 call bamlMermaidNodeClick() "Go to source"
-  click n2 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
+  click n1 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.baml b/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.baml
new file mode 100644
index 0000000000..9b1ca2dd43
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.baml
@@ -0,0 +1,48 @@
+//# Case 1
+function IfThenElse() -> int {
+    let x = 1;
+
+    //# Before 
+
+    //# If should always do else
+    if (false) { 
+        //## impossible: return 1
+        1
+    } else {
+        //## should always be 5
+        5
+    }
+
+    //# After
+    y
+}
+
+
+//# Main Function
+function ComplexHeaderTest(x: int) -> int {
+    let z = 5;
+    //# oopsie
+
+    //# standalone will render
+
+    //# will not render
+    let k = [1, 2, 3];
+    //# will not render
+    5;
+    //# will not render
+    true;
+    //# will not render
+    k[0];
+
+    //# increment maybe?
+    if (x < 10) {
+        //## increment x
+        x = x + 10;
+    } else {
+        //## dummy variable y
+        let y = 5;
+    }
+    
+    //# another increm
+    x + 1234
+}
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.mmd b/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.mmd
new file mode 100644
index 0000000000..eeb36e8776
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/if_then_else.mmd
@@ -0,0 +1,42 @@
+flowchart TD
+  subgraph sg0["Case 1"]
+    direction TB
+    n0["Before"]
+    n4["After"]
+    subgraph sg1["If should always do else"]
+      direction TB
+      n1{"If should always do else"}
+      class n1 decisionNode;
+      n2["impossible: return 1"]
+      n3["should always be 5"]
+    end
+  end
+  subgraph sg2["Main Function"]
+    direction TB
+    n5["oopsie"]
+    n8["another increm"]
+    subgraph sg3["increment maybe?"]
+      direction TB
+      n6{"increment maybe?"}
+      class n6 decisionNode;
+      n7["dummy variable y"]
+    end
+  end
+  n1 --> n2
+  n1 --> n3
+  n6 --> n7
+  n0 --> n1
+  n2 --> n4
+  n3 --> n4
+  n5 --> n6
+  n7 --> n8
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":4,"start":4,"end_line":4,"end":15},"n1":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":6,"start":4,"end_line":6,"end":32},"n2":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":8,"start":8,"end_line":8,"end":33},"n3":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":11,"start":8,"end_line":11,"end":31},"n4":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":15,"start":4,"end_line":15,"end":13},"n5":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":23,"start":4,"end_line":23,"end":14},"n6":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":25,"start":4,"end_line":25,"end":24},"n7":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":30,"start":8,"end_line":30,"end":29},"n8":{"file_path":"tests/validation_files/headers/if_then_else.baml","start_line":34,"start":4,"end_line":34,"end":22}}
+  click n0 call bamlMermaidNodeClick() "Go to source"
+  click n1 call bamlMermaidNodeClick() "Go to source"
+  click n2 call bamlMermaidNodeClick() "Go to source"
+  click n3 call bamlMermaidNodeClick() "Go to source"
+  click n4 call bamlMermaidNodeClick() "Go to source"
+  click n5 call bamlMermaidNodeClick() "Go to source"
+  click n6 call bamlMermaidNodeClick() "Go to source"
+  click n7 call bamlMermaidNodeClick() "Go to source"
+  click n8 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.baml b/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.baml
index 27b717edde..5078efd333 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.baml
+++ b/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.baml
@@ -1,9 +1,47 @@
+//# Case 1
 function IfThenOnlyHeader() -> int {
     let x = 1;
 
-    //# Start
+    //# Before 
+
+    //# If
     let y = if (false) { 1 };
 
     //## After
     y
 }
+
+//# Case 2
+function IfThenOnlyHeader2() -> int {
+    let x = 1;
+    //## Before
+
+    let z = 5;
+
+    //### If
+    let y = if (false) { 
+        //## Then
+        1
+    };
+
+    //## After
+    y
+}
+
+
+//# Case 3
+function IfThenOnlyHeader2() -> int {
+    let x = 1;
+    //### Before
+
+    let z = 5;
+
+    //# If
+    let y = if (false) { 
+        //## Then
+        1
+    };
+
+    //## After
+    y
+}
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.mmd b/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.mmd
index cd3d4363a1..0b79d72d02 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.mmd
+++ b/engine/baml-lib/baml/tests/validation_files/headers/if_then_only_header.mmd
@@ -1,11 +1,53 @@
 flowchart TD
-  subgraph sg0["Start"]
-    direction LR
-    n0{"Start"}
-    class n0 decisionNode;
-    n1["After"]
+  subgraph sg0["Case 1"]
+    direction TB
+    n0["Before"]
+    subgraph sg1["If"]
+      direction TB
+      n1{"If"}
+      class n1 decisionNode;
+      n2["After"]
+    end
   end
+  subgraph sg2["Case 2"]
+    direction TB
+    n5["After"]
+    subgraph sg3["Before"]
+      direction TB
+      subgraph sg4["If"]
+        direction TB
+        n3{"If"}
+        class n3 decisionNode;
+        n4["Then"]
+      end
+    end
+  end
+  subgraph sg5["Case 3"]
+    direction TB
+    n6["Before"]
+    subgraph sg6["If"]
+      direction TB
+      n7{"If"}
+      class n7 decisionNode;
+      n8["Then"]
+      n9["After"]
+    end
+  end
+  n1 --> n2
+  n3 --> n4
+  n7 --> n8
+  n8 --> n9
   n0 --> n1
-%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":3,"start":4,"end_line":3,"end":13},"n1":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":6,"start":4,"end_line":6,"end":14}}
+  n4 --> n5
+  n6 --> n7
+%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":4,"start":4,"end_line":4,"end":15},"n1":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":6,"start":4,"end_line":6,"end":10},"n2":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":9,"start":4,"end_line":9,"end":14},"n3":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":20,"start":4,"end_line":20,"end":12},"n4":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":22,"start":8,"end_line":22,"end":17},"n5":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":26,"start":4,"end_line":26,"end":14},"n6":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":34,"start":4,"end_line":34,"end":16},"n7":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":38,"start":4,"end_line":38,"end":10},"n8":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":40,"start":8,"end_line":40,"end":17},"n9":{"file_path":"tests/validation_files/headers/if_then_only_header.baml","start_line":44,"start":4,"end_line":44,"end":14}}
   click n0 call bamlMermaidNodeClick() "Go to source"
-  click n1 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
+  click n1 call bamlMermaidNodeClick() "Go to source"
+  click n2 call bamlMermaidNodeClick() "Go to source"
+  click n3 call bamlMermaidNodeClick() "Go to source"
+  click n4 call bamlMermaidNodeClick() "Go to source"
+  click n5 call bamlMermaidNodeClick() "Go to source"
+  click n6 call bamlMermaidNodeClick() "Go to source"
+  click n7 call bamlMermaidNodeClick() "Go to source"
+  click n8 call bamlMermaidNodeClick() "Go to source"
+  click n9 call bamlMermaidNodeClick() "Go to source"
\ No newline at end of file
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/invalid.err b/engine/baml-lib/baml/tests/validation_files/headers/invalid.err
index d023853109..8ad8570452 100644
--- a/engine/baml-lib/baml/tests/validation_files/headers/invalid.err
+++ b/engine/baml-lib/baml/tests/validation_files/headers/invalid.err
@@ -1,64 +1,64 @@
-error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
-  -->  tests/validation_files/headers/invalid.baml:12
-   | 
-11 | function MixedOnSameLine() -> string {
-12 |     let x = "hello" ## Inline Header // Should fail
-13 |     x
-   | 
-error: Error validating Function "MixedOnSameLine": This field declaration is invalid. It is either missing a name or a type.
-  -->  tests/validation_files/headers/invalid.baml:13
-   | 
-12 |     let x = "hello" ## Inline Header // Should fail
-13 |     x
-   | 
-error: Statement must end with a semicolon.
-  -->  tests/validation_files/headers/invalid.baml:18
-   | 
-17 |     //#No Space After Hash // Should potentially fail depending on grammar rules
-18 |     let x = "test"
-19 |     x
-   | 
-error: Statement must end with a semicolon.
-  -->  tests/validation_files/headers/invalid.baml:25
-   | 
-24 |     // Empty header title - this should fail
-25 |     let x = "test"
-26 |     //##
-   | 
-error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
-  -->  tests/validation_files/headers/invalid.baml:33
-   | 
-32 | function HeaderInExpression() -> string {
-33 |     let x = "start" + # Middle Header // Should fail
-34 |         "end"
-   | 
-error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
-  -->  tests/validation_files/headers/invalid.baml:34
-   | 
-33 |     let x = "start" + # Middle Header // Should fail
-34 |         "end"
-35 |     x
-   | 
-error: Error validating Function "HeaderInExpression": This field declaration is invalid. It is either missing a name or a type.
-  -->  tests/validation_files/headers/invalid.baml:35
-   | 
-34 |         "end"
-35 |     x
-   | 
-error: Statement must end with a semicolon.
-  -->  tests/validation_files/headers/invalid.baml:39
-   | 
-38 | function HeaderInStringLiteral() -> string {
-39 |     let x = "This string contains # Header Text"
-40 |     let y = #"
-   | 
-error: Statement must end with a semicolon.
-  -->  tests/validation_files/headers/invalid.baml:40
-   | 
-39 |     let x = "This string contains # Header Text"
-40 |     let y = #"
-41 |         Raw string with # Header
-42 |         Should not be parsed
-43 |     "#
-44 |     x + y
-   | 
+error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
+  -->  tests/validation_files/headers/invalid.baml:12
+   | 
+11 | function MixedOnSameLine() -> string {
+12 |     let x = "hello" ## Inline Header // Should fail
+13 |     x
+   | 
+error: Error validating Function "MixedOnSameLine": This field declaration is invalid. It is either missing a name or a type.
+  -->  tests/validation_files/headers/invalid.baml:13
+   | 
+12 |     let x = "hello" ## Inline Header // Should fail
+13 |     x
+   | 
+error: Statement must end with a semicolon.
+  -->  tests/validation_files/headers/invalid.baml:18
+   | 
+17 |     //#No Space After Hash // Should potentially fail depending on grammar rules
+18 |     let x = "test"
+19 |     x
+   | 
+error: Statement must end with a semicolon.
+  -->  tests/validation_files/headers/invalid.baml:25
+   | 
+24 |     // Empty header title - this should fail
+25 |     let x = "test"
+26 |     //##
+   | 
+error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
+  -->  tests/validation_files/headers/invalid.baml:33
+   | 
+32 | function HeaderInExpression() -> string {
+33 |     let x = "start" + # Middle Header // Should fail
+34 |         "end"
+   | 
+error: Error validating: This line is not a valid field or attribute definition. A valid property may look like: 'myProperty "some value"' for example, with no colons.
+  -->  tests/validation_files/headers/invalid.baml:34
+   | 
+33 |     let x = "start" + # Middle Header // Should fail
+34 |         "end"
+35 |     x
+   | 
+error: Error validating Function "HeaderInExpression": This field declaration is invalid. It is either missing a name or a type.
+  -->  tests/validation_files/headers/invalid.baml:35
+   | 
+34 |         "end"
+35 |     x
+   | 
+error: Statement must end with a semicolon.
+  -->  tests/validation_files/headers/invalid.baml:39
+   | 
+38 | function HeaderInStringLiteral() -> string {
+39 |     let x = "This string contains # Header Text"
+40 |     let y = #"
+   | 
+error: Statement must end with a semicolon.
+  -->  tests/validation_files/headers/invalid.baml:40
+   | 
+39 |     let x = "This string contains # Header Text"
+40 |     let y = #"
+41 |         Raw string with # Header
+42 |         Should not be parsed
+43 |     "#
+44 |     x + y
+   | 
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/.gitignore b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/.gitignore
new file mode 100644
index 0000000000..5ef6a52078
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/README.md b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/README.md
new file mode 100644
index 0000000000..e215bc4ccf
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/api/watch/route.ts b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/api/watch/route.ts
new file mode 100644
index 0000000000..edcc7dbc91
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/api/watch/route.ts
@@ -0,0 +1,50 @@
+import fs from "node:fs";
+import path from "node:path";
+
+const headersDir = path.resolve(process.cwd(), "..");
+const watchedExtensions = new Set([".mmd", ".baml"]);
+
+export const runtime = "nodejs";
+
+export async function GET() {
+  const encoder = new TextEncoder();
+
+  const stream = new ReadableStream({
+    start(controller) {
+      const send = (payload: string) => {
+        controller.enqueue(encoder.encode(`data: ${payload}\n\n`));
+      };
+
+      send("ready");
+
+      const watcher = fs.watch(headersDir, (eventType, filename) => {
+        if (!filename) {
+          return;
+        }
+
+        const ext = path.extname(filename);
+        if (!watchedExtensions.has(ext)) {
+          return;
+        }
+
+        send(`${eventType}:${filename}`);
+      });
+
+      watcher.on("error", (error) => {
+        send(`error:${error.message}`);
+      });
+
+      return () => {
+        watcher.close();
+      };
+    },
+  });
+
+  return new Response(stream, {
+    headers: {
+      "Content-Type": "text/event-stream",
+      "Cache-Control": "no-cache, no-transform",
+      Connection: "keep-alive",
+    },
+  });
+}
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/HeaderFileWatcher.tsx b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/HeaderFileWatcher.tsx
new file mode 100644
index 0000000000..0585b172dd
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/HeaderFileWatcher.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+import { useRouter } from "next/navigation";
+import { useEffect, useRef } from "react";
+
+const WATCH_ENDPOINT = "/api/watch";
+
+export default function HeaderFileWatcher() {
+  const router = useRouter();
+  const reconnectTimeout = useRef | null>(null);
+
+  useEffect(() => {
+    let isMounted = true;
+    let eventSource: EventSource | null = null;
+
+    const setup = () => {
+      if (!isMounted) {
+        return;
+      }
+
+      eventSource = new EventSource(WATCH_ENDPOINT);
+
+      eventSource.onmessage = (event) => {
+        if (event.data === "ready") {
+          return;
+        }
+        router.refresh();
+      };
+
+      eventSource.onerror = () => {
+        eventSource?.close();
+        if (!isMounted) {
+          return;
+        }
+        if (reconnectTimeout.current) {
+          clearTimeout(reconnectTimeout.current);
+        }
+        reconnectTimeout.current = setTimeout(setup, 1000);
+      };
+    };
+
+    setup();
+
+    return () => {
+      isMounted = false;
+      eventSource?.close();
+      if (reconnectTimeout.current) {
+        clearTimeout(reconnectTimeout.current);
+        reconnectTimeout.current = null;
+      }
+    };
+  }, [router]);
+
+  return null;
+}
diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/MermaidDiagram.tsx b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/MermaidDiagram.tsx
new file mode 100644
index 0000000000..2e822247dd
--- /dev/null
+++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/components/MermaidDiagram.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import mermaid from "mermaid";
+import { useEffect, useRef, useState } from "react";
+
+type MermaidDiagramProps = {
+  chart: string;
+  className?: string;
+};
+
+let initialized = false;
+
+const escapeHtml = (input: string) =>
+  input
+    .replaceAll("&", "&")
+    .replaceAll("<", "<")
+    .replaceAll(">", ">")
+    .replaceAll('"', """)
+    .replaceAll("'", "'");
+
+const sanitizeSvgForError = (error: unknown) => {
+  if (error instanceof Error) {
+    return escapeHtml(error.message);
+  }
+  return escapeHtml(String(error));
+};
+
+export default function MermaidDiagram({ chart, className }: MermaidDiagramProps) {
+  const containerRef = useRef(null);
+  const [svg, setSvg] = useState("");
+  const idRef = useRef();
+
+  if (!idRef.current) {
+    idRef.current = `mermaid-${Math.random().toString(36).slice(2, 11)}`;
+  }
+
+  useEffect(() => {
+    if (!initialized) {
+      mermaid.initialize({ startOnLoad: false });
+      initialized = true;
+    }
+
+    let cancelled = false;
+
+    const render = async () => {
+      try {
+        const { svg: renderedSvg } = await mermaid.render(idRef.current!, chart);
+        if (!cancelled) {
+          setSvg(renderedSvg);
+        }
+      } catch (error) {
+        if (!cancelled) {
+          const safeError = sanitizeSvgForError(error);
+          setSvg(`
${safeError}
`); + } + console.error("Failed to render mermaid diagram", error); + } + }; + + render(); + + return () => { + cancelled = true; + setSvg(""); + if (containerRef.current) { + containerRef.current.innerHTML = ""; + } + }; + }, [chart]); + + return ( +
+ ); +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/favicon.ico b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/favicon.ico new file mode 100644 index 0000000000..718d6fea48 Binary files /dev/null and b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/favicon.ico differ diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/globals.css b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/globals.css new file mode 100644 index 0000000000..a2dc41ecee --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/layout.tsx b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/layout.tsx new file mode 100644 index 0000000000..bd7b36ba2c --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/layout.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + ); +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/page.tsx b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/page.tsx new file mode 100644 index 0000000000..f35222da1a --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/app/page.tsx @@ -0,0 +1,95 @@ +import path from "node:path"; +import fs from "node:fs/promises"; + +import HeaderFileWatcher from "./components/HeaderFileWatcher"; +import MermaidDiagram from "./components/MermaidDiagram"; + +type HeaderExample = { + baseName: string; + mmdFileName: string; + mmdContents: string; + bamlFileName: string | null; + bamlContents: string | null; +}; + +const headersDir = path.resolve(process.cwd(), ".."); + +async function loadHeaderExamples(): Promise { + const entries = await fs.readdir(headersDir, { withFileTypes: true }); + const mmdFiles = entries + .filter((entry) => entry.isFile() && entry.name.endsWith(".mmd")) + .map((entry) => entry.name) + .sort((a, b) => a.localeCompare(b)); + + const examples: HeaderExample[] = []; + + for (const mmdFile of mmdFiles) { + const baseName = mmdFile.replace(/\.mmd$/, ""); + const mmdPath = path.join(headersDir, mmdFile); + const mmdContents = await fs.readFile(mmdPath, "utf8"); + + const bamlFile = `${baseName}.baml`; + const bamlPath = path.join(headersDir, bamlFile); + + let bamlContents: string | null = null; + let bamlFileName: string | null = null; + + try { + bamlContents = await fs.readFile(bamlPath, "utf8"); + bamlFileName = bamlFile; + } catch (error: unknown) { + const err = error as NodeJS.ErrnoException; + if (err?.code !== "ENOENT") { + throw error; + } + } + + examples.push({ + baseName, + mmdFileName: mmdFile, + mmdContents, + bamlFileName, + bamlContents, + }); + } + + return examples; +} + +export default async function Home() { + const files = await loadHeaderExamples(); + + return ( +
+ +

Mermaid Headers Playground

+ + {files.length === 0 ? ( +

No Mermaid files found in the headers directory.

+ ) : ( +
+ {files.map((file) => ( +
+
+

{file.baseName}

+

+ {file.bamlFileName ?? "(missing .baml)"} · {file.mmdFileName} +

+
+
+
+                  
+                    {file.bamlContents ?? "// No matching .baml file found for this diagram."}
+                  
+                
+
+ +
+
+
+ ))} +
+ )} +
+ ); +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/next.config.ts b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/next.config.ts new file mode 100644 index 0000000000..dbb1137166 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/next.config.ts @@ -0,0 +1,61 @@ +import type { NextConfig } from "next"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const headersDir = path.resolve(__dirname, ".."); + +const watchedExtensions = new Set([".mmd", ".baml"]); + +const collectWatchedFiles = (dir: string): string[] => { + const files: string[] = []; + if (!fs.existsSync(dir)) { + return files; + } + + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === "mermaid-sandbox") { + continue; + } + + const fullPath = path.join(dir, entry.name); + if (entry.isFile() && watchedExtensions.has(path.extname(entry.name))) { + files.push(fullPath); + } + } + + return files; +}; + +class WatchHeaderFilesPlugin { + private directory: string; + + constructor(directory: string) { + this.directory = directory; + } + + apply(compiler: any) { + compiler.hooks.afterCompile.tap("WatchHeaderFilesPlugin", (compilation) => { + compilation.contextDependencies.add(this.directory); + + for (const file of collectWatchedFiles(this.directory)) { + compilation.fileDependencies.add(file); + } + }); + } +} + +const nextConfig: NextConfig = { + webpack(config, { dev }) { + if (dev) { + config.plugins = config.plugins ?? []; + config.plugins.push(new WatchHeaderFilesPlugin(headersDir)); + } + + return config; + }, +}; + +export default nextConfig; diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/package.json b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/package.json new file mode 100644 index 0000000000..17f50deadb --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/package.json @@ -0,0 +1,24 @@ +{ + "name": "mermaid-sandbox", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "react": "19.1.0", + "react-dom": "19.1.0", + "next": "15.5.5", + "mermaid": "^11.4.1" + }, + "devDependencies": { + "typescript": "^5", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "@tailwindcss/postcss": "^4", + "tailwindcss": "^4" + } +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/postcss.config.mjs b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/postcss.config.mjs new file mode 100644 index 0000000000..c7bcb4b1ee --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/file.svg b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/file.svg new file mode 100644 index 0000000000..004145cddf --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/globe.svg b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/globe.svg new file mode 100644 index 0000000000..567f17b0d7 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/next.svg b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/next.svg new file mode 100644 index 0000000000..5174b28c56 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/vercel.svg b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/vercel.svg new file mode 100644 index 0000000000..7705396033 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/window.svg b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/window.svg new file mode 100644 index 0000000000..b2b2a44f6e --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/tsconfig.json b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/tsconfig.json new file mode 100644 index 0000000000..d8b93235f2 --- /dev/null +++ b/engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.baml b/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.baml index c912d03d94..cb4f36bdc7 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.baml +++ b/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.baml @@ -3,8 +3,7 @@ function MixedChildrenNotFlattened() -> string { //## MD Child let items = [42]; for (let item in items) { - //### Loop Child - //### And stuff + //# Loop Child item } "done" diff --git a/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.mmd b/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.mmd index 2f7e5c8695..8a48e4d44e 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/mixed_children_not_flattened.mmd @@ -1,13 +1,10 @@ flowchart TD - n0["Parent"] - subgraph sg0["MD Child"] - direction LR - n1["Loop Child"] - n2["And stuff"] + subgraph sg0["Parent"] + direction TB + subgraph sg1["MD Child"] + direction TB + n0["Loop Child"] + end end - n1 --> n2 - n0 --> n1 -%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/mixed_children_not_flattened.baml","start_line":0,"start":0,"end_line":0,"end":10},"n1":{"file_path":"tests/validation_files/headers/mixed_children_not_flattened.baml","start_line":5,"start":8,"end_line":5,"end":24},"n2":{"file_path":"tests/validation_files/headers/mixed_children_not_flattened.baml","start_line":6,"start":0,"end_line":6,"end":23}} - click n0 call bamlMermaidNodeClick() "Go to source" - click n1 call bamlMermaidNodeClick() "Go to source" - click n2 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file +%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/mixed_children_not_flattened.baml","start_line":5,"start":8,"end_line":5,"end":22}} + click n0 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/multiple_levels.mmd b/engine/baml-lib/baml/tests/validation_files/headers/multiple_levels.mmd index 3d866152cc..86fb5070c9 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/multiple_levels.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/multiple_levels.mmd @@ -1,16 +1,16 @@ flowchart TD n0["Section One"] - n1["Section Two"] - n2["Subsection 2.1"] - n3["Subsubsection 2.1.1"] - n4["Section Three"] - n2 --> n3 - n1 --> n2 + n2["Section Three"] + subgraph sg0["Section Two"] + direction TB + subgraph sg1["Subsection 2.1"] + direction TB + n1["Subsubsection 2.1.1"] + end + end n0 --> n1 - n3 --> n4 -%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":1,"start":4,"end_line":1,"end":19},"n1":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":4,"start":4,"end_line":4,"end":19},"n2":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":7,"start":4,"end_line":7,"end":23},"n3":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":10,"start":4,"end_line":10,"end":29},"n4":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":13,"start":4,"end_line":13,"end":21}} + n1 --> n2 +%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":1,"start":4,"end_line":1,"end":19},"n1":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":10,"start":4,"end_line":10,"end":29},"n2":{"file_path":"tests/validation_files/headers/multiple_levels.baml","start_line":13,"start":4,"end_line":13,"end":21}} click n0 call bamlMermaidNodeClick() "Go to source" click n1 call bamlMermaidNodeClick() "Go to source" - click n2 call bamlMermaidNodeClick() "Go to source" - click n3 call bamlMermaidNodeClick() "Go to source" - click n4 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file + click n2 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/nested_if_statements.mmd b/engine/baml-lib/baml/tests/validation_files/headers/nested_if_statements.mmd index 4202957667..70a93371c7 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/nested_if_statements.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/nested_if_statements.mmd @@ -1,64 +1,65 @@ flowchart TD - n0["Main Function"] - subgraph sg0["If statement 1"] - direction LR - n1{"If statement 1"} - class n1 decisionNode; - subgraph sg1["If statement 2.1"] - direction LR - n2{"If statement 2.1"} - class n2 decisionNode; - subgraph sg2["If statement 3.1"] - direction LR - n3{"If statement 3.1"} - class n3 decisionNode; - n4["True"] - n5["Third False"] + subgraph sg0["Main Function"] + direction TB + subgraph sg1["If statement 1"] + direction TB + n0{"If statement 1"} + class n0 decisionNode; + subgraph sg2["If statement 2.1"] + direction TB + n1{"If statement 2.1"} + class n1 decisionNode; + subgraph sg3["If statement 3.1"] + direction TB + n2{"If statement 3.1"} + class n2 decisionNode; + n3["True"] + n4["Third False"] + end + subgraph sg4["If statement 3.2"] + direction TB + n5{"If statement 3.2"} + class n5 decisionNode; + n6["True"] + n7["Third False"] + end end - subgraph sg3["If statement 3.2"] - direction LR - n6{"If statement 3.2"} - class n6 decisionNode; - n7["True"] - n8["Third False"] - end - end - subgraph sg4["If statement 2.2"] - direction LR - n9{"If statement 2.2"} - class n9 decisionNode; - subgraph sg5["If statement 3.1"] - direction LR - n10{"If statement 3.1"} - class n10 decisionNode; - n11["True"] - n12["Third False"] - end - subgraph sg6["If statement 3.2"] - direction LR - n13{"If statement 3.2"} - class n13 decisionNode; - n14["True"] - n15["Third False"] + subgraph sg5["If statement 2.2"] + direction TB + n8{"If statement 2.2"} + class n8 decisionNode; + subgraph sg6["If statement 3.1"] + direction TB + n9{"If statement 3.1"} + class n9 decisionNode; + n10["True"] + n11["Third False"] + end + subgraph sg7["If statement 3.2"] + direction TB + n12{"If statement 3.2"} + class n12 decisionNode; + n13["True"] + n14["Third False"] + end end end end - n3 --> n4 - n3 --> n5 n2 --> n3 - n6 --> n7 - n6 --> n8 - n2 --> n6 + n2 --> n4 + n5 --> n6 + n5 --> n7 n1 --> n2 - n10 --> n11 - n10 --> n12 + n1 --> n5 n9 --> n10 - n13 --> n14 - n13 --> n15 - n9 --> n13 - n1 --> n9 + n9 --> n11 + n12 --> n13 + n12 --> n14 + n8 --> n9 + n8 --> n12 n0 --> n1 -%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":0,"start":0,"end_line":0,"end":17},"n1":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":2,"start":4,"end_line":2,"end":23},"n2":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":5,"start":8,"end_line":5,"end":30},"n3":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":7,"start":12,"end_line":7,"end":34},"n4":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":9,"start":16,"end_line":9,"end":26},"n5":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":12,"start":16,"end_line":12,"end":33},"n6":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":18,"start":12,"end_line":18,"end":34},"n7":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":20,"start":16,"end_line":20,"end":26},"n8":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":23,"start":16,"end_line":23,"end":33},"n9":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":31,"start":8,"end_line":31,"end":30},"n10":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":33,"start":12,"end_line":33,"end":34},"n11":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":35,"start":16,"end_line":35,"end":26},"n12":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":38,"start":16,"end_line":38,"end":33},"n13":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":44,"start":12,"end_line":44,"end":34},"n14":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":46,"start":16,"end_line":46,"end":26},"n15":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":49,"start":16,"end_line":49,"end":33}} + n0 --> n8 +%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":2,"start":4,"end_line":2,"end":23},"n1":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":5,"start":8,"end_line":5,"end":30},"n2":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":7,"start":12,"end_line":7,"end":34},"n3":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":9,"start":16,"end_line":9,"end":26},"n4":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":12,"start":16,"end_line":12,"end":33},"n5":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":18,"start":12,"end_line":18,"end":34},"n6":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":20,"start":16,"end_line":20,"end":26},"n7":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":23,"start":16,"end_line":23,"end":33},"n8":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":31,"start":8,"end_line":31,"end":30},"n9":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":33,"start":12,"end_line":33,"end":34},"n10":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":35,"start":16,"end_line":35,"end":26},"n11":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":38,"start":16,"end_line":38,"end":33},"n12":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":44,"start":12,"end_line":44,"end":34},"n13":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":46,"start":16,"end_line":46,"end":26},"n14":{"file_path":"tests/validation_files/headers/nested_if_statements.baml","start_line":49,"start":16,"end_line":49,"end":33}} click n0 call bamlMermaidNodeClick() "Go to source" click n1 call bamlMermaidNodeClick() "Go to source" click n2 call bamlMermaidNodeClick() "Go to source" @@ -73,5 +74,4 @@ flowchart TD click n11 call bamlMermaidNodeClick() "Go to source" click n12 call bamlMermaidNodeClick() "Go to source" click n13 call bamlMermaidNodeClick() "Go to source" - click n14 call bamlMermaidNodeClick() "Go to source" - click n15 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file + click n14 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/nested_scopes.mmd b/engine/baml-lib/baml/tests/validation_files/headers/nested_scopes.mmd index 4ff785a6e3..772a17152b 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/nested_scopes.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/nested_scopes.mmd @@ -1,17 +1,17 @@ flowchart TD - n0["Top Level"] - subgraph sg0["First Section"] - direction LR - n1{"First Section"} - class n1 decisionNode; - n2["Inside If Block"] - n3["Inside Else Block"] + subgraph sg0["Top Level"] + direction TB + subgraph sg1["First Section"] + direction TB + n0{"First Section"} + class n0 decisionNode; + n1["Inside If Block"] + n2["Inside Else Block"] + end end - n1 --> n2 - n1 --> n3 n0 --> n1 -%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":1,"start":4,"end_line":1,"end":17},"n1":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":4,"start":4,"end_line":4,"end":22},"n2":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":6,"start":8,"end_line":6,"end":29},"n3":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":10,"start":8,"end_line":10,"end":31}} + n0 --> n2 +%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":4,"start":4,"end_line":4,"end":22},"n1":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":6,"start":8,"end_line":6,"end":29},"n2":{"file_path":"tests/validation_files/headers/nested_scopes.baml","start_line":10,"start":8,"end_line":10,"end":31}} click n0 call bamlMermaidNodeClick() "Go to source" click n1 call bamlMermaidNodeClick() "Go to source" - click n2 call bamlMermaidNodeClick() "Go to source" - click n3 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file + click n2 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/headers/single_nested_child_multi_items.mmd b/engine/baml-lib/baml/tests/validation_files/headers/single_nested_child_multi_items.mmd index b5ec948664..2e9c7840dd 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/single_nested_child_multi_items.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/single_nested_child_multi_items.mmd @@ -1,6 +1,6 @@ flowchart TD subgraph sg0["Parent"] - direction LR + direction TB n0["First"] n1["Second"] end diff --git a/engine/baml-lib/baml/tests/validation_files/headers/top_level_not_flattened.mmd b/engine/baml-lib/baml/tests/validation_files/headers/top_level_not_flattened.mmd index 857eb9a1af..469c9be41d 100644 --- a/engine/baml-lib/baml/tests/validation_files/headers/top_level_not_flattened.mmd +++ b/engine/baml-lib/baml/tests/validation_files/headers/top_level_not_flattened.mmd @@ -1,7 +1,7 @@ flowchart TD - n0["Top Container"] - n1["Only Child"] - n0 --> n1 -%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/top_level_not_flattened.baml","start_line":0,"start":0,"end_line":0,"end":17},"n1":{"file_path":"tests/validation_files/headers/top_level_not_flattened.baml","start_line":2,"start":4,"end_line":2,"end":19}} - click n0 call bamlMermaidNodeClick() "Go to source" - click n1 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file + subgraph sg0["Top Container"] + direction TB + n0["Only Child"] + end +%%__BAML_SPANMAP__={"n0":{"file_path":"tests/validation_files/headers/top_level_not_flattened.baml","start_line":2,"start":4,"end_line":2,"end":19}} + click n0 call bamlMermaidNodeClick() "Go to source" \ No newline at end of file diff --git a/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml b/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml index d21ed5cb3f..ce29b41a35 100644 --- a/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml +++ b/engine/baml-lib/baml/tests/validation_files/prompt_fiddle_example.baml @@ -1,853 +1,21 @@ -// This is a BAML file, which extends the Jinja2 templating language to write LLM functions. -// Run a test to see how it works! -// https://docs.boundaryml.com - -// We want the LLM to extract this info from an image receipt -class Receipt { - establishment_name string - date string @description("ISO8601 formatted date") - total int @description("The total amount of the receipt") - currency string - items Item[] @description("The items on the receipt") -} - -class Item { - name string - price float - quantity int @description("If not specified, assume 1") -} - -// This is our LLM function we can call in Python or Typescript -// the receipt can be an image OR text here! -function ExtractReceipt(receipt: image | string) -> Receipt { - // see clients.baml - client GPT4o - prompt #" - {# start a user message #} - {{ _.role("user") }} - - Extract info from this receipt: - {{ receipt }} - - {# special macro to print the output schema instructions. #} - {{ ctx.output_format }} - "# -} - -// Test when the input is an image -test ImageReceiptTest { - functions [ExtractReceipt] - args { - receipt { url "https://i.redd.it/adzt4bz4llfc1.jpeg"} - } -} - -// Test when the input is a string -test StarbucksTextReceiptTest { - functions [ExtractReceipt] - args { - // use #""# for multi-line strings - receipt #" - Starbucks - Date: 2022-01-01 - Total: $5.00 USD - Items: - - Coffee - - $2.50 - - 1 - - Croissant - - $2.50 - - 1 - "# - } -} - -// This is a BAML config file, which extends the Jinja2 templating language to write LLM functions. - -class Resume { - name string - education Education[] @description("Extract in the same order listed") - skills string[] @description("Only include programming languages") -} - -class Education { - school string - degree string - year int -} - -function ExtractResume(resume_text: string) -> Resume { - // see clients.baml - client GPT4o - - // The prompt uses Jinja syntax. Change the models or this text and watch the prompt preview change! - prompt #" - Parse the following resume and return a structured representation of the data in the schema below. - - Resume: - --- - {{ resume_text }} - --- - - {# special macro to print the output instructions. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test Test1 { - functions [ExtractResume] - args { - resume_text #" - John Doe - - Education - - University of California, Berkeley - - B.S. in Computer Science - - 2020 - - Skills - - Python - - Java - - C++ - "# - } -} -// This will be available as an enum in your Python and Typescript code. -enum Category { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -class Message { - userName string - message string -} - -// inputs can be more complex than just a string -function ClassifyMessage(message: Message) -> Category { - client GPT4o - - prompt #" - Classify the following INPUT into ONE - of categories listed. - - INPUT: - --- - {{ message.userName }}: {{ message.message }} - --- - - {{ ctx.output_format }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessage] - args { - message { - userName "Alice" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." +//# Main Function +function ComplexHeaderTest(x: int) -> int { + //# increment by 10 + if (x < 10) { + x = x + 10; + } else { + //## dummy variable y + let y = 5; } - } -} -// These are LLM clients you can use in your functions. We currently support Anthropic, OpenAI / Azure, Gemini, and Ollama as providers. - -// We also support any other provider that follows the OpenAI API specification, such as HuggingFace. - -// For this playground, we have setup a few clients for you to use already with some free credits. - -client GPT4 { - // Use one of the following: https://docs.boundaryml.com/docs/snippets/clients/providers/openai - provider openai - // You can pass in any parameters from the OpenAI Python documentation into the options block. - options { - model gpt-4 - api_key env.OPENAI_API_KEY - } -} - -client GPT4o { - provider openai - retry_policy CustomRetry - options { - model gpt-4o - api_key env.OPENAI_API_KEY - } -} - -retry_policy CustomRetry { - max_retries 3 -} - -client Claude { - provider anthropic - options { - model claude-3-haiku-20240307 - api_key env.ANTHROPIC_API_KEY - max_tokens 1000 - - } -} - -client Gemini { - provider google-ai - options { - model "gemini-2.5-flash" - api_key env.GOOGLE_API_KEY - } -} - -// You can use the fallback provider to add more resilience to your application. - -// A fallback will attempt to use the first client, and if it fails, it will try the second client, and so on. - -// https://docs.boundaryml.com/docs/snippets/clients/fallback - -client SuperDuperClient { - provider fallback - options { - // clients from clients.baml - strategy [ - GPT4 - GPT4o - Gemini - ] - } -} - -// An example of a BAML client with a retry policy - -// https://docs.boundaryml.com/docs/snippets/clients/retry - - -retry_policy MyPolicyName { - max_retries 3 -} - -client MyClient { - provider anthropic - retry_policy MyPolicyName - options { - model "claude-3-sonnet-20240229" - api_key env.ANTHROPIC_API_KEY - } -} -// The round_robin provider allows you to distribute requests across multiple clients in a round-robin fashion. After each call, the next client in the list will be used. - -client MyRoundRobinClient { - provider round-robin - options { - strategy [ - GPT4o - Gemini - ] - } -} - -class WeatherAPI { - city string @description("the user's city") - timeOfDay string @description("As an ISO8601 timestamp") -} - -function UseTool(user_message: string) -> WeatherAPI { - client GPT4o - prompt #" - Extract the info from this message - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestOne { - functions [UseTool] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - "# - } -} - -class WeatherAPI2 { - city string @description("the user's city") - timeOfDay string @description("As an ISO8601 timestamp") -} - -class SendEmail { - emailTo string - emailSubject string - emailBody string -} - -function ChooseOneTool(user_message: string) -> WeatherAPI2 | SendEmail { - client GPT4o - prompt #" - Choose the right schema that contains all the information in this message: - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestOneFunc { - functions [ChooseOneTool] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - "# - } -} - -test TestOneFunc2 { - functions [ChooseOneTool] - args { - user_message #" - Send an email to John Doe with the subject 'Hello' and the body 'How are you doing?' - "# - } -} - - -function ChooseNTools(user_message: string) -> (WeatherAPI2 | SendEmail)[] { - client GPT4o - prompt #" - Choose the right schema(s) that contains all the information in this message: - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestNTools { - functions [ChooseNTools] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - - Also send an email to Mark Gonzalez with the subject 'Hello' and the body 'How are you doing?' - "# - } -} - -// BAML supports multi-modal inputs! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#image - -function AudioInput(audioInput: audio) -> string { - client Gemini - prompt #" - {{ _.role("user") }} - - Does this sound like a roar? Yes or no? One word no other characters. - {{ audioInput }} - "# + //# another increment + x + 1234 } - -test TestURLAudioInput{ - functions [AudioInput] - args { - audioInput { - url https://actions.google.com/sounds/v1/emergency/beeper_emergency_call.ogg +test Foo { + functions [ComplexHeaderTest] + args { + x 5 } - } } - -// BAML supports multi-modal inputs! Check the raw cURL request toggle in the playground to see the prompt is transformed into an API call - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#image - -class ImageDescription { - description string - tags string[] @description(#" - Tags that describe the image - "#) -} - -function DescribeImage(myImage: image) -> ImageDescription { - client GPT4o - prompt #" - {{ _.role("user") }} - Describe this in 2 sentences: {{ myImage }} - - {{ ctx.output_format }} - "# -} - - -test ImageTest { - functions [DescribeImage] - args { - myImage { url "https://imgs.xkcd.com/comics/standards.png"} - } -} - -// BAML supports multi-modal inputs including Pdfs! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#pdf - -function PdfInput(pdfInput: pdf) -> string { - client GPT4o - prompt #" - {{ _.role("user") }} - - Get the title of the pdfs - - {{ pdfInput }} - "# -} - -test TestFilePdfInput { - functions [PdfInput] - args { - pdfInput { - url "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" - } - } -} -// BAML supports multi-modal inputs including videos! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#video -// Note: Video input only works with Google models (Gemini) - -function VideoInput(videoInput: video) -> string { - client Gemini - prompt #" - {{ _.role("user") }} - - Describe what happens in this video in 5 words or less. - - {{ videoInput }} - "# -} - -test TestFileVideoInput { - functions [VideoInput] - args { - videoInput { - url "https://www.youtube.com/watch?v=dQw4w9WgXcQ" - media_type "video/mp4" - } - } -} -class OResume { - name string - education OEducation[] @description("Extract in the same order listed") - skills string[] @description("Only include programming languages") -} - -class OEducation { - school string - degree string - year int -} - -function ExtractResumeUsingOllama(resume_text: string) -> OResume { - // see ollama-clients.baml - client Llama3 - // client Mistral - // client Gemma2 - // client Phi3 - - // The prompt uses Jinja syntax. Change the models or this text and watch the prompt preview change! - prompt #" - Parse the following resume and return a structured representation of the data in the schema below. - - Resume: - --- - {{ resume_text }} - --- - - {# special macro to print the output instructions. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test OllamaTest1 { - functions [ExtractResumeUsingOllama] - args { - resume_text #" - John Doe - - Education - - University of California, Berkeley - - B.S. in Computer Science - - 2020 - - Skills - - Python - - Java - - C++ - "# - } -} -enum OCategory { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -class OMessage { - userName string - message string -} - -// inputs can be more complex than just a string -// This will be available as an enum in your Python and Typescript code. -// inputs can be more complex than just a string -function ClassifyMessageUsingOllama(message: OMessage) -> OCategory { - client Llama3 - // client Mistral - // client Gemma2 - // client Phi3 - - prompt #" - Classify the following INPUT into ONE - of categories listed. - - INPUT: - --- - {{ message.userName }}: {{ message.message }} - --- - - {{ ctx.output_format }} - - Response: - "# -} - -test OllamaTest1 { - functions [ClassifyMessageUsingOllama] - args { - message { - userName "Alice" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } - } -} -client Llama3 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull llama3` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "llama3" - } -} - -client Mistral { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull mistral` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "mistral" - } -} - -client Gemma2 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull gemma2` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "gemma2" - } -} - -client Phi3 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull phi3` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "phi3" - } -} - -// BAML excels at parsing the schemas from LLM responses. - -// In this example we tell the model to think step by step and explain its reasoning -// before outputting the answer. - -// Run this prompt, and you'll notice the BAML function still returns the expected return type (OrderInfo), -// even though there is a bunch of "reasoning" text present in the "raw text". It works with streaming or even standalone enum outputs too! -class Email { - subject string - body string - from_address string -} - -enum OrderStatus { - ORDERED - SHIPPED - DELIVERED - CANCELLED -} - -class OrderInfo { - order_status OrderStatus - tracking_number string? - estimated_arrival_date string? -} - -function GetOrderInfo(email: Email) -> OrderInfo { - client GPT4o - prompt #" - Extract the info from this email in the INPUT: - - INPUT: - ------- - from: {{email.from_address}} - Email Subject: {{email.subject}} - Email Body: {{email.body}} - ------- - - {{ ctx.output_format }} - - Before you output the JSON, please explain your - reasoning step-by-step. Here is an example on how to do this: - 'If we think step by step we can see that ... - therefore the output JSON is: - { - ... the json schema ... - }' - "# -} - -test Test1 { - functions [GetOrderInfo] - args { - email { - from_address "hello@amazon.com" - subject "Your Amazon.com order of 'Wood Dowel Rods...' has shipped!" - body #" - Hi Sam, your package will arrive: - Thurs, April 4 - Track your package: - www.amazon.com/gp/your-account/ship-track?ie=23&orderId123 - - On the way: - Wood Dowel Rods... - Order #113-7540940 - Ship to: - Sam - SEATTLE, WA - - Shipment total: - $0.00 - "# - - } - } -} -// You can improve results by aliasing field names to symbols like "k1" ... "kN". -// This makes the LLM pay attention to your descriptions instead of just the enum or property name, since those can introduce biases. See paper: https://arxiv.org/abs/2305.08298 for more details. - -// Check the prompt preview to see that aliases are being sent. - -// When you add aliases you don't need to change your resulting python code. Your models -// stay the same. They are merely a way to aid in prompt engineering. -enum MyClass { - Refund @alias("k1") - @description("Customer wants to refund a product") - - CancelOrder @alias("k2") - @description("Customer wants to cancel an order") - - TechnicalSupport @alias("k3") - @description("Customer needs help with a technical issue unrelated to account creation or login") - - AccountIssue @alias("k4") - @description("Specifically relates to account-login or account-creation") - - Question @alias("k5") - @description("Customer has a question") -} - -function ClassifyMessageWithSymbol(input: string) -> MyClass { - client GPT4o - - prompt #" - Classify the following INPUT into ONE - of the following categories: - - INPUT: {{ input }} - - {{ ctx.output_format }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessageWithSymbol] - args { - input "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } -} -// This will be available as an enum in your Python and Typescript code. -enum MyCategory { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -function ClassifyMessageWithRoles(input: string) -> MyCategory { - client GPT4o - - prompt #" - {# _.role("system") starts a system message #} - {{ _.role("system") }} - - Classify the following INPUT into ONE - of the following categories: - - {{ ctx.output_format }} - - {# This starts a user message #} - {{ _.role("user") }} - - INPUT: {{ input }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessageWithRoles] - args { - input "Can't access my account using my usual login credentials, and each attempt results in an error message stating 'Invalid username or password.' I have tried resetting my password using the 'Forgot Password' link, but I haven't received the promised password reset email." - } -} -class User { - name string - is_active bool -} - -function FunctionWithConditionals(user: User) -> string { - client GPT4o - prompt #" - {% if user.is_active %} - Greet the user {{ user.name }}! - {% else %} - Tell the user to activate their account - {% endif %} - "# -} -class MyUserMessage { - user_name string - content string -} - -function FunctionWithLoops(messages: MyUserMessage[]) -> string { - client GPT4o - prompt #" - {% for message in messages %} - {{ message.user_name }}: {{ message.content }} - {% endfor %} - "# -} - -test TestName { - functions [FunctionWithLoops] - args { - messages [ - { - user_name "Alice" - content "Hello!" - } - { - user_name "Bob" - content "Hi!" - } - ] - } -} - -// We can define other "template string" variables that can be used in a prompt. -class UserMessage { - message string - role string -} - -// prompt strings and template strings use Jinja2 syntax: https://jinja.palletsprojects.com/en/3.1.x/templates/ -template_string PrintMessages(messages: UserMessage[]) #" - {% for m in messages %} - {{ _.role(m.role) }} - {{ m.message }} - {% endfor %} -"# - -function ClassifyConversation(messages: UserMessage[]) -> Category[] { - client GPT4o - prompt #" - Classify this conversation: - {{ PrintMessages(messages) }} - - Use the following categories: - {{ ctx.output_format}} - "# -} - -test TestClassifyConvo { - functions [ClassifyConversation] - args { - messages [ - { - role "user" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } - { - role "assistant" - message "I'm sorry to hear that. Let me help you with that." - } - { - role "user" - message "Thank you." - } - ] - } -} - diff --git a/engine/baml-runtime/src/lib.rs b/engine/baml-runtime/src/lib.rs index e2c1e2c2b1..417c4d7542 100644 --- a/engine/baml-runtime/src/lib.rs +++ b/engine/baml-runtime/src/lib.rs @@ -771,6 +771,12 @@ impl BamlRuntime { { baml_log::set_from_env(&env_vars).unwrap(); + log::info!( + "[Runtime] run_test_with_expr_events start function={} test={}", + function_name, + test_name + ); + let call = self .tracer_wrapper .get_or_create_tracer(&env_vars) @@ -789,11 +795,36 @@ impl BamlRuntime { // If it's an expr function, use the simpler expr execution path if is_expr_fn { - return self + log::info!( + "[Runtime] run_test_with_expr_events taking expr path for function={} test={}", + function_name, + test_name + ); + let result = self .run_expr_test(function_name, test_name, ctx, env_vars) .await; + match &result.0 { + Ok(_) => log::info!( + "[Runtime] run_test_with_expr_events expr path success function={} test={}", + function_name, + test_name + ), + Err(e) => log::error!( + "[Runtime] run_test_with_expr_events expr path error function={} test={} err={:?}", + function_name, + test_name, + e + ), + } + return result; } + log::info!( + "[Runtime] run_test_with_expr_events taking LLM path function={} test={}", + function_name, + test_name + ); + let run_to_response = || async { // acceptable clone, just used for testing let rctx_no_tb = @@ -885,6 +916,19 @@ impl BamlRuntime { }; let response = run_to_response().await; + match &response { + Ok(_) => log::info!( + "[Runtime] run_test_with_expr_events LLM path success function={} test={}", + function_name, + test_name + ), + Err(e) => log::error!( + "[Runtime] run_test_with_expr_events LLM path error function={} test={} err={:?}", + function_name, + test_name, + e + ), + } let call_id = call.curr_call_id(); { @@ -953,6 +997,11 @@ impl BamlRuntime { ctx: &RuntimeContextManager, env_vars: HashMap, ) -> (Result, FunctionCallId) { + log::info!( + "[Runtime] run_expr_test start function={} test={}", + function_name, + test_name + ); // Get test parameters let rctx = ctx.create_ctx_with_default(); let params = match self.get_test_params(function_name, test_name, &rctx, true) { @@ -1058,6 +1107,25 @@ impl BamlRuntime { constraints_result, }; + match &test_response.expr_function_response { + Some(Ok(_)) => log::info!( + "[Runtime] run_expr_test success function={} test={}", + function_name, + test_name + ), + Some(Err(e)) => log::error!( + "[Runtime] run_expr_test evaluation error function={} test={} err={:?}", + function_name, + test_name, + e + ), + None => log::warn!( + "[Runtime] run_expr_test produced no response function={} test={}", + function_name, + test_name + ), + } + (Ok(test_response), call_id) } @@ -1825,10 +1893,15 @@ impl InternalRuntimeInterface for BamlRuntime { client.iter_orchestrator(&mut Default::default(), Default::default(), ctx, self) } - fn function_graph(&self, _function_name: &str, _ctx: &RuntimeContext) -> Result { + fn function_graph(&self, function_name: &str, _ctx: &RuntimeContext) -> Result { let ast = self.db.ast(); - let graph = - internal_baml_core::ast::BamlVisDiagramGenerator::generate_headers_flowchart(ast); + let graph = internal_baml_core::ast::diagram_generator::generate_with_styling( + internal_baml_core::ast::diagram_generator::MermaidGeneratorContext { + use_fancy: false, + function_filter: Some(function_name.to_string()), + }, + ast, + ); Ok(graph) } diff --git a/engine/baml-runtime/src/runtime/runtime_interface.rs b/engine/baml-runtime/src/runtime/runtime_interface.rs index 29a6c7bbbe..92aa430ef8 100644 --- a/engine/baml-runtime/src/runtime/runtime_interface.rs +++ b/engine/baml-runtime/src/runtime/runtime_interface.rs @@ -5,8 +5,8 @@ use baml_types::{ tracing::events::{FunctionEnd, FunctionStart, TraceData, TraceEvent}, BamlMap, BamlValue, Constraint, EvaluationContext, }; +use internal_baml_ast::diagram_generator; use internal_baml_core::{ - ast::BamlVisDiagramGenerator, internal_baml_diagnostics::SourceFile, ir::{ repr::{IntermediateRepr, Node, TypeBuilderEntry}, @@ -209,7 +209,12 @@ impl InternalRuntimeInterface for InternalBamlRuntime { fn function_graph(&self, _function_name: &str, _ctx: &RuntimeContext) -> Result { // Use baml-vis to generate a Mermaid diagram for the current AST let ast = self.db.ast(); - let graph = BamlVisDiagramGenerator::generate_headers_flowchart(ast); + log::info!("[runtime_interface] generating function graph for AST"); + let graph = diagram_generator::generate_headers_flowchart(ast); + log::info!( + "[runtime_interface] generated Mermaid graph (chars={})", + graph.len() + ); Ok(graph) } diff --git a/engine/baml-schema-wasm/src/runtime_wasm/mod.rs b/engine/baml-schema-wasm/src/runtime_wasm/mod.rs index 2439db13fe..96643b3bef 100644 --- a/engine/baml-schema-wasm/src/runtime_wasm/mod.rs +++ b/engine/baml-schema-wasm/src/runtime_wasm/mod.rs @@ -1121,9 +1121,13 @@ impl WasmRuntime { let ctx = ctx.create_ctx_with_default(); let ctx = ctx.eval_ctx(false); - self.runtime - .ir() - .walk_expr_fns() + let expr_fns = self.runtime.ir().walk_expr_fns().collect::>(); + log::info!( + "[WasmRuntime] list_expr_fns discovered {} functions", + expr_fns.len() + ); + expr_fns + .into_iter() .map(|f| { let snippet = format!( r#"test TestName {{ @@ -2000,6 +2004,11 @@ impl WasmFunction { get_baml_src_cb: js_sys::Function, env: js_sys::Object, ) -> JsResult { + log::info!( + "[WasmFunction] render_prompt_for_test start function={} test={}", + self.name, + test_name + ); let context_manager = rt.runtime.create_ctx_manager( BamlValue::String("wasm".to_string()), js_fn_to_baml_src_reader(get_baml_src_cb), @@ -2034,13 +2043,31 @@ impl WasmFunction { .get_test_params(&self.name, &test_name, &ctx, false) .map_err(|e| JsError::new(format!("{e:?}").as_str()))?; - rt.runtime + match rt + .runtime .internal() .render_prompt(&self.name, &ctx, ¶ms, wasm_call_context.node_index) .await - .as_ref() - .map(|(p, scope, allowed)| (p, scope, allowed).into()) - .map_err(|e| JsError::new(format!("{e:?}").as_str())) + { + Ok(rendered) => { + log::info!( + "[WasmFunction] render_prompt_for_test success function={} test={}", + self.name, + test_name + ); + let prompt = (&rendered.0, &rendered.1, &rendered.2).into(); + Ok(prompt) + } + Err(e) => { + log::error!( + "[WasmFunction] render_prompt_for_test error function={} test={} err={:?}", + self.name, + test_name, + e + ); + Err(JsError::new(format!("{e:?}").as_str())) + } + } } #[wasm_bindgen] @@ -2165,9 +2192,13 @@ impl WasmFunction { let rt = &rt.runtime; let function_name = self.name.clone(); - let function_name_for_test_pair = function_name.clone(); let test_name_for_test_pair = test_name.clone(); + log::info!( + "[WasmFunction] run_test_with_expr_events start function={} test={}", + function_name, + test_name.as_str() + ); // Create the closure to handle partial responses: let cb = Box::new(move |r: FunctionResult| { @@ -2232,6 +2263,19 @@ impl WasmFunction { .await; let (test_response, span) = result; + match &test_response { + Ok(_) => log::info!( + "[WasmFunction] run_test_with_expr_events success function={} test={}", + function_name, + test_name.as_str() + ), + Err(e) => log::error!( + "[WasmFunction] run_test_with_expr_events error function={} test={} err={:?}", + function_name, + test_name.as_str(), + e + ), + } Ok(WasmTestResponse { test_response, @@ -2328,10 +2372,18 @@ impl WasmFunction { let ctx = rt .create_ctx_manager(BamlValue::String("wasm".to_string()), None) .create_ctx_with_default(); + log::info!( + "[wasm::function_graph] generating graph for function {}", + self.name + ); let graph = rt .internal() .function_graph(&self.name, &ctx) .map_err(|e| JsValue::from_str(&format!("{e:?}")))?; + log::info!( + "[wasm::function_graph] generated Mermaid graph (chars={})", + graph.len() + ); Ok(graph) } diff --git a/integ-tests/react/package.json b/integ-tests/react/package.json index 77338c6466..d02f56ecaf 100644 --- a/integ-tests/react/package.json +++ b/integ-tests/react/package.json @@ -45,8 +45,8 @@ "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.9", "@types/node": "^22.13.11", - "@types/react": "^19.0.12", - "@types/react-dom": "^19.0.4", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "dotenv": "^16.4.5", "dotenv-cli": "^8.0.0", "jest": "^29.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed8319c412..f4be0c5141 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,37 @@ importers: engine: {} + engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox: + dependencies: + next: + specifier: 15.5.5 + version: 15.5.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: + specifier: 19.1.0 + version: 19.1.0 + react-dom: + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.13 + '@types/node': + specifier: ^20 + version: 20.17.17 + '@types/react': + specifier: ^19 + version: 19.1.9 + '@types/react-dom': + specifier: ^19 + version: 19.1.7(@types/react@19.1.9) + tailwindcss: + specifier: ^4 + version: 4.1.13 + typescript: + specifier: ^5 + version: 5.8.3 + engine/baml-rpc: devDependencies: typescript: @@ -88,7 +119,7 @@ importers: version: 6.2.6 ts-jest: specifier: ^29.1.1 - version: 29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@20.19.1)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@20.19.1)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.27.7)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.27.7))(jest-util@30.0.2)(jest@29.7.0(@types/node@20.19.1)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@20.19.1)(typescript@5.8.3)))(typescript@5.8.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.12.7)(@types/node@20.19.1)(typescript@5.8.3) @@ -135,7 +166,7 @@ importers: version: link:../../typescript/packages/nextjs-plugin jotai: specifier: 2.12.2 - version: 2.12.2(@types/react@19.1.8)(react@19.1.0) + version: 2.12.2(@types/react@19.1.9)(react@19.1.0) js-yaml: specifier: ^4.1.0 version: 4.1.0 @@ -147,13 +178,13 @@ importers: version: 15.0.12 next: specifier: 15.2.3 - version: 15.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.2.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) nuqs: specifier: 2.4.1 - version: 2.4.1(next@15.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 2.4.1(next@15.2.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) react: specifier: ^19.0.0 version: 19.1.0 @@ -190,10 +221,10 @@ importers: version: 6.6.3 '@testing-library/react': specifier: ^16.2.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/react-hooks': specifier: ^8.0.1 - version: 8.0.1(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 8.0.1(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@types/jest': specifier: ^29.5.12 version: 29.5.14 @@ -204,11 +235,11 @@ importers: specifier: ^22.13.11 version: 22.15.33 '@types/react': - specifier: ^19.0.12 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: ^19.0.4 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -241,7 +272,7 @@ importers: version: 3.4.17(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3)) ts-jest: specifier: ^29.2.6 - version: 29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@22.15.33)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3)))(typescript@5.8.3) + version: 29.4.0(@babel/core@7.28.4)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@30.0.2)(jest@29.7.0(@types/node@22.15.33)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3)))(typescript@5.8.3) ts-node: specifier: ^10.9.2 version: 10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3) @@ -332,7 +363,7 @@ importers: version: 0.0.2 ts-jest: specifier: 29.1.2 - version: 29.1.2(@babel/core@7.27.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(esbuild@0.25.2)(jest@29.7.0(@types/node@20.17.17)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.15))(@types/node@20.17.17)(typescript@5.4.2)))(typescript@5.4.2) + version: 29.1.2(@babel/core@7.28.4)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.2)(jest@29.7.0(@types/node@20.17.17)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.15))(@types/node@20.17.17)(typescript@5.4.2)))(typescript@5.4.2) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.15))(@types/node@20.17.17)(typescript@5.4.2) @@ -414,10 +445,10 @@ importers: version: 4.0.10 devDependencies: '@types/react': - specifier: ^19.1.9 + specifier: 19.1.9 version: 19.1.9 '@types/react-dom': - specifier: ^19.1.7 + specifier: 19.1.7 version: 19.1.7(@types/react@19.1.9) '@types/react-syntax-highlighter': specifier: ^15.5.13 @@ -475,10 +506,10 @@ importers: version: 1.18.1(debug@4.4.1) jotai: specifier: 2.12.5 - version: 2.12.5(@types/react@19.1.8)(react@19.1.0) + version: 2.12.5(@types/react@19.1.9)(react@19.1.0) jotai-devtools: specifier: 0.12.0 - version: 0.12.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) + version: 0.12.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) lucide-react: specifier: 0.516.0 version: 0.516.0(react@19.1.0) @@ -499,7 +530,7 @@ importers: version: 19.1.0 react-arborist: specifier: 3.4.3 - version: 3.4.3(@types/node@24.0.3)(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.4.3(@types/node@24.0.3)(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-device-detect: specifier: 2.2.3 version: 2.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -514,7 +545,7 @@ importers: version: 5.5.0(react@19.1.0) react-joyride: specifier: 2.9.3 - version: 2.9.3(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.9.3(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react18-json-view: specifier: 0.2.9 version: 0.2.9(react@19.1.0) @@ -532,11 +563,11 @@ importers: specifier: 24.0.3 version: 24.0.3 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) typescript: specifier: 5.8.3 version: 5.8.3 @@ -557,7 +588,7 @@ importers: version: 1.20.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) jotai: specifier: 2.12.5 - version: 2.12.5(@types/react@19.1.8)(react@19.1.0) + version: 2.12.5(@types/react@19.1.9)(react@19.1.0) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -575,11 +606,11 @@ importers: specifier: 24.0.3 version: 24.0.3 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) '@vitejs/plugin-react': specifier: 4.5.2 version: 4.5.2(vite@7.0.0(@types/node@24.0.3)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) @@ -669,10 +700,10 @@ importers: specifier: ^20 version: 20.17.17 '@types/react': - specifier: ^19 + specifier: 19.1.9 version: 19.1.9 '@types/react-dom': - specifier: ^19 + specifier: 19.1.7 version: 19.1.7(@types/react@19.1.9) gray-matter: specifier: ^4.0.3 @@ -733,10 +764,10 @@ importers: version: 3.0.5 jotai: specifier: 2.12.5 - version: 2.12.5(@types/react@19.1.8)(react@19.1.0) + version: 2.12.5(@types/react@19.1.9)(react@19.1.0) jotai-devtools: specifier: 0.12.0 - version: 0.12.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) + version: 0.12.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) lucide-react: specifier: 0.516.0 version: 0.516.0(react@19.1.0) @@ -790,11 +821,11 @@ importers: specifier: 24.0.3 version: 24.0.3 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) '@types/semver': specifier: 7.7.0 version: 7.7.0 @@ -916,7 +947,7 @@ importers: version: 5.28.5(@swc/core@1.12.7) next: specifier: 15.3.3 - version: 15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) typescript: specifier: 5.8.3 version: 5.8.3 @@ -988,7 +1019,7 @@ importers: version: 2.3.1(@codemirror/autocomplete@6.18.6)(@codemirror/lint@6.8.5)(@codemirror/state@6.5.2)(@codemirror/view@6.37.2) '@xyflow/react': specifier: 12.7.0 - version: 12.7.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 12.7.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) anser: specifier: 2.3.2 version: 2.3.2 @@ -1012,7 +1043,7 @@ importers: version: 1.2.0 jotai: specifier: '*' - version: 2.12.5(@types/react@19.1.8)(react@19.1.0) + version: 2.12.5(@types/react@19.1.9)(react@19.1.0) js-tiktoken: specifier: 1.0.20 version: 1.0.20 @@ -1036,7 +1067,7 @@ importers: version: 19.1.0 react-arborist: specifier: 3.4.3 - version: 3.4.3(@types/node@24.0.3)(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.4.3(@types/node@24.0.3)(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-error-boundary: specifier: 6.0.0 version: 6.0.0(react@19.1.0) @@ -1045,19 +1076,22 @@ importers: version: 5.5.0(react@19.1.0) react-pdf: specifier: 9.0.0 - version: 9.0.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 9.0.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-resizable-panels: specifier: 3.0.3 version: 3.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-svg-pan-zoom: + specifier: ^3.13.1 + version: 3.13.1 + react-zoom-pan-pinch: + specifier: ^3.7.0 + version: 3.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) rehype-stringify: specifier: 10.0.1 version: 10.0.1 shiki: specifier: 1.24.4 version: 1.24.4 - svg-pan-zoom: - specifier: ^3.6.2 - version: 3.6.2 swr: specifier: 2.3.3 version: 2.3.3(react@19.1.0) @@ -1084,17 +1118,17 @@ importers: specifier: 24.0.3 version: 24.0.3 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) '@types/vscode': specifier: 1.96.0 version: 1.96.0 jotai-devtools: specifier: 0.12.0 - version: 0.12.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) + version: 0.12.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14) react-dom: specifier: 19.1.0 version: 19.1.0(react@19.1.0) @@ -1131,91 +1165,91 @@ importers: version: 13.1.0(react@19.1.0) '@radix-ui/react-accordion': specifier: 1.2.11 - version: 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-alert-dialog': specifier: 1.1.14 - version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-aspect-ratio': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-avatar': specifier: 1.1.10 - version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-checkbox': specifier: 1.3.2 - version: 1.3.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.2(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-collapsible': specifier: 1.1.11 - version: 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-context-menu': specifier: 2.2.15 - version: 2.2.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.2.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': specifier: 1.1.14 - version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: 2.1.15 - version: 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-hover-card': specifier: 1.1.14 - version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-icons': specifier: 1.3.2 version: 1.3.2(react@19.1.0) '@radix-ui/react-label': specifier: 2.1.7 - version: 2.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-menubar': specifier: 1.1.15 - version: 1.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-navigation-menu': specifier: 1.2.13 - version: 1.2.13(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.13(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-popover': specifier: 1.1.14 - version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-portal': specifier: 1.1.9 - version: 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-progress': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-radio-group': specifier: 1.3.7 - version: 1.3.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-scroll-area': specifier: 1.2.9 - version: 1.2.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-select': specifier: 2.2.5 - version: 2.2.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.2.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-separator': specifier: 1.1.7 - version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slider': specifier: 1.3.5 - version: 1.3.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': specifier: 1.2.3 - version: 1.2.3(@types/react@19.1.8)(react@19.1.0) + version: 1.2.3(@types/react@19.1.9)(react@19.1.0) '@radix-ui/react-switch': specifier: 1.2.5 - version: 1.2.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tabs': specifier: 1.1.12 - version: 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.12(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toast': specifier: 1.2.14 - version: 1.2.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toggle': specifier: 1.1.9 - version: 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toggle-group': specifier: 1.1.10 - version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: 1.2.7 - version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tailwindcss/typography': specifier: 0.5.16 version: 0.5.16(tailwindcss@4.1.13) @@ -1230,7 +1264,7 @@ importers: version: 2.1.1 cmdk: specifier: 1.1.1 - version: 1.1.1(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.1(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) cobe: specifier: 0.6.4 version: 0.6.4 @@ -1266,13 +1300,13 @@ importers: version: 5.1.5 next: specifier: 15.3.3 - version: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) nuqs: specifier: 2.4.3 - version: 2.4.3(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 2.4.3(next@15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) react: specifier: 19.1.0 version: 19.1.0 @@ -1290,19 +1324,19 @@ importers: version: 7.58.0(react@19.1.0) react-markdown: specifier: 10.1.0 - version: 10.1.0(@types/react@19.1.8)(react@19.1.0) + version: 10.1.0(@types/react@19.1.9)(react@19.1.0) react-resizable-panels: specifier: 3.0.3 version: 3.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-scan: specifier: 0.3.4 - version: 0.3.4(@types/react@19.1.8)(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0) + version: 0.3.4(@types/react@19.1.9)(next@15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0) react-spring: specifier: 10.0.1 - version: 10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0))(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react-konva@19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react-zdog@1.2.2)(react@19.1.0)(three@0.177.0)(zdog@1.1.3) + version: 10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0))(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react-konva@19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react-zdog@1.2.2)(react@19.1.0)(three@0.177.0)(zdog@1.1.3) react-twc: specifier: 1.4.2 - version: 1.4.2(@types/react@19.1.8)(react@19.1.0) + version: 1.4.2(@types/react@19.1.9)(react@19.1.0) recharts: specifier: 2.15.3 version: 2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1326,7 +1360,7 @@ importers: version: 3.0.0 vaul: specifier: 1.1.2 - version: 1.1.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.2(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) devDependencies: '@baml/tsconfig': specifier: workspace:* @@ -1335,11 +1369,11 @@ importers: specifier: 4.1.13 version: 4.1.13 '@types/react': - specifier: 19.1.8 - version: 19.1.8 + specifier: 19.1.9 + version: 19.1.9 '@types/react-dom': - specifier: 19.1.6 - version: 19.1.6(@types/react@19.1.8) + specifier: 19.1.7 + version: 19.1.7(@types/react@19.1.9) tailwindcss: specifier: 4.1.13 version: 4.1.13 @@ -1800,6 +1834,10 @@ packages: resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.27.5': resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} engines: {node: '>=6.9.0'} @@ -1808,6 +1846,10 @@ packages: resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} @@ -1826,6 +1868,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} @@ -1846,8 +1894,8 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.2': - resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} '@babel/parser@7.27.7': @@ -1860,6 +1908,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -1983,6 +2036,10 @@ packages: resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.27.7': resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==} engines: {node: '>=6.9.0'} @@ -1991,6 +2048,10 @@ packages: resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -3561,6 +3622,9 @@ packages: '@next/env@15.4.5': resolution: {integrity: sha512-ruM+q2SCOVCepUiERoxOmZY9ZVoecR3gcXNwCYZRvQQWRjhOiPJGmQ2fAiLR6YKWXcSAh7G79KEFxN3rwhs4LQ==} + '@next/env@15.5.5': + resolution: {integrity: sha512-2Zhvss36s/yL+YSxD5ZL5dz5pI6ki1OLxYlh6O77VJ68sBnlUrl5YqhBgCy7FkdMsp9RBeGFwpuDCdpJOqdKeQ==} + '@next/swc-darwin-arm64@15.2.3': resolution: {integrity: sha512-uaBhA8aLbXLqwjnsHSkxs353WrRgQgiFjduDpc7YXEU0B54IKx3vU+cxQlYwPCyC8uYEEX7THhtQQsfHnvv8dw==} engines: {node: '>= 10'} @@ -3579,6 +3643,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.5.5': + resolution: {integrity: sha512-lYExGHuFIHeOxf40mRLWoA84iY2sLELB23BV5FIDHhdJkN1LpRTPc1MDOawgTo5ifbM5dvAwnGuHyNm60G1+jw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@15.2.3': resolution: {integrity: sha512-pVwKvJ4Zk7h+4hwhqOUuMx7Ib02u3gDX3HXPKIShBi9JlYllI0nU6TWLbPT94dt7FSi6mSBhfc2JrHViwqbOdw==} engines: {node: '>= 10'} @@ -3597,6 +3667,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.5.5': + resolution: {integrity: sha512-cacs/WQqa96IhqUm+7CY+z/0j9sW6X80KE07v3IAJuv+z0UNvJtKSlT/T1w1SpaQRa9l0wCYYZlRZUhUOvEVmg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@15.2.3': resolution: {integrity: sha512-50ibWdn2RuFFkOEUmo9NCcQbbV9ViQOrUfG48zHBCONciHjaUKtHcYFiCwBVuzD08fzvzkWuuZkd4AqbvKO7UQ==} engines: {node: '>= 10'} @@ -3615,6 +3691,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.5.5': + resolution: {integrity: sha512-tLd90SvkRFik6LSfuYjcJEmwqcNEnVYVOyKTacSazya/SLlSwy/VYKsDE4GIzOBd+h3gW+FXqShc2XBavccHCg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@15.2.3': resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==} engines: {node: '>= 10'} @@ -3633,6 +3715,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.5.5': + resolution: {integrity: sha512-ekV76G2R/l3nkvylkfy9jBSYHeB4QcJ7LdDseT6INnn1p51bmDS1eGoSoq+RxfQ7B1wt+Qa0pIl5aqcx0GLpbw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@15.2.3': resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==} engines: {node: '>= 10'} @@ -3651,6 +3739,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.5.5': + resolution: {integrity: sha512-tI+sBu+3FmWtqlqD4xKJcj3KJtqbniLombKTE7/UWyyoHmOyAo3aZ7QcEHIOgInXOG1nt0rwh0KGmNbvSB0Djg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@15.2.3': resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==} engines: {node: '>= 10'} @@ -3669,6 +3763,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.5.5': + resolution: {integrity: sha512-kDRh+epN/ulroNJLr+toDjN+/JClY5L+OAWjOrrKCI0qcKvTw9GBx7CU/rdA2bgi4WpZN3l0rf/3+b8rduEwrQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@15.2.3': resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==} engines: {node: '>= 10'} @@ -3687,6 +3787,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.5.5': + resolution: {integrity: sha512-GDgdNPFFqiKjTrmfw01sMMRWhVN5wOCmFzPloxa7ksDfX6TZt62tAK986f0ZYqWpvDFqeBCLAzmgTURvtQBdgw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-x64-msvc@15.2.3': resolution: {integrity: sha512-gHYS9tc+G2W0ZC8rBL+H6RdtXIyk40uLiaos0yj5US85FNhbFEndMA2nW3z47nzOWiSvXTZ5kBClc3rD0zJg0w==} engines: {node: '>= 10'} @@ -3705,6 +3811,12 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.5.5': + resolution: {integrity: sha512-5kE3oRJxc7M8RmcTANP8RGoJkaYlwIiDD92gSwCjJY0+j8w8Sl1lvxgQ3bxfHY2KkHFai9tpy/Qx1saWV8eaJQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nextjournal/lang-clojure@1.0.0': resolution: {integrity: sha512-gOCV71XrYD0DhwGoPMWZmZ0r92/lIHsqQu9QWdpZYYBwiChNwMO4sbVMP7eTuAqffFB2BTtCSC+1skSH9d3bNg==} @@ -6072,11 +6184,6 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-dom@19.1.6': - resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} - peerDependencies: - '@types/react': ^19.0.0 - '@types/react-dom@19.1.7': resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==} peerDependencies: @@ -6095,9 +6202,6 @@ packages: '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@19.1.8': - resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} - '@types/react@19.1.9': resolution: {integrity: sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==} @@ -9269,6 +9373,7 @@ packages: resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} engines: {node: '>=8.17.0'} hasBin: true + bundledDependencies: [] jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -10130,6 +10235,27 @@ packages: sass: optional: true + next@15.5.5: + resolution: {integrity: sha512-OQVdBPtpBfq7HxFN0kOVb7rXXOSIkt5lTzDJDGRBcOyVvNRIWFauMqi1gIHd1pszq1542vMOGY0HP4CaiALfkA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + node-abi@3.75.0: resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} engines: {node: '>=10'} @@ -10902,6 +11028,11 @@ packages: peerDependencies: react: ^19.1.1 + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + peerDependencies: + react: ^19.2.0 + react-error-boundary@3.1.4: resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} engines: {node: '>=10', npm: '>=6'} @@ -11120,6 +11251,9 @@ packages: '@types/react': optional: true + react-svg-pan-zoom@3.13.1: + resolution: {integrity: sha512-mUGoXfo255c+T/FAdZ5Fr0paw9OpAz4owGku5bRjUOzSKUmqUJpuYbscuuzOIJWbmrJ0N/9mOWPE8JdjSYITbQ==} + react-syntax-highlighter@15.6.1: resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} peerDependencies: @@ -11164,6 +11298,13 @@ packages: react-zdog@1.2.2: resolution: {integrity: sha512-Ix7ALha91aOEwiHuxumCeYbARS5XNpc/w0v145oGkM6poF/CvhKJwzLhM5sEZbtrghMA+psAhOJkCTzJoseicA==} + react-zoom-pan-pinch@3.7.0: + resolution: {integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '*' + react-dom: '*' + react18-json-view@0.2.9: resolution: {integrity: sha512-z3JQgCwZRKbmWh54U94loCU6vE0ZoDBK7C8ZpcMYQB8jYMi+mR/fcgMI9jKgATeF0I6+OAF025PD+UKkXIqueQ==} peerDependencies: @@ -11181,6 +11322,10 @@ packages: resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} engines: {node: '>=0.10.0'} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + engines: {node: '>=0.10.0'} + read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} @@ -11415,6 +11560,9 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + schema-utils@4.3.2: resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} engines: {node: '>= 10.13.0'} @@ -11828,9 +11976,6 @@ packages: peerDependencies: react: '>=17.0' - svg-pan-zoom@3.6.2: - resolution: {integrity: sha512-JwnvRWfVKw/Xzfe6jriFyfey/lWJLq4bUh2jwoR5ChWQuQoOH8FEh1l/bEp46iHHKHEJWIyFJETbazraxNWECg==} - swr@2.3.3: resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==} peerDependencies: @@ -12016,6 +12161,9 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} + transformation-matrix@2.16.1: + resolution: {integrity: sha512-tdtC3wxVEuzU7X/ydL131Q3JU5cPMEn37oqVLITjRDSDsnSHVFzW2JiCLfZLIQEgWzZHdSy3J6bZzvKEN24jGA==} + tree-changes@0.11.3: resolution: {integrity: sha512-r14mvDZ6tqz8PRQmlFKjhUVngu4VZ9d92ON3tp0EGpFBE6PAHOq8Bx8m8ahbNoGE3uI/npjYcJiqVydyOiYXag==} @@ -14280,7 +14428,7 @@ snapshots: '@babel/generator': 7.28.0 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) - '@babel/helpers': 7.28.2 + '@babel/helpers': 7.27.6 '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 @@ -14293,6 +14441,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.27.5': dependencies: '@babel/parser': 7.27.7 @@ -14309,6 +14477,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.27.7 @@ -14344,6 +14520,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.27.1': {} @@ -14357,10 +14542,10 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.27.7 - '@babel/helpers@7.28.2': + '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.28.4 '@babel/parser@7.27.7': dependencies: @@ -14370,82 +14555,79 @@ snapshots: dependencies: '@babel/types': 7.28.2 + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.7)': dependencies: @@ -14457,88 +14639,80 @@ snapshots: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.27.7)': dependencies: '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - optional: true '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.7)': dependencies: @@ -14589,6 +14763,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + '@babel/types@7.27.7': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -14599,6 +14785,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@1.9.4': @@ -15899,7 +16090,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.12': dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/gen-mapping@0.3.8': @@ -16048,9 +16239,9 @@ snapshots: '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 - '@mantine/code-highlight@7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@mantine/code-highlight@7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mantine/hooks': 7.17.8(react@19.1.0) clsx: 2.1.1 highlight.js: 11.11.1 @@ -16066,7 +16257,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react': 0.26.28(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mantine/hooks': 7.17.8(react@19.1.0) @@ -16074,8 +16265,8 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) react-number-format: 5.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) - react-textarea-autosize: 8.5.9(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0) + react-textarea-autosize: 8.5.9(@types/react@19.1.9)(react@19.1.0) type-fest: 4.41.0 transitivePeerDependencies: - '@types/react' @@ -16422,6 +16613,8 @@ snapshots: '@next/env@15.4.5': {} + '@next/env@15.5.5': {} + '@next/swc-darwin-arm64@15.2.3': optional: true @@ -16431,6 +16624,9 @@ snapshots: '@next/swc-darwin-arm64@15.4.5': optional: true + '@next/swc-darwin-arm64@15.5.5': + optional: true + '@next/swc-darwin-x64@15.2.3': optional: true @@ -16440,6 +16636,9 @@ snapshots: '@next/swc-darwin-x64@15.4.5': optional: true + '@next/swc-darwin-x64@15.5.5': + optional: true + '@next/swc-linux-arm64-gnu@15.2.3': optional: true @@ -16449,6 +16648,9 @@ snapshots: '@next/swc-linux-arm64-gnu@15.4.5': optional: true + '@next/swc-linux-arm64-gnu@15.5.5': + optional: true + '@next/swc-linux-arm64-musl@15.2.3': optional: true @@ -16458,6 +16660,9 @@ snapshots: '@next/swc-linux-arm64-musl@15.4.5': optional: true + '@next/swc-linux-arm64-musl@15.5.5': + optional: true + '@next/swc-linux-x64-gnu@15.2.3': optional: true @@ -16467,6 +16672,9 @@ snapshots: '@next/swc-linux-x64-gnu@15.4.5': optional: true + '@next/swc-linux-x64-gnu@15.5.5': + optional: true + '@next/swc-linux-x64-musl@15.2.3': optional: true @@ -16476,6 +16684,9 @@ snapshots: '@next/swc-linux-x64-musl@15.4.5': optional: true + '@next/swc-linux-x64-musl@15.5.5': + optional: true + '@next/swc-win32-arm64-msvc@15.2.3': optional: true @@ -16485,6 +16696,9 @@ snapshots: '@next/swc-win32-arm64-msvc@15.4.5': optional: true + '@next/swc-win32-arm64-msvc@15.5.5': + optional: true + '@next/swc-win32-x64-msvc@15.2.3': optional: true @@ -16494,6 +16708,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.4.5': optional: true + '@next/swc-win32-x64-msvc@15.5.5': + optional: true + '@nextjournal/lang-clojure@1.0.0': dependencies: '@codemirror/language': 6.11.1 @@ -16603,117 +16820,117 @@ snapshots: '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-accordion@1.2.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-accordion@1.2.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-collapsible@1.1.11(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collapsible@1.1.11(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.9)(react@19.1.1)': dependencies: @@ -16721,397 +16938,397 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - '@radix-ui/react-context-menu@2.2.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-context-menu@2.2.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-context@1.1.2(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) aria-hidden: 1.2.6 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-direction@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-hover-card@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-hover-card@1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) '@radix-ui/react-icons@1.3.2(react@19.1.0)': dependencies: react: 19.1.0 - '@radix-ui/react-id@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menu@2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) aria-hidden: 1.2.6 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-menubar@1.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menubar@1.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-navigation-menu@1.2.13(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-navigation-menu@1.2.13(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) aria-hidden: 1.2.6 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.9)(react@19.1.0) '@radix-ui/rect': 1.1.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-radio-group@1.3.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-radio-group@1.3.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-scroll-area@1.2.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-scroll-area@1.2.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-select@2.2.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-select@2.2.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) aria-hidden: 1.2.6 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.1(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-slider@1.3.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-slider@1.3.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-slot@1.2.3(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 '@radix-ui/react-slot@1.2.3(@types/react@19.1.9)(react@19.1.1)': dependencies: @@ -17120,172 +17337,172 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - '@radix-ui/react-switch@1.2.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-switch@1.2.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-toast@1.2.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toast@1.2.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-toggle-group@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toggle-group@1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toggle': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-toggle@1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toggle@1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 use-sync-external-store: 1.5.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: '@radix-ui/rect': 1.1.1 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-use-size@1.1.1(@types/react@19.1.8)(react@19.1.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.9)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) '@radix-ui/rect@1.1.1': {} @@ -17306,9 +17523,9 @@ snapshots: '@react-native/assets-registry@0.80.0': {} - '@react-native/codegen@0.80.0(@babel/core@7.27.7)': + '@react-native/codegen@0.80.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.27.7 + '@babel/core': 7.28.4 glob: 7.2.3 hermes-parser: 0.28.1 invariant: 2.2.4 @@ -17356,14 +17573,14 @@ snapshots: '@react-native/normalize-colors@0.80.0': {} - '@react-native/virtualized-lists@0.80.0(@types/react@19.1.8)(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)': + '@react-native/virtualized-lists@0.80.0(@types/react@19.1.9)(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.1.0 - react-native: 0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0) + react-native: 0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 '@react-spring/animated@10.0.1(react@19.1.0)': dependencies: @@ -17378,7 +17595,7 @@ snapshots: '@react-spring/types': 10.0.1 react: 19.1.0 - '@react-spring/konva@10.0.1(konva@9.3.20)(react-konva@19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': + '@react-spring/konva@10.0.1(konva@9.3.20)(react-konva@19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)': dependencies: '@react-spring/animated': 10.0.1(react@19.1.0) '@react-spring/core': 10.0.1(react@19.1.0) @@ -17386,16 +17603,16 @@ snapshots: '@react-spring/types': 10.0.1 konva: 9.3.20 react: 19.1.0 - react-konva: 19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-konva: 19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-spring/native@10.0.1(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)': + '@react-spring/native@10.0.1(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)': dependencies: '@react-spring/animated': 10.0.1(react@19.1.0) '@react-spring/core': 10.0.1(react@19.1.0) '@react-spring/shared': 10.0.1(react@19.1.0) '@react-spring/types': 10.0.1 react: 19.1.0 - react-native: 0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0) + react-native: 0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0) '@react-spring/rafz@10.0.1': {} @@ -17405,13 +17622,13 @@ snapshots: '@react-spring/types': 10.0.1 react: 19.1.0 - '@react-spring/three@10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0))(react@19.1.0)(three@0.177.0)': + '@react-spring/three@10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0))(react@19.1.0)(three@0.177.0)': dependencies: '@react-spring/animated': 10.0.1(react@19.1.0) '@react-spring/core': 10.0.1(react@19.1.0) '@react-spring/shared': 10.0.1(react@19.1.0) '@react-spring/types': 10.0.1 - '@react-three/fiber': 9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0) + '@react-three/fiber': 9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0) react: 19.1.0 three: 0.177.0 @@ -17437,14 +17654,14 @@ snapshots: react-zdog: 1.2.2 zdog: 1.1.3 - '@react-three/fiber@9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0)': + '@react-three/fiber@9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0)': dependencies: '@babel/runtime': 7.28.2 - '@types/react-reconciler': 0.28.9(@types/react@19.1.8) + '@types/react-reconciler': 0.28.9(@types/react@19.1.9) '@types/webxr': 0.5.22 base64-js: 1.5.1 buffer: 6.0.3 - its-fine: 2.0.0(@types/react@19.1.8)(react@19.1.0) + its-fine: 2.0.0(@types/react@19.1.9)(react@19.1.0) react: 19.1.0 react-reconciler: 0.31.0(react@19.1.0) react-use-measure: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -17452,10 +17669,10 @@ snapshots: suspend-react: 0.1.3(react@19.1.0) three: 0.177.0 use-sync-external-store: 1.5.0(react@19.1.0) - zustand: 5.0.7(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) + zustand: 5.0.7(@types/react@19.1.9)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) optionalDependencies: react-dom: 19.1.0(react@19.1.0) - react-native: 0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0) + react-native: 0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0) transitivePeerDependencies: - '@types/react' - immer @@ -18896,24 +19113,24 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react-hooks@8.0.1(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-hooks@8.0.1(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 react: 19.1.0 react-error-boundary: 3.1.4(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react-dom: 19.1.0(react@19.1.0) - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 '@testing-library/dom': 10.4.0 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 - '@types/react-dom': 19.1.6(@types/react@19.1.8) + '@types/react': 19.1.9 + '@types/react-dom': 19.1.7(@types/react@19.1.9) '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': dependencies: @@ -18985,20 +19202,20 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.7 - '@babel/types': 7.27.7 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.7 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.7 + '@babel/types': 7.28.2 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.7 - '@babel/types': 7.27.7 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 '@types/babel__traverse@7.20.7': dependencies: @@ -19284,30 +19501,22 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@19.1.6(@types/react@19.1.8)': - dependencies: - '@types/react': 19.1.8 - '@types/react-dom@19.1.7(@types/react@19.1.9)': dependencies: '@types/react': 19.1.9 - '@types/react-reconciler@0.28.9(@types/react@19.1.8)': + '@types/react-reconciler@0.28.9(@types/react@19.1.9)': dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 - '@types/react-reconciler@0.32.0(@types/react@19.1.8)': + '@types/react-reconciler@0.32.0(@types/react@19.1.9)': dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 '@types/react-syntax-highlighter@15.5.13': dependencies: '@types/react': 19.1.9 - '@types/react@19.1.8': - dependencies: - csstype: 3.1.3 - '@types/react@19.1.9': dependencies: csstype: 3.1.3 @@ -19959,13 +20168,13 @@ snapshots: '@xtuc/long@4.2.2': {} - '@xyflow/react@12.7.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@xyflow/react@12.7.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@xyflow/system': 0.0.62 classcat: 5.0.5 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - zustand: 4.5.7(@types/react@19.1.8)(react@19.1.0) + zustand: 4.5.7(@types/react@19.1.9)(react@19.1.0) transitivePeerDependencies: - '@types/react' - immer @@ -20227,19 +20436,18 @@ snapshots: transitivePeerDependencies: - supports-color - babel-jest@29.7.0(@babel/core@7.28.0): + babel-jest@29.7.0(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.28.0) + babel-preset-jest: 29.6.3(@babel/core@7.28.4) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - optional: true babel-plugin-istanbul@6.1.1: dependencies: @@ -20291,25 +20499,24 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.27.7) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.27.7) - babel-preset-current-node-syntax@1.1.0(@babel/core@7.28.0): - dependencies: - '@babel/core': 7.28.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.0) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) - optional: true + babel-preset-current-node-syntax@1.1.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) babel-preset-jest@29.6.3(@babel/core@7.27.7): dependencies: @@ -20317,12 +20524,11 @@ snapshots: babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.1.0(@babel/core@7.27.7) - babel-preset-jest@29.6.3(@babel/core@7.28.0): + babel-preset-jest@29.6.3(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) - optional: true + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.4) bail@2.0.2: {} @@ -20352,9 +20558,9 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 - bippy@0.3.17(@types/react@19.1.8)(react@19.1.0): + bippy@0.3.17(@types/react@19.1.9)(react@19.1.0): dependencies: - '@types/react-reconciler': 0.28.9(@types/react@19.1.8) + '@types/react-reconciler': 0.28.9(@types/react@19.1.9) react: 19.1.0 transitivePeerDependencies: - '@types/react' @@ -20685,12 +20891,12 @@ snapshots: clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + cmdk@1.1.1(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.1.8)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.9)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -22546,7 +22752,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.27.7 + '@babel/core': 7.28.0 '@babel/parser': 7.27.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 @@ -22597,9 +22803,9 @@ snapshots: editions: 6.21.0 textextensions: 6.11.0 - its-fine@2.0.0(@types/react@19.1.8)(react@19.1.0): + its-fine@2.0.0(@types/react@19.1.9)(react@19.1.0): dependencies: - '@types/react-reconciler': 0.28.9(@types/react@19.1.8) + '@types/react-reconciler': 0.28.9(@types/react@19.1.9) react: 19.1.0 transitivePeerDependencies: - '@types/react' @@ -22892,7 +23098,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 22.15.33 + '@types/node': 20.17.17 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3(canvas@2.11.2) @@ -23232,21 +23438,21 @@ snapshots: jose@5.9.6: {} - jotai-devtools@0.12.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14): + jotai-devtools@0.12.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(redux@5.0.1)(storybook@8.6.14): dependencies: - '@mantine/code-highlight': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/code-highlight': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@7.17.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.1.0))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@mantine/hooks': 7.17.8(react@19.1.0) '@redux-devtools/extension': 3.3.0(redux@5.0.1) '@storybook/test': 8.6.14(storybook@8.6.14) clsx: 2.1.1 javascript-stringify: 2.1.0 - jotai: 2.12.5(@types/react@19.1.8)(react@19.1.0) + jotai: 2.12.5(@types/react@19.1.9)(react@19.1.0) jsondiffpatch: 0.5.0 react: 19.1.0 react-base16-styling: 0.9.1 react-error-boundary: 5.0.0(react@19.1.0) - react-json-tree: 0.18.0(@types/react@19.1.8)(react@19.1.0) + react-json-tree: 0.18.0(@types/react@19.1.9)(react@19.1.0) react-resizable-panels: 2.1.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@types/react' @@ -23276,14 +23482,14 @@ snapshots: - redux - storybook - jotai@2.12.2(@types/react@19.1.8)(react@19.1.0): + jotai@2.12.2(@types/react@19.1.9)(react@19.1.0): optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 - jotai@2.12.5(@types/react@19.1.8)(react@19.1.0): + jotai@2.12.5(@types/react@19.1.9)(react@19.1.0): optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 jotai@2.12.5(@types/react@19.1.9)(react@19.1.1): @@ -23867,9 +24073,9 @@ snapshots: merge-descriptors@2.0.0: {} - merge-refs@1.3.0(@types/react@19.1.8): + merge-refs@1.3.0(@types/react@19.1.9): optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 merge-stream@2.0.0: {} @@ -23902,7 +24108,7 @@ snapshots: metro-babel-transformer@0.82.5: dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 flow-enums-runtime: 0.0.6 hermes-parser: 0.29.1 nullthrows: 1.1.1 @@ -23973,9 +24179,9 @@ snapshots: metro-source-map@0.82.5: dependencies: - '@babel/traverse': 7.28.0 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.0' - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.4' + '@babel/types': 7.28.4 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.82.5 @@ -23999,10 +24205,10 @@ snapshots: metro-transform-plugins@0.82.5: dependencies: - '@babel/core': 7.28.0 - '@babel/generator': 7.28.0 + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: @@ -24010,10 +24216,10 @@ snapshots: metro-transform-worker@0.82.5: dependencies: - '@babel/core': 7.28.0 - '@babel/generator': 7.28.0 - '@babel/parser': 7.28.0 - '@babel/types': 7.28.2 + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 flow-enums-runtime: 0.0.6 metro: 0.82.5 metro-babel-transformer: 0.82.5 @@ -24031,12 +24237,12 @@ snapshots: metro@0.82.5: dependencies: '@babel/code-frame': 7.27.1 - '@babel/core': 7.28.0 - '@babel/generator': 7.28.0 - '@babel/parser': 7.28.0 + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/parser': 7.28.4 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 @@ -24511,7 +24717,7 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - next@15.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.2.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.2.3 '@swc/counter': 0.1.3 @@ -24521,7 +24727,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.28.0)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.2.3 '@next/swc-darwin-x64': 15.2.3 @@ -24537,7 +24743,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.3.3 '@swc/counter': 0.1.3 @@ -24547,7 +24753,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.27.7)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.3 '@next/swc-darwin-x64': 15.3.3 @@ -24563,7 +24769,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next@15.3.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 15.3.3 '@swc/counter': 0.1.3 @@ -24571,9 +24777,9 @@ snapshots: busboy: 1.6.0 caniuse-lite: 1.0.30001726 postcss: 8.4.31 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - styled-jsx: 5.1.6(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + styled-jsx: 5.1.6(react@19.2.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.3 '@next/swc-darwin-x64': 15.3.3 @@ -24597,7 +24803,7 @@ snapshots: postcss: 8.4.31 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.28.0)(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.4.5 '@next/swc-darwin-x64': 15.4.5 @@ -24613,6 +24819,30 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.5.5(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@next/env': 15.5.5 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001726 + postcss: 8.4.31 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.5 + '@next/swc-darwin-x64': 15.5.5 + '@next/swc-linux-arm64-gnu': 15.5.5 + '@next/swc-linux-arm64-musl': 15.5.5 + '@next/swc-linux-x64-gnu': 15.5.5 + '@next/swc-linux-x64-musl': 15.5.5 + '@next/swc-win32-arm64-msvc': 15.5.5 + '@next/swc-win32-x64-msvc': 15.5.5 + '@opentelemetry/api': 1.9.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + node-abi@3.75.0: dependencies: semver: 7.7.2 @@ -24711,19 +24941,19 @@ snapshots: nullthrows@1.1.1: {} - nuqs@2.4.1(next@15.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): + nuqs@2.4.1(next@15.2.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): dependencies: mitt: 3.0.1 react: 19.1.0 optionalDependencies: - next: 15.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.2.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - nuqs@2.4.3(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): + nuqs@2.4.3(next@15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0): dependencies: mitt: 3.0.1 react: 19.1.0 optionalDependencies: - next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) nwsapi@2.2.20: {} @@ -25312,10 +25542,10 @@ snapshots: strip-json-comments: 2.0.1 optional: true - react-arborist@3.4.3(@types/node@24.0.3)(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-arborist@3.4.3(@types/node@24.0.3)(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 - react-dnd: 14.0.5(@types/node@24.0.3)(@types/react@19.1.8)(react@19.1.0) + react-dnd: 14.0.5(@types/node@24.0.3)(@types/react@19.1.9)(react@19.1.0) react-dnd-html5-backend: 14.1.0 react-dom: 19.1.0(react@19.1.0) react-window: 1.8.11(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -25361,7 +25591,7 @@ snapshots: dependencies: dnd-core: 14.0.1 - react-dnd@14.0.5(@types/node@24.0.3)(@types/react@19.1.8)(react@19.1.0): + react-dnd@14.0.5(@types/node@24.0.3)(@types/react@19.1.9)(react@19.1.0): dependencies: '@react-dnd/invariant': 2.0.0 '@react-dnd/shallowequal': 2.0.0 @@ -25371,7 +25601,7 @@ snapshots: react: 19.1.0 optionalDependencies: '@types/node': 24.0.3 - '@types/react': 19.1.8 + '@types/react': 19.1.9 react-dom@18.3.1(react@18.3.1): dependencies: @@ -25389,6 +25619,11 @@ snapshots: react: 19.1.1 scheduler: 0.26.0 + react-dom@19.2.0(react@19.2.0): + dependencies: + react: 19.2.0 + scheduler: 0.27.0 + react-error-boundary@3.1.4(react@19.1.0): dependencies: '@babel/runtime': 7.27.6 @@ -25434,9 +25669,9 @@ snapshots: dependencies: react: 19.1.0 - react-innertext@1.1.5(@types/react@19.1.8)(react@19.1.0): + react-innertext@1.1.5(@types/react@19.1.9)(react@19.1.0): dependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 react-instantsearch-core@7.16.2(algoliasearch@5.34.1)(react@19.1.1): @@ -25464,7 +25699,7 @@ snapshots: react-is@18.3.1: {} - react-joyride@2.9.3(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-joyride@2.9.3(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@gilbarbara/deep-equal': 0.3.1 deep-diff: 1.0.2 @@ -25473,7 +25708,7 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) react-floater: 0.7.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-innertext: 1.1.5(@types/react@19.1.8)(react@19.1.0) + react-innertext: 1.1.5(@types/react@19.1.9)(react@19.1.0) react-is: 16.13.1 scroll: 3.0.1 scrollparent: 2.1.0 @@ -25482,11 +25717,11 @@ snapshots: transitivePeerDependencies: - '@types/react' - react-json-tree@0.18.0(@types/react@19.1.8)(react@19.1.0): + react-json-tree@0.18.0(@types/react@19.1.9)(react@19.1.0): dependencies: '@babel/runtime': 7.28.2 '@types/lodash': 4.17.17 - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 react-base16-styling: 0.9.1 @@ -25498,10 +25733,10 @@ snapshots: react: 19.1.1 react-base16-styling: 0.9.1 - react-konva@19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-konva@19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@types/react-reconciler': 0.32.0(@types/react@19.1.8) - its-fine: 2.0.0(@types/react@19.1.8)(react@19.1.0) + '@types/react-reconciler': 0.32.0(@types/react@19.1.9) + its-fine: 2.0.0(@types/react@19.1.9)(react@19.1.0) konva: 9.3.20 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -25510,11 +25745,11 @@ snapshots: transitivePeerDependencies: - '@types/react' - react-markdown@10.1.0(@types/react@19.1.8)(react@19.1.0): + react-markdown@10.1.0(@types/react@19.1.9)(react@19.1.0): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.1.8 + '@types/react': 19.1.9 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -25546,20 +25781,20 @@ snapshots: transitivePeerDependencies: - supports-color - react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0): + react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.80.0 - '@react-native/codegen': 0.80.0(@babel/core@7.27.7) + '@react-native/codegen': 0.80.0(@babel/core@7.28.4) '@react-native/community-cli-plugin': 0.80.0 '@react-native/gradle-plugin': 0.80.0 '@react-native/js-polyfills': 0.80.0 '@react-native/normalize-colors': 0.80.0 - '@react-native/virtualized-lists': 0.80.0(@types/react@19.1.8)(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0) + '@react-native/virtualized-lists': 0.80.0(@types/react@19.1.9)(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.27.7) + babel-jest: 29.7.0(@babel/core@7.28.4) babel-plugin-syntax-hermes-parser: 0.28.1 base64-js: 1.5.1 chalk: 4.1.2 @@ -25585,7 +25820,7 @@ snapshots: ws: 6.2.3 yargs: 17.7.2 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 transitivePeerDependencies: - '@babel/core' - '@react-native-community/cli' @@ -25603,20 +25838,20 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - react-pdf@9.0.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-pdf@9.0.0(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: clsx: 2.1.1 dequal: 2.0.3 make-cancellable-promise: 1.3.2 make-event-props: 1.6.2 - merge-refs: 1.3.0(@types/react@19.1.8) + merge-refs: 1.3.0(@types/react@19.1.9) pdfjs-dist: 4.3.136 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) tiny-invariant: 1.3.3 warning: 4.0.3 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 transitivePeerDependencies: - encoding - supports-color @@ -25635,13 +25870,13 @@ snapshots: react-refresh@0.17.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.1.8)(react@19.1.0): + react-remove-scroll-bar@2.3.8(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 - react-style-singleton: 2.2.3(@types/react@19.1.8)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.9)(react@19.1.0) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react-remove-scroll-bar@2.3.8(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -25651,16 +25886,16 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - react-remove-scroll@2.7.1(@types/react@19.1.8)(react@19.1.0): + react-remove-scroll@2.7.1(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.1.8)(react@19.1.0) - react-style-singleton: 2.2.3(@types/react@19.1.8)(react@19.1.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.1.9)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.1.9)(react@19.1.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.1.8)(react@19.1.0) - use-sidecar: 1.1.3(@types/react@19.1.8)(react@19.1.0) + use-callback-ref: 1.3.3(@types/react@19.1.9)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react-remove-scroll@2.7.1(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -25688,7 +25923,7 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-scan@0.3.4(@types/react@19.1.8)(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0): + react-scan@0.3.4(@types/react@19.1.9)(next@15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0): dependencies: '@babel/core': 7.27.7 '@babel/generator': 7.27.5 @@ -25699,7 +25934,7 @@ snapshots: '@preact/signals': 1.3.2(preact@10.26.9) '@rollup/pluginutils': 5.2.0(rollup@4.43.0) '@types/node': 20.17.17 - bippy: 0.3.17(@types/react@19.1.8)(react@19.1.0) + bippy: 0.3.17(@types/react@19.1.9)(react@19.1.0) esbuild: 0.25.2 estree-walker: 3.0.3 kleur: 4.1.5 @@ -25710,7 +25945,7 @@ snapshots: react-dom: 19.1.0(react@19.1.0) tsx: 4.20.3 optionalDependencies: - next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) unplugin: 2.1.0 transitivePeerDependencies: - '@types/react' @@ -25725,12 +25960,12 @@ snapshots: react-dom: 19.1.0(react@19.1.0) react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-spring@10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0))(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react-konva@19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react-zdog@1.2.2)(react@19.1.0)(three@0.177.0)(zdog@1.1.3): + react-spring@10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0))(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react-konva@19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react-zdog@1.2.2)(react@19.1.0)(three@0.177.0)(zdog@1.1.3): dependencies: '@react-spring/core': 10.0.1(react@19.1.0) - '@react-spring/konva': 10.0.1(konva@9.3.20)(react-konva@19.0.6(@types/react@19.1.8)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) - '@react-spring/native': 10.0.1(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0) - '@react-spring/three': 10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.27.7)(@types/react@19.1.8)(react@19.1.0))(react@19.1.0)(three@0.177.0))(react@19.1.0)(three@0.177.0) + '@react-spring/konva': 10.0.1(konva@9.3.20)(react-konva@19.0.6(@types/react@19.1.9)(konva@9.3.20)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + '@react-spring/native': 10.0.1(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0) + '@react-spring/three': 10.0.1(@react-three/fiber@9.1.2(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react-native@0.80.0(@babel/core@7.28.4)(@types/react@19.1.9)(react@19.1.0))(react@19.1.0)(three@0.177.0))(react@19.1.0)(three@0.177.0) '@react-spring/web': 10.0.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@react-spring/zdog': 10.0.1(react-dom@19.1.0(react@19.1.0))(react-zdog@1.2.2)(react@19.1.0)(zdog@1.1.3) react: 19.1.0 @@ -25744,13 +25979,13 @@ snapshots: - three - zdog - react-style-singleton@2.2.3(@types/react@19.1.8)(react@19.1.0): + react-style-singleton@2.2.3(@types/react@19.1.9)(react@19.1.0): dependencies: get-nonce: 1.0.1 react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react-style-singleton@2.2.3(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -25760,6 +25995,11 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 + react-svg-pan-zoom@3.13.1: + dependencies: + prop-types: 15.8.1 + transformation-matrix: 2.16.1 + react-syntax-highlighter@15.6.1(react@19.1.1): dependencies: '@babel/runtime': 7.28.2 @@ -25770,12 +26010,12 @@ snapshots: react: 19.1.1 refractor: 3.6.0 - react-textarea-autosize@8.5.9(@types/react@19.1.8)(react@19.1.0): + react-textarea-autosize@8.5.9(@types/react@19.1.9)(react@19.1.0): dependencies: '@babel/runtime': 7.28.2 react: 19.1.0 - use-composed-ref: 1.4.0(@types/react@19.1.8)(react@19.1.0) - use-latest: 1.3.0(@types/react@19.1.8)(react@19.1.0) + use-composed-ref: 1.4.0(@types/react@19.1.9)(react@19.1.0) + use-latest: 1.3.0(@types/react@19.1.9)(react@19.1.0) transitivePeerDependencies: - '@types/react' @@ -25797,9 +26037,9 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - react-twc@1.4.2(@types/react@19.1.8)(react@19.1.0): + react-twc@1.4.2(@types/react@19.1.9)(react@19.1.0): dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.9)(react@19.1.0) clsx: 2.1.1 transitivePeerDependencies: - '@types/react' @@ -25829,6 +26069,11 @@ snapshots: react-dom: 18.3.1(react@18.3.1) resize-observer-polyfill: 1.5.1 + react-zoom-pan-pinch@3.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react18-json-view@0.2.9(react@19.1.0): dependencies: copy-to-clipboard: 3.3.3 @@ -25842,6 +26087,8 @@ snapshots: react@19.1.1: {} + react@19.2.0: {} + read-cache@1.0.0: dependencies: pify: 2.3.0 @@ -26168,6 +26415,8 @@ snapshots: scheduler@0.26.0: {} + scheduler@0.27.0: {} + schema-utils@4.3.2: dependencies: '@types/json-schema': 7.0.15 @@ -26649,24 +26898,17 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.27.7)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.1.0): dependencies: client-only: 0.0.1 react: 19.1.0 optionalDependencies: - '@babel/core': 7.27.7 - - styled-jsx@5.1.6(@babel/core@7.28.0)(react@19.1.0): - dependencies: - client-only: 0.0.1 - react: 19.1.0 - optionalDependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 - styled-jsx@5.1.6(react@19.1.1): + styled-jsx@5.1.6(react@19.2.0): dependencies: client-only: 0.0.1 - react: 19.1.1 + react: 19.2.0 stylis@4.3.6: {} @@ -26703,8 +26945,6 @@ snapshots: dependencies: react: 19.1.0 - svg-pan-zoom@3.6.2: {} - swr@2.3.3(react@19.1.0): dependencies: dequal: 2.0.3 @@ -26922,6 +27162,8 @@ snapshots: dependencies: punycode: 2.3.1 + transformation-matrix@2.16.1: {} + tree-changes@0.11.3: dependencies: '@gilbarbara/deep-equal': 0.3.1 @@ -26942,7 +27184,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.1.2(@babel/core@7.27.7)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.7))(esbuild@0.25.2)(jest@29.7.0(@types/node@20.17.17)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.15))(@types/node@20.17.17)(typescript@5.4.2)))(typescript@5.4.2): + ts-jest@29.1.2(@babel/core@7.28.4)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.2)(jest@29.7.0(@types/node@20.17.17)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.15))(@types/node@20.17.17)(typescript@5.4.2)))(typescript@5.4.2): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -26955,12 +27197,12 @@ snapshots: typescript: 5.4.2 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.27.7 + '@babel/core': 7.28.4 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.7) + babel-jest: 29.7.0(@babel/core@7.28.4) esbuild: 0.25.2 - ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@20.19.1)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@20.19.1)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.27.7)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.27.7))(jest-util@30.0.2)(jest@29.7.0(@types/node@20.19.1)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@20.19.1)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -26974,13 +27216,13 @@ snapshots: typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.27.7 '@jest/transform': 30.0.2 '@jest/types': 30.0.1 - babel-jest: 29.7.0(@babel/core@7.28.0) + babel-jest: 29.7.0(@babel/core@7.27.7) jest-util: 30.0.2 - ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@30.0.2)(jest@29.7.0(@types/node@22.15.33)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3)))(typescript@5.8.3): + ts-jest@29.4.0(@babel/core@7.28.4)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@30.0.2)(jest@29.7.0(@types/node@22.15.33)(ts-node@10.9.2(@swc/core@1.12.7)(@types/node@22.15.33)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 @@ -26994,10 +27236,10 @@ snapshots: typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@jest/transform': 30.0.2 '@jest/types': 30.0.1 - babel-jest: 29.7.0(@babel/core@7.28.0) + babel-jest: 29.7.0(@babel/core@7.28.4) jest-util: 30.0.2 ts-loader@9.5.2(typescript@5.8.3)(webpack@5.99.9): @@ -27370,12 +27612,12 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@19.1.8)(react@19.1.0): + use-callback-ref@1.3.3(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 use-callback-ref@1.3.3(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -27384,11 +27626,11 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - use-composed-ref@1.4.0(@types/react@19.1.8)(react@19.1.0): + use-composed-ref@1.4.0(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 use-composed-ref@1.4.0(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -27396,11 +27638,11 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - use-isomorphic-layout-effect@1.2.1(@types/react@19.1.8)(react@19.1.0): + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 use-isomorphic-layout-effect@1.2.1(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -27408,12 +27650,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - use-latest@1.3.0(@types/react@19.1.8)(react@19.1.0): + use-latest@1.3.0(@types/react@19.1.9)(react@19.1.0): dependencies: react: 19.1.0 - use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.8)(react@19.1.0) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.9)(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 use-latest@1.3.0(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -27428,13 +27670,13 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - use-sidecar@1.1.3(@types/react@19.1.8)(react@19.1.0): + use-sidecar@1.1.3(@types/react@19.1.9)(react@19.1.0): dependencies: detect-node-es: 1.1.0 react: 19.1.0 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 use-sidecar@1.1.3(@types/react@19.1.9)(react@19.1.1): dependencies: @@ -27487,9 +27729,9 @@ snapshots: vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + vaul@1.1.2(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -28197,16 +28439,16 @@ snapshots: dependencies: zod: 3.24.2 - zustand@4.5.7(@types/react@19.1.8)(react@19.1.0): + zustand@4.5.7(@types/react@19.1.9)(react@19.1.0): dependencies: use-sync-external-store: 1.5.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 - zustand@5.0.7(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): + zustand@5.0.7(@types/react@19.1.9)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): optionalDependencies: - '@types/react': 19.1.8 + '@types/react': 19.1.9 react: 19.1.0 use-sync-external-store: 1.5.0(react@19.1.0) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0172ec53df..2c8b71869a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,7 @@ packages: - integ-tests/* - engine - engine/* + - engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/ - engine/baml-schema-wasm/* - fern # - engine/language_client_typescript/npm/darwin-arm64 diff --git a/tools/build b/tools/build index 3baf98e48e..1b9ead2558 100755 --- a/tools/build +++ b/tools/build @@ -119,6 +119,25 @@ case "$_path" in fi ;; + /engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox | /engine/baml-lib/baml/tests/validation_files/headers/mermaid-sandbox/* ) + command="cd ${_repo_root}/engine && UPDATE=1 cargo test -p baml-lib --test mermaid_graph_tests -- --nocapture" + + if [ "$_watch_mode" -eq 1 ]; then + npx nodemon \ + --ext rs,toml,baml \ + --watch "${_repo_root}/engine" \ + --ignore '**/target/**' \ + --ignore '**/target-rust-analyzer/**' \ + --ignore '**/tmp/**' \ + --ignore 'prompt_fiddle_example.baml' \ + --ignore 'baml_cli_init.baml' \ + --exec "${command}" + else + eval "${command}" + date + fi + ;; + /engine/baml-schema-wasm | /engine/baml-schema-wasm/* ) command="cargo build --target=wasm32-unknown-unknown" @@ -319,6 +338,21 @@ case "$_path" in fi ;; + /typescript/apps/fiddle-web-app | /typescript/apps/fiddle-web-app/* ) + command="(cd ${_repo_root} && turbo run dev --filter=@baml/fiddle-web-app... --force)" + + if [ "$_watch_mode" -eq 1 ]; then + npx nodemon \ + --ext grammar,js,ts,tsx,json \ + --ignore '**/dist' \ + --exec "${command}" + else + eval "${comamnd}" + date + fi + ;; + + /typescript/packages/codemirror-lang-baml | /typescript/packages/codemirror-lang-baml/* ) if [ "$_watch_mode" -eq 1 ]; then npx nodemon \ diff --git a/typescript/apps/ask-baml-client/package.json b/typescript/apps/ask-baml-client/package.json index 7df5754ee8..aeeeb1026b 100644 --- a/typescript/apps/ask-baml-client/package.json +++ b/typescript/apps/ask-baml-client/package.json @@ -28,8 +28,8 @@ "zod": "^4.0.10" }, "devDependencies": { - "@types/react": "^19.1.9", - "@types/react-dom": "^19.1.7", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "@types/react-syntax-highlighter": "^15.5.13", "css-loader": "^7.1.2", "jotai-devtools": "0.12.0", diff --git a/typescript/apps/fiddle-web-app/next.config.mjs b/typescript/apps/fiddle-web-app/next.config.mjs index 6935addb68..72a7627956 100644 --- a/typescript/apps/fiddle-web-app/next.config.mjs +++ b/typescript/apps/fiddle-web-app/next.config.mjs @@ -17,6 +17,7 @@ const nextConfig = { ...config.experiments, asyncWebAssembly: true, syncWebAssembly: true, + // esmExternals: 'loose', layers: true, topLevelAwait: true, }; diff --git a/typescript/apps/fiddle-web-app/package.json b/typescript/apps/fiddle-web-app/package.json index 704d437977..2d4f859202 100644 --- a/typescript/apps/fiddle-web-app/package.json +++ b/typescript/apps/fiddle-web-app/package.json @@ -43,8 +43,8 @@ "devDependencies": { "@types/http-proxy": "1.17.16", "@types/node": "24.0.3", - "@types/react": "19.1.8", - "@types/react-dom": "19.1.6", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "typescript": "5.8.3", "vitest": "3.2.4" } diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/01-extract-receipt.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/01-extract-receipt.baml deleted file mode 100644 index 5ce6543402..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/01-extract-receipt.baml +++ /dev/null @@ -1,64 +0,0 @@ -// This is a BAML file, which extends the Jinja2 templating language to write LLM functions. -// Run a test to see how it works! - -// https://docs.boundaryml.com - -// We want the LLM to extract this info from an image receipt -class Receipt { - establishment_name string - date string @description("ISO8601 formatted date") - total int @description("The total amount of the receipt") - currency string - items Item[] @description("The items on the receipt") -} - -class Item { - name string - price float - quantity int @description("If not specified, assume 1") -} - -// This is our LLM function we can call in Python or Typescript -// the receipt can be an image OR text here! -function ExtractReceipt(receipt: image | string) -> Receipt { - // see clients.baml - client GPT4o - prompt #" - {# start a user message #} - {{ _.role("user") }} - - Extract info from this receipt: - {{ receipt }} - - {# special macro to print the output schema instructions. #} - {{ ctx.output_format }} - "# -} - -// Test when the input is an image -test ImageReceiptTest { - functions [ExtractReceipt] - args { - receipt { url "https://i.redd.it/adzt4bz4llfc1.jpeg"} - } -} - -// Test when the input is a string -test StarbucksTextReceiptTest { - functions [ExtractReceipt] - args { - // use #""# for multi-line strings - receipt #" - Starbucks - Date: 2022-01-01 - Total: $5.00 USD - Items: - - Coffee - - $2.50 - - 1 - - Croissant - - $2.50 - - 1 - "# - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/02-extract-resume.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/02-extract-resume.baml deleted file mode 100644 index 526a5818dc..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/02-extract-resume.baml +++ /dev/null @@ -1,52 +0,0 @@ -// This is a BAML config file, which extends the Jinja2 templating language to write LLM functions. - -class Resume { - name string - education Education[] @description("Extract in the same order listed") - skills string[] @description("Only include programming languages") -} - -class Education { - school string - degree string - year int -} - -function ExtractResume(resume_text: string) -> Resume { - // see clients.baml - client GPT4o - - // The prompt uses Jinja syntax. Change the models or this text and watch the prompt preview change! - prompt #" - Parse the following resume and return a structured representation of the data in the schema below. - - Resume: - --- - {{ resume_text }} - --- - - {# special macro to print the output instructions. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test Test1 { - functions [ExtractResume] - args { - resume_text #" - John Doe - - Education - - University of California, Berkeley - - B.S. in Computer Science - - 2020 - - Skills - - Python - - Java - - C++ - "# - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/03-classify-user-msg.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/03-classify-user-msg.baml deleted file mode 100644 index 70aeb9127e..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/03-classify-user-msg.baml +++ /dev/null @@ -1,42 +0,0 @@ -// This will be available as an enum in your Python and Typescript code. -enum Category { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -class Message { - userName string - message string -} - -// inputs can be more complex than just a string -function ClassifyMessage(message: Message) -> Category { - client GPT4o - - prompt #" - Classify the following INPUT into ONE - of categories listed. - - INPUT: - --- - {{ message.userName }}: {{ message.message }} - --- - - {{ ctx.output_format }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessage] - args { - message { - userName "Alice" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/04-clients.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/04-clients.baml deleted file mode 100644 index 4dd85736d5..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/04-clients.baml +++ /dev/null @@ -1,46 +0,0 @@ -// These are LLM clients you can use in your functions. We currently support Anthropic, OpenAI / Azure, Gemini, and Ollama as providers. - -// We also support any other provider that follows the OpenAI API specification, such as HuggingFace. - -// For this playground, we have setup a few clients for you to use already with some free credits. - -client GPT4 { - // Use one of the following: https://docs.boundaryml.com/docs/snippets/clients/providers/openai - provider openai - // You can pass in any parameters from the OpenAI Python documentation into the options block. - options { - model gpt-4 - api_key env.OPENAI_API_KEY - } -} - -client GPT4o { - provider openai - retry_policy CustomRetry - options { - model gpt-4o - api_key env.OPENAI_API_KEY - } -} - -retry_policy CustomRetry { - max_retries 3 -} - -client Claude { - provider anthropic - options { - model claude-3-haiku-20240307 - api_key env.ANTHROPIC_API_KEY - max_tokens 1000 - - } -} - -client Gemini { - provider google-ai - options { - model "gemini-2.5-flash" - api_key env.GOOGLE_API_KEY - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/fallbacks.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/fallbacks.baml deleted file mode 100644 index f7f51e4746..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/fallbacks.baml +++ /dev/null @@ -1,17 +0,0 @@ -// You can use the fallback provider to add more resilience to your application. - -// A fallback will attempt to use the first client, and if it fails, it will try the second client, and so on. - -// https://docs.boundaryml.com/docs/snippets/clients/fallback - -client SuperDuperClient { - provider fallback - options { - // clients from clients.baml - strategy [ - GPT4 - GPT4o - Gemini - ] - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/retries.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/retries.baml deleted file mode 100644 index 202e30334e..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/retries.baml +++ /dev/null @@ -1,17 +0,0 @@ -// An example of a BAML client with a retry policy - -// https://docs.boundaryml.com/docs/snippets/clients/retry - - -retry_policy MyPolicyName { - max_retries 3 -} - -client MyClient { - provider anthropic - retry_policy MyPolicyName - options { - model "claude-3-sonnet-20240229" - api_key env.ANTHROPIC_API_KEY - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/roundrobin.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/roundrobin.baml deleted file mode 100644 index f99098ddb0..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/adding-resiliency/roundrobin.baml +++ /dev/null @@ -1,11 +0,0 @@ -// The round_robin provider allows you to distribute requests across multiple clients in a round-robin fashion. After each call, the next client in the list will be used. - -client MyRoundRobinClient { - provider round-robin - options { - strategy [ - GPT4o - Gemini - ] - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/complex_headers_test.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/complex_headers_test.baml new file mode 100644 index 0000000000..be7fe9c0ab --- /dev/null +++ b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/complex_headers_test.baml @@ -0,0 +1,21 @@ + +//# Main Function +function ComplexHeaderTest(x: int) -> int { + //# increment by 10 + if (x < 10) { + x = x + 10; + } else { + //## dummy variable y + let y = 5; + } + + //# another increment + x + 1234 +} + +test Foo { + functions [ComplexHeaderTest] + args { + x 5 + } +} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/01-single-function.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/01-single-function.baml deleted file mode 100644 index ecee880996..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/01-single-function.baml +++ /dev/null @@ -1,28 +0,0 @@ -class WeatherAPI { - city string @description("the user's city") - timeOfDay string @description("As an ISO8601 timestamp") -} - -function UseTool(user_message: string) -> WeatherAPI { - client GPT4o - prompt #" - Extract the info from this message - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestOne { - functions [UseTool] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - "# - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/02-choose-1-function.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/02-choose-1-function.baml deleted file mode 100644 index 127ea1b06c..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/02-choose-1-function.baml +++ /dev/null @@ -1,43 +0,0 @@ -class WeatherAPI2 { - city string @description("the user's city") - timeOfDay string @description("As an ISO8601 timestamp") -} - -class SendEmail { - emailTo string - emailSubject string - emailBody string -} - -function ChooseOneTool(user_message: string) -> WeatherAPI2 | SendEmail { - client GPT4o - prompt #" - Choose the right schema that contains all the information in this message: - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestOneFunc { - functions [ChooseOneTool] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - "# - } -} - -test TestOneFunc2 { - functions [ChooseOneTool] - args { - user_message #" - Send an email to John Doe with the subject 'Hello' and the body 'How are you doing?' - "# - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/03-choose-n-functions.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/03-choose-n-functions.baml deleted file mode 100644 index 961493460d..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/function-calling(tools)/03-choose-n-functions.baml +++ /dev/null @@ -1,27 +0,0 @@ - -function ChooseNTools(user_message: string) -> (WeatherAPI2 | SendEmail)[] { - client GPT4o - prompt #" - Choose the right schema(s) that contains all the information in this message: - --- - {{ user_message }} - --- - - {# special macro to print the output schema. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test TestNTools { - functions [ChooseNTools] - args { - user_message #" - what's the weather in san francisco tomorrow april 23rd 2024 at 3pm? - - Also send an email to Mark Gonzalez with the subject 'Hello' and the body 'How are you doing?' - "# - } -} - \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/audio-input.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/audio-input.baml deleted file mode 100644 index 1177a3bbe6..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/audio-input.baml +++ /dev/null @@ -1,24 +0,0 @@ -// BAML supports multi-modal inputs! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#image - -function AudioInput(audioInput: audio) -> string { - client Gemini - prompt #" - {{ _.role("user") }} - - Does this sound like a roar? Yes or no? One word no other characters. - - {{ audioInput }} - "# -} - - -test TestURLAudioInput{ - functions [AudioInput] - args { - audioInput { - url https://actions.google.com/sounds/v1/emergency/beeper_emergency_call.ogg - } - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/image-input.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/image-input.baml deleted file mode 100644 index 48c13e5a3a..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/image-input.baml +++ /dev/null @@ -1,28 +0,0 @@ -// BAML supports multi-modal inputs! Check the raw cURL request toggle in the playground to see the prompt is transformed into an API call - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#image - -class ImageDescription { - description string - tags string[] @description(#" - Tags that describe the image - "#) -} - -function DescribeImage(myImage: image) -> ImageDescription { - client GPT4o - prompt #" - {{ _.role("user") }} - Describe this in 2 sentences: {{ myImage }} - - {{ ctx.output_format }} - "# -} - - -test ImageTest { - functions [DescribeImage] - args { - myImage { url "https://imgs.xkcd.com/comics/standards.png"} - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/pdf-input.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/pdf-input.baml deleted file mode 100644 index 9e7ec4bb71..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/pdf-input.baml +++ /dev/null @@ -1,23 +0,0 @@ -// BAML supports multi-modal inputs including Pdfs! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#pdf - -function PdfInput(pdfInput: pdf) -> string { - client GPT4o - prompt #" - {{ _.role("user") }} - - Get the title of the pdfs - - {{ pdfInput }} - "# -} - -test TestFilePdfInput { - functions [PdfInput] - args { - pdfInput { - url "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" - } - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/video-input.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/video-input.baml deleted file mode 100644 index 0c97b8f34e..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/multi-modal/video-input.baml +++ /dev/null @@ -1,25 +0,0 @@ -// BAML supports multi-modal inputs including videos! - -// How to call this BAML function in python or Typescript: https://docs.boundaryml.com/docs/snippets/supported-types#video -// Note: Video input only works with Google models (Gemini) - -function VideoInput(videoInput: video) -> string { - client Gemini - prompt #" - {{ _.role("user") }} - - Describe what happens in this video in 5 words or less. - - {{ videoInput }} - "# -} - -test TestFileVideoInput { - functions [VideoInput] - args { - videoInput { - url "https://www.youtube.com/watch?v=dQw4w9WgXcQ" - media_type "video/mp4" - } - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/00-extract-resume.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/00-extract-resume.baml deleted file mode 100644 index 60931677bd..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/00-extract-resume.baml +++ /dev/null @@ -1,53 +0,0 @@ -class OResume { - name string - education OEducation[] @description("Extract in the same order listed") - skills string[] @description("Only include programming languages") -} - -class OEducation { - school string - degree string - year int -} - -function ExtractResumeUsingOllama(resume_text: string) -> OResume { - // see ollama-clients.baml - client Llama3 - // client Mistral - // client Gemma2 - // client Phi3 - - // The prompt uses Jinja syntax. Change the models or this text and watch the prompt preview change! - prompt #" - Parse the following resume and return a structured representation of the data in the schema below. - - Resume: - --- - {{ resume_text }} - --- - - {# special macro to print the output instructions. #} - {{ ctx.output_format }} - - JSON: - "# -} - -test OllamaTest1 { - functions [ExtractResumeUsingOllama] - args { - resume_text #" - John Doe - - Education - - University of California, Berkeley - - B.S. in Computer Science - - 2020 - - Skills - - Python - - Java - - C++ - "# - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/01-classify-message.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/01-classify-message.baml deleted file mode 100644 index 53c2c2dc3c..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/01-classify-message.baml +++ /dev/null @@ -1,46 +0,0 @@ -enum OCategory { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -class OMessage { - userName string - message string -} - -// inputs can be more complex than just a string -// This will be available as an enum in your Python and Typescript code. -// inputs can be more complex than just a string -function ClassifyMessageUsingOllama(message: OMessage) -> OCategory { - client Llama3 - // client Mistral - // client Gemma2 - // client Phi3 - - prompt #" - Classify the following INPUT into ONE - of categories listed. - - INPUT: - --- - {{ message.userName }}: {{ message.message }} - --- - - {{ ctx.output_format }} - - Response: - "# -} - -test OllamaTest1 { - functions [ClassifyMessageUsingOllama] - args { - message { - userName "Alice" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/ollama-clients.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/ollama-clients.baml deleted file mode 100644 index 2a45e43e61..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/ollama/ollama-clients.baml +++ /dev/null @@ -1,51 +0,0 @@ -client Llama3 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull llama3` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "llama3" - } -} - -client Mistral { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull mistral` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "mistral" - } -} - -client Gemma2 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull gemma2` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "gemma2" - } -} - -client Phi3 { - // See https://docs.boundaryml.com/docs/snippets/clients/providers/ollama - // to learn more about how to configure this client - // - // Note that you should run ollama using `OLLAMA_ORIGINS='*' ollama serve` - // and you'll also need to `ollama pull phi3` to use this client - provider ollama - options { - base_url "http://localhost:11434/v1" - model "phi3" - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/chain-of-thought.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/chain-of-thought.baml deleted file mode 100644 index 1f05e07a06..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/chain-of-thought.baml +++ /dev/null @@ -1,76 +0,0 @@ -// BAML excels at parsing the schemas from LLM responses. - -// In this example we tell the model to think step by step and explain its reasoning -// before outputting the answer. - -// Run this prompt, and you'll notice the BAML function still returns the expected return type (OrderInfo), -// even though there is a bunch of "reasoning" text present in the "raw text". It works with streaming or even standalone enum outputs too! -class Email { - subject string - body string - from_address string -} - -enum OrderStatus { - ORDERED - SHIPPED - DELIVERED - CANCELLED -} - -class OrderInfo { - order_status OrderStatus - tracking_number string? - estimated_arrival_date string? -} - -function GetOrderInfo(email: Email) -> OrderInfo { - client GPT4o - prompt #" - Extract the info from this email in the INPUT: - - INPUT: - ------- - from: {{email.from_address}} - Email Subject: {{email.subject}} - Email Body: {{email.body}} - ------- - - {{ ctx.output_format }} - - Before you output the JSON, please explain your - reasoning step-by-step. Here is an example on how to do this: - 'If we think step by step we can see that ... - therefore the output JSON is: - { - ... the json schema ... - }' - "# -} - -test Test1 { - functions [GetOrderInfo] - args { - email { - from_address "hello@amazon.com" - subject "Your Amazon.com order of 'Wood Dowel Rods...' has shipped!" - body #" - Hi Sam, your package will arrive: - Thurs, April 4 - Track your package: - www.amazon.com/gp/your-account/ship-track?ie=23&orderId123 - - On the way: - Wood Dowel Rods... - Order #113-7540940 - Ship to: - Sam - SEATTLE, WA - - Shipment total: - $0.00 - "# - - } - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/symbol-tuning.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/symbol-tuning.baml deleted file mode 100644 index 54f3edcb83..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-engineering/symbol-tuning.baml +++ /dev/null @@ -1,45 +0,0 @@ -// You can improve results by aliasing field names to symbols like "k1" ... "kN". -// This makes the LLM pay attention to your descriptions instead of just the enum or property name, since those can introduce biases. See paper: https://arxiv.org/abs/2305.08298 for more details. - -// Check the prompt preview to see that aliases are being sent. - -// When you add aliases you don't need to change your resulting python code. Your models -// stay the same. They are merely a way to aid in prompt engineering. -enum MyClass { - Refund @alias("k1") - @description("Customer wants to refund a product") - - CancelOrder @alias("k2") - @description("Customer wants to cancel an order") - - TechnicalSupport @alias("k3") - @description("Customer needs help with a technical issue unrelated to account creation or login") - - AccountIssue @alias("k4") - @description("Specifically relates to account-login or account-creation") - - Question @alias("k5") - @description("Customer has a question") -} - -function ClassifyMessageWithSymbol(input: string) -> MyClass { - client GPT4o - - prompt #" - Classify the following INPUT into ONE - of the following categories: - - INPUT: {{ input }} - - {{ ctx.output_format }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessageWithSymbol] - args { - input "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/chat-roles.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/chat-roles.baml deleted file mode 100644 index c76d493b2c..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/chat-roles.baml +++ /dev/null @@ -1,36 +0,0 @@ -// This will be available as an enum in your Python and Typescript code. -enum MyCategory { - Refund - CancelOrder - TechnicalSupport - AccountIssue - Question -} - -function ClassifyMessageWithRoles(input: string) -> MyCategory { - client GPT4o - - prompt #" - {# _.role("system") starts a system message #} - {{ _.role("system") }} - - Classify the following INPUT into ONE - of the following categories: - - {{ ctx.output_format }} - - {# This starts a user message #} - {{ _.role("user") }} - - INPUT: {{ input }} - - Response: - "# -} - -test Test1 { - functions [ClassifyMessageWithRoles] - args { - input "Can't access my account using my usual login credentials, and each attempt results in an error message stating 'Invalid username or password.' I have tried resetting my password using the 'Forgot Password' link, but I haven't received the promised password reset email." - } -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/conditionals.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/conditionals.baml deleted file mode 100644 index f6a28cfb09..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/conditionals.baml +++ /dev/null @@ -1,15 +0,0 @@ -class User { - name string - is_active bool -} - -function FunctionWithConditionals(user: User) -> string { - client GPT4o - prompt #" - {% if user.is_active %} - Greet the user {{ user.name }}! - {% else %} - Tell the user to activate their account - {% endif %} - "# -} \ No newline at end of file diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/loops.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/loops.baml deleted file mode 100644 index dfcc78f165..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/loops.baml +++ /dev/null @@ -1,29 +0,0 @@ -class MyUserMessage { - user_name string - content string -} - -function FunctionWithLoops(messages: MyUserMessage[]) -> string { - client GPT4o - prompt #" - {% for message in messages %} - {{ message.user_name }}: {{ message.content }} - {% endfor %} - "# -} - -test TestName { - functions [FunctionWithLoops] - args { - messages [ - { - user_name "Alice" - content "Hello!" - } - { - user_name "Bob" - content "Hi!" - } - ] - } -} diff --git a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/variables.baml b/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/variables.baml deleted file mode 100644 index 1d3135a753..0000000000 --- a/typescript/apps/fiddle-web-app/public/_examples/all-projects/baml_src/prompt-syntax/variables.baml +++ /dev/null @@ -1,44 +0,0 @@ -// We can define other "template string" variables that can be used in a prompt. -class UserMessage { - message string - role string -} - -// prompt strings and template strings use Jinja2 syntax: https://jinja.palletsprojects.com/en/3.1.x/templates/ -template_string PrintMessages(messages: UserMessage[]) #" - {% for m in messages %} - {{ _.role(m.role) }} - {{ m.message }} - {% endfor %} -"# - -function ClassifyConversation(messages: UserMessage[]) -> Category[] { - client GPT4o - prompt #" - Classify this conversation: - {{ PrintMessages(messages) }} - - Use the following categories: - {{ ctx.output_format}} - "# -} - -test TestClassifyConvo { - functions [ClassifyConversation] - args { - messages [ - { - role "user" - message "I can't access my account using my login credentials. I havent received the promised reset password email. Please help." - } - { - role "assistant" - message "I'm sorry to hear that. Let me help you with that." - } - { - role "user" - message "Thank you." - } - ] - } -} diff --git a/typescript/apps/playground/package.json b/typescript/apps/playground/package.json index 577d55a6c3..34673a5fb0 100644 --- a/typescript/apps/playground/package.json +++ b/typescript/apps/playground/package.json @@ -25,8 +25,8 @@ }, "devDependencies": { "@types/node": "24.0.3", - "@types/react": "19.1.8", - "@types/react-dom": "19.1.6", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "@vitejs/plugin-react": "4.5.2", "rimraf": "6.0.1", "vite": "7.0.0", diff --git a/typescript/apps/sage-backend/package.json b/typescript/apps/sage-backend/package.json index 8cb7b4971a..cfcd58f760 100644 --- a/typescript/apps/sage-backend/package.json +++ b/typescript/apps/sage-backend/package.json @@ -34,8 +34,8 @@ "@types/cheerio": "^0.22.35", "@types/lodash": "4.17.17", "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "gray-matter": "^4.0.3", "tailwindcss": "^4", "tsx": "^4.20.3", diff --git a/typescript/apps/vscode-ext/package.json b/typescript/apps/vscode-ext/package.json index d975cfb2c1..e3202b8322 100644 --- a/typescript/apps/vscode-ext/package.json +++ b/typescript/apps/vscode-ext/package.json @@ -12,7 +12,12 @@ "engines": { "vscode": ">=1.63.0" }, - "categories": ["Programming Languages", "Debuggers", "Testing", "Other"], + "categories": [ + "Programming Languages", + "Debuggers", + "Testing", + "Other" + ], "main": "./dist/extension.js", "activationEvents": [ "onStartupFinished", @@ -22,13 +27,18 @@ "onLanguage:typescriptreact" ], "contributes": { - "activationEvents": ["onLanguage:baml"], + "activationEvents": [ + "onLanguage:baml" + ], "configuration": { "type": "object", "title": "Baml extension settings", "properties": { "baml.cliPath": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "description": "Use the locally installed baml CLI instead of the one bundled with the extension. Helps keeps generated code more in sync.", "scope": "machine-overridable" @@ -36,13 +46,20 @@ "baml.generateCodeOnSave": { "type": "string", "default": "always", - "enum": ["never", "always"], + "enum": [ + "never", + "always" + ], "description": "Generate code on save" }, "baml.syncExtensionToGeneratorVersion": { "type": "string", "default": "auto", - "enum": ["auto", "never", "always"], + "enum": [ + "auto", + "never", + "always" + ], "description": "Sync the extension to the currently selected baml_src generator version (provided all your generators have the same version). This will automatically download the correct version of the baml-cli to generate the client code -- preventing issues with mismatched versions. Note that on some platforms, if you have this on `auto`, it may be disabled." }, "baml.enablePlaygroundProxy": { @@ -59,7 +76,11 @@ "baml.trace.server": { "scope": "window", "type": "string", - "enum": ["off", "messages", "verbose"], + "enum": [ + "off", + "messages", + "verbose" + ], "default": "off", "description": "Setting for logging between the VS Code extension and the language server." }, @@ -82,7 +103,10 @@ "type": "array", "items": { "type": "string", - "enum": ["beta", "display_all_warnings"] + "enum": [ + "beta", + "display_all_warnings" + ] }, "uniqueItems": true, "default": [], @@ -93,8 +117,13 @@ "languages": [ { "id": "baml", - "aliases": ["Baml", "baml"], - "extensions": [".baml"], + "aliases": [ + "Baml", + "baml" + ], + "extensions": [ + ".baml" + ], "configuration": "./language-configuration.json", "icon": { "light": "./baml-logo.png", @@ -103,8 +132,14 @@ }, { "id": "baml-jinja", - "aliases": ["Jinja baml", "jinja baml"], - "extensions": [".baml.jinja", ".baml.jinja"], + "aliases": [ + "Jinja baml", + "jinja baml" + ], + "extensions": [ + ".baml.jinja", + ".baml.jinja" + ], "configuration": "./language-configuration.json" } ], @@ -208,8 +243,8 @@ "vscode-tmgrammar-test": "0.1.3", "@types/express": "5.0.3", "@types/node": "24.0.3", - "@types/react": "19.1.8", - "@types/react-dom": "19.1.6", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "@types/semver": "7.7.0", "@types/tar": "6.1.13", "@types/vscode": "1.63.0", diff --git a/typescript/packages/playground-common/package.json b/typescript/packages/playground-common/package.json index e8591582c2..70e99d0bef 100644 --- a/typescript/packages/playground-common/package.json +++ b/typescript/packages/playground-common/package.json @@ -73,9 +73,10 @@ "react-icons": "5.5.0", "react-pdf": "9.0.0", "react-resizable-panels": "3.0.3", + "react-svg-pan-zoom": "^3.13.1", + "react-zoom-pan-pinch": "^3.7.0", "rehype-stringify": "10.0.1", "shiki": "1.24.4", - "svg-pan-zoom": "^3.6.2", "swr": "2.3.3", "tailwind-merge": "3.3.1", "unist-util-visit": "5.0.0", @@ -86,8 +87,8 @@ "@types/he": "1.2.3", "@types/lodash": "4.17.17", "@types/node": "24.0.3", - "@types/react": "19.1.8", - "@types/react-dom": "19.1.6", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "@types/vscode": "1.96.0", "jotai-devtools": "0.12.0", "react-dom": "19.1.0", diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/atoms.ts b/typescript/packages/playground-common/src/shared/baml-project-panel/atoms.ts index 23077ea731..9a05221eb3 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/atoms.ts +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/atoms.ts @@ -1,4 +1,4 @@ -import { atom, useAtomValue, useSetAtom } from 'jotai'; +import { atom, getDefaultStore, useAtomValue, useSetAtom } from 'jotai'; import { atomFamily, atomWithStorage } from 'jotai/utils'; import { useEffect } from 'react'; @@ -100,7 +100,7 @@ export const useClearWasmPanic = () => { // Unified beta feature atom that works in both VS Code and standalone environments export const betaFeatureEnabledAtom = atom((get) => { const isInVSCode = isVSCodeEnvironment(); - + if (isInVSCode) { // In VSCode: try vscodeSettingsAtom, then bamlConfig fallback const vscodeSettings = get(vscodeSettingsAtom); @@ -117,15 +117,46 @@ export const betaFeatureEnabledAtom = atom((get) => { } }); -const wasmAtomAsync = atom(async () => { + +let wasmAtomAsync = atom(async () => { const wasm = await import('@gloo-ai/baml-schema-wasm-web/baml_schema_build'); // Enable WASM logging for debugging wasm.init_js_callback_bridge(vscode.loadAwsCreds, vscode.loadGcpCreds); return wasm; -}); +}, + + async (_get, set, newValue: null) => { + set(wasmAtomAsync, null) + } +); export const wasmAtom = unwrap(wasmAtomAsync); +const store = getDefaultStore(); + +declare const module: { + hot?: { + dispose(callback: () => void): void; + }; +}; + +if (module.hot) { + module.hot.dispose(() => { + console.log("sam HMR module.hot.dispose") + wasmAtomAsync = atom(async () => { + const wasm = await import('@gloo-ai/baml-schema-wasm-web/baml_schema_build'); + // Enable WASM logging for debugging + wasm.init_js_callback_bridge(vscode.loadAwsCreds, vscode.loadGcpCreds); + return wasm; + }, + + async (_get, set, newValue: null) => { + set(wasmAtomAsync, null) + } + ) + }); +} + export const useWaitForWasm = () => { const wasm = useAtomValue(wasmAtom); return wasm !== undefined; @@ -188,7 +219,7 @@ export const runtimeAtom = atom<{ // Determine environment and get appropriate feature flags const isInVSCode = isVSCodeEnvironment(); let featureFlags: string[]; - + if (isInVSCode) { // In VSCode: try vscodeSettingsAtom, then bamlConfig fallback const vscodeSettings = get(vscodeSettingsAtom); diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/feature-flags.ts b/typescript/packages/playground-common/src/shared/baml-project-panel/feature-flags.ts index c2f401bdec..8e93844141 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/feature-flags.ts +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/feature-flags.ts @@ -2,8 +2,16 @@ import { atom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; import { sessionStore } from '../../baml_wasm_web/JotaiProvider'; +const betaDefaultValue = (process.env.NEXT_PUBLIC_FIDDLE_BETA_DEFAULT ?? '').toLowerCase(); +const betaDefaults = ['1', 'true', 'yes', 'on']; +const defaultStandaloneFlags = betaDefaults.includes(betaDefaultValue) ? ['beta'] : []; + // Feature flags atom for standalone playground -export const standaloneFeatureFlagsAtom = atomWithStorage('baml-feature-flags', [], sessionStore); +export const standaloneFeatureFlagsAtom = atomWithStorage( + 'baml-feature-flags', + defaultStandaloneFlags, + sessionStore, +); // Beta feature flag convenience atom for standalone use (with setter) export const standaloneBetaFeatureEnabledAtom = atom( @@ -21,4 +29,4 @@ export const standaloneBetaFeatureEnabledAtom = atom( export const isVSCodeEnvironment = () => { if (typeof window === 'undefined') return false; return 'acquireVsCodeApi' in window; -}; \ No newline at end of file +}; diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/atoms-orch-graph.ts b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/atoms-orch-graph.ts index 49ff16b1d0..a44cdeec76 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/atoms-orch-graph.ts +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/atoms-orch-graph.ts @@ -105,6 +105,11 @@ export const functionGraphAtom = atom( length: typeof graph === 'string' ? graph.length : undefined, preview: typeof graph === 'string' ? graph.slice(0, 160) : graph, }); + if (typeof graph !== 'string') { + console.error('[functionGraphAtom] graph is not a string', { type: typeof graph }); + } else if (graph.trim().length === 0) { + console.warn('[functionGraphAtom] graph string is empty after trimming'); + } return { graph }; } catch (e) { console.error('[functionGraphAtom] failed to generate graph', e); diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-preview-content.tsx b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-preview-content.tsx index a737198d3d..21d89b4b28 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-preview-content.tsx +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-preview-content.tsx @@ -34,16 +34,34 @@ export const PromptPreviewContent = () => { ) { return; } - const newPreview = await selectedFn.render_prompt_for_test( - rt, - selectedTc.name, - ctx, - vscode.loadMediaFile, - apiKeys, - ); - setLastKnownPreview(newPreview); - setPromptData(newPreview); - return newPreview; + console.log('[PromptPreview] Attempt render_prompt_for_test', { + functionName: selectedFn.name, + testCaseName: selectedTc.name, + hasExprTests: selectedFn.test_cases?.length ?? 0, + }); + try { + const newPreview = await selectedFn.render_prompt_for_test( + rt, + selectedTc.name, + ctx, + vscode.loadMediaFile, + apiKeys, + ); + console.log('[PromptPreview] render_prompt_for_test success', { + functionName: selectedFn.name, + testCaseName: selectedTc.name, + }); + setLastKnownPreview(newPreview); + setPromptData(newPreview); + return newPreview; + } catch (error) { + console.error('[PromptPreview] render_prompt_for_test failed', { + functionName: selectedFn.name, + testCaseName: selectedTc.name, + error, + }); + throw error; + } }, [rt, ctx, selectedFn, selectedTc, apiKeys, files, setPromptData], ); diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-render-wrapper.tsx b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-render-wrapper.tsx index 7506ef30c9..de5e3441ed 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-render-wrapper.tsx +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/prompt-render-wrapper.tsx @@ -84,12 +84,27 @@ const FunctionMetadata: React.FC = () => { export const PromptRenderWrapper = () => { const [displaySettings, setDisplaySettings] = useAtom(displaySettingsAtom); const renderedPrompt = useAtomValue(renderedPromptAtom); - const [showCopied, setShowCopied] = React.useState(false); const { open: isSidebarOpen } = useSidebar(); const isBetaEnabled = useAtomValue(betaFeatureEnabledAtom); - const [activeTab, setActiveTab] = React.useState<'preview' | 'curl' | 'client-graph' | 'mermaid-graph'>('preview'); + const [activeTab, setActiveTab] = React.useState<'preview' | 'curl' | 'client-graph' | 'mermaid-graph'>( + () => (isBetaEnabled ? 'mermaid-graph' : 'preview'), + ); + const [showCopied, setShowCopied] = React.useState(false); + const betaWasEnabledRef = React.useRef(isBetaEnabled); const curl = useAtomValue(curlAtom); + React.useEffect(() => { + const wasEnabled = betaWasEnabledRef.current; + + if (!isBetaEnabled && activeTab === 'mermaid-graph') { + setActiveTab('preview'); + } else if (!wasEnabled && isBetaEnabled && activeTab === 'preview') { + setActiveTab('mermaid-graph'); + } + + betaWasEnabledRef.current = isBetaEnabled; + }, [activeTab, isBetaEnabled]); + // Hide text when sidebar is open or on smaller screens const getButtonTextClass = () => { if (isSidebarOpen) { diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/components/MermaidGraphView.tsx b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/components/MermaidGraphView.tsx index c51e6e41cc..011ea888bc 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/components/MermaidGraphView.tsx +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/components/MermaidGraphView.tsx @@ -4,7 +4,8 @@ import { useEffect, useRef, useCallback } from 'react'; import mermaid from 'mermaid'; import { Button } from '@baml/ui/button'; import { ZoomIn, ZoomOut, Maximize2 } from 'lucide-react'; -import svgPanZoom from 'svg-pan-zoom'; +import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'; +import type { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'; import { functionGraphAtom } from '../../../atoms-orch-graph'; import { vscode } from '../../../../vscode'; import { flashRangesAtom } from '../../../atoms'; @@ -129,22 +130,64 @@ export const MermaidGraphView: React.FC = () => { const mermaidRef = useRef(null); const containerRef = useRef(null); const svgRef = useRef(null); - const panZoomRef = useRef | null>(null); - const setFlashRanges = useSetAtom(flashRangesAtom) + const transformRef = useRef(null); + const setFlashRanges = useSetAtom(flashRangesAtom); + + const computeFitScale = useCallback(() => { + const containerEl = containerRef.current; + const svgEl = svgRef.current; + if (!containerEl || !svgEl) return 1; + + const containerRect = containerEl.getBoundingClientRect(); + if (containerRect.width <= 0 || containerRect.height <= 0) return 1; + + let bbox: DOMRect | null = null; + try { + const rawBox = svgEl.getBBox(); + if (rawBox.width > 0 && rawBox.height > 0) { + bbox = rawBox as DOMRect; + } + } catch { + // getBBox may throw if SVG isn't in the DOM yet; fall back to client rect + } + + const targetBox = bbox ?? svgEl.getBoundingClientRect(); + const svgWidth = targetBox.width; + const svgHeight = targetBox.height; + if (svgWidth <= 0 || svgHeight <= 0) return 1; + + const PADDING = 32; + const availableWidth = Math.max(containerRect.width - PADDING, 1); + const availableHeight = Math.max(containerRect.height - PADDING, 1); + const widthRatio = availableWidth / svgWidth; + const heightRatio = availableHeight / svgHeight; + const fitScale = Math.min(widthRatio, heightRatio); + if (!Number.isFinite(fitScale) || fitScale <= 0) return 1; + return Math.min(1, fitScale); + }, []); const zoomIn = useCallback(() => { - panZoomRef.current?.zoomBy(1.2); + transformRef.current?.zoomIn(0.2); }, []); const zoomOut = useCallback(() => { - panZoomRef.current?.zoomBy(1 / 1.2); + transformRef.current?.zoomOut(0.2); }, []); + const centerGraph = useCallback((scale?: number, animationTime = 0) => { + const instance = transformRef.current; + if (!instance) return; + const fallbackScale = computeFitScale(); + const nextScale = typeof scale === 'number' ? scale : fallbackScale; + if (!Number.isFinite(nextScale)) return; + instance.centerView(nextScale, animationTime); + }, [computeFitScale]); const resetView = useCallback(() => { - if (!panZoomRef.current) return; - panZoomRef.current.resetZoom(); - panZoomRef.current.resize(); - panZoomRef.current.fit(); - panZoomRef.current.center(); - }, []); + const instance = transformRef.current; + if (!instance) return; + requestAnimationFrame(() => { + const initialScale = computeFitScale(); + centerGraph(initialScale, 200); + }); + }, [centerGraph, computeFitScale]); useEffect(() => { if (!graph || !mermaidRef.current) return; @@ -158,10 +201,7 @@ export const MermaidGraphView: React.FC = () => { } catch { } const onResize = () => { - if (!panZoomRef.current) return; - panZoomRef.current.resize(); - panZoomRef.current.fit(); - panZoomRef.current.center(); + requestAnimationFrame(() => centerGraph()); }; window.addEventListener('resize', onResize); @@ -237,22 +277,14 @@ export const MermaidGraphView: React.FC = () => { const { svg } = await mermaid.render('bamlMermaidSvg', graph); if (isCancelled || !mermaidRef.current) return; - const normalizedSvg = svg - .replace(/[ ]*max-width:[ 0-9\.]*px;/i, '') - .replace(/width="[0-9\.]+px"/i, '') - .replace(/height="[0-9\.]+px"/i, ''); + const normalizedSvg = svg.replace(/[ ]*max-width:[ 0-9\.]*px;/i, ''); mermaidRef.current.innerHTML = normalizedSvg; const svgEl = mermaidRef.current.querySelector('#bamlMermaidSvg') as SVGSVGElement | null; if (!svgEl) return; svgRef.current = svgEl; - // Make the SVG fill the container - svgEl.setAttribute('width', '100%'); - svgEl.setAttribute('height', '100%'); + // Ensure the SVG keeps its natural size while preserving aspect ratio svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet'); - svgEl.style.width = '100%'; - svgEl.style.height = '100%'; - svgEl.style.display = 'block'; // Inject CSS override so it takes precedence over Mermaid defaults inside the SVG try { @@ -289,32 +321,11 @@ export const MermaidGraphView: React.FC = () => { } catch { } // Keep arrowhead markers near default sizing (no scaling), only color is overridden via CSS - - // Initialize svg-pan-zoom on the generated SVG - panZoomRef.current = svgPanZoom(svgEl as unknown as SVGElement, { - panEnabled: true, - zoomEnabled: true, - controlIconsEnabled: false, - fit: true, - center: true, - minZoom: 0.25, - maxZoom: 4, - zoomScaleSensitivity: 0.3, - preventMouseEventsDefault: false, + requestAnimationFrame(() => { + const initialScale = computeFitScale(); + centerGraph(initialScale); }); - // After init, ensure proper fit/center - panZoomRef.current.resize(); - panZoomRef.current.fit(); - panZoomRef.current.center(); - - // Prevent wheel from scrolling parent while zooming/panning - const wheelPreventer = (e: WheelEvent) => { - e.preventDefault(); - }; - // Attach to the container hosting the SVG - svgEl.addEventListener('wheel', wheelPreventer, { passive: false }); - // Parse span map from a special mermaid comment emitted at the end // Look for a comment node like %%__BAML_SPANMAP__={...} const raw = graph as string; @@ -413,10 +424,7 @@ export const MermaidGraphView: React.FC = () => { // Observe container size changes (ResizablePanel drags) if (containerRef.current && typeof ResizeObserver !== 'undefined') { resizeObserver = new ResizeObserver(() => { - if (!panZoomRef.current) return; - panZoomRef.current.resize(); - panZoomRef.current.fit(); - panZoomRef.current.center(); + requestAnimationFrame(() => centerGraph()); }); resizeObserver.observe(containerRef.current); } @@ -429,36 +437,51 @@ export const MermaidGraphView: React.FC = () => { isCancelled = true; window.removeEventListener('resize', onResize); if (resizeObserver) resizeObserver.disconnect(); - panZoomRef.current?.destroy(); - panZoomRef.current = null; try { const svgEl = svgRef.current; - if (svgEl) { - svgEl.removeEventListener('wheel', (e: any) => e.preventDefault()); - } if ((window as any).__bamlCleanupListeners) { (window as any).__bamlCleanupListeners(); delete (window as any).__bamlCleanupListeners; } } catch { } }; - }, [graph]); + }, [graph, centerGraph, computeFitScale]); return (
-
+
+ + +
+
+
+ + +
{/* Controls overlay */}
@@ -478,5 +501,3 @@ export const MermaidGraphView: React.FC = () => {
); }; - - diff --git a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/test-runner.ts b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/test-runner.ts index 9dfa9c74c1..8676e8e0aa 100644 --- a/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/test-runner.ts +++ b/typescript/packages/playground-common/src/shared/baml-project-panel/playground-panel/prompt-preview/test-panel/test-runner.ts @@ -116,6 +116,12 @@ const useRunTests = (maxBatchSize = 5) => { hasSignal: !!controller.signal, signalAborted: controller.signal.aborted }) + console.log('[TestRunner] Starting run_test_with_expr_events', { + functionName: testCase.fn.name, + testCaseName: testCase.tc.name, + signature: testCase.fn.signature, + abortSignalAborted: controller.signal.aborted, + }) const result = await testCase.fn.run_test_with_expr_events( rt, testCase.tc.name, @@ -154,6 +160,11 @@ const useRunTests = (maxBatchSize = 5) => { const endTime = performance.now() const response_status = result.status() + console.log('[TestRunner] run_test_with_expr_events completed', { + functionName: testCase.fn.name, + testCaseName: testCase.tc.name, + responseStatus: response_status, + }) const responseStatusMap = { [wasm.TestStatus.Passed]: 'passed', [wasm.TestStatus.LLMFailure]: 'llm_failed', @@ -172,7 +183,11 @@ const useRunTests = (maxBatchSize = 5) => { }) } catch (e) { console.log('test error!') - console.error(e) + console.error('[TestRunner] run_test_with_expr_events error', { + functionName: test.functionName, + testCaseName: test.testName, + error: e, + }) // Check if this is an abort error if (e instanceof Error && (e.name === 'AbortError' || e.message?.includes('BamlAbortError'))) { diff --git a/typescript/packages/playground-common/src/types/react-svg-pan-zoom.d.ts b/typescript/packages/playground-common/src/types/react-svg-pan-zoom.d.ts new file mode 100644 index 0000000000..9d2d6f9465 --- /dev/null +++ b/typescript/packages/playground-common/src/types/react-svg-pan-zoom.d.ts @@ -0,0 +1,40 @@ +declare module 'react-svg-pan-zoom' { + import * as React from 'react'; + + export const POSITION_NONE: 'none'; + export const TOOL_NONE: 'none'; + export const TOOL_AUTO: 'auto'; + + export interface ReactSvgPanZoomInstance { + pan(SVGDeltaX: number, SVGDeltaY: number): void; + zoom(SVGPointX: number, SVGPointY: number, scaleFactor: number): void; + fitSelection(selectionSVGPointX: number, selectionSVGPointY: number, selectionWidth: number, selectionHeight: number): void; + fitToViewer(alignX?: string, alignY?: string): void; + zoomOnViewerCenter(scaleFactor: number): void; + setPointOnViewerCenter(SVGPointX: number, SVGPointY: number, zoomLevel: number): void; + reset(): void; + openMiniature(): void; + closeMiniature(): void; + } + + export interface UncontrolledReactSVGPanZoomProps { + width: number; + height: number; + defaultValue?: object; + defaultTool?: string; + detectAutoPan?: boolean; + toolbarProps?: { + position?: string; + }; + miniatureProps?: { + position?: string; + }; + background?: string; + children: React.ReactElement; + [key: string]: unknown; + } + + export const UncontrolledReactSVGPanZoom: React.ForwardRefExoticComponent< + UncontrolledReactSVGPanZoomProps & React.RefAttributes + >; +} diff --git a/typescript/packages/ui/package.json b/typescript/packages/ui/package.json index 38e9c86a14..8d85a8ba72 100644 --- a/typescript/packages/ui/package.json +++ b/typescript/packages/ui/package.json @@ -73,8 +73,8 @@ "devDependencies": { "@baml/tsconfig": "workspace:*", "@tailwindcss/postcss": "4.1.13", - "@types/react": "19.1.8", - "@types/react-dom": "19.1.6", + "@types/react": "19.1.9", + "@types/react-dom": "19.1.7", "tailwindcss": "4.1.13", "typescript": "5.8.3", "zod": "3.25.66"