From 8da0f77be7a32183134528877bd3df009dc01d68 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Wed, 29 Apr 2026 14:24:43 -0700 Subject: [PATCH 01/81] @over types --- packages/catlog/src/tt/context.rs | 17 +++++++++ packages/catlog/src/tt/eval.rs | 34 +++++++++++++++++ packages/catlog/src/tt/modelgen.rs | 1 + packages/catlog/src/tt/stx.rs | 17 +++++++++ packages/catlog/src/tt/text_elab.rs | 58 ++++++++++++++++++++++++++++- packages/catlog/src/tt/toplevel.rs | 26 ++++++++++++- packages/catlog/src/tt/val.rs | 10 +++++ 7 files changed, 160 insertions(+), 3 deletions(-) diff --git a/packages/catlog/src/tt/context.rs b/packages/catlog/src/tt/context.rs index 0e313d478..6b6b50d4c 100644 --- a/packages/catlog/src/tt/context.rs +++ b/packages/catlog/src/tt/context.rs @@ -75,4 +75,21 @@ impl Context { .find(|(_, v)| v.name == name) .map(|(i, v)| (i.into(), v.label, v.ty.clone())) } + + /// Lookup a variable by label. + /// + /// Used to find variables bound under a reserved label (e.g. `@diag`) + /// where the caller wants to recover the variable's name rather than + /// look it up by name. + pub fn lookup_by_label( + &self, + label: LabelSegment, + ) -> Option<(BwdIdx, VarName, Option)> { + self.scope + .iter() + .rev() + .enumerate() + .find(|(_, v)| v.label == label) + .map(|(i, v)| (i.into(), v.name, v.ty.clone())) + } } diff --git a/packages/catlog/src/tt/eval.rs b/packages/catlog/src/tt/eval.rs index bce90d26f..8eba2112e 100644 --- a/packages/catlog/src/tt/eval.rs +++ b/packages/catlog/src/tt/eval.rs @@ -66,6 +66,7 @@ impl<'a> Evaluator<'a> { } TyS_::Unit => TyV::unit(), TyS_::Meta(mv) => TyV::meta(*mv), + TyS_::Over(name, path) => TyV::over(*name, path.clone()), } } @@ -192,6 +193,7 @@ impl<'a> Evaluator<'a> { } TyV_::Unit => TyS::unit(), TyV_::Meta(mv) => TyS::meta(*mv), + TyV_::Over(name, path) => TyS::over(*name, path.clone()), } } @@ -255,6 +257,7 @@ impl<'a> Evaluator<'a> { TyV_::Id(_, _, _) => Ok(()), TyV_::Unit => Ok(()), TyV_::Meta(_) => Ok(()), + TyV_::Over(_, _) => Ok(()), } } @@ -321,6 +324,7 @@ impl<'a> Evaluator<'a> { TyV_::Id(_, _, _) => TmV::tt(), // Extensional equality at a 100% discount! TyV_::Unit => TmV::tt(), TyV_::Meta(_) => TmV::neu(n.clone(), ty.clone()), + TyV_::Over(_, _) => TmV::neu(n.clone(), ty.clone()), } } @@ -423,6 +427,8 @@ impl<'a> Evaluator<'a> { } } + // TODO: refactor to use [`Evaluator::path_ty`] for the descent and + // keep only the subtype check here. fn can_specialize( &self, ty: &TyV, @@ -455,6 +461,34 @@ impl<'a> Evaluator<'a> { } } + /// Walk `path` from the value `val` of record type `ty`, returning + /// the type of the field at the end of the path. + /// + /// An empty path returns `ty` unchanged. Each segment requires the + /// current type to be a record containing the named field. + pub fn path_ty( + &self, + ty: &TyV, + val: &TmV, + path: &[(FieldName, LabelSegment)], + ) -> Result { + let mut ty = ty.clone(); + let mut val = val.clone(); + for &(name, label) in path { + let TyV_::Record(r) = &*ty.clone() else { + return Err(format!("expected a record type at .{label}")); + }; + if !r.fields.has(name) { + return Err(format!("no such field .{label}")); + } + let next_ty = self.field_ty(&ty, &val, name); + let next_val = self.proj(&val, name, label); + ty = next_ty; + val = next_val; + } + Ok(ty) + } + /// Try to specialize the record `r` with the subtype `ty` at `path`. /// /// Precondition: `path` is non-empty. diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 3b5bcb3b0..9128f49a3 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -398,6 +398,7 @@ impl<'a> ModelGenerator<'a> { } TyV_::Unit => None, TyV_::Meta(_) => None, + TyV_::Over(_, _) => None, } } } diff --git a/packages/catlog/src/tt/stx.rs b/packages/catlog/src/tt/stx.rs index e01fc7886..2187283d7 100644 --- a/packages/catlog/src/tt/stx.rs +++ b/packages/catlog/src/tt/stx.rs @@ -94,6 +94,15 @@ pub enum TyS_ { /// Currently, this is only used for handling elaboration errors, we might /// add more unification/holes later. Meta(MetaVar), + + /// The type of terms of a fiber over a generator of a diagram's + /// codomain model. + /// + /// The first component names a top-level diagram declaration; the + /// second is a path into that diagram's theory record, identifying + /// an object generator. Example surface syntax: `e : @over .E`, + /// where `E` is a field of the enclosing diagram's theory. + Over(TopVarName, Vec<(FieldName, LabelSegment)>), } /// Syntax for total types, dereferences to [TyS_]. @@ -152,6 +161,11 @@ impl TyS { pub fn meta(mv: MetaVar) -> Self { Self(Rc::new(TyS_::Meta(mv))) } + + /// Smart constructor for [TyS], [TyS_::Over] case. + pub fn over(diag_name: TopVarName, path: Vec<(FieldName, LabelSegment)>) -> Self { + Self(Rc::new(TyS_::Over(diag_name, path))) + } } impl ToDoc for TyS { @@ -176,6 +190,9 @@ impl ToDoc for TyS { ), TyS_::Unit => t("Unit"), TyS_::Meta(mv) => t(format!("?{}", mv.id)), + TyS_::Over(diag_name, path) => { + t(format!("@over({}){}", diag_name, path_to_string(path))) + } } } } diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index 64532fc95..dc78d3bf6 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -24,7 +24,7 @@ pub const TT_PARSE_CONFIG: ParseConfig = ParseConfig::new( ("==", Prec::nonassoc(30)), ], &[":", ":=", "&", "Unit", "Hom", "*", "=="], - &["type", "def", "syn", "chk", "norm", "generate", "uwd", "set_theory"], + &["type", "def", "syn", "chk", "norm", "generate", "uwd", "set_theory", "diagram"], ); /// The result of elaborating a top-level statement. @@ -129,6 +129,29 @@ impl TopElaborator { TopDecl::Type(Type::new(theory.clone(), ty_s, ty_v)), )) } + "diagram" => { + let theory = self.get_theory(tn.loc)?; + let mut elab = self.elaborator(&theory, toplevel); + let (name, args_n, annotn, valn) = self.annotated_def(tn.body).or_else(|| { + self.error( + tn.loc, + "unknown syntax for diagram declaration, expected : := ", + ) + })?; + if args_n.is_some() { + return self.error(tn.loc, "diagrams cannot have arguments"); + } + let (_, ret_ty_v) = elab.ty(annotn); + // Bind the diagram's name in scope under the reserved + // label `@diag` so that `@over .E` inside the body can + // recover both the diagram's name and its codomain type. + elab.intro(name, label_seg("@diag"), Some(ret_ty_v.clone())); + let (val_s, val_v) = elab.ty(valn); + Some(TopElabResult::Declaration( + name, + TopDecl::Diag(Diag::new(theory.clone(), ret_ty_v, val_s, val_v)), + )) + } "def" => { let theory = self.get_theory(tn.loc)?; let (name, args_n, ty_n, tm_n) = self.annotated_def(tn.body).or_else(|| { @@ -379,6 +402,9 @@ impl<'a> Elaborator<'a> { )) } } + TopDecl::Diag(_) => { + self.ty_error(format!("{name} refers to a diagram, not a type")) + } TopDecl::Def(_) | TopDecl::DefConst(_) => { self.ty_error(format!("{name} refers to a term not a type")) } @@ -387,7 +413,6 @@ impl<'a> Elaborator<'a> { self.ty_error(format!("no such type {name} defined")) } } - fn morphism_ty(&mut self, n: &FNtn) -> Option<(MorType, ObType, ObType)> { let elab = self.enter(n.loc()); let theory = elab.theory(); @@ -459,6 +484,32 @@ impl<'a> Elaborator<'a> { let (tm_s, tm_v, ty_v) = elab.syn(tm_n); (TyS::sing(elab.evaluator().quote_ty(&ty_v), tm_s), TyV::sing(ty_v, tm_v)) } + App1(L(_, Prim("over")), p_n) => { + let Some(path) = elab.path(p_n) else { + return elab.ty_hole(); + }; + // Recover the enclosing diagram from the reserved `@diag` + // binding pushed by the `diagram` toplevel arm. + let Some((_, diag_name, model_ty)) = + elab.ctx.lookup_by_label(label_seg("@diag")) + else { + return elab.ty_error("@over is only allowed inside a diagram declaration"); + }; + let Some(model_ty) = model_ty else { + return elab.ty_error("enclosing diagram has no known codomain type"); + }; + let evaluator = elab.evaluator(); + let (self_n, _) = evaluator.bind_self(model_ty.clone()); + let self_val = evaluator.eta_neu(&self_n, &model_ty); + let resolved = match evaluator.path_ty(&model_ty, &self_val, &path) { + Ok(t) => t, + Err(msg) => return elab.ty_error(msg), + }; + let TyV_::Object(_) = &*resolved else { + return elab.ty_error("path does not refer to an object generator"); + }; + (TyS::over(diag_name, path.clone()), TyV::over(diag_name, path)) + } App1(mt_n, L(_, Tuple(domcod_n))) => { let [dom_n, cod_n] = domcod_n.as_slice() else { return elab.ty_error("expected two arguments for morphism type"); @@ -568,6 +619,9 @@ impl<'a> Elaborator<'a> { TopDecl::Type(_) => self.syn_error(format!("{name} refers type, not term")), TopDecl::DefConst(d) => (TmS::topvar(name), d.val.clone(), d.ty.clone()), TopDecl::Def(_) => self.syn_error(format!("{name} must be applied to arguments")), + TopDecl::Diag(_) => { + self.syn_error(format!("{name} refers to a diagram, not a term")) + } } } else { self.syn_error(format!("no such variable {name}")) diff --git a/packages/catlog/src/tt/toplevel.rs b/packages/catlog/src/tt/toplevel.rs index e9f447bcf..6c96febb9 100644 --- a/packages/catlog/src/tt/toplevel.rs +++ b/packages/catlog/src/tt/toplevel.rs @@ -1,6 +1,7 @@ //! Data structures for managing toplevel declarations in the type theory. //! -//! Specifically, notebooks will produce [TopDecl::Type] declarations. +//! Specifically, notebooks will produce [TopDecl::Type] declarations, or +//! maybe [TopDecl::Diag] declartions. use derive_more::Constructor; @@ -16,6 +17,8 @@ pub enum TopDecl { DefConst(DefConst), /// See [Def]. Def(Def), + /// See [Diag] + Diag(Diag), } /// A toplevel declaration of a type. @@ -64,6 +67,19 @@ pub struct Def { pub body: TmS, } +/// A toplevel declaration of a diagram. +#[derive(Constructor, Clone)] +pub struct Diag { + /// The theory that the diagram is defined in. + pub theory: Theory, + /// The model G that this diagram is an instance of. + pub model: TyV, + /// The body: a record TyS whose fields have @over-typed entries, + /// presenting both the domain generators and the mapping. + pub body_stx: TyS, + /// Evaluated body — useful for downstream consumers that want the TyV directly. + pub body_val: TyV, +} impl TopDecl { /// Unwraps the type for a toplevel-declaration of a type, or panics. /// @@ -94,6 +110,14 @@ impl TopDecl { _ => panic!("top-level should be a term judgment"), } } + + /// Unwraps the diagram for a toplevel diagram declaration, or panics. + pub fn unwrap_diag(self) -> Diag { + match self { + TopDecl::Diag(d) => d, + _ => panic!("top-level should be a diagram declaration"), + } + } } /// Storage for toplevel declarations. diff --git a/packages/catlog/src/tt/val.rs b/packages/catlog/src/tt/val.rs index 3caa62e11..b00a4a554 100644 --- a/packages/catlog/src/tt/val.rs +++ b/packages/catlog/src/tt/val.rs @@ -98,6 +98,11 @@ pub enum TyV_ { Unit, /// A metavariable, also see [TyS_::Meta]. Meta(MetaVar), + /// The type of terms of a fiber over a generator of a diagram's + /// codomain model. + /// + /// See [TyS_::Over] for the syntactic counterpart. + Over(TopVarName, Vec<(FieldName, LabelSegment)>), } /// Value for total types, dereferences to [TyV_]. @@ -177,6 +182,11 @@ impl TyV { pub fn meta(mv: MetaVar) -> Self { Self(Rc::new(TyV_::Meta(mv))) } + + /// Smart constructor for [TyV], [TyV_::Over] case. + pub fn over(diag_name: TopVarName, path: Vec<(FieldName, LabelSegment)>) -> Self { + Self(Rc::new(TyV_::Over(diag_name, path))) + } } /// Inner enum for [TmN]. From 3d5cb2732573164f8f59ffedbbf681b4d172f514 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Wed, 29 Apr 2026 15:04:25 -0700 Subject: [PATCH 02/81] Instance without specializations declares --- packages/catlog/src/tt/context.rs | 50 +++++++++++++++++-------- packages/catlog/src/tt/notebook_elab.rs | 4 +- packages/catlog/src/tt/text_elab.rs | 38 +++++++++++++------ 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/packages/catlog/src/tt/context.rs b/packages/catlog/src/tt/context.rs index 6b6b50d4c..8a60f19c7 100644 --- a/packages/catlog/src/tt/context.rs +++ b/packages/catlog/src/tt/context.rs @@ -4,6 +4,21 @@ use derive_more::Constructor; use crate::tt::{prelude::*, val::*}; +/// What kind of binding a context entry represents. +/// +/// Most bindings are ordinary `Term` bindings. `Diagram` bindings are pushed +/// by the `diagram` toplevel arm so that `@over .E` can recover the +/// enclosing diagram's name and codomain type without exposing the diagram +/// to ordinary term lookup (where it would masquerade as a term of the +/// codomain type). +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum VarKind { + /// An ordinary term-level binding. + Term, + /// A binding for a diagram declaration; not resolvable by [`Context::lookup`]. + Diagram, +} + /// Each variable in context is associated with a label and a type. /// /// Multiple variables with the same name can show up in context; in this case @@ -19,6 +34,8 @@ pub struct VarInContext { /// We allow the type to be null as a hack for the `self` variable before we /// know the type of the `self` variable. pub ty: Option, + /// What kind of binding this is. + pub kind: VarKind, } /// The variable context during elaboration. @@ -61,35 +78,38 @@ impl Context { self.scope.truncate(c.scope); } - /// Add a new variable to scope (note: does not add it to the environment). + /// Add a new term-kind variable to scope (note: does not add it to the environment). pub fn push_scope(&mut self, name: VarName, label: LabelSegment, ty: Option) { - self.scope.push(VarInContext::new(name, label, ty)) + self.scope.push(VarInContext::new(name, label, ty, VarKind::Term)) } - /// Lookup a variable by name. + /// Add a new diagram-kind variable to scope. + pub fn push_diagram(&mut self, name: VarName, label: LabelSegment, ty: TyV) { + self.scope.push(VarInContext::new(name, label, Some(ty), VarKind::Diagram)) + } + + /// Lookup a term-kind variable by name. + /// + /// Diagram-kind entries are skipped, so a `Var(I)` referring to an + /// enclosing `diagram I := ...` will not resolve here. pub fn lookup(&self, name: VarName) -> Option<(BwdIdx, LabelSegment, Option)> { self.scope .iter() .rev() .enumerate() - .find(|(_, v)| v.name == name) + .find(|(_, v)| v.kind == VarKind::Term && v.name == name) .map(|(i, v)| (i.into(), v.label, v.ty.clone())) } - /// Lookup a variable by label. + /// Find the most recent diagram-kind binding in scope, if any. /// - /// Used to find variables bound under a reserved label (e.g. `@diag`) - /// where the caller wants to recover the variable's name rather than - /// look it up by name. - pub fn lookup_by_label( - &self, - label: LabelSegment, - ) -> Option<(BwdIdx, VarName, Option)> { + /// Returns the diagram's name and codomain type. Used by the `@over .E` + /// type elaborator. + pub fn lookup_diagram(&self) -> Option<(VarName, TyV)> { self.scope .iter() .rev() - .enumerate() - .find(|(_, v)| v.label == label) - .map(|(i, v)| (i.into(), v.name, v.ty.clone())) + .find(|v| v.kind == VarKind::Diagram) + .map(|v| (v.name, v.ty.clone().expect("diagram binding must have a type"))) } } diff --git a/packages/catlog/src/tt/notebook_elab.rs b/packages/catlog/src/tt/notebook_elab.rs index f84623bb8..349579c3d 100644 --- a/packages/catlog/src/tt/notebook_elab.rs +++ b/packages/catlog/src/tt/notebook_elab.rs @@ -78,7 +78,7 @@ impl<'a> Elaborator<'a> { v }; self.ctx.env = self.ctx.env.snoc(v.clone()); - self.ctx.scope.push(VarInContext::new(name, label, ty)); + self.ctx.scope.push(VarInContext::new(name, label, ty, VarKind::Term)); v } @@ -452,7 +452,7 @@ impl<'a> Elaborator<'a> { nb::ModelJudgment::Equation(eqn_decl) => self.equation_cell(eqn_decl), }; field_ty_vs.push((name, (label, ty_v.clone()))); - self.ctx.scope.push(VarInContext::new(name, label, Some(ty_v.clone()))); + self.ctx.scope.push(VarInContext::new(name, label, Some(ty_v.clone()), VarKind::Term)); self.ctx.env = self.ctx.env.snoc(TmV::neu(TmN::proj(self_var.clone(), name, label), ty_v)); } diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index dc78d3bf6..6aad0a5b9 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -142,10 +142,15 @@ impl TopElaborator { return self.error(tn.loc, "diagrams cannot have arguments"); } let (_, ret_ty_v) = elab.ty(annotn); - // Bind the diagram's name in scope under the reserved - // label `@diag` so that `@over .E` inside the body can - // recover both the diagram's name and its codomain type. - elab.intro(name, label_seg("@diag"), Some(ret_ty_v.clone())); + // Bind the diagram's name as a diagram-kind context entry + // so that `@over .E` inside the body can recover both the + // diagram's name and its codomain type, while ordinary + // term lookups cannot see it. + let diag_label = match name { + NameSegment::Text(s) => label_seg(s), + NameSegment::Uuid(_) => label_seg("diag"), + }; + elab.intro_diagram(name, diag_label, ret_ty_v.clone()); let (val_s, val_v) = elab.ty(valn); Some(TopElabResult::Declaration( name, @@ -375,6 +380,15 @@ impl<'a> Elaborator<'a> { v } + /// Like [`Self::intro`], but pushes a diagram-kind binding so it is + /// invisible to ordinary term lookup. + fn intro_diagram(&mut self, name: VarName, label: LabelSegment, ty: TyV) { + let v = TmV::neu(TmN::var(self.ctx.scope.len().into(), name, label), ty.clone()); + let v = self.evaluator().eta(&v, Some(&ty)); + self.ctx.env = self.ctx.env.snoc(v); + self.ctx.push_diagram(name, label, ty); + } + fn binding(&mut self, n: &FNtn) -> Option<(VarName, LabelSegment, TyS, TyV)> { let mut elab = self.enter(n.loc()); match n.ast0() { @@ -484,20 +498,20 @@ impl<'a> Elaborator<'a> { let (tm_s, tm_v, ty_v) = elab.syn(tm_n); (TyS::sing(elab.evaluator().quote_ty(&ty_v), tm_s), TyV::sing(ty_v, tm_v)) } + // `@Instance(X)` is a structural alias for `X`, used in diagram + // annotations. Treating it as an alias keeps the type system + // simple; `I` is kept out of term lookup by the diagram-kind + // binding pushed in the `diagram` toplevel arm. + App1(L(_, Prim("Instance")), x_n) => elab.ty(x_n), App1(L(_, Prim("over")), p_n) => { let Some(path) = elab.path(p_n) else { return elab.ty_hole(); }; - // Recover the enclosing diagram from the reserved `@diag` - // binding pushed by the `diagram` toplevel arm. - let Some((_, diag_name, model_ty)) = - elab.ctx.lookup_by_label(label_seg("@diag")) - else { + // Recover the enclosing diagram from the most recent + // diagram-kind binding pushed by the `diagram` toplevel arm. + let Some((diag_name, model_ty)) = elab.ctx.lookup_diagram() else { return elab.ty_error("@over is only allowed inside a diagram declaration"); }; - let Some(model_ty) = model_ty else { - return elab.ty_error("enclosing diagram has no known codomain type"); - }; let evaluator = elab.evaluator(); let (self_n, _) = evaluator.bind_self(model_ty.clone()); let self_val = evaluator.eta_neu(&self_n, &model_ty); From 40436e706f224ff46086fd52d5496a5d77782f76 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Mon, 4 May 2026 18:42:17 -0700 Subject: [PATCH 03/81] Diagrams in discrete theories declaring and validating, generating DiscreteDblModelDiagrams, behaving as dopfs --- packages/catlog/src/tt/batch.rs | 114 ++++++++++++++- packages/catlog/src/tt/eval.rs | 21 ++- packages/catlog/src/tt/modelgen.rs | 209 +++++++++++++++++++++++++++- packages/catlog/src/tt/stx.rs | 24 ++-- packages/catlog/src/tt/text_elab.rs | 158 ++++++++++++++++++++- packages/catlog/src/tt/val.rs | 13 +- 6 files changed, 505 insertions(+), 34 deletions(-) diff --git a/packages/catlog/src/tt/batch.rs b/packages/catlog/src/tt/batch.rs index 42be88292..c333ab36e 100644 --- a/packages/catlog/src/tt/batch.rs +++ b/packages/catlog/src/tt/batch.rs @@ -11,8 +11,18 @@ use scopeguard::guard; use tattle::display::SourceInfo; use tattle::{Reporter, declare_error}; -use super::{text_elab::*, theory::std_theories, toplevel::*}; -use crate::zero::NameSegment; +use super::{ + modelgen::{Model, diagram_from_diag}, + text_elab::*, + theory::std_theories, + toplevel::*, +}; +use crate::dbl::discrete::{DiscreteDblModelDiagram, InvalidDiscreteDblModelDiagram}; +use crate::dbl::model::{FpDblModel, MutDblModel}; +use crate::dbl::model_diagram::DblModelDiagram; +use crate::one::category::FgCategory; +use crate::one::path::Path; +use crate::zero::{Mapping, NameSegment, QualifiedName}; declare_error!(TOP_ERROR, "top", "an error at the top-level"); @@ -64,6 +74,69 @@ impl BatchOutput { } } + fn diagram_summary(&self, diagram: &DiscreteDblModelDiagram) { + if let BatchOutput::Snapshot(out) = self { + let DblModelDiagram(mapping, domain) = diagram; + let mut out = out.borrow_mut(); + let ob_gens: Vec<_> = domain.ob_generators().collect(); + let mor_gens: Vec<_> = domain.mor_generators().collect(); + if ob_gens.is_empty() && mor_gens.is_empty() { + writeln!(out, "#/ diagram has no domain generators").unwrap(); + return; + } + writeln!(out, "#/ diagram domain:").unwrap(); + for g in &ob_gens { + let ot = domain.ob_generator_type(g); + match mapping.0.ob_generator_map.apply_to_ref(g) { + Some(target) => writeln!(out, "#/ {g} : {ot} -> {target}").unwrap(), + None => writeln!(out, "#/ {g} : {ot}").unwrap(), + } + } + for g in &mor_gens { + let mt = format_path(&domain.mor_generator_type(g)); + let dom_str = domain + .get_dom(g) + .map(|o| format!("{o}")) + .unwrap_or_else(|| "?".into()); + let cod_str = domain + .get_cod(g) + .map(|o| format!("{o}")) + .unwrap_or_else(|| "?".into()); + let target_str = mapping + .0 + .mor_generator_map + .apply_to_ref(g) + .map(|p| format!(" -> {}", format_path(&p))) + .unwrap_or_default(); + writeln!(out, "#/ {g} : {mt}[{dom_str}, {cod_str}]{target_str}").unwrap(); + } + } + } + + fn diagram_error(&self, msg: &str) { + if let BatchOutput::Snapshot(out) = self { + writeln!(out.borrow_mut(), "#/ diagram generation failed: {msg}").unwrap(); + } + } + + fn validation_result>( + &self, + errs: I, + ) { + if let BatchOutput::Snapshot(out) = self { + let mut out = out.borrow_mut(); + let mut iter = errs.into_iter().peekable(); + if iter.peek().is_none() { + writeln!(out, "#/ diagram validates against codomain").unwrap(); + } else { + writeln!(out, "#/ diagram validation failed:").unwrap(); + for e in iter { + writeln!(out, "#/ {e:?}").unwrap(); + } + } + } + } + fn got_result(&self, result: &str) { match self { BatchOutput::Snapshot(out) => { @@ -179,8 +252,34 @@ pub fn elaborate(src: &str, path: &str, output: &BatchOutput) -> io::Result { + let is_diag = matches!(top_decl, TopDecl::Diag(_)); toplevel.declarations.insert(name_segment, top_decl); output.declared(name_segment); + if is_diag + && let Some(TopDecl::Diag(diag)) = + toplevel.declarations.get(&name_segment) + { + match diagram_from_diag( + &toplevel, + &diag.theory.definition, + diag, + ) { + Ok((model_diag, _)) => { + output.diagram_summary(&model_diag); + let (cod, _) = Model::from_ty( + &toplevel, + &diag.theory.definition, + &diag.model, + ); + if let Some(cod) = cod.as_discrete() { + output.validation_result( + model_diag.iter_invalid_in(&cod), + ); + } + } + Err(msg) => output.diagram_error(&msg), + } + } } TopElabResult::Output(s) => { output.got_result(&s); @@ -242,3 +341,14 @@ fn snapshot_examples() { } assert!(succeeded); } + +/// Render a [`QualifiedPath`] for snapshot output. +fn format_path(p: &Path) -> String { + match p { + Path::Id(v) => format!("Hom({v})"), + Path::Seq(es) => { + let parts: Vec = es.iter().map(|e| format!("{e}")).collect(); + parts.join(".") + } + } +} diff --git a/packages/catlog/src/tt/eval.rs b/packages/catlog/src/tt/eval.rs index 8eba2112e..af27e23fc 100644 --- a/packages/catlog/src/tt/eval.rs +++ b/packages/catlog/src/tt/eval.rs @@ -49,7 +49,11 @@ impl<'a> Evaluator<'a> { /// to self.env. pub fn eval_ty(&self, ty: &TyS) -> TyV { match &**ty { - TyS_::TopVar(tv) => self.toplevel.declarations.get(tv).unwrap().clone().unwrap_ty().val, + TyS_::TopVar(tv) => match self.toplevel.declarations.get(tv).unwrap() { + TopDecl::Type(t) => t.val.clone(), + TopDecl::Diag(d) => d.body_val.clone(), + _ => panic!("top-level {tv} should be a type or diagram declaration"), + }, TyS_::Object(ot) => TyV::object(ot.clone()), TyS_::Morphism(pt, dom, cod) => { TyV::morphism(pt.clone(), self.eval_tm(dom), self.eval_tm(cod)) @@ -66,7 +70,7 @@ impl<'a> Evaluator<'a> { } TyS_::Unit => TyV::unit(), TyS_::Meta(mv) => TyV::meta(*mv), - TyS_::Over(name, path) => TyV::over(*name, path.clone()), + TyS_::Over(path) => TyV::over(path.clone()), } } @@ -193,7 +197,7 @@ impl<'a> Evaluator<'a> { } TyV_::Unit => TyS::unit(), TyV_::Meta(mv) => TyS::meta(*mv), - TyV_::Over(name, path) => TyS::over(*name, path.clone()), + TyV_::Over(path) => TyS::over(path.clone()), } } @@ -257,7 +261,7 @@ impl<'a> Evaluator<'a> { TyV_::Id(_, _, _) => Ok(()), TyV_::Unit => Ok(()), TyV_::Meta(_) => Ok(()), - TyV_::Over(_, _) => Ok(()), + TyV_::Over(_) => Ok(()), } } @@ -302,6 +306,13 @@ impl<'a> Evaluator<'a> { (TyV_::Sing(ty1, _), _) => self.convertible_ty(ty1, ty2), (_, TyV_::Sing(ty2, _)) => self.convertible_ty(ty1, ty2), (TyV_::Unit, TyV_::Unit) => Ok(()), + (TyV_::Over(p1), TyV_::Over(p2)) => { + if p1 == p2 { + Ok(()) + } else { + Err(t("over-types refer to different paths in the codomain")) + } + } _ => Err(t("tried to convert between types of different type constructors")), } } @@ -324,7 +335,7 @@ impl<'a> Evaluator<'a> { TyV_::Id(_, _, _) => TmV::tt(), // Extensional equality at a 100% discount! TyV_::Unit => TmV::tt(), TyV_::Meta(_) => TmV::neu(n.clone(), ty.clone()), - TyV_::Over(_, _) => TmV::neu(n.clone(), ty.clone()), + TyV_::Over(_) => TmV::neu(n.clone(), ty.clone()), } } diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 9128f49a3..9be670f9c 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -8,10 +8,11 @@ use super::{eval::*, prelude::*, text_elab, theory::*, toplevel::*, val::*}; use crate::dbl::{ discrete, discrete_tabulator, modal, model::{DblModel, DblModelPrinter, MutDblModel}, + model_diagram::DblModelDiagram, theory::{DblTheory, DblTheoryKind, NonUnital, Unital}, }; use crate::one::{ - Category, + Category, QualifiedPath, path::{Path, PathEq}, }; use crate::zero::{Namespace, QualifiedName}; @@ -398,7 +399,211 @@ impl<'a> ModelGenerator<'a> { } TyV_::Unit => None, TyV_::Meta(_) => None, - TyV_::Over(_, _) => None, + TyV_::Over(_) => None, } } } + +/// Generates a [`DiscreteDblModelDiagram`] from an elaborated [`Diag`]. +/// +/// Restricted to discrete double theories for the moment; other theory +/// kinds will need their own diagram types in catlog before this can be +/// promoted to an enum mirroring [`Model`]. +pub fn diagram_from_diag( + toplevel: &Toplevel, + th: &TheoryDef, + diag: &Diag, +) -> Result<(discrete::DiscreteDblModelDiagram, Namespace), String> { + let TheoryDef::Discrete(theory) = th else { + return Err("diagram generation only supports discrete double theories".into()); + }; + let mut generator = DiagramGenerator { + eval: Evaluator::empty(toplevel), + codomain_ty: diag.model.clone(), + domain: discrete::DiscreteDblModel::new(theory.clone()), + mapping: discrete::DiscreteDblModelMapping::default(), + }; + let namespace = generator.generate(&diag.body_val)?; + let DiagramGenerator { domain, mapping, .. } = generator; + Ok((DblModelDiagram(mapping, domain), namespace)) +} + +struct DiagramGenerator<'a> { + eval: Evaluator<'a>, + codomain_ty: TyV, + domain: discrete::DiscreteDblModel, + mapping: discrete::DiscreteDblModelMapping, +} + +impl DiagramGenerator<'_> { + fn generate(&mut self, body_ty: &TyV) -> Result { + let (self_n, eval_with_self) = self.eval.bind_self(body_ty.clone()); + self.eval = eval_with_self; + let self_val = self.eval.eta_neu(&self_n, body_ty); + Ok(self.extract(vec![], &self_val, body_ty)?.unwrap_or_else(Namespace::new_for_uuid)) + } + + fn extract( + &mut self, + prefix: Vec, + val: &TmV, + ty: &TyV, + ) -> Result, String> { + match &**ty { + TyV_::Record(r) => { + let mut namespace = Namespace::new_for_uuid(); + for (name, (label, _)) in r.fields.iter() { + let mut child_prefix = prefix.clone(); + child_prefix.push(*name); + if let NameSegment::Uuid(uuid) = name { + namespace.set_label(*uuid, *label); + } + let field_tm = self.eval.proj(val, *name, *label); + let field_ty = self.eval.field_ty(ty, val, *name); + if let Some(inner) = self.extract(child_prefix, &field_tm, &field_ty)? { + namespace.add_inner(*name, inner); + } + } + Ok(Some(namespace)) + } + TyV_::Over(path) => { + // Resolve the path against the codomain to find the + // catlog object type for this generator. + let (cod_self_n, cod_eval) = self.eval.bind_self(self.codomain_ty.clone()); + let cod_val = cod_eval.eta_neu(&cod_self_n, &self.codomain_ty); + let resolved = cod_eval.path_ty(&self.codomain_ty, &cod_val, path)?; + let TyV_::Object(ot) = &*resolved else { + return Err( + "@over path does not refer to an object generator in the codomain".into(), + ); + }; + let ot: QualifiedName = ot.clone().try_into().map_err(|_| { + "@over codomain object type is not valid for a discrete theory".to_string() + })?; + let qname: QualifiedName = prefix.into(); + self.domain.add_ob(qname.clone(), ot); + let target: QualifiedName = + path.iter().map(|(seg, _)| *seg).collect::>().into(); + self.mapping.assign_ob(qname, target); + Ok(None) + } + TyV_::Morphism(mt, dom, cod) => { + // Augmentation produces lift morphisms whose dom and cod + // are projections from the body's self, resolving to the + // qualified names of generators (declared or specialized). + let dom_name = neutral_qualified_name(dom).ok_or_else(|| { + "morphism domain does not resolve to a generator name".to_string() + })?; + let cod_name = neutral_qualified_name(cod).ok_or_else(|| { + "morphism codomain does not resolve to a generator name".to_string() + })?; + let mt_inner: QualifiedPath = mt.clone().try_into().map_err(|_| { + "morphism type is not valid for a discrete theory".to_string() + })?; + let qname: QualifiedName = prefix.clone().into(); + self.domain.add_mor(qname.clone(), dom_name, cod_name, mt_inner); + // Mapping target: extract the codomain morphism's name + // from the synthetic lift name `~f(e)`. For other shapes + // (e.g., a future user-declared morphism in a body), fall + // back to the field name itself. + if let Some(last) = prefix.last() + && let Some(cod_mor) = parse_lift_morphism_name(last) + { + let target = Path::single(QualifiedName::single(cod_mor)); + self.mapping.assign_mor(qname, target); + } + Ok(None) + } + TyV_::Object(_) + | TyV_::Sing(_, _) + | TyV_::Id(_, _, _) + | TyV_::Unit + | TyV_::Meta(_) => Ok(None), + } + } +} + +/// Read off a [`QualifiedName`] from a value that's a neutral chain of +/// projections from a self-variable (regardless of which level the self +/// is bound at). Returns `None` if the value isn't of that shape. +fn neutral_qualified_name(tm: &TmV) -> Option { + let TmV_::Neu(n, _) = &**tm else { + return None; + }; + Some(n.to_qualified_name()) +} + +/// Parse a synthetic lift-morphism field name of the form `~f(e)` and +/// return the codomain morphism's name `f`. Returns `None` if the name +/// isn't of that shape. +fn parse_lift_morphism_name(name: &NameSegment) -> Option { + let NameSegment::Text(s) = name else { + return None; + }; + let s = s.as_str(); + let rest = s.strip_prefix('~')?; + let paren = rest.find('(')?; + Some(name_seg(ustr(&rest[..paren]))) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dbl::model::FpDblModel; + use crate::tt::text_elab::{TT_PARSE_CONFIG, TopElabResult, TopElaborator}; + use crate::tt::theory::std_theories; + use crate::zero::Mapping; + + fn elaborate_to_toplevel(src: &str) -> Toplevel { + let reporter = Reporter::new(); + let mut toplevel = Toplevel::new(std_theories()); + let _ = TT_PARSE_CONFIG.with_parsed_top(src, reporter.clone(), |topntns| { + let mut topelab = TopElaborator::new(reporter.clone()); + for topntn in topntns.iter() { + if let Some(TopElabResult::Declaration(name, decl)) = + topelab.elab(&toplevel, topntn) + { + toplevel.declarations.insert(name, decl); + } + } + Some(()) + }); + assert!(!reporter.errored(), "elaboration produced errors"); + toplevel + } + + #[test] + fn diagram_over_weighted_graph() { + let src = r#" +set_theory ThSchema + +type WeightedGraph := [ + V : Entity, + E : Entity, + Weight : AttrType, + src : (Hom Entity)[E, V], + tgt : (Hom Entity)[E, V], + weight : Attr[E, Weight] +] + +diagram I : @Instance(WeightedGraph) := [ + e : @over .E, +] +"#; + let toplevel = elaborate_to_toplevel(src); + let diag = match toplevel.declarations.get(&name_seg("I")) { + Some(TopDecl::Diag(d)) => d.clone(), + _ => panic!("expected I to be a diagram declaration"), + }; + let (DblModelDiagram(mapping, domain), _ns) = + diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + + let e_qname: QualifiedName = vec![name_seg("e")].into(); + let entity_ot: QualifiedName = vec![name_seg("Entity")].into(); + let e_target: QualifiedName = vec![name_seg("E")].into(); + + assert!(domain.has_ob(&e_qname)); + assert_eq!(domain.ob_generator_type(&e_qname), entity_ot); + assert_eq!(mapping.0.ob_generator_map.apply_to_ref(&e_qname), Some(e_target)); + } +} diff --git a/packages/catlog/src/tt/stx.rs b/packages/catlog/src/tt/stx.rs index 2187283d7..5bb3dbdf8 100644 --- a/packages/catlog/src/tt/stx.rs +++ b/packages/catlog/src/tt/stx.rs @@ -95,14 +95,16 @@ pub enum TyS_ { /// add more unification/holes later. Meta(MetaVar), - /// The type of terms of a fiber over a generator of a diagram's - /// codomain model. + /// The type of terms in a fiber over an object generator of some + /// diagram's codomain model. /// - /// The first component names a top-level diagram declaration; the - /// second is a path into that diagram's theory record, identifying - /// an object generator. Example surface syntax: `e : @over .E`, - /// where `E` is a field of the enclosing diagram's theory. - Over(TopVarName, Vec<(FieldName, LabelSegment)>), + /// The path identifies the object generator in the codomain. Diagram + /// identity is contextual rather than part of the type: any diagram + /// in scope contributes its terms to this type, so two `@over .V` + /// types from different diagram declarations are convertible. The + /// elaborator validates the path against the enclosing diagram's + /// codomain at construction time. Example surface syntax: `e : @over .E`. + Over(Vec<(FieldName, LabelSegment)>), } /// Syntax for total types, dereferences to [TyS_]. @@ -163,8 +165,8 @@ impl TyS { } /// Smart constructor for [TyS], [TyS_::Over] case. - pub fn over(diag_name: TopVarName, path: Vec<(FieldName, LabelSegment)>) -> Self { - Self(Rc::new(TyS_::Over(diag_name, path))) + pub fn over(path: Vec<(FieldName, LabelSegment)>) -> Self { + Self(Rc::new(TyS_::Over(path))) } } @@ -190,9 +192,7 @@ impl ToDoc for TyS { ), TyS_::Unit => t("Unit"), TyS_::Meta(mv) => t(format!("?{}", mv.id)), - TyS_::Over(diag_name, path) => { - t(format!("@over({}){}", diag_name, path_to_string(path))) - } + TyS_::Over(path) => t(format!("@over{}", path_to_string(path))), } } } diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index 6aad0a5b9..db6eb7d76 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -151,7 +151,13 @@ impl TopElaborator { NameSegment::Uuid(_) => label_seg("diag"), }; elab.intro_diagram(name, diag_label, ret_ty_v.clone()); - let (val_s, val_v) = elab.ty(valn); + let (val_s, _val_v) = elab.ty(valn); + // Augment the body with synthetic lift-target fields forced + // by the discrete-opfibration condition: for each declared + // `e : @over .X` and codomain morphism `f : X → Y`, add a + // synthetic field `f(e) : @over .Y`. + let val_s = augment_with_lifts(&val_s, &ret_ty_v); + let val_v = elab.evaluator().eval_ty(&val_s); Some(TopElabResult::Declaration( name, TopDecl::Diag(Diag::new(theory.clone(), ret_ty_v, val_s, val_v)), @@ -416,8 +422,18 @@ impl<'a> Elaborator<'a> { )) } } - TopDecl::Diag(_) => { - self.ty_error(format!("{name} refers to a diagram, not a type")) + TopDecl::Diag(d) => { + if d.theory == self.theory { + // Using a diagram name as a type yields the diagram's + // body record, allowing `we : I & [...]`-style + // specializations to walk paths through I's fields. + (TyS::topvar(name), d.body_val.clone()) + } else { + self.ty_error(format!( + "{name} refers to a diagram in theory {}, expected theory {}", + d.theory, self.theory + )) + } } TopDecl::Def(_) | TopDecl::DefConst(_) => { self.ty_error(format!("{name} refers to a term not a type")) @@ -466,6 +482,23 @@ impl<'a> Elaborator<'a> { p.push((name_seg(*f), label_seg(*f))); Some(p) } + // `.f(x)` — applied path segment, resolved to a single synthetic + // field whose name is the canonical string `"f(x)"`. This makes + // the lift-target objects forced by the discrete-opfibration + // condition addressable by ordinary path-walking. + App1(head_n, L(_, Var(x))) => match head_n.ast0() { + Field(f) => { + let s = ustr(&format!("{f}({x})")); + Some(vec![(name_seg(s), label_seg(s))]) + } + App1(p_n, L(_, Field(f))) => { + let mut p = elab.path(p_n)?; + let s = ustr(&format!("{f}({x})")); + p.push((name_seg(s), label_seg(s))); + Some(p) + } + _ => elab.error("unexpected notation for path"), + }, _ => elab.error("unexpected notation for path"), } } @@ -507,9 +540,11 @@ impl<'a> Elaborator<'a> { let Some(path) = elab.path(p_n) else { return elab.ty_hole(); }; - // Recover the enclosing diagram from the most recent - // diagram-kind binding pushed by the `diagram` toplevel arm. - let Some((diag_name, model_ty)) = elab.ctx.lookup_diagram() else { + // The enclosing diagram-kind binding gives the codomain + // against which we validate the path. The diagram's + // identity is not stored in the resulting type — `@over` + // names a fiber-type that's shared across diagrams. + let Some((_, model_ty)) = elab.ctx.lookup_diagram() else { return elab.ty_error("@over is only allowed inside a diagram declaration"); }; let evaluator = elab.evaluator(); @@ -522,7 +557,7 @@ impl<'a> Elaborator<'a> { let TyV_::Object(_) = &*resolved else { return elab.ty_error("path does not refer to an object generator"); }; - (TyS::over(diag_name, path.clone()), TyV::over(diag_name, path)) + (TyS::over(path.clone()), TyV::over(path)) } App1(mt_n, L(_, Tuple(domcod_n))) => { let [dom_n, cod_n] = domcod_n.as_slice() else { @@ -842,6 +877,115 @@ impl<'a> Elaborator<'a> { } } +/// Augment a diagram's body with synthetic lift-target fields. +/// +/// For each declared `e : @over .X` field, and each codomain morphism +/// `f : X → Y`, add a synthetic field named `"f(e)" : @over .Y`. Iterates +/// to a fixpoint, with a depth bound to guard against cyclic theories. +/// +/// The synthetic fields exist only in the type-theoretic body so that +/// path-walking and specialization (e.g., `we : I & [.src(e) := v1]`) +/// can resolve `.src(e)` as an ordinary field. Modelgen filters them +/// out by name when extracting the domain model. +fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { + const MAX_DEPTH: usize = 16; + let TyS_::Record(initial) = &**body_s else { + return body_s.clone(); + }; + let TyV_::Record(cod_r) = &**codomain else { + return body_s.clone(); + }; + + // Collect (mor_name, mt, dom_path, cod_path) for each codomain + // morphism field whose source/target reduce to a single chain of + // fields. + type LiftPath = Vec<(NameSegment, LabelSegment)>; + type CodMor = (NameSegment, MorType, LiftPath, LiftPath); + let mut cod_morphisms: Vec = Vec::new(); + for (mor_name, (_, mor_ty_s)) in cod_r.fields.iter() { + if let TyS_::Morphism(mt, dom_s, cod_s) = &**mor_ty_s + && let (Some(dom_path), Some(cod_path)) = (tms_to_path(dom_s), tms_to_path(cod_s)) + { + cod_morphisms.push((*mor_name, mt.clone(), dom_path, cod_path)); + } + } + + let mut row = initial.clone(); + for _ in 0..MAX_DEPTH { + let snapshot = row.clone(); + let mut added = false; + for (field_name, (field_label, field_ty)) in snapshot.iter() { + let TyS_::Over(path) = &**field_ty else { + continue; + }; + for (mor_name, mt, dom_path, cod_path) in &cod_morphisms { + if dom_path != path { + continue; + } + // Synthetic object field `f(e) : @over .Y`, the unique + // lift target forced by the discrete-opfibration condition. + let synth = ustr(&format!("{mor_name}({field_name})")); + let synth_seg = name_seg(synth); + let synth_label = label_seg(synth); + if !row.has(synth_seg) { + row.insert(synth_seg, synth_label, TyS::over(cod_path.clone())); + added = true; + } + // Synthetic morphism field `~f(e) : Morphism(mt, e, f(e))`, + // the lift morphism itself. The `~` prefix evokes the + // overline notation for lifts and disambiguates per source + // object so multiple declared `@over .X` fields can each + // get their own lift; modelgen strips the wrapper to + // recover the codomain morphism's name. + let lift_name = ustr(&format!("~{mor_name}({field_name})")); + let lift_seg = name_seg(lift_name); + let lift_label = label_seg(lift_name); + if !row.has(lift_seg) { + let self_var = TmS::var( + BwdIdx::from(0), + name_seg("self"), + label_seg("self"), + ); + let dom_term = TmS::proj(self_var.clone(), *field_name, *field_label); + let cod_term = TmS::proj(self_var, synth_seg, synth_label); + row.insert( + lift_seg, + lift_label, + TyS::morphism(mt.clone(), dom_term, cod_term), + ); + added = true; + } + } + } + if !added { + return TyS::record(row); + } + } + // Hit the depth bound — return what we have. Cyclic theories will + // produce an under-augmented body; this is a known limitation. + TyS::record(row) +} + +/// Read off a path of `(name, label)` segments from a term that is a chain +/// of projections rooted in a variable. +/// +/// The leading variable is treated as the implicit root (e.g., the `self` +/// of an enclosing record) and contributes no segment. So `Var(self)` +/// returns `[]` and `Proj(Var(self), E, E_label)` returns `[(E, E_label)]`, +/// matching the path representation produced by the surface `.E` syntax. +/// Returns `None` if the term has any other shape. +fn tms_to_path(tm: &TmS) -> Option> { + match &**tm { + TmS_::Var(_, _, _) => Some(vec![]), + TmS_::Proj(inner, name, label) => { + let mut p = tms_to_path(inner)?; + p.push((*name, *label)); + Some(p) + } + _ => None, + } +} + // NOTE: Most tests for the text elaborator are in the `examples` dir. #[cfg(test)] mod tests { diff --git a/packages/catlog/src/tt/val.rs b/packages/catlog/src/tt/val.rs index b00a4a554..428289b43 100644 --- a/packages/catlog/src/tt/val.rs +++ b/packages/catlog/src/tt/val.rs @@ -98,11 +98,12 @@ pub enum TyV_ { Unit, /// A metavariable, also see [TyS_::Meta]. Meta(MetaVar), - /// The type of terms of a fiber over a generator of a diagram's - /// codomain model. + /// The type of terms in a fiber over an object generator of some + /// diagram's codomain model. /// - /// See [TyS_::Over] for the syntactic counterpart. - Over(TopVarName, Vec<(FieldName, LabelSegment)>), + /// See [TyS_::Over] for the syntactic counterpart and explanation + /// of why diagram identity is not part of this type. + Over(Vec<(FieldName, LabelSegment)>), } /// Value for total types, dereferences to [TyV_]. @@ -184,8 +185,8 @@ impl TyV { } /// Smart constructor for [TyV], [TyV_::Over] case. - pub fn over(diag_name: TopVarName, path: Vec<(FieldName, LabelSegment)>) -> Self { - Self(Rc::new(TyV_::Over(diag_name, path))) + pub fn over(path: Vec<(FieldName, LabelSegment)>) -> Self { + Self(Rc::new(TyV_::Over(path))) } } From a8d725d282e18fa55f3b00f9ddad93491e4e69c8 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Mon, 4 May 2026 18:47:42 -0700 Subject: [PATCH 04/81] Add the test files --- .../examples/tt/text/test_instances.dbltt | 28 ++++++++ .../tt/text/test_instances.dbltt.snapshot | 71 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 packages/catlog/examples/tt/text/test_instances.dbltt create mode 100644 packages/catlog/examples/tt/text/test_instances.dbltt.snapshot diff --git a/packages/catlog/examples/tt/text/test_instances.dbltt b/packages/catlog/examples/tt/text/test_instances.dbltt new file mode 100644 index 000000000..9f9857a9e --- /dev/null +++ b/packages/catlog/examples/tt/text/test_instances.dbltt @@ -0,0 +1,28 @@ +set_theory ThSchema + +type WeightedGraph := [ + V : Entity, + E : Entity, + Weight : AttrType, + src : (Hom Entity)[E, V], + tgt : (Hom Entity)[E, V], + weight : Attr[E, Weight] +] + +diagram I : @Instance(WeightedGraph) := [ + e : @over .E, +] + +#/ Should there be some sugar for this situation, where I'm basically just renaming default-named guys? +diagram I2 : @Instance(WeightedGraph) := [ + v1 : @over .V, + v2 : @over .V, + w : @over .Weight, + we : I & [.src(e) := v1, .tgt(e) := v2, .weight(e) := w], + wf : I & [.src(e) := v2, .tgt(e) := v1, .weight(e) := w] +] + +diagram I3 : @Instance(WeightedGraph) := [ + e1 : @over .E, + e2 : @over .E +] \ No newline at end of file diff --git a/packages/catlog/examples/tt/text/test_instances.dbltt.snapshot b/packages/catlog/examples/tt/text/test_instances.dbltt.snapshot new file mode 100644 index 000000000..8a5c474e7 --- /dev/null +++ b/packages/catlog/examples/tt/text/test_instances.dbltt.snapshot @@ -0,0 +1,71 @@ +set_theory ThSchema +#/ result: set theory to ThSchema + +type WeightedGraph := [ + V : Entity, + E : Entity, + Weight : AttrType, + src : (Hom Entity)[E, V], + tgt : (Hom Entity)[E, V], + weight : Attr[E, Weight] +] +#/ declared: WeightedGraph + +diagram I : @Instance(WeightedGraph) := [ + e : @over .E, +] +#/ declared: I +#/ diagram domain: +#/ e : Entity -> E +#/ src(e) : Entity -> V +#/ tgt(e) : Entity -> V +#/ weight(e) : AttrType -> Weight +#/ ~src(e) : Hom(Entity)[e, src(e)] -> src +#/ ~tgt(e) : Hom(Entity)[e, tgt(e)] -> tgt +#/ ~weight(e) : Attr[e, weight(e)] -> weight +#/ diagram validates against codomain + +diagram I2 : @Instance(WeightedGraph) := [ + v1 : @over .V, + v2 : @over .V, + w : @over .Weight, + we : I & [.src(e) := v1, .tgt(e) := v2, .weight(e) := w], + wf : I & [.src(e) := v2, .tgt(e) := v1, .weight(e) := w] +] +#/ declared: I2 +#/ diagram domain: +#/ v1 : Entity -> V +#/ v2 : Entity -> V +#/ w : AttrType -> Weight +#/ we.e : Entity -> E +#/ wf.e : Entity -> E +#/ we.~src(e) : Hom(Entity)[we.e, v1] -> src +#/ we.~tgt(e) : Hom(Entity)[we.e, v2] -> tgt +#/ we.~weight(e) : Attr[we.e, w] -> weight +#/ wf.~src(e) : Hom(Entity)[wf.e, v2] -> src +#/ wf.~tgt(e) : Hom(Entity)[wf.e, v1] -> tgt +#/ wf.~weight(e) : Attr[wf.e, w] -> weight +#/ diagram validates against codomain + +diagram I3 : @Instance(WeightedGraph) := [ + e1 : @over .E, + e2 : @over .E +] +#/ declared: I3 +#/ diagram domain: +#/ e1 : Entity -> E +#/ e2 : Entity -> E +#/ src(e1) : Entity -> V +#/ tgt(e1) : Entity -> V +#/ weight(e1) : AttrType -> Weight +#/ src(e2) : Entity -> V +#/ tgt(e2) : Entity -> V +#/ weight(e2) : AttrType -> Weight +#/ ~src(e1) : Hom(Entity)[e1, src(e1)] -> src +#/ ~tgt(e1) : Hom(Entity)[e1, tgt(e1)] -> tgt +#/ ~weight(e1) : Attr[e1, weight(e1)] -> weight +#/ ~src(e2) : Hom(Entity)[e2, src(e2)] -> src +#/ ~tgt(e2) : Hom(Entity)[e2, tgt(e2)] -> tgt +#/ ~weight(e2) : Attr[e2, weight(e2)] -> weight +#/ diagram validates against codomain + From b233e687f9756d975d236b1458e4ce3eaadc7619 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Wed, 6 May 2026 15:35:59 -0700 Subject: [PATCH 05/81] missing commit --- packages/catlog/src/tt/batch.rs | 23 ++++++----------------- packages/catlog/src/tt/modelgen.rs | 21 +++++++++++---------- packages/catlog/src/tt/notebook_elab.rs | 4 +++- packages/catlog/src/tt/text_elab.rs | 12 ++---------- 4 files changed, 22 insertions(+), 38 deletions(-) diff --git a/packages/catlog/src/tt/batch.rs b/packages/catlog/src/tt/batch.rs index c333ab36e..da490338f 100644 --- a/packages/catlog/src/tt/batch.rs +++ b/packages/catlog/src/tt/batch.rs @@ -94,14 +94,10 @@ impl BatchOutput { } for g in &mor_gens { let mt = format_path(&domain.mor_generator_type(g)); - let dom_str = domain - .get_dom(g) - .map(|o| format!("{o}")) - .unwrap_or_else(|| "?".into()); - let cod_str = domain - .get_cod(g) - .map(|o| format!("{o}")) - .unwrap_or_else(|| "?".into()); + let dom_str = + domain.get_dom(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); + let cod_str = + domain.get_cod(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); let target_str = mapping .0 .mor_generator_map @@ -119,10 +115,7 @@ impl BatchOutput { } } - fn validation_result>( - &self, - errs: I, - ) { + fn validation_result>(&self, errs: I) { if let BatchOutput::Snapshot(out) = self { let mut out = out.borrow_mut(); let mut iter = errs.into_iter().peekable(); @@ -259,11 +252,7 @@ pub fn elaborate(src: &str, path: &str, output: &BatchOutput) -> io::Result { output.diagram_summary(&model_diag); let (cod, _) = Model::from_ty( diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 9be670f9c..17b57c4f2 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -440,7 +440,9 @@ impl DiagramGenerator<'_> { let (self_n, eval_with_self) = self.eval.bind_self(body_ty.clone()); self.eval = eval_with_self; let self_val = self.eval.eta_neu(&self_n, body_ty); - Ok(self.extract(vec![], &self_val, body_ty)?.unwrap_or_else(Namespace::new_for_uuid)) + Ok(self + .extract(vec![], &self_val, body_ty)? + .unwrap_or_else(Namespace::new_for_uuid)) } fn extract( @@ -474,7 +476,7 @@ impl DiagramGenerator<'_> { let resolved = cod_eval.path_ty(&self.codomain_ty, &cod_val, path)?; let TyV_::Object(ot) = &*resolved else { return Err( - "@over path does not refer to an object generator in the codomain".into(), + "@over path does not refer to an object generator in the codomain".into() ); }; let ot: QualifiedName = ot.clone().try_into().map_err(|_| { @@ -497,9 +499,10 @@ impl DiagramGenerator<'_> { let cod_name = neutral_qualified_name(cod).ok_or_else(|| { "morphism codomain does not resolve to a generator name".to_string() })?; - let mt_inner: QualifiedPath = mt.clone().try_into().map_err(|_| { - "morphism type is not valid for a discrete theory".to_string() - })?; + let mt_inner: QualifiedPath = mt + .clone() + .try_into() + .map_err(|_| "morphism type is not valid for a discrete theory".to_string())?; let qname: QualifiedName = prefix.clone().into(); self.domain.add_mor(qname.clone(), dom_name, cod_name, mt_inner); // Mapping target: extract the codomain morphism's name @@ -514,11 +517,9 @@ impl DiagramGenerator<'_> { } Ok(None) } - TyV_::Object(_) - | TyV_::Sing(_, _) - | TyV_::Id(_, _, _) - | TyV_::Unit - | TyV_::Meta(_) => Ok(None), + TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Id(_, _, _) | TyV_::Unit | TyV_::Meta(_) => { + Ok(None) + } } } } diff --git a/packages/catlog/src/tt/notebook_elab.rs b/packages/catlog/src/tt/notebook_elab.rs index 349579c3d..7fbe38408 100644 --- a/packages/catlog/src/tt/notebook_elab.rs +++ b/packages/catlog/src/tt/notebook_elab.rs @@ -452,7 +452,9 @@ impl<'a> Elaborator<'a> { nb::ModelJudgment::Equation(eqn_decl) => self.equation_cell(eqn_decl), }; field_ty_vs.push((name, (label, ty_v.clone()))); - self.ctx.scope.push(VarInContext::new(name, label, Some(ty_v.clone()), VarKind::Term)); + self.ctx + .scope + .push(VarInContext::new(name, label, Some(ty_v.clone()), VarKind::Term)); self.ctx.env = self.ctx.env.snoc(TmV::neu(TmN::proj(self_var.clone(), name, label), ty_v)); } diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index db6eb7d76..01104e72c 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -941,18 +941,10 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { let lift_seg = name_seg(lift_name); let lift_label = label_seg(lift_name); if !row.has(lift_seg) { - let self_var = TmS::var( - BwdIdx::from(0), - name_seg("self"), - label_seg("self"), - ); + let self_var = TmS::var(BwdIdx::from(0), name_seg("self"), label_seg("self")); let dom_term = TmS::proj(self_var.clone(), *field_name, *field_label); let cod_term = TmS::proj(self_var, synth_seg, synth_label); - row.insert( - lift_seg, - lift_label, - TyS::morphism(mt.clone(), dom_term, cod_term), - ); + row.insert(lift_seg, lift_label, TyS::morphism(mt.clone(), dom_term, cod_term)); added = true; } } From 38f66942f6c80143cd064e0e35d529777205282a Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Wed, 6 May 2026 15:37:21 -0700 Subject: [PATCH 06/81] clippy --- packages/catlog/src/tt/toplevel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catlog/src/tt/toplevel.rs b/packages/catlog/src/tt/toplevel.rs index 6c96febb9..03808a16f 100644 --- a/packages/catlog/src/tt/toplevel.rs +++ b/packages/catlog/src/tt/toplevel.rs @@ -17,7 +17,7 @@ pub enum TopDecl { DefConst(DefConst), /// See [Def]. Def(Def), - /// See [Diag] + /// See [Diag]. Diag(Diag), } From 7f6f9bd2701e8e849fb5b08e065896a6cc4e1a8e Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Thu, 7 May 2026 14:19:06 -0700 Subject: [PATCH 07/81] docs fix --- packages/catlog/src/tt/modelgen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 17b57c4f2..f55cdec9f 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -404,7 +404,7 @@ impl<'a> ModelGenerator<'a> { } } -/// Generates a [`DiscreteDblModelDiagram`] from an elaborated [`Diag`]. +/// Generates a [`discrete::DiscreteDblModelDiagram`] from an elaborated [`Diag`]. /// /// Restricted to discrete double theories for the moment; other theory /// kinds will need their own diagram types in catlog before this can be From 7a5e55d27edb3359b855184c75cf6d01e0ecaf87 Mon Sep 17 00:00:00 2001 From: Kevin Carlson Date: Tue, 12 May 2026 11:14:23 -0700 Subject: [PATCH 08/81] BUILD: Patch for owner change on `wasm-pack` (#1258) --- dev-docs/pnpm-lock.yaml | 5 +++++ packages/backend/pkg/pnpm-lock.yaml | 5 +++++ packages/document-methods/pnpm-lock.yaml | 9 ++++++-- packages/frontend/default.nix | 5 +++-- packages/frontend/pnpm-lock.yaml | 26 +++++++++++------------- packages/gaios/pnpm-lock.yaml | 5 +++++ packages/ui-components/pnpm-lock.yaml | 5 +++++ patches/wasm-pack@0.14.0.patch | 13 ++++++++++++ pnpm-lock.yaml | 5 +++++ pnpm-workspace.yaml | 2 ++ 10 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 patches/wasm-pack@0.14.0.patch diff --git a/dev-docs/pnpm-lock.yaml b/dev-docs/pnpm-lock.yaml index a36a548cf..a9bc6e643 100644 --- a/dev-docs/pnpm-lock.yaml +++ b/dev-docs/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../patches/wasm-pack@0.14.0.patch + importers: .: diff --git a/packages/backend/pkg/pnpm-lock.yaml b/packages/backend/pkg/pnpm-lock.yaml index 814241c97..dece28881 100644 --- a/packages/backend/pkg/pnpm-lock.yaml +++ b/packages/backend/pkg/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../../../patches/wasm-pack@0.14.0.patch + importers: .: diff --git a/packages/document-methods/pnpm-lock.yaml b/packages/document-methods/pnpm-lock.yaml index 4caf9daf6..eac366654 100644 --- a/packages/document-methods/pnpm-lock.yaml +++ b/packages/document-methods/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../../patches/wasm-pack@0.14.0.patch + importers: .: @@ -26,7 +31,7 @@ importers: version: 5.9.3 wasm-pack: specifier: ^0.14.0 - version: 0.14.0 + version: 0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56) packages: @@ -235,7 +240,7 @@ snapshots: uuid@13.0.0: {} - wasm-pack@0.14.0: + wasm-pack@0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56): dependencies: binary-install: 1.1.2 transitivePeerDependencies: diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index 52186de96..b0420c8e7 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -19,6 +19,7 @@ let ../../.npmrc ../../pnpm-workspace.yaml ../../pnpm-lock.yaml + ../../patches ../../packages/frontend ../../packages/ui-components ../../packages/document-methods @@ -36,8 +37,7 @@ let pnpmDeps = pkgs.fetchPnpmDeps { # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-n/0a/wK2mLkGvKWSBs/8zdfgF+dxMmPz3Ir1k30Qw9s="; - + hash = "sha256-LpQ4TxbVpdXPe6MGdtg/TQX7n4b0QAkgkj0ju9U+gZc="; pname = name; fetcherVersion = 2; # Only includes package.json and pnpm-lock.yaml files to ensure consistent hashing in different @@ -48,6 +48,7 @@ let ../../.npmrc ../../pnpm-workspace.yaml ../../pnpm-lock.yaml + ../../patches ../../packages/frontend/package.json ../../packages/frontend/pnpm-lock.yaml ../../packages/ui-components/package.json diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index 562181a83..57705f9b8 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../../patches/wasm-pack@0.14.0.patch + importers: .: @@ -212,7 +217,7 @@ importers: version: 4.0.8(@types/debug@4.1.12)(@types/node@24.10.0)(yaml@2.5.1) wasm-pack: specifier: ^0.14.0 - version: 0.14.0 + version: 0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56) web-worker: specifier: ^1.5.0 version: 1.5.0 @@ -1748,8 +1753,8 @@ packages: flatted@3.4.1: resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -2330,9 +2335,6 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} @@ -4443,7 +4445,7 @@ snapshots: axios@0.26.1: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.16.0 transitivePeerDependencies: - debug @@ -4935,7 +4937,7 @@ snapshots: flatted@3.4.1: {} - follow-redirects@1.15.9: {} + follow-redirects@1.16.0: {} foreground-child@3.3.0: dependencies: @@ -4977,7 +4979,7 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.2 + minimatch: 3.1.5 once: 1.4.0 path-is-absolute: 1.0.1 @@ -5986,10 +5988,6 @@ snapshots: dependencies: brace-expansion: 5.0.4 - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - minimatch@3.1.5: dependencies: brace-expansion: 1.1.11 @@ -6838,7 +6836,7 @@ snapshots: w3c-keyname@2.2.8: {} - wasm-pack@0.14.0: + wasm-pack@0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56): dependencies: binary-install: 1.1.2 transitivePeerDependencies: diff --git a/packages/gaios/pnpm-lock.yaml b/packages/gaios/pnpm-lock.yaml index 1cf81d4c7..b5b1cc9e7 100644 --- a/packages/gaios/pnpm-lock.yaml +++ b/packages/gaios/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../../patches/wasm-pack@0.14.0.patch + importers: .: diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 728992e35..4a1aa1d64 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: ../../patches/wasm-pack@0.14.0.patch + importers: .: diff --git a/patches/wasm-pack@0.14.0.patch b/patches/wasm-pack@0.14.0.patch new file mode 100644 index 000000000..a3a2cf447 --- /dev/null +++ b/patches/wasm-pack@0.14.0.patch @@ -0,0 +1,13 @@ +diff --git a/binary.js b/binary.js +index 7f0472a1f80325d4b75860f0ec9f784779a90c01..f9d1fae49b5bb6dd6baa1a8e268c8aa930fe299a 100644 +--- a/binary.js ++++ b/binary.js +@@ -31,7 +31,7 @@ const getPlatform = () => { + const getBinary = () => { + const platform = getPlatform(); + const version = require("./package.json").version; +- const author = "drager"; ++ const author = "wasm-bindgen"; + const name = "wasm-pack"; + const url = `https://github.com/${author}/${name}/releases/download/v${version}/${name}-v${version}-${platform}.tar.gz`; + return new Binary(platform === windows ? "wasm-pack.exe" : "wasm-pack", url, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 32d941a73..c00edf2bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + wasm-pack@0.14.0: + hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 + path: patches/wasm-pack@0.14.0.patch + importers: .: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 27d266014..53374b52d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,3 +7,5 @@ packages: - "packages/document-methods" - "packages/ui-components" - "packages/gaios" +patchedDependencies: + wasm-pack@0.14.0: patches/wasm-pack@0.14.0.patch From 96856355bc0f6c7947d277055e9ee7c953ad785f Mon Sep 17 00:00:00 2001 From: Kaspar Date: Tue, 12 May 2026 19:41:14 +0100 Subject: [PATCH 09/81] BUILD: Use "type" "bug" in issue template (#1261) --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8fbfa9d43..f28b2c15d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Let us know if something is broken, or not working how you expect it to title: '' -labels: bug +type: bug assignees: '' --- From 9e0287d63646a3602a60db2fa2d3bb23011ff75f Mon Sep 17 00:00:00 2001 From: Kris Brown Date: Tue, 12 May 2026 15:40:40 -0700 Subject: [PATCH 10/81] BUILD: Make optional deps be ordinary deps in AlgJulia interop (#1268) Co-authored-by: Kris Brown --- packages/algjulia-interop/Project.toml | 3 -- packages/algjulia-interop/scripts/endpoint.jl | 11 ++-- .../algjulia-interop/src/CatColabInterop.jl | 8 +-- .../CatlabExt.jl => src/CatlabInterop.jl} | 2 +- .../algjulia-interop/{ => test}/Manifest.toml | 52 +++++++++++-------- packages/algjulia-interop/test/TestCatlab.jl | 19 ++++--- 6 files changed, 50 insertions(+), 45 deletions(-) rename packages/algjulia-interop/{ext/CatlabExt.jl => src/CatlabInterop.jl} (99%) rename packages/algjulia-interop/{ => test}/Manifest.toml (94%) diff --git a/packages/algjulia-interop/Project.toml b/packages/algjulia-interop/Project.toml index aadcd1a4f..8e8512424 100644 --- a/packages/algjulia-interop/Project.toml +++ b/packages/algjulia-interop/Project.toml @@ -13,9 +13,6 @@ Oxygen = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -[extensions] -CatlabExt = ["Catlab", "ACSets"] - [compat] ACSets = "0.2.26" Catlab = "0.17.2" diff --git a/packages/algjulia-interop/scripts/endpoint.jl b/packages/algjulia-interop/scripts/endpoint.jl index 5ab0413ca..4ebc18cb9 100644 --- a/packages/algjulia-interop/scripts/endpoint.jl +++ b/packages/algjulia-interop/scripts/endpoint.jl @@ -10,6 +10,9 @@ using CatColabInterop using Oxygen using HTTP +using Catlab +using ACSets + const port = parse(Int, get(ENV, "JULIA_PORT", "8080")) const CORS_HEADERS = [ @@ -31,14 +34,6 @@ function CorsHandler(handle) end end -defaults = [:Catlab,:ACSets] # all extensions to date - -# Dynamically load packages in command lin eargs -for pkg in (isempty(ARGS) ? defaults : Symbol.(ARGS) ) - @info "using $pkg" - @eval using $pkg -end - for m in methods(CatColabInterop.endpoint) sig = m.sig.parameters (length(sig)==2 && sig[2].instance isa Val) || error("Unexpected signature $sig") diff --git a/packages/algjulia-interop/src/CatColabInterop.jl b/packages/algjulia-interop/src/CatColabInterop.jl index 18bd4b81a..cc35146fd 100644 --- a/packages/algjulia-interop/src/CatColabInterop.jl +++ b/packages/algjulia-interop/src/CatColabInterop.jl @@ -4,12 +4,14 @@ export endpoint using Reexport -""" -Extend this method with endpoint(::Val{my_analysis_name}) in extension packages. -""" +""" Extend this method with endpoint(::Val{my_analysis_name}). """ function endpoint end include("Types.jl") @reexport using .Types +include("CatlabInterop.jl") +# Add more interops here... + + end # module diff --git a/packages/algjulia-interop/ext/CatlabExt.jl b/packages/algjulia-interop/src/CatlabInterop.jl similarity index 99% rename from packages/algjulia-interop/ext/CatlabExt.jl rename to packages/algjulia-interop/src/CatlabInterop.jl index 34fe01fde..792ecfa1f 100644 --- a/packages/algjulia-interop/ext/CatlabExt.jl +++ b/packages/algjulia-interop/src/CatlabInterop.jl @@ -1,4 +1,4 @@ -module CatlabExt +module CatlabInterop using ACSets using Catlab: Presentation, FreeSchema, Left diff --git a/packages/algjulia-interop/Manifest.toml b/packages/algjulia-interop/test/Manifest.toml similarity index 94% rename from packages/algjulia-interop/Manifest.toml rename to packages/algjulia-interop/test/Manifest.toml index ed8697aba..d63984100 100644 --- a/packages/algjulia-interop/Manifest.toml +++ b/packages/algjulia-interop/test/Manifest.toml @@ -2,13 +2,13 @@ julia_version = "1.12.5" manifest_format = "2.0" -project_hash = "ae3caef2a0866469616d51a6bb9ac744f063ad55" +project_hash = "8e27b017675e11d4ba34f579de046e8ab3cb258a" [[deps.ACSets]] deps = ["AlgebraicInterfaces", "Base64", "CompTime", "DataStructures", "JSON3", "MLStyle", "OrderedCollections", "PEG", "Permutations", "Pkg", "PrettyTables", "Random", "Reexport", "SHA", "StaticArrays", "StructEquality", "StructTypes", "Tables"] -git-tree-sha1 = "80ca69d6844aa5a1e67338b5362e393007c4c168" +git-tree-sha1 = "dabea268e85bfa2d87019772d1e5b5115542450f" uuid = "227ef7b5-1206-438b-ac65-934d6da304b8" -version = "0.2.26" +version = "0.2.28" [deps.ACSets.extensions] DataFramesACSetsExt = "DataFrames" @@ -44,18 +44,15 @@ version = "0.1.9" [[deps.CatColabInterop]] deps = ["ACSets", "Catlab", "HTTP", "MLStyle", "Oxygen", "Reexport", "StructTypes"] -path = "." +path = ".." uuid = "9ecda8fb-39ab-46a2-a496-7285fa6368c1" version = "0.1.1" - [deps.CatColabInterop.extensions] - CatlabExt = ["Catlab", "ACSets"] - [[deps.Catlab]] deps = ["ACSets", "AlgebraicInterfaces", "Colors", "CompTime", "Compose", "DataStructures", "GATlab", "GeneralizedGenerated", "JSON3", "LightXML", "LinearAlgebra", "Logging", "MLStyle", "PEG", "PrettyTables", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StructEquality", "StructTypes", "Tables"] -git-tree-sha1 = "bf515a1858d0dfd00887a3ba1daf587d14ec7b51" +git-tree-sha1 = "fadf5ec3e429cc88be1178b6baf687b99bb3177c" uuid = "134e5e36-593f-5add-ad60-77f754baafbe" -version = "0.17.4" +version = "0.17.5" [deps.Catlab.extensions] CatlabConvexExt = "Convex" @@ -217,9 +214,9 @@ version = "1.0.0" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "0533e564aae234aff59ab625543145446d8b6ec2" +git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.7.1" +version = "1.8.0" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -339,9 +336,9 @@ version = "1.1.10" [[deps.MbedTLS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "926c6af3a037c68d02596a44c22ec3595f5f760b" +git-tree-sha1 = "ff69a2b1330bcb730b9ac1ab7dd680176f5896b8" uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" +version = "2.28.1010+0" [[deps.Measures]] git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" @@ -418,9 +415,9 @@ version = "1.0.4" [[deps.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "7d2f8f21da5db6a806faf7b9b292296da42b2810" +git-tree-sha1 = "5d5e0a78e971354b1c7bff0655d11fdc1b0e12c8" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.3" +version = "2.8.4" [[deps.Permutations]] deps = ["Combinatorics", "LinearAlgebra", "Random"] @@ -432,18 +429,16 @@ version = "0.4.23" deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.12.1" +weakdeps = ["REPL"] [deps.Pkg.extensions] REPLExt = "REPL" - [deps.Pkg.weakdeps] - REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - [[deps.PrecompileTools]] deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +git-tree-sha1 = "07a921781cab75691315adc645096ed5e370cb77" uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" +version = "1.3.3" [[deps.Preferences]] deps = ["TOML"] @@ -457,16 +452,27 @@ uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" version = "0.2.0" [[deps.PrettyTables]] -deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "1101cd475833706e4d0e7b122218257178f48f34" +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "624de6279ab7d94fc9f672f0068107eb6619732c" uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.4.0" +version = "3.3.2" + + [deps.PrettyTables.extensions] + PrettyTablesTypstryExt = "Typstry" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" version = "1.11.0" +[[deps.REPL]] +deps = ["InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" + [[deps.Random]] deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/packages/algjulia-interop/test/TestCatlab.jl b/packages/algjulia-interop/test/TestCatlab.jl index eeaf86682..83a07bbd5 100644 --- a/packages/algjulia-interop/test/TestCatlab.jl +++ b/packages/algjulia-interop/test/TestCatlab.jl @@ -4,8 +4,7 @@ using CatColabInterop, Catlab using Catlab.CategoricalAlgebra.Pointwise.FunctorialDataMigrations.Yoneda: colimit_representables using HTTP, Test, Oxygen, JSON3 -const CatlabExt = Base.get_extension(CatColabInterop, :CatlabExt) - +import CatColabInterop.CatlabInterop # Example JSON #------------- body = read((@__DIR__)*"/data/diagrams/acset.json", String) @@ -16,11 +15,11 @@ p = JSON3.read(body, ModelDiagram) # Convert to ACSet #----------------- -schema, names = CatlabExt.model_to_schema(p.model) +schema, ids = CatColabInterop.CatlabInterop.model_to_schema(p.model) acset_type = AnonACSet( schema; type_assignment=Dict(a=>Nothing for a in schema.attrtypes)) y = yoneda(constructor(acset_type)) -data = CatlabExt.diagram_to_data(p.diagram, names) +data = CatlabInterop.diagram_to_data(p.diagram, ids) names, res = colimit_representables(data, y) S = acset_schema(res) @@ -36,8 +35,14 @@ expected[1, :g] = AttrVar(1) # Test final JSON output #----------------------- -expected_json = Dict(:Z => ["z"],:f => ["y"],:X => ["x"],:Y => ["y"],:g => ["z"]) -@test expected_json == CatlabExt.acset_to_json(res, schema, CatlabExt.make_names(res, names)) +expected_json = Dict( + "019a60e3-1785-72b9-90d2-84dc8bdddc85" => ["z"], + "019a6042-2872-7654-a9b4-67becc9ef693" => ["y"], + "019a6042-1241-77bd-8055-bfea5c206bc7" => ["x"], + "019a6042-1f1c-745e-bfea-753eeaccedf2" => ["y"], + "019a60e3-2ccf-74ef-a1e1-1c940564e1ca" => ["z"] +) +@test expected_json == CatlabInterop.acset_to_json(res, schema, ids, CatlabInterop.make_names(res, names)) # Optionally test the endpoint if running endpoint.jl: # resp = HTTP.post("http://127.0.0.1:8080/acsetcolim"; body) @@ -49,6 +54,6 @@ expected_json = Dict(:Z => ["z"],:f => ["y"],:X => ["x"],:Y => ["y"],:g => ["z"] @acset_type T(SchThree) exT = @acset T begin A=2; B=3; C=3; f=[2,3]; g=[2,3,2] end names = (z=(:C, 1), y= (:B, 1), x = (:A, 1), x2 = (:A, 2)) -@test CatlabExt.make_names(exT, names) == Dict(:A=>["x","x2"], :B=>["y","f(x)","f(x2)"], :C=>["z","g(y)","g(f(x))"]) +@test CatlabInterop.make_names(exT, names) == Dict(:A=>["x","x2"], :B=>["y","f(x)","f(x2)"], :C=>["z","g(y)","g(f(x))"]) end # module From 1607e5f670f0eac25ea2100d631c308a906e728e Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 14 Apr 2026 12:18:18 +0100 Subject: [PATCH 11/81] DOC: Example: internal language of database schemas. --- rfc/0004.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 7cd0f1560..961f6acfd 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -562,14 +562,16 @@ is only syntactic sugar for the former. \hypo{\dbl{T} \vDash (\cal{X} \xproto{P} \cal{Y} \xproto{Q} \cal{Z})} \hypo{\Gamma \mid u: X \vdash_P t: Y} \hypo{\Gamma \vdash f: Q(Y, Z)} - \infer3{\Gamma \mid u: X \vdash_{P \odot Q} f\,t: Z} + \infer3{\Gamma \mid u: X \vdash_{P \odot Q} f(t): Z} \end{prooftree} \] ``` where the first premise tacitly includes the assumption that the proarrows $P$ and $Q$ have a composite. This rule can then be interpreted as applying the - binary composition cell out of $P$ and $Q$. + binary composition cell out of $P$ and $Q$. Note that in our application + syntax, here and elsewhere, parentheses are optional; thus the expressions + $f(t)$ and $f\,t$ are identical terms. - *Operation application*: for each unary cell $\alpha$ in $\dbl{T}$ as on the left, there is a rule as on the right: @@ -672,7 +674,50 @@ is only syntactic sugar for the former. ## Examples -**TODO** +### Unary languages + +\newcommand{\Entity}{\mathsf{Entity}} +\newcommand{\AttrType}{\mathsf{AttrType}} +\newcommand{\Mapping}{\mathrm{Mapping}} +\newcommand{\Attr}{\mathrm{Attr}} + +\newcommand{\Person}{\mathrm{Person}} +\newcommand{\Dog}{\mathrm{Dog}} +\newcommand{\String}{\mathrm{String}} + +As an example of a unary language a bit more interesting than the internal +language of a bare category, consider database schemas, as axiomatized in +idealized form by the walking proarrow, i.e., by the discrete double theory: + +$$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ + +With the abbreviation $\Mapping \coloneqq \Hom_{\Entity}$ for mappings (foreign +keys) between entities, we can form the outer context representing a simple +schema involving people and dogs: + +$$ +\begin{aligned} +\Gamma = [ +&\String: \Ob_{\AttrType}, \\ +&\Person: \Ob_{\Entity}, \\ +&\text{first-name}: \Attr(\Person, \String), \\ +&\text{last-name}: \Attr(\Person, \String), \\ +&\Dog: \Ob_{\Entity}, \\ +&\text{owner}: \Mapping(\Dog, \Person) +]. +\end{aligned} +$$ + +Within the inner context $\Gamma \mid p: \Person$, we can make the following +derivation: + +1. $\Gamma \mid d: \Dog \vdash_{\Mapping} d: \Dog$ +2. $\Gamma \mid d: \Dog \vdash_{\Mapping} \text{owner}(d): \Person$ +3. $\Gamma \mid d: \Dog \vdash_{\Attr} \text{first-name}(\text{owner}(d)): \String$ + +where step 1 uses the identity rule and steps 2 and 3 use the composition rule. +The final term denotes the morphism that maps a dog to the first name of its +owner. ## Rationale and alternatives From 1e0c37513641c54e52941baff6dc69b4298314d3 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 14 Apr 2026 21:49:25 +0100 Subject: [PATCH 12/81] DOC: Example: internal language of multicategories. --- rfc/0004.md | 163 ++++++++++++++++++++++++++++++++++------ rfc/_macros.qmd | 2 +- rfc/_templates/tikz.tex | 2 +- 3 files changed, 143 insertions(+), 24 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 961f6acfd..c2c6055ac 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -674,7 +674,7 @@ is only syntactic sugar for the former. ## Examples -### Unary languages +### A unary language \newcommand{\Entity}{\mathsf{Entity}} \newcommand{\AttrType}{\mathsf{AttrType}} @@ -685,9 +685,12 @@ is only syntactic sugar for the former. \newcommand{\Dog}{\mathrm{Dog}} \newcommand{\String}{\mathrm{String}} -As an example of a unary language a bit more interesting than the internal -language of a bare category, consider database schemas, as axiomatized in -idealized form by the walking proarrow, i.e., by the discrete double theory: +The simplest example of an internal language is that of a bare category. This +language is a unary type theory [@shulman-2016, Section 1.2]. + +For an example of a unary language that is a bit more interesting, consider the +internal language of a database schema, as axiomatized in idealized form by the +walking proarrow, i.e., by the discrete double theory: $$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ @@ -708,8 +711,7 @@ $$ \end{aligned} $$ -Within the inner context $\Gamma \mid p: \Person$, we can make the following -derivation: +In the inner context $\Gamma \mid p: \Person$, we can derive the terms: 1. $\Gamma \mid d: \Dog \vdash_{\Mapping} d: \Dog$ 2. $\Gamma \mid d: \Dog \vdash_{\Mapping} \text{owner}(d): \Person$ @@ -719,24 +721,142 @@ where step 1 uses the identity rule and steps 2 and 3 use the composition rule. The final term denotes the morphism that maps a dog to the first name of its owner. +### Multicategories + +Perhaps the simplest multi-ary language is the internal language of a (planar) +multicategory. In this highly substructural language, variables must be used +exactly once and in the same order that they appear in the context. + +Let us briefly review the modal double theory of a [generalized +multicategory](https://next.catcolab.org/math/dct-000F.xml), to be used with the +list modality $T = \List$. The generators of this double theory are an object +$\cal{X}$ and a proarrow $P: \List(\cal{X}) \proto \cal{X}$. There are two key +axioms, the *composition axiom* and the *normalization axiom*: + +$$ +\List P \odot P = P(\mu_{\cal{X}}, 1_{\cal{X}}) +\qquad\text{and}\qquad +\Hom_{\cal{X}} = P(\eta_{\cal{X}}, 1_{\cal{X}}). +$$ + +By the universal property of restriction, the composition axiom amounts to a +cell + +```tikz +% https://q.uiver.app/#q=WzAsNSxbMCwwLCJcXExpc3RcXExpc3RcXGNhbHtYfSJdLFsxLDAsIlxcTGlzdFxcY2Fse1h9Il0sWzIsMCwiXFxjYWx7WH0iXSxbMCwxLCJcXExpc3RcXGNhbHtYfSJdLFsyLDEsIlxcY2Fse1h9Il0sWzAsMSwiXFxMaXN0IFAiLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMSwyLCJQIiwwLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzIsNCwiIiwwLHsibGV2ZWwiOjIsInN0eWxlIjp7ImhlYWQiOnsibmFtZSI6Im5vbmUifX19XSxbMyw0LCJQIiwyLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzAsMywiXFxtdV97XFxjYWx7WH19IiwyXSxbMSw4LCJcXHRleHR7Y29tcH0iLDEseyJsYWJlbF9wb3NpdGlvbiI6NDAsInNob3J0ZW4iOnsidGFyZ2V0IjoyMH0sInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6Im5vbmUifSwiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dXQ== +\begin{tikzcd} + {\List\List\cal{X}} & {\List\cal{X}} & {\cal{X}} \\ + {\List\cal{X}} && {\cal{X}} + \arrow["{\List P}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] + \arrow["{\mu_{\cal{X}}}"', from=1-1, to=2-1] + \arrow["P"{inner sep=.8ex}, "\shortmid"{marking}, from=1-2, to=1-3] + \arrow[equals, from=1-3, to=2-3] + \arrow[""{name=0, anchor=center, inner sep=0}, "P"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-3] + \arrow["{\text{comp}}"{description, pos=0.4}, draw=none, from=1-2, to=0] +\end{tikzcd} +``` + +giving the composition operation in the multicategory. Recall, however, that +this binary cell cannot be directly applied in the internal language; instead, +one uses the proarrow composite $\List P \odot P$, equal to the restricted +proarrow $P(\mu_{\cal{X}},1)$, then applies the restriction cell. The +normalization axiom + +```tikz +% https://q.uiver.app/#q=WzAsNCxbMCwwLCJcXGNhbHtYfSJdLFsxLDAsIlxcY2Fse1h9Il0sWzAsMSwiXFxMaXN0XFxjYWx7WH0iXSxbMSwxLCJcXGNhbHtYfSJdLFswLDEsIlxcSG9tX3tcXGNhbHtYfX0iLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMCwyLCJcXGV0YV97XFxjYWx7WH19IiwyXSxbMSwzLCIiLDAseyJsZXZlbCI6Miwic3R5bGUiOnsiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dLFsyLDMsIlAiLDIseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbNCw3LCJcXHRleHR7cmVzfSIsMSx7InNob3J0ZW4iOnsic291cmNlIjoyMCwidGFyZ2V0IjoyMH0sInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6Im5vbmUifSwiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dXQ== +\begin{tikzcd} + {\cal{X}} & {\cal{X}} \\ + {\List\cal{X}} & {\cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "{\Hom_{\cal{X}}}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] + \arrow["{\eta_{\cal{X}}}"', from=1-1, to=2-1] + \arrow[equals, from=1-2, to=2-2] + \arrow[""{name=1, anchor=center, inner sep=0}, "P"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-2] + \arrow["{\text{res}}"{description}, draw=none, from=0, to=1] +\end{tikzcd} +``` + +says that the unary multimorphisms of $P$ are in natural bijection with the +morphisms of $\cal{X}$. For the remaining axioms of the double theory, see the +page linked above. + +As outer context, consider the signature for the theory of monoids: + +$$ +\Gamma = [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)]. +$$ + +Let's build up a term in context $\Gamma \mid [x,y,z]: [X,X,X]$ that could serve +as the the left-hand side of the associativity axiom for monoids. To start, use +the identity rule to form the term $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} x: +X$ and similarly for a variable $y$. Combining these, form the list + +$$ +\Gamma \mid [x,y]: [X,X] \vdash_{\List\Hom_{\cal{X}}} [x,y]: [X,X]. +$$ + +Apply the composition rule at the proarrrows $\List\Hom_{\cal{X}} = +\Hom_{\List\cal{X}}$ and $P: \List\cal{X} \proto \cal{X}$ to build the term + +$$ +\Gamma \mid [x,y]: [X,X] \vdash_P m[x,y]: X. +$$ + +Next, applying the restriction rule at the theory's normalization axiom, we +obtain a variable + +$$ +\Gamma \mid [z]: [X] \vdash_P z: X +$$ + +but now viewed as a unary multimorphism of $P$ rather than a morphism of +$\cal{X}$. We can thus form the list + +$$ +\Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{\List P} [m[x,y],z]: [X,X], +$$ + +apply the composition rule at the theory's composition axiom + +$$ +\Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{P(\mu_{\cal{X}}, 1)} m[m[x,y],z]: X, +$$ + +and finally apply the restriction rule again to derive the term + +$$ +\Gamma \mid [x,y,z]: [X,X,X] \vdash_P m[m[x,y],z]: X. +$$ + +As promised, this term is the left-hand side of the associativity axiom for +monoids. It would be written more conventionally as + +$$ +\Gamma \mid [x: X, y: X, z: X] \vdash m[m[x,y],z]: X +$$ + +but we caution again that in the present type theory, this expression can only +be read as an abbreviation for the previous one. + ## Rationale and alternatives ### Bespoke internal languages -An obvious alternative would be to apply the usual methodology: for each -categorical structure of interest, find its internal language and produce a -bespoke implementation of that type theory. In this approach, every logic in -CatColab requiring an internal language would have its own custom text frontend. +An obvious alternative to this RFC is to apply the standard methodology: for +each categorical structure of interest, find its internal language and then +implement that specific type theory. In this approach, every logic in CatColab +requiring an internal language would have its own custom type-theoretic +frontend. + From the "semantics first" perspective of CatColab, this approach is not unreasonable. By carefully separating the textual syntax, the data structures for models and morphisms to which the syntax compiles, and the analyses that can be run on those data structures, we could hope to retain many of the benefits of -a uniform meta-logic, at least outside the frontend. Indeed, this has been our -intended approach to point-and-click *graphical* editors for graphs, Petri nets, -and so forth, whose variety seems to defy systemization. Yet I would still -rather see such an approach to internal languages as a fallback option, being at -best a pragmatic compromise to the vision of CatColab as an interoperable family -of modeling and programming languages. +having a uniform meta-logic, at least outside of the frontend. Indeed, this has +been our intended approach to *graphical* editors for graphs, Petri nets, and so +forth, whose variety seems to defy systematization. Yet I would still rather see +the ad hoc approach to internal languages as a fallback option---at best a +pragmatic compromise to the vision of CatColab as an interoperable family of +programming languages. ### String diagrams @@ -748,12 +868,11 @@ canonical forms for morphisms in monoidal categories is to use *string diagrams* demonstrating that pointfulness is not a necessary property of normalizing syntax. -From our "semantics first" perspective, string diagrams are not so much an -alternative to type-theoretic textual notation as a complement to it. In -CatColab, it is the categorical structures that are primary; both graphical and -textual syntaxes should be available. Making multiple syntaxes work together -seamlessly poses architectural challenges. This document, however, is solely -about textual syntax. +String diagrams are not so much an alternative to type-theoretic textual +notation as a complement to it. Graphical and textual syntaxes each have their +pros and cons, and both should be available in CatColab. Making multiple kinds +of syntax work together seamlessly poses engineering and possibly also +mathematical challenges. This document is, however, solely about textual syntax. ## Prior art diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index 4a058398b..731f452b8 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -29,7 +29,7 @@ \newcommand{\proTo}{\mathrel{\mkern3mu\vcenter{\hbox{$\shortmid$}}\mkern-10mu{\Rightarrow}}} -\newcommand{\List}{\mathrm{List}} +\newcommand{\List}{\mathop{\mathrm{List}}} \newcommand{\Disc}{\mathop{\mathrm{Disc}}} \newcommand{\coDisc}{\mathop{\mathrm{coDisc}}} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index 26fad127a..5a1c4081f 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -36,7 +36,7 @@ \newcommand{\proTo}{\mathrel{\mkern3mu\vcenter{\hbox{$\shortmid$}}\mkern-10mu{\Rightarrow}}} % Monads -\newcommand{\List}{\mathrm{List}} +\newcommand{\List}{\mathop{\mathrm{List}}} \newcommand{\Disc}{\mathop{\mathrm{Disc}}} \newcommand{\coDisc}{\mathop{\mathrm{coDisc}}} From b3f279e99c2576fbc7dc193357e52021aec0dd39 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 19 Apr 2026 21:10:22 +0100 Subject: [PATCH 13/81] DOC: Remark on (non)uniqueness of derivations in internal language. --- rfc/0004.md | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index c2c6055ac..462ba7c06 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -725,7 +725,9 @@ owner. Perhaps the simplest multi-ary language is the internal language of a (planar) multicategory. In this highly substructural language, variables must be used -exactly once and in the same order that they appear in the context. +exactly once and in the same order that they appear in the context. The type +theory here should be compared with the *cut-free type theory for +multicategories* in [@shulman-2016, Section 2.4.1]. Let us briefly review the modal double theory of a [generalized multicategory](https://next.catcolab.org/math/dct-000F.xml), to be used with the @@ -779,23 +781,24 @@ says that the unary multimorphisms of $P$ are in natural bijection with the morphisms of $\cal{X}$. For the remaining axioms of the double theory, see the page linked above. -As outer context, consider the signature for the theory of monoids: +As outer context, take the signature for the theory of monoids: $$ \Gamma = [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)]. $$ -Let's build up a term in context $\Gamma \mid [x,y,z]: [X,X,X]$ that could serve -as the the left-hand side of the associativity axiom for monoids. To start, use -the identity rule to form the term $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} x: -X$ and similarly for a variable $y$. Combining these, form the list +Let's build up a term in inner context $\Gamma \mid [x,y,z]: [X,X,X]$ that could +serve as the the left-hand side of the associativity axiom for monoids. To +start, use the identity rule to form the term $\Gamma \mid x: X +\vdash_{\Hom_{\cal{X}}} x: X$ and similarly for a variable $y$. Combining these, +form the list $$ \Gamma \mid [x,y]: [X,X] \vdash_{\List\Hom_{\cal{X}}} [x,y]: [X,X]. $$ -Apply the composition rule at the proarrrows $\List\Hom_{\cal{X}} = -\Hom_{\List\cal{X}}$ and $P: \List\cal{X} \proto \cal{X}$ to build the term +Apply the composition rule at the composable proarrrows $\List\Hom_{\cal{X}} = +\Hom_{\List\cal{X}}$ and $P$ to build the term $$ \Gamma \mid [x,y]: [X,X] \vdash_P m[x,y]: X. @@ -815,7 +818,7 @@ $$ \Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{\List P} [m[x,y],z]: [X,X], $$ -apply the composition rule at the theory's composition axiom +apply the composition rule at the composable proarrows $P$ and $P$ $$ \Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{P(\mu_{\cal{X}}, 1)} m[m[x,y],z]: X, @@ -837,6 +840,29 @@ $$ but we caution again that in the present type theory, this expression can only be read as an abbreviation for the previous one. +::: {.callout-warning} +#### Uniqueness of derivations + +There was another, slightly longer way we could have derived the term $m[x,y]$, +parallel to how we derived the final term $m[m[x,y],z]$. Namely, after +introducing the variables $x$ and $y$, apply the restriction rule to obtain +terms $\Gamma \mid [v]: [X] \vdash_P v: X$ for $v = x,y$, then form the list +$\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List P} [x,y]: [X,Y]$, and finally +proceed as before to construct the term $\Gamma \mid [x,y]: [X,Y] \vdash_P +m[x,y] \vdash X$. + +By the theory's compatibility axiom for the left action of $P$, both derivations +denote the same multimorphism in any model of the theory. By making restriction +act implicitly on terms, the same thing is achieved in the type theory: the two +derivations yield identical terms. So, turning this around, terms in this type +theory do not have unique derivations. + +It is often a desideratum of a type theory that (derivable) terms have a unique +derivation. At the expense of some ad hocery, we could achieve that in this case +by prohibiting use of $P$'s left and right actions. + +::: + ## Rationale and alternatives ### Bespoke internal languages From f898748293350911dc368e3b1a68d71d3e4d1b9a Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 19 Apr 2026 22:28:40 +0100 Subject: [PATCH 14/81] DOC: Let binding rule for internal languages of models. --- rfc/0004.md | 68 +++++++++++++++++++++++++++++++---------- rfc/_macros.qmd | 1 + rfc/_templates/tikz.tex | 1 + 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 462ba7c06..634565be6 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -74,10 +74,10 @@ using variables. That's what the [`Path`](https://next.catcolab.org/dev/rust/catlog/one/path/enum.path) data structure in `catlog` does. -Finding canonical forms becomes more difficult as categorical structure -accumulates. For example, let $X \xto{f} Y \xto{g} Z$ and $X' \xto{f'} Y' -\xto{g'} Z'$ be morphisms in a monoidal category. In point-free syntax, two -different expressions, +As more categorical structure accumulates, finding canonical forms becomes more +difficult. For example, let $X \xto{f} Y \xto{g} Z$ and $X' \xto{f'} Y' \xto{g'} +Z'$ be morphisms in a monoidal category. In point-free syntax, two different +expressions, $$ (g \circ f) \otimes (g' \circ f') @@ -87,18 +87,23 @@ $$ denote the same morphism, by the interchange law. Moreover, due to the symmetry of the situation, there is no obvious reason to prefer one over the other. In -the pointful notation of a suitable type theory, this symmetry is broken, and -there will be a canonical way to denote this morphism, namely +the pointful notation of a suitable type theory, this symmetry is broken: of the +corresponding two ways to denote this morphism, $$ -(x, x'): X \otimes X' \vdash (g(f(x)), g'(f'(x'))): Z \otimes Z'. +(x, x'): X \otimes X' \vdash (g(f(x)), g'(f'(x'))): Z \otimes Z' $$ -Intuitively, this term corresponds to the first of the above point-free -expressions, whereas the second has no counterpart because forming and applying -the tensored operation $g \otimes g'$ is not allowed. This point of view---that -type theory is a toolkit to find canonical forms for morphisms---is a theme in -Shulman's lecture notes on categorical logic [@shulman-2016]. +and + +$$ +(x, x'): X \otimes X' \vdash \letin{(y, y') = (f(x), f(x'))} (g(y), g(y')): Z \otimes Z', +$$ + +the former is canonical as it contains no eliminable let bindings, whereas the +latter does. This general point of view---that type theory is a toolkit to find +canonical forms for morphisms---is a theme in Shulman's lecture notes on +categorical logic [@shulman-2016]. In short, among textual notations, pointful notation has both practical advantages, being familiar and ergonomic, as well as theoretical ones, inasmuch @@ -442,7 +447,7 @@ inner terms. #### Contexts ::: {.callout-warning} -#### Contexts vs lists +#### Warning: Contexts vs lists Contexts are typically defined inductively to be lists of typed variables (i.e., variable-type pairs), but the inner contexts of this type theory are constructed @@ -662,19 +667,50 @@ is only syntactic sugar for the former. Consequently, terms $\Gamma \mid u: X \vdash_{P(F,G)} t: Y$ and $\Gamma \mid F(u): F(X) \vdash_P t: G(Y)$ are in bijection. -- *Destructuring*: **TODO** +- *Let binding*: + + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (\cal{X} \xproto{P} \cal{Y} \xproto{Q} \cal{Z})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \hypo{\Gamma \mid v: Y \vdash_Q s: Z} + \infer3{\Gamma \mid u: X \vdash_{P \odot Q} \letin{v = t} s: Z} + \end{prooftree} + \] + ``` + + where the first premise tacitly includes the assumption that the proarrows $P$ + and $Q$ have a composite. This rule can be interpreted as destructuring the + value $t$ and binding it to the variables in the context $v$. + +::: {.callout} +##### Remark: Let binding as cut rule + +Let binding is a necessary part of some internal languages but not others, +depending on whether morphisms have "multi-ary" codomains. For example, let +binding is needed in the internal languages of monoidal categories and of +PRO(P)s, but not of multicategories. + +The let binding rule is precisely an explicit cut rule. So, we should obviously +omit it when proving cut admissibility for a particular internal language. On +the other hand, even when it's not strictly necessary, we might choose to +include it on the pragmatic ground of being a standard feature of programming +notation. + +::: ##### Lists **TODO** -### Meta-theoretic results +### Meta-theoretic properties **TODO**: Cut admissibility ## Examples -### A unary language +### Unary languages \newcommand{\Entity}{\mathsf{Entity}} \newcommand{\AttrType}{\mathsf{AttrType}} diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index 731f452b8..61fad851a 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -36,3 +36,4 @@ \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} +\newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index 5a1c4081f..c9d027b68 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -43,6 +43,7 @@ % Type theory \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} +\newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} @OPTIONAL_PREAMBLE From e113145186dd497c0ae6f7fc1ae0e56bbad1dbf7 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 20 Apr 2026 23:37:55 +0100 Subject: [PATCH 15/81] DOC: Assume flatness of double theory in internal language of models. --- rfc/0004.md | 215 ++++++++++++++++++++++++++------------------ rfc/_references.bib | 8 ++ 2 files changed, 134 insertions(+), 89 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 634565be6..d18e5040f 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -111,28 +111,25 @@ as it sometimes leads to canonical forms. Before moving on, it's worth acknowledging that point-free notation is not without its own merits. Conveniently for computer implementation, point-free -notation can be mechanically extracted for any categorical structure defined in -a suitable formal language, such as GATs or double theories. In contrast, while -the field of type theory has recurring principles and techniques, designing *a* -type theory remains a fiddly business. Finding the [internal -language](https://ncatlab.org/nlab/show/internal+logic) of a new categorical -structure---a type theory whose types and terms faithfully denote such -categories' objects and morphisms---is often a research problem in its own -right. So, the research problem behind this RFC is that while CatColab has a -uniform meta-logic for (parts of) logic at the level of categorical *semantics*, -we now need a uniform approach at the level of type-theoretic *syntax*. - -Not only is point-free syntax readily available for *any* categorical structure, -it is equivariant under categorical duality, whereas pointful notation is -intrinsically biased toward the domain of a morphism (its inputs) and also, to a -lesser extent, toward cartesian logics, in which variables can be freely -duplicated and discarded. The happiest categorical semantics for pointful -notation is a free cartesian multicategory or, what is nearly the same, a free -cartesian category whose basic morphisms have codomains that are basic objects. -The further one departs from this ideal, the more likely standard techniques are -to break down. Nevertheless, providing an intuitive pointful syntax seems so -indispensable to the vision of CatColab as a family of interoperable programming -languages that we cannot neglect to pursue a unified approach. +notation can be mechanically extracted for categorical structures defined in any +reasonable formal language, such as GATs or double theories. In contrast, +finding the [internal language](https://ncatlab.org/nlab/show/internal+logic) of +a new categorical structure---a type theory whose types and terms correspond +precisely to the structure's objects and morphisms---is often a research problem +in its own right. So, the research problem behind this RFC is that while +CatColab has a uniform meta-logic for (parts of) categorical logic at the level +of *semantics*, we now seek a uniform account of the corresponding +type-theoretic *syntax*. + +In addition to being readily available for any categorical structure, point-free +notation is equivariant under categorical duality. Pointful notation, on the +other hand, is intrinsically biased toward the domain of a morphism (its inputs) +and also, to a lesser extent, toward cartesian logics, in which variables can be +freely duplicated and discarded. The further one departs from the ideal +semantics of a cartesian multicategory, the more likely standard techniques are +to break down. Nevertheless, we aim to push a uniform approach as far as we are +able, because providing a familiar pointful notation seems indispensable to the +vision of CatColab as a family of interoperable programming languages. ## Technical approach @@ -168,8 +165,8 @@ a double theory. The second and third levels belong to the type theory itself. Depending on the double theory, models will be categorical structures like categories or multicategories; in fact, since they are finitely presented, such models are given by combinatorial structures like graphs or multigraphs, along -with generating equations. So it is free models, internal to this type theory, -that play the role of signatures external to traditional type theories. +with generating equations. So it is free models, internalized in this type +theory, that play the role of signatures external to traditional type theories. Models of a double theory are described by a dependent type theory combining standard and specialized rules. The judgmental structure is exactly that of @@ -188,10 +185,10 @@ What is here new are two additional kinds of judgments: In both simple and dependent type theory, contexts are lists of typed variables, with the list structure belonging to the *meta*-theory. We are forced depart from this tradition since our internal languages encompass both unary type -theories [@shulman-2016, Chapter 1] and multi-ary ("simple") type theories +theories [@shulman-2016, Chapter 1] and multiary ("simple") type theories [@shulman-2016, Chapter 2]; depending on the object type, contexts may or may not be lists. Instead, we rely on the list modality in a modal double theory to -supply the list structure needed for multi-ary internal languages. So, again, +supply the list structure needed for multiary internal languages. So, again, something usually external to the type theory is internalized. The price paid is that operations like flattening nested lists, usually swept under the meta-theoretic rug, must now be explicitly handled within in the type theory. @@ -225,28 +222,52 @@ an ordinary double category; (2) binary composition cells; and (3) nullary composition cells, i.e., extension cells for units. This case analysis will appear directly the rules of our type theory. -**Definition**. A **double theory** is an almost representable [virtual -equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost -representable VDC with units and restrictions. - -In practice, double theories are finitely presented, but formalizing such -presentations is a problem for future work. - -**Definition**. A **model** of a double theory $\dbl{T}$ is a functor of VDCs -$\dbl{T} \to \SSet$ that preserves restrictions (but not composites!); -equivalently, a model of $\dbl{T}$ is a functor of virtual equipments $\dbl{T} -\to \CCat$, i.e., a *normal* functor of VDCs that preserves restrictions (but -not composites of arity greater than 1). - -We will use the term "double theory" somewhat loosely to refer to any object -with *at least* the structure postulated above. A **simple double theory** is a -double theory with *exactly* that structure. The only other double doctrine that -we will treat is modal double theories. The list modalities will be crucial for -extracting familiar-looking internal languages for "multi-ary" categorical -structures like multicategories, monoidal categories, PROs, and generalizations -thereof. - -#### Modal theories +**Definition**. + +- A **double theory** is an almost representable [virtual + equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost + representable VDC with units and restrictions. +- A **model** of a double theory $\dbl{T}$ is a functor of VDCs $\dbl{T} \to + \SSet$ that preserves restrictions (but not composites!); equivalently, a + model of $\dbl{T}$ is a functor of virtual equipments $\dbl{T} \to \CCat$, + i.e., a *normal* functor of VDCs that preserves restrictions. + +Double theories are usually finitely presented. A type theory for models, such +as this one, should really be parameterized by a *finite presentation* of a +theory rather than by a theory itself. However, we leave the problem of +formalizing such presentations to future work. + +#### Flatness + +Recall that a (virtual) double category is **flat** if a cell is determined by +its boundary, i.e., if for each possible cell boundary, there is at most one +cell filling it [@grandis-2019, Section 3.2.1]. + +**Assumption**. The double theory parameterizing the type theory is assumed to +be flat. + +This assumption might seem both strong and strange, but it's not hard to +motivate. Recall that the (tight) arrows of a double theory are viewed as +operations acting on objects and the cells as operations acting on morphisms. In +a pointful notation, then, arrows should act on types, which correspond to +objects, and also on variables and terms, which represent elements of types. For +example, a product operation in the double theory should act on types to form +product types and on terms to form pairs. But in pointful notation, elements are +all there is! As there is nothing nothing else in the syntax for cells to act +on, the cell being applied must be unambiguously determined by its boundary. +Flatness ensures that. + +When a double theory is given by finite presentation, proving flatness can be +trivial, as it amounts to a kind of coherence or unbiasing theorem. + +#### Simple and modal theories + +In what follows we use the term "double theory" somewhat loosely to refer to any +object with *at least* the structure postulated above. A **simple double +theory** is a double theory with *exactly* that structure. The only other +double-categorical doctrine that we need is modal double theories. The list +modalities TODO extract familiar-looking internal languages for +"multiary" categorical structures like multicategories and monoidal categories. **TODO** @@ -264,7 +285,7 @@ and singleton types, though do not actually need either for present purposes. For details, see [RFC-0002](0002.md) and references therein. We state the special rules for DoubleTT in full, as there are changes from the -original spec. Let $\dbl{T}$ be a double theory. +original spec. In what follows, let $\dbl{T}$ be a double theory. #### Object types @@ -411,6 +432,8 @@ applications of $\mu_{\cal{X}}$ and $\eta_{\cal{X}}$ in terms can be eliminated. ### Inner type theory: morphisms +We now assume that $\dbl{T}$ is a *flat* double theory. + #### Judgments The inner type theory introduces two new judgments, both depending on contexts @@ -574,9 +597,9 @@ is only syntactic sugar for the former. where the first premise tacitly includes the assumption that the proarrows $P$ and $Q$ have a composite. This rule can then be interpreted as applying the - binary composition cell out of $P$ and $Q$. Note that in our application - syntax, here and elsewhere, parentheses are optional; thus the expressions - $f(t)$ and $f\,t$ are identical terms. + binary composition cell out of $P$ and $Q$. In our application syntax here and + elsewhere, parentheses are optional; thus the expressions $f(t)$ and $f\,t$ + are identical terms. - *Operation application*: for each unary cell $\alpha$ in $\dbl{T}$ as on the left, there is a rule as on the right: @@ -597,29 +620,29 @@ is only syntactic sugar for the former. \begin{prooftree} \hypo{\dbl{T} \vDash \alpha} \hypo{\Gamma \mid u: X \vdash_P t: Y} - \infer2{\Gamma \mid F(u): F(X) \vdash_Q \alpha(t): G(Y)} + \infer2{\Gamma \mid F(u): F(X) \vdash_Q G(t): G(Y)} \end{prooftree} \] ``` - - Two special cases are worth recording. First, for each arrow $F: \cal{X} \to - \cal{Y}$ in $\dbl{T}$, there is an identity cell $\id_F = \Hom_F$, whose - application to a term $t$ we abbreviate as $F(t)$. With this notation, there - is a derived rule: + + Notice that the cell's name $\alpha$ does not appear on the right-hand side, + hence the need to assume that $\dbl{T}$ is flat. Two special cases are worth + recording. First, for each arrow $F: \cal{X} \to \cal{Y}$ in $\dbl{T}$, there + is an identity cell $\id_F = \Hom_F$ and hence a derived rule: ```tikz \[ \begin{prooftree} \hypo{\dbl{T} \vDash (F: \cal{X} \to \cal{Y})} \hypo{\Gamma \mid u: X \vdash_{\Hom_{\cal{X}}} t: X'} - \infer[dashed]2{\Gamma \mid F(u): F(x) \vdash_{\Hom_{\cal{Y}}} F(t): F(X')} + \infer[dashed]2{\Gamma \mid F(u): F(X) \vdash_{\Hom_{\cal{Y}}} F(t): F(X')} \end{prooftree} \] ``` - Second, each niche in $\dbl{T}$ as on the left can be filled with restriction - cell with domain $P(F,G): \cal{X} \proto \cal{Y}$. Leaving the application of - restriction cells to terms *implicit*, there is a derived rule: + Second, each niche in $\dbl{T}$ as on the left can be filled with a + restriction cell with domain $P(F,G): \cal{X} \proto \cal{Y}$. Hence, there is + a derived rule: ```tikz \[ @@ -635,7 +658,7 @@ is only syntactic sugar for the former. \begin{prooftree} \hypo{\dbl{T} \vDash (F,P,G)} \hypo{\Gamma \mid u: X \vdash_{P(F,G)} t: Y} - \infer[dashed]2{\Gamma \mid F(u): F(X) \vdash_P t: G(Y)} + \infer[dashed]2{\Gamma \mid F(u): F(X) \vdash_P G(t): G(Y)} \end{prooftree} \] ``` @@ -658,14 +681,14 @@ is only syntactic sugar for the former. \hypo{\dbl{T} \vDash (F,P,G)} \hypo{\Gamma \vdash X: \Ob_{\cal{X}}} \infer[no rule]1{\Gamma \vdash Y: \Ob_{\cal{Y}}} - \hypo{\Gamma \mid F(u): F(X) \vdash_{P} t: G(Y)} + \hypo{\Gamma \mid F(u): F(X) \vdash_{P} G(t): G(Y)} \infer3{\Gamma \mid u: X \vdash_{P(F,G)} t: Y} \end{prooftree} \] ``` Consequently, terms $\Gamma \mid u: X \vdash_{P(F,G)} t: Y$ and - $\Gamma \mid F(u): F(X) \vdash_P t: G(Y)$ are in bijection. + $\Gamma \mid F(u): F(X) \vdash_P G(t): G(Y)$ are in bijection. - *Let binding*: @@ -688,15 +711,15 @@ is only syntactic sugar for the former. ##### Remark: Let binding as cut rule Let binding is a necessary part of some internal languages but not others, -depending on whether morphisms have "multi-ary" codomains. For example, let +depending on whether morphisms have "multiary" codomains. For example, let binding is needed in the internal languages of monoidal categories and of PRO(P)s, but not of multicategories. -The let binding rule is precisely an explicit cut rule. So, we should obviously -omit it when proving cut admissibility for a particular internal language. On -the other hand, even when it's not strictly necessary, we might choose to -include it on the pragmatic ground of being a standard feature of programming -notation. +The let binding rule is nothing but an explicit cut rule. So, we should +obviously omit it when proving cut admissibility for a particular internal +language. On the other hand, even when it's not strictly necessary, we might +choose to include it on the pragmatic ground of being a standard feature of +programming notation. ::: @@ -721,12 +744,25 @@ notation. \newcommand{\Dog}{\mathrm{Dog}} \newcommand{\String}{\mathrm{String}} -The simplest example of an internal language is that of a bare category. This -language is a unary type theory [@shulman-2016, Section 1.2]. +Among type theories, the simplest are the *unary* ones. Unary type theories are +rarely considered by type theorists except perhaps for pedagogical reasons, but +they're quite natural from the categorical point of view because they are the +internal languages of categories without any products, genuine or virtual +[@shulman-2016, Chapter 1]. For us, they are the internal languages of models of +*simple* double theories.[^simple-type-theories] + +[^simple-type-theories]: + We regret that our usage of "simple" departs from the type theorist's. All + of the internal languages covered by this RFC are [simple type + theories](https://ncatlab.org/nlab/show/simple+type+theory). + +As a first example, when the double theory is the point, i.e., is freely +generated by one object, we recover the internal language a bare category +[@shulman-2016, Section 1.2]. -For an example of a unary language that is a bit more interesting, consider the -internal language of a database schema, as axiomatized in idealized form by the -walking proarrow, i.e., by the discrete double theory: +For an example that's a bit more interesting, consider the internal language of +a database schema, as axiomatized in idealized form by the walking proarrow, +i.e., the double theory freely generated by: $$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ @@ -759,7 +795,7 @@ owner. ### Multicategories -Perhaps the simplest multi-ary language is the internal language of a (planar) +Perhaps the simplest multiary language is the internal language of a (planar) multicategory. In this highly substructural language, variables must be used exactly once and in the same order that they appear in the context. The type theory here should be compared with the *cut-free type theory for @@ -877,25 +913,26 @@ but we caution again that in the present type theory, this expression can only be read as an abbreviation for the previous one. ::: {.callout-warning} -#### Uniqueness of derivations +#### Non-uniqueness of derivations There was another, slightly longer way we could have derived the term $m[x,y]$, parallel to how we derived the final term $m[m[x,y],z]$. Namely, after introducing the variables $x$ and $y$, apply the restriction rule to obtain -terms $\Gamma \mid [v]: [X] \vdash_P v: X$ for $v = x,y$, then form the list -$\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List P} [x,y]: [X,Y]$, and finally -proceed as before to construct the term $\Gamma \mid [x,y]: [X,Y] \vdash_P +terms $\Gamma \mid [v]: [X] \vdash_P v: X$ for variables $v = x,y$, then form +the list $\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List P} [x,y]: [X,X]$, and +finally proceed as before to build the term $\Gamma \mid [x,y]: [X,X] \vdash_P m[x,y] \vdash X$. -By the theory's compatibility axiom for the left action of $P$, both derivations -denote the same multimorphism in any model of the theory. By making restriction -act implicitly on terms, the same thing is achieved in the type theory: the two -derivations yield identical terms. So, turning this around, terms in this type -theory do not have unique derivations. +By the theory's compatibility axiom for the left action of $P$ (contributing to +the theory's flatness), both derivations denote the same multimorphism in any +model of the theory. Thus, the type theory is sound in allowing the two +derivations to produce identical terms. -It is often a desideratum of a type theory that (derivable) terms have a unique -derivation. At the expense of some ad hocery, we could achieve that in this case -by prohibiting use of $P$'s left and right actions. +Turning this around, terms in this type theory evidently do not have unique +derivations. That's not necessarily a problem, though it can be a desideratum of +a type theory that any (derivable) term has a unique derivation. At the expense +of ad hocery, we could achieve that in this logic by prohibiting use of $P$'s +left and right actions. ::: diff --git a/rfc/_references.bib b/rfc/_references.bib index 66bed68d9..be39d2d27 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -48,6 +48,14 @@ @phdthesis{dreyer-2005 url = {https://csd.cmu.edu/sites/default/files/phd-thesis/CMU-CS-05-131.pdf} } +@book{grandis-2019, + title = {Higher dimensional categories: From double to multiple categories}, + author = {Grandis, Marco}, + year = {2019}, + publisher = {World Scientific}, + doi = {10.1142/11406} +} + @book{jacobs-1998, author = {Jacobs, Bart}, title = {Categorical logic and type theory}, From cbcabf5b5f3aac96956e04d290aa870506369717 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Thu, 23 Apr 2026 14:18:07 +0100 Subject: [PATCH 16/81] DOC: Double list monads on SSet and formation rule for terms. --- rfc/0004.md | 178 +++++++++++++++++++++++++++++----------- rfc/_macros.qmd | 1 + rfc/_templates/tikz.tex | 1 + 3 files changed, 130 insertions(+), 50 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index d18e5040f..1102ae9d4 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -38,8 +38,8 @@ or $x: X \vdash g(f(x)): Z$ (to use a type-theoretic syntax). In a bare category, the most general data that can be composed is a path of morphisms, and there is little practical difference between point-free and -pointful notations. However, as more categorical structure is introduced, the -difference can become dramatic. As another example---still tiny by practical +pointful notations. However, in richer categorical structures, the difference +can be dramatic. As another illustration---still tiny by practical standards---suppose that $f: X \times Y \to W$, $g: X \to W'$, and $h: W \times W' \to Z$ are morphisms in a cartesian category. The composite morphism with signature $X \times Y \to Z$ can be written in point-free notation as @@ -116,10 +116,10 @@ reasonable formal language, such as GATs or double theories. In contrast, finding the [internal language](https://ncatlab.org/nlab/show/internal+logic) of a new categorical structure---a type theory whose types and terms correspond precisely to the structure's objects and morphisms---is often a research problem -in its own right. So, the research problem behind this RFC is that while -CatColab has a uniform meta-logic for (parts of) categorical logic at the level -of *semantics*, we now seek a uniform account of the corresponding -type-theoretic *syntax*. +in its own right. The research problem behind this RFC is that while CatColab +has a uniform meta-logic for (parts of) categorical logic at the level of +*semantics*, we now seek a uniform account of the corresponding type-theoretic +*syntax*. In addition to being readily available for any categorical structure, point-free notation is equivariant under categorical duality. Pointful notation, on the @@ -128,7 +128,7 @@ and also, to a lesser extent, toward cartesian logics, in which variables can be freely duplicated and discarded. The further one departs from the ideal semantics of a cartesian multicategory, the more likely standard techniques are to break down. Nevertheless, we aim to push a uniform approach as far as we are -able, because providing a familiar pointful notation seems indispensable to the +able, as providing a familiar pointful notation seems indispensable to the vision of CatColab as a family of interoperable programming languages. ## Technical approach @@ -202,6 +202,9 @@ constructing lists of morphism terms, but not lists of morphism generators. So, a major difference with the original DoubleTT is that point-free operations on morphisms like $f \cdot g$ and $[f, g]$ are not part of the type theory. +We turn now to the mathematical specification, but the reader may find it +helpful peruse the [examples](#examples) before or along with the spec. + ## Mathematical specification ### Double theories @@ -211,16 +214,21 @@ interpolating between an arbitrary virtual double category (VDC) and a *representable* VDC (equivalent to a genuine double category). **Definition**. A virtual double category is **almost representable** if for -every multicell, every subpath of its domain has a composite. +every multicell, every subpath of proarrows in its domain has a composite. Equivalently, by the universal property of composites, a VDC is almost representable if every multicell factors through a composition cell and, moreover, every composition cell can be factored arbitrarily as a tight -composite of binary and nullary composition cells. In such a VDC, the analysis -of an arbitrary multicell reduces to three simpler cases: (1) unary cells, as in -an ordinary double category; (2) binary composition cells; and (3) nullary -composition cells, i.e., extension cells for units. This case analysis will -appear directly the rules of our type theory. +composite of binary and nullary composition cells. Thus, while such a VDC is not +necessarily representable---there may be composable proarrows that do not have a +composite---this failure is anodyne in that it cannot affect any multicell +actually appearing in the VDC. + +The analysis of an arbitrary multicell in an almost representable VDC reduces to +three simpler cases: (1) unary cells, as in a genuine double category; (2) +binary composition cells; and (3) nullary composition cells, i.e., extension +cells for units. This case decomposition will appear directly in the rules of +our type theory. **Definition**. @@ -232,18 +240,18 @@ appear directly the rules of our type theory. model of $\dbl{T}$ is a functor of virtual equipments $\dbl{T} \to \CCat$, i.e., a *normal* functor of VDCs that preserves restrictions. -Double theories are usually finitely presented. A type theory for models, such -as this one, should really be parameterized by a *finite presentation* of a -theory rather than by a theory itself. However, we leave the problem of +Double theories are usually finitely presented. A type theory for models, like +the present one, should really be parameterized by a *finite presentation* of a +theory rather than by the theory itself. However, we leave the problem of formalizing such presentations to future work. #### Flatness -Recall that a (virtual) double category is **flat** if a cell is determined by -its boundary, i.e., if for each possible cell boundary, there is at most one -cell filling it [@grandis-2019, Section 3.2.1]. +Recall that a (virtual) double category is **flat**, also called **locally +thin**, if a cell is determined by its boundary, i.e., if for each possible cell +boundary, there is at most one cell filling it [@grandis-2019, Section 3.2.1]. -**Assumption**. The double theory parameterizing the type theory is assumed to +**Assumption**. The double theory parameterizing this type theory is assumed to be flat. This assumption might seem both strong and strange, but it's not hard to @@ -252,28 +260,68 @@ operations acting on objects and the cells as operations acting on morphisms. In a pointful notation, then, arrows should act on types, which correspond to objects, and also on variables and terms, which represent elements of types. For example, a product operation in the double theory should act on types to form -product types and on terms to form pairs. But in pointful notation, elements are -all there is! As there is nothing nothing else in the syntax for cells to act -on, the cell being applied must be unambiguously determined by its boundary. +product types and on terms to form pairs. But in pointful notation, types and +elements are all there is! As there is nothing else in the syntax for cells to +act on, the cell being applied must be unambiguously determined by its boundary. Flatness ensures that. When a double theory is given by finite presentation, proving flatness can be -trivial, as it amounts to a kind of coherence or unbiasing theorem. +nontrivial, amounting to a coherence or unbiasing theorem. #### Simple and modal theories In what follows we use the term "double theory" somewhat loosely to refer to any object with *at least* the structure postulated above. A **simple double -theory** is a double theory with *exactly* that structure. The only other -double-categorical doctrine that we need is modal double theories. The list -modalities TODO extract familiar-looking internal languages for -"multiary" categorical structures like multicategories and monoidal categories. +theory** is a double theory with *exactly* that structure. -**TODO** +The only other double-categorical doctrine that we need is modal double +theories. The family of list modalities in particular is essential to extract +familiar-looking internal languages for "multiary" categorical structures like +multicategories and monoidal categories. + +**Definition**. **TODO** #### List modalities -**TODO** +The *list monad*, or *free monoid monad*, is among the most famous examples of a +monad. Upgrading the list monad on $\Set$ to a double monad on $\SSet$ creates +new degrees of freedom in how a list of arrows relates to its lists of sources +and targets. + +Let $\cat{F}$ be a wide subcategory of the skeleton of $\FinSet$ that is a +*faithful cartesian club* [@shulman-2016, Definition 2.6.3]. The examples of +interest are the subcategories consisting of: + +- identity functions +- bijections +- injections +- surjections +- arbitrary functions + +There is a double monad $\List_{\cat{F}}: \SSet \to \SSet$ that is the ordinary +list monad in degree zero, + +$$ (\List_{\cat{F}})_0 \coloneqq \List: \Set \to \Set, $$ + +and that sends a family of elements $P: A \proto B$ to the family +$\List_{\cat{F}} P: \List A \proto \List B$ whose elements + +$$ (\sigma, \vec{p}): (a_1, \dots, a_n) \proto (b_1, \dots, b_m) $$ + +consist of a function $\sigma: [m] \to [n]$ together with a list $\vec{p} = +(p_1, \dots, p_m)$ of form + +$$ p_i: a_{\sigma(i)} \proto b_i, \qquad i = 1,\dots,m. $$ + +This double monad is a monad in the 2-category of VDCs, functors of VDCs, and +natural transformations, or, equivalently since $\SSet$ is representable, in the +2-category of double categories, lax double functors, and (tight) natural +transformations. For details and generalizations, see the [math +docs](https://next.catcolab.org/math/mdt-0007.xml). + +By convention, rules below that reference the double monad $\List$ without +further adornment are taken to mean $\List_{\cat{F}}$ for any choice of +$\cat{F}$. ### Outer type theory: models @@ -421,12 +469,13 @@ for $\Gamma \vdash [X_1, [X_2, [X_3, [\,]]]]: \Ob_{\List\cal{X}}$. ::: {.callout-tip} #### Remark: List operations compute -We follow tradition in defining the list monad, including the monad unit -(singleton list) and monad multiplication (flattening), by induction. For -practical purposes, this presentation is not very important; we'll likely -implement lists as arrays rather than linked lists. What *is* important is that -the list operations compute: by iteratively applying the computation rules, any -applications of $\mu_{\cal{X}}$ and $\eta_{\cal{X}}$ in terms can be eliminated. +In the spirit of type theory, the rules above define the list monad, including +the monad's unit (singleton list) and multiplication (flattening), by induction, +but this presentation is not very important. In fact, the rules for terms in the +inner type theory will describe lists in an unbiased style. What *is* important +is that the list operations compute: by iteratively applying the computation +rules, any applications of $\mu_{\cal{X}}$ and $\eta_{\cal{X}}$ in terms can be +eliminated. ::: @@ -474,9 +523,8 @@ inner terms. Contexts are typically defined inductively to be lists of typed variables (i.e., variable-type pairs), but the inner contexts of this type theory are constructed -differently. First of all, contexts need not be lists at all; contexts are unary -by default. Only over an object type of form $\List\cal{X}$ can we build -contexts like +differently. First of all, contexts need not be lists at all. Only over an +object type of form $\List\cal{X}$ can we build contexts like $$ \Gamma \mid [x_1, x_2, x_3] : [X_1, X_2, X_3], @@ -561,8 +609,8 @@ is only syntactic sugar for the former. \] ``` -- *Computation rules, monad multiplication*: analogous to computation rules for - list modality above. +- *Computation rules, monad multiplication*: like the above rule, mirrors the + the computation rules for the list modality in the outer type theory. #### Terms @@ -715,7 +763,7 @@ depending on whether morphisms have "multiary" codomains. For example, let binding is needed in the internal languages of monoidal categories and of PRO(P)s, but not of multicategories. -The let binding rule is nothing but an explicit cut rule. So, we should +The let binding rule is none other than an explicit cut rule. So, we should obviously omit it when proving cut admissibility for a particular internal language. On the other hand, even when it's not strictly necessary, we might choose to include it on the pragmatic ground of being a standard feature of @@ -725,11 +773,31 @@ programming notation. ##### Lists -**TODO** +- *Formation rule*: -### Meta-theoretic properties + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\cat{F} \vDash (\sigma: [m] \to [n])} + \hypo{\Gamma \mid u_j: X_j} + \infer[no rule]1{(j = 1,\dots,n)} + \hypo{\Gamma \mid u_{\sigma(i)}: X_{\sigma(i)} \vdash_P t_i: Y_i} + \infer[no rule]1{(i = 1,\dots,m)} + \infer4{ + \Gamma \mid [u_1, \dots, u_n]: [X_1, \dots X_m] + \vdash_{\List_{\cat{F}} P} + [t_1, \dots, t_m]: [Y_1, \dots, Y_m] + } + \end{prooftree} + \] + ``` + + where it is implicitly assumed that the context in the conclusion is + well-formed, i.e., the variables appearing at any level among the contexts + $\Gamma \mid u_j : X_j$ are disjoint. -**TODO**: Cut admissibility +- *Computation rules*: **TODO** ## Examples @@ -745,11 +813,11 @@ programming notation. \newcommand{\String}{\mathrm{String}} Among type theories, the simplest are the *unary* ones. Unary type theories are -rarely considered by type theorists except perhaps for pedagogical reasons, but -they're quite natural from the categorical point of view because they are the -internal languages of categories without any products, genuine or virtual -[@shulman-2016, Chapter 1]. For us, they are the internal languages of models of -*simple* double theories.[^simple-type-theories] +rarely considered by type theorists outside of pedagogy, but they're quite +natural from the categorical point of view because they are the internal +languages of categories without any products, genuine or virtual [@shulman-2016, +Chapter 1]. For us, they are the internal languages of models of *simple* double +theories.[^simple-type-theories] [^simple-type-theories]: We regret that our usage of "simple" departs from the type theorist's. All @@ -936,6 +1004,10 @@ left and right actions. ::: +### Symmetric multicategories + +**TODO** + ## Rationale and alternatives ### Bespoke internal languages @@ -982,3 +1054,9 @@ mathematical challenges. This document is, however, solely about textual syntax. ### Two-level type theories **TODO** + +## Future possibilities + +### Meta-theoretic properties + +**TODO**: Cut admissibility diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index 61fad851a..ac5cd1abe 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -11,6 +11,7 @@ \newcommand{\cat}[1]{\mathsf{#1}} \newcommand{\Set}{\cat{Set}} +\newcommand{\FinSet}{\cat{FinSet}} \newcommand{\dbl}[1]{\mathbb{#1}} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index c9d027b68..3e7bb9d44 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -19,6 +19,7 @@ % Categories \newcommand{\cat}[1]{\mathsf{#1}} +\newcommand{\Set}{\cat{Set}} % Double categories \newcommand{\dbl}[1]{\mathbb{#1}} From 465713d8ccedc55e46dfe2b24ac5df92712c0c55 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 26 Apr 2026 13:23:43 -0700 Subject: [PATCH 17/81] DOC: Morphism equality types using internal language. --- rfc/0004.md | 93 +++++++++++++++++++++++++++++------------ rfc/_templates/tikz.tex | 3 ++ 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 1102ae9d4..bcadd6fe0 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -171,30 +171,30 @@ theory, that play the role of signatures external to traditional type theories. Models of a double theory are described by a dependent type theory combining standard and specialized rules. The judgmental structure is exactly that of standard dependent type theory. The type theory has record types (equivalent to -but more ergonomic than dependent sums) and singleton types (to enable model -composition); it does *not* have dependent products or universes. To this base -is added specialized rules for the object and morphisms types of the double -theory. All this follows the original DoubleTT, with an important exception to -be described shortly. +dependent sums but more ergonomic) and singleton types (to enable model +composition); it does *not* have dependent products, equality types, or +universes. To this base is added specialized rules for the object and morphisms +types of the double theory. All this follows the original DoubleTT, with an +important exception to be described shortly. -What is here new are two additional kinds of judgments: +New here are two additional kinds of judgments: -1. **contexts** over an object type, and -2. **terms in context**, or just **terms**, over a morphism type. +1. **contexts** over an object type in the theory, and +2. **terms in context**, or just **terms**, over a morphism type in the theory. In both simple and dependent type theory, contexts are lists of typed variables, -with the list structure belonging to the *meta*-theory. We are forced depart -from this tradition since our internal languages encompass both unary type -theories [@shulman-2016, Chapter 1] and multiary ("simple") type theories -[@shulman-2016, Chapter 2]; depending on the object type, contexts may or may -not be lists. Instead, we rely on the list modality in a modal double theory to -supply the list structure needed for multiary internal languages. So, again, -something usually external to the type theory is internalized. The price paid is -that operations like flattening nested lists, usually swept under the -meta-theoretic rug, must now be explicitly handled within in the type theory. - -Another key design goal is that the type theory for morphisms be **cut-free**. -That is, composition of morphisms should be not a primitive operation but an +with the list structure belonging to the *meta*-theory. We depart from this +tradition since our internal languages encompass both unary type theories +[@shulman-2016, Chapter 1] and multiary ("simple") type theories [@shulman-2016, +Chapter 2]; a context may or may not be a list, depending on which object type +it is over. Instead, the list modality of a modal double theory supplies the +list structure needed for multiary internal languages. So, again, a structure +usually external to the type theory is internalized. The price paid is that +operations like flattening nested lists, usually swept under the meta-theoretic +rug, must now be explicitly handled within in the type theory. + +Another design goal is that the type theory for morphisms be **cut-free**. That +is, composition of morphisms should be not a primitive operation but an **admissible rule**: a meta-theoretic operation derivable from the other rules of the system. Eliminating primitive rules in favor of admissible ones is how type theory can produce canonical forms. Similarly, the type theory will allow @@ -329,7 +329,7 @@ The outer type theory, a dependent type theory, has the standard judgments for contexts, types in context, and terms in context, as well as for equalities between types and between terms. We are agnostic as to whether substitutions are implicit or explicit. Moreover, the outer type theory has dependent record types -and singleton types, though do not actually need either for present purposes. +and singleton types, though neither are actually needed for present purposes. For details, see [RFC-0002](0002.md) and references therein. We state the special rules for DoubleTT in full, as there are changes from the @@ -394,6 +394,47 @@ original spec. In what follows, let $\dbl{T}$ be a double theory. \] ``` +::: {.callout-warning} +#### Warning: Morphisms vs morphism generators + +We emphasize that, in contrast to the original version of DoubleTT, a term of +type $P(X,Y)$ in the outer type theory represents a morphism *generator* from +$X$ to $Y$. That's why morphism operations (unary cells in $\dbl{T}$) cannot be +applied to terms of type $P(X,Y)$. An arbitrary morphism from $X$ to $Y$ is +represented by a term $u: X \vdash_P t: Y$ in the inner type theory. + +::: + +#### Morphism equality types + +- *Morphism equality type former*: For each proarrow $P$ in the double theory, + there is a type of equalities between *inner* terms over $P$: + + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \hypo{\Gamma \mid u: X \vdash_P s: Y} + \infer3{\Gamma \vdash \mathsf{eq}(t,s)\ \type} + \end{prooftree} + \] + ``` + + Intuitively, a term of type $\mathsf{eq}(t,s)$ witnesses an equality between + the morphisms denoted by $t$ and $s$. + +::: {.callout-warning} +#### Warning: Outer vs inner equality types + +The type above is not an equality type in the usual sense of dependent type +theory [@angiuli-gratzer-2025]. Indeed, the outer type theory does *not* have +equality types, i.e., there is no type of equalities between a pair of outer +terms of the same type. Rather, there is an type of equalities between a pair of +inner terms (morphisms) with the same source and target. + +::: + #### List modality ::: {.callout} @@ -496,7 +537,7 @@ from the outer type theory. $$ asserts that "$u$ is an (inner) context of type $X$, in (outer) context - $\Gamma$". + $\Gamma$ and over $\cal{X}$". 2. *Terms*: Presupposing that $P: \cal{X} \proto \cal{Y}$ is a morphism type of $\dbl{T}$, that $\Gamma \vdash X: \Ob_{\cal{X}}$ and $\Gamma \vdash Y: @@ -507,8 +548,8 @@ from the outer type theory. \Gamma \mid u: X \vdash_P t: Y $$ - asserts that "$t$ is an (inner) term of type $P$, in context $\Gamma \mid u : - X$". + asserts that "$t$ is an (inner) term of type $Y$, in context $\Gamma \mid u: + X$ and over $P$". When $\cal{X} = \cal{Y}$ and $P = \Hom_{\cal{X}}$, the subscript $P$ on the turnstile can be dropped. @@ -781,9 +822,9 @@ programming notation. \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} \hypo{\cat{F} \vDash (\sigma: [m] \to [n])} \hypo{\Gamma \mid u_j: X_j} - \infer[no rule]1{(j = 1,\dots,n)} + \infer[no rule, small]1{(j = 1,\dots,n)} \hypo{\Gamma \mid u_{\sigma(i)}: X_{\sigma(i)} \vdash_P t_i: Y_i} - \infer[no rule]1{(i = 1,\dots,m)} + \infer[no rule, small]1{(i = 1,\dots,m)} \infer4{ \Gamma \mid [u_1, \dots, u_n]: [X_1, \dots X_m] \vdash_{\List_{\cat{F}} P} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index 3e7bb9d44..0d1aababe 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -1,7 +1,10 @@ \documentclass[dvisvgm,preview]{standalone} \usepackage{amsmath,amssymb} \usepackage{quiver} + +% Proof trees \usepackage{ebproof} +\ebproofnewstyle{small}{template=\footnotesize$\inserttext$} % WARNING: the macros here are (currently) manually copied from _macros.qmd From af476c6e1f6c871425aefe04a285372cbc9da3ce Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 26 Apr 2026 17:10:18 -0700 Subject: [PATCH 18/81] DOC: Ex: commutativity in internal language of symmetric multicats. --- rfc/0004.md | 83 ++++++++++++++++++++++++++++------------- rfc/_macros.qmd | 1 + rfc/_templates/tikz.tex | 1 + 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index bcadd6fe0..187676881 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -416,13 +416,13 @@ represented by a term $u: X \vdash_P t: Y$ in the inner type theory. \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} \hypo{\Gamma \mid u: X \vdash_P t: Y} \hypo{\Gamma \mid u: X \vdash_P s: Y} - \infer3{\Gamma \vdash \mathsf{eq}(t,s)\ \type} + \infer3{\Gamma \vdash \eqtype(t,s)\ \type} \end{prooftree} \] ``` - Intuitively, a term of type $\mathsf{eq}(t,s)$ witnesses an equality between - the morphisms denoted by $t$ and $s$. + Intuitively, a term of type $\eqtype(t,s)$ witnesses an equality between the + morphisms denoted by $t$ and $s$. ::: {.callout-warning} #### Warning: Outer vs inner equality types @@ -671,7 +671,7 @@ is only syntactic sugar for the former. This rule can be interpreted as applying the nullary composition cell out of $\cal{X}$. -- *Composition rule*: +- *Post-composition rule*: ```tikz \[ @@ -910,11 +910,12 @@ exactly once and in the same order that they appear in the context. The type theory here should be compared with the *cut-free type theory for multicategories* in [@shulman-2016, Section 2.4.1]. -Let us briefly review the modal double theory of a [generalized -multicategory](https://next.catcolab.org/math/dct-000F.xml), to be used with the -list modality $T = \List$. The generators of this double theory are an object -$\cal{X}$ and a proarrow $P: \List(\cal{X}) \proto \cal{X}$. There are two key -axioms, the *composition axiom* and the *normalization axiom*: +Let's briefly review the modal double theory of [generalized +multicategories](https://next.catcolab.org/math/dct-000F.xml), to be +instantiated with the list modality $T = \List = \List_{\id}$. The generators of +this double theory are an object $\cal{X}$ and a proarrow $P: \List(\cal{X}) +\proto \cal{X}$. There are two key axioms, the *composition axiom* and the +*normalization axiom*: $$ \List P \odot P = P(\mu_{\cal{X}}, 1_{\cal{X}}) @@ -962,15 +963,23 @@ says that the unary multimorphisms of $P$ are in natural bijection with the morphisms of $\cal{X}$. For the remaining axioms of the double theory, see the page linked above. -As outer context, take the signature for the theory of monoids: +To illustrate the type theory, we construct the associativity axiom of a monoid +as an equality type $$ -\Gamma = [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)]. +\Gamma \vdash \eqtype(m[m[x,y],z], m[x,m[y,z]])\ \type, $$ -Let's build up a term in inner context $\Gamma \mid [x,y,z]: [X,X,X]$ that could -serve as the the left-hand side of the associativity axiom for monoids. To -start, use the identity rule to form the term $\Gamma \mid x: X +where + +$$ +\Gamma \coloneqq [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)] +$$ + +is the signature of the theory of monoids. We build the left-hand side as a term +in context $\Gamma \mid [x,y,z]: [X,X,X]$. The right-hand side is similar. + +To start, use the identity rule to form the term $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} x: X$ and similarly for a variable $y$. Combining these, form the list @@ -978,8 +987,8 @@ $$ \Gamma \mid [x,y]: [X,X] \vdash_{\List\Hom_{\cal{X}}} [x,y]: [X,X]. $$ -Apply the composition rule at the composable proarrrows $\List\Hom_{\cal{X}} = -\Hom_{\List\cal{X}}$ and $P$ to build the term +Apply the post-composition rule at the composable proarrrows +$\List\Hom_{\cal{X}} = \Hom_{\List\cal{X}}$ and $P$ to build the term $$ \Gamma \mid [x,y]: [X,X] \vdash_P m[x,y]: X. @@ -999,7 +1008,7 @@ $$ \Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{\List P} [m[x,y],z]: [X,X], $$ -apply the composition rule at the composable proarrows $P$ and $P$ +apply the post-composition rule at the composable proarrows $P$ and $P$ $$ \Gamma \mid [[x,y],[z]]: [[X,X],[X]] \vdash_{P(\mu_{\cal{X}}, 1)} m[m[x,y],z]: X, @@ -1018,8 +1027,8 @@ $$ \Gamma \mid [x: X, y: X, z: X] \vdash m[m[x,y],z]: X $$ -but we caution again that in the present type theory, this expression can only -be read as an abbreviation for the previous one. +but we caution again that in the present type theory, this expression must be +understood only as an abbreviation for the previous one. ::: {.callout-warning} #### Non-uniqueness of derivations @@ -1037,17 +1046,41 @@ the theory's flatness), both derivations denote the same multimorphism in any model of the theory. Thus, the type theory is sound in allowing the two derivations to produce identical terms. -Turning this around, terms in this type theory evidently do not have unique -derivations. That's not necessarily a problem, though it can be a desideratum of -a type theory that any (derivable) term has a unique derivation. At the expense -of ad hocery, we could achieve that in this logic by prohibiting use of $P$'s -left and right actions. +Turning this around, we see that terms in this type theory do not have unique +derivations. While that's not necessarily a problem, it can be a desideratum of +a type theory that any (derivable) term has a unique derivation. For this +particular double theory, we could, at the expense of some ad hocery, achieve +uniqueness of derivations by prohibiting use of $P$'s left and right actions. In +general, though, we should not expect to have unique derivations. ::: ### Symmetric multicategories -**TODO** +\newcommand{\bij}{\mathrm{bij}} + +Symmetric multicategories also arise from the modal double theory of +[generalized multicategories](https://next.catcolab.org/math/dct-000F.xml), now +instantiated with the list modality $T = \List_{\bij}$. To illustrate, we derive +the commutativity axiom of a commutative monoid as an equality type + +$$ +\Gamma \vdash \eqtype(m[x,y], m[y,x])\ \type, +$$ + +where $\Gamma$ is the signature of the theory of monoids as before. We've +already derived the left-hand side, so let's derive the right-hand side as a +term in context $\Gamma \mid [x,y]: [X,X]$. + +Start with the variables $\Gamma \mid x: X \vdash x: X$ and $\Gamma \mid y: X +\vdash y: X$ and apply the list formation rule using the swap permutation +$\sigma: [2] \xto{\cong} [2]$ to obtain the term $\Gamma \mid [x,y]: [X,X] +\vdash_{\List_{\bij} \cal{X}} [y,x]: [X,X]$. Then post-compose with the +generator $m$ to obtain + +$$ +\Gamma \mid [x,y]: [X,X] \vdash_P m[y,x]: X. +$$ ## Rationale and alternatives diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index ac5cd1abe..16bd7cbf0 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -37,4 +37,5 @@ \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} +\newcommand{\eqtype}{\mathsf{eq}} \newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index 0d1aababe..d6146f6eb 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -47,6 +47,7 @@ % Type theory \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} +\newcommand{\eqtype}{\mathsf{eq}} \newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} @OPTIONAL_PREAMBLE From f03029b06ed130ee549363d326266732bcd4e8a3 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 27 Apr 2026 13:10:13 -0700 Subject: [PATCH 19/81] DOC: Pre-composition rule for internal languages of models. And example of distributivity law using internal language of cartesian multicategories. --- rfc/0004.md | 189 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 48 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 187676881..afcc4599d 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -17,17 +17,18 @@ This RFC is tracked in ## Summary -Any pragmatic tool for "programming" inside categorical structures will include -a pointful notation to define morphisms (programs), based on variables and -function application as in a conventional programming language. Such a notation -is the *internal language* for the categorical structure. This RFC seeks a -uniform method to construct the internal languages of different categorical -structures, suitable for implementation in CatColab. The technical approach -involves a two-level type theory parameterized by a modal double theory. +Any pragmatic tool for programming inside categorical structures will allow +morphisms to be denoted using variables and function applications, as in +conventional programming languages. In the jargon, such a notation involving +variables is called the *internal language* of the categorical structure. This +RFC seeks a uniform method to construct the internal languages of different +categorical structures, suitable for implementation in CatColab. The technical +approach involves a two-level type theory parameterized by a modal double +theory. ## Motivation -Several styles of notation are commonly used to denote a morphism in a +Several styles of notation are commonly used to denote morphisms in a categorical structure. Among text-based notations, the most important distinction is between **point-free** notations, which do not involve "elements" or "variables," and **pointful** notations, which do. To illustrate, suppose @@ -52,11 +53,11 @@ which the reader must exert some effort to compile in their head, in contrast to the pointful notation $$ -x : X,\ y : Y \vdash h(f(x, y), g(x)) : Z, +(x, y): X \times Y \vdash h(f(x, y), g(x)) : Z, $$ which is just the familiar syntax for composing functions. Besides its -familiarity, the key ergonomic advantage of pointful notation is that operations +familiarity, a key ergonomic advantage of pointful notation is that operations like copying and permuting, which must be *explicitly* stated in point-free notation, become *implicit*. @@ -74,7 +75,7 @@ using variables. That's what the [`Path`](https://next.catcolab.org/dev/rust/catlog/one/path/enum.path) data structure in `catlog` does. -As more categorical structure accumulates, finding canonical forms becomes more +As categorical structure accumulates, finding canonical forms becomes more difficult. For example, let $X \xto{f} Y \xto{g} Z$ and $X' \xto{f'} Y' \xto{g'} Z'$ be morphisms in a monoidal category. In point-free syntax, two different expressions, @@ -107,7 +108,17 @@ categorical logic [@shulman-2016]. In short, among textual notations, pointful notation has both practical advantages, being familiar and ergonomic, as well as theoretical ones, inasmuch -as it sometimes leads to canonical forms. +as it can help find canonical forms. + +In CatColab, we plan to use pointful notation in several places. First, to +specify equations between morphisms when finitely presenting a model. From the +perspective that a model of a double theory is a *theory* in the sense of +(one-dimensional) categorical logic, such equations are *axioms* of the theory. +This feature is covered by the type theory presented in this RFC. In addition, +we intend to use pointful notation in specifying the morphism part of a morphism +between models, i.e., an *interpretation* of one theory in another, and also, +moving up a dimension, in specifying the components of a transformation between +model morphisms. The details of these features are beyond the scope of this RFC. Before moving on, it's worth acknowledging that point-free notation is not without its own merits. Conveniently for computer implementation, point-free @@ -779,7 +790,31 @@ is only syntactic sugar for the former. Consequently, terms $\Gamma \mid u: X \vdash_{P(F,G)} t: Y$ and $\Gamma \mid F(u): F(X) \vdash_P G(t): G(Y)$ are in bijection. -- *Let binding*: +##### Further composition rules + +**TODO**: Preamble + +- *Pre-composition rule*: + + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (\cal{X} \xproto{P} \cal{Y} \xproto{Q} \cal{Z})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \hypo{\Gamma \mid v: Y \vdash_Q s: Z} + \hypo{\gamma: \mathsf{var}(v) \to \mathsf{var}(u)} + \infer[no rule]1{t = v[\gamma]} + \infer4{\Gamma \mid u: X \vdash_{P \odot Q} s[\gamma]: Z} + \end{prooftree} + \] + ``` + + where the first premise includes the assumption that the proarrows $P$ and $Q$ + have a composite and in the last premise $\gamma$ is a function from the set + of variables contained in $v$ to the set of variables in $u$. **TODO**: + Elaborate + +- *Let binding*, or general *composition rule*: ```tikz \[ @@ -792,7 +827,7 @@ is only syntactic sugar for the former. \] ``` - where the first premise tacitly includes the assumption that the proarrows $P$ + where again the first premise includes the assumption that the proarrows $P$ and $Q$ have a composite. This rule can be interpreted as destructuring the value $t$ and binding it to the variables in the context $v$. @@ -826,7 +861,7 @@ programming notation. \hypo{\Gamma \mid u_{\sigma(i)}: X_{\sigma(i)} \vdash_P t_i: Y_i} \infer[no rule, small]1{(i = 1,\dots,m)} \infer4{ - \Gamma \mid [u_1, \dots, u_n]: [X_1, \dots X_m] + \Gamma \mid [u_1, \dots, u_n]: [X_1, \dots X_n] \vdash_{\List_{\cat{F}} P} [t_1, \dots, t_m]: [Y_1, \dots, Y_m] } @@ -853,35 +888,35 @@ programming notation. \newcommand{\Dog}{\mathrm{Dog}} \newcommand{\String}{\mathrm{String}} -Among type theories, the simplest are the *unary* ones. Unary type theories are -rarely considered by type theorists outside of pedagogy, but they're quite -natural from the categorical point of view because they are the internal -languages of categories without any products, genuine or virtual [@shulman-2016, -Chapter 1]. For us, they are the internal languages of models of *simple* double -theories.[^simple-type-theories] +Simplest among type theories are the *unary* ones---so simple, in fact, that +they are rarely considered by type theorists outside of pedagogy. Yet from the +categorical point of view, unary type theories are quite natural, being the +internal languages of categories that do not have products, genuine or virtual +[@shulman-2016, Chapter 1]. For us, they are the internal languages of models of +*simple* double theories.[^simple-type-theories] [^simple-type-theories]: We regret that our usage of "simple" departs from the type theorist's. All of the internal languages covered by this RFC are [simple type theories](https://ncatlab.org/nlab/show/simple+type+theory). -As a first example, when the double theory is the point, i.e., is freely -generated by one object, we recover the internal language a bare category +As a first example, taking the double theory to be the point, i.e., to be freely +generated by one object, recovers the internal language a bare category [@shulman-2016, Section 1.2]. For an example that's a bit more interesting, consider the internal language of a database schema, as axiomatized in idealized form by the walking proarrow, -i.e., the double theory freely generated by: +i.e., by the freely generated double theory $$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ -With the abbreviation $\Mapping \coloneqq \Hom_{\Entity}$ for mappings (foreign +Writing $\Mapping \coloneqq \Hom_{\Entity}$ for the type of mappings (foreign keys) between entities, we can form the outer context representing a simple schema involving people and dogs: $$ \begin{aligned} -\Gamma = [ +\Gamma \coloneqq [ &\String: \Ob_{\AttrType}, \\ &\Person: \Ob_{\Entity}, \\ &\text{first-name}: \Attr(\Person, \String), \\ @@ -898,9 +933,9 @@ In the inner context $\Gamma \mid p: \Person$, we can derive the terms: 2. $\Gamma \mid d: \Dog \vdash_{\Mapping} \text{owner}(d): \Person$ 3. $\Gamma \mid d: \Dog \vdash_{\Attr} \text{first-name}(\text{owner}(d)): \String$ -where step 1 uses the identity rule and steps 2 and 3 use the composition rule. -The final term denotes the morphism that maps a dog to the first name of its -owner. +where step 1 uses the identity rule and steps 2 and 3 use the post-composition +rule. The final term denotes the morphism that maps a dog to the first name of +its owner. ### Multicategories @@ -910,12 +945,12 @@ exactly once and in the same order that they appear in the context. The type theory here should be compared with the *cut-free type theory for multicategories* in [@shulman-2016, Section 2.4.1]. -Let's briefly review the modal double theory of [generalized +Let's begin by reviewing the modal double theory of [generalized multicategories](https://next.catcolab.org/math/dct-000F.xml), to be instantiated with the list modality $T = \List = \List_{\id}$. The generators of this double theory are an object $\cal{X}$ and a proarrow $P: \List(\cal{X}) -\proto \cal{X}$. There are two key axioms, the *composition axiom* and the -*normalization axiom*: +\proto \cal{X}$. There are two key axioms, the *composition* and *normalization* +axioms: $$ \List P \odot P = P(\mu_{\cal{X}}, 1_{\cal{X}}) @@ -923,8 +958,8 @@ $$ \Hom_{\cal{X}} = P(\eta_{\cal{X}}, 1_{\cal{X}}). $$ -By the universal property of restriction, the composition axiom amounts to a -cell +By the universal property of restriction, the composition axiom amounts to +having a cell ```tikz % https://q.uiver.app/#q=WzAsNSxbMCwwLCJcXExpc3RcXExpc3RcXGNhbHtYfSJdLFsxLDAsIlxcTGlzdFxcY2Fse1h9Il0sWzIsMCwiXFxjYWx7WH0iXSxbMCwxLCJcXExpc3RcXGNhbHtYfSJdLFsyLDEsIlxcY2Fse1h9Il0sWzAsMSwiXFxMaXN0IFAiLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMSwyLCJQIiwwLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzIsNCwiIiwwLHsibGV2ZWwiOjIsInN0eWxlIjp7ImhlYWQiOnsibmFtZSI6Im5vbmUifX19XSxbMyw0LCJQIiwyLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzAsMywiXFxtdV97XFxjYWx7WH19IiwyXSxbMSw4LCJcXHRleHR7Y29tcH0iLDEseyJsYWJlbF9wb3NpdGlvbiI6NDAsInNob3J0ZW4iOnsidGFyZ2V0IjoyMH0sInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6Im5vbmUifSwiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dXQ== @@ -970,18 +1005,18 @@ $$ \Gamma \vdash \eqtype(m[m[x,y],z], m[x,m[y,z]])\ \type, $$ -where +in the context $$ \Gamma \coloneqq [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)] $$ -is the signature of the theory of monoids. We build the left-hand side as a term -in context $\Gamma \mid [x,y,z]: [X,X,X]$. The right-hand side is similar. +that is the signature of the theory of monoids. We build the left-hand side as a +term in context $\Gamma \mid [x,y,z]: [X,X,X]$. The right-hand side is similar. To start, use the identity rule to form the term $\Gamma \mid x: X -\vdash_{\Hom_{\cal{X}}} x: X$ and similarly for a variable $y$. Combining these, -form the list +\vdash_{\Hom_{\cal{X}}} x: X$ and similarly for a second variable $y$. Combining +these, form the list $$ \Gamma \mid [x,y]: [X,X] \vdash_{\List\Hom_{\cal{X}}} [x,y]: [X,X]. @@ -994,14 +1029,14 @@ $$ \Gamma \mid [x,y]: [X,X] \vdash_P m[x,y]: X. $$ -Next, applying the restriction rule at the theory's normalization axiom, we -obtain a variable +Next, apply the restriction rule at the theory's normalization axiom to obtain a +variable $$ \Gamma \mid [z]: [X] \vdash_P z: X $$ -but now viewed as a unary multimorphism of $P$ rather than a morphism of +but viewed now as a unary multimorphism of $P$ rather than as a morphism of $\cal{X}$. We can thus form the list $$ @@ -1020,8 +1055,8 @@ $$ \Gamma \mid [x,y,z]: [X,X,X] \vdash_P m[m[x,y],z]: X. $$ -As promised, this term is the left-hand side of the associativity axiom for -monoids. It would be written more conventionally as +As promised, this term is the left-hand side of the associativity axiom. It +would be written more conventionally as $$ \Gamma \mid [x: X, y: X, z: X] \vdash m[m[x,y],z]: X @@ -1031,7 +1066,7 @@ but we caution again that in the present type theory, this expression must be understood only as an abbreviation for the previous one. ::: {.callout-warning} -#### Non-uniqueness of derivations +#### Warning: Non-uniqueness of derivations There was another, slightly longer way we could have derived the term $m[x,y]$, parallel to how we derived the final term $m[m[x,y],z]$. Namely, after @@ -1074,14 +1109,72 @@ term in context $\Gamma \mid [x,y]: [X,X]$. Start with the variables $\Gamma \mid x: X \vdash x: X$ and $\Gamma \mid y: X \vdash y: X$ and apply the list formation rule using the swap permutation -$\sigma: [2] \xto{\cong} [2]$ to obtain the term $\Gamma \mid [x,y]: [X,X] -\vdash_{\List_{\bij} \cal{X}} [y,x]: [X,X]$. Then post-compose with the -generator $m$ to obtain +$\sigma: [2] \xto{\cong} [2]$ to obtain the term + +$$ +\Gamma \mid [x,y]: [X,X] \vdash_{\List_{\bij} \Hom_{\cal{X}}} [y,x]: [X,X]. +$$ + +Then post-compose with the generator $m$ to obtain $$ \Gamma \mid [x,y]: [X,X] \vdash_P m[y,x]: X. $$ +### Cartesian multicategories + +For non-planar multicategories, the list formation and post-composition rules do +not suffice to build all of the expected terms; the *pre*-composition rule is +also needed. To illustrate, using the internal language of a cartesian +multicategory, we construct the distributivity axiom of a rig + +$$ +\Gamma \vdash \eqtype(r \cdot (x + y), (r \cdot x) + (r \cdot y))\ \type +$$ + +in the context + +$$ +\Gamma \coloneqq [ +R: \Ob_{\cal{X}}, X: \Ob_{\cal{X}}, +(+): P([X,X], X), 0: P([], X), (\cdot): P([R,X], X) +] +$$ + +that is a fragment of the signature of the theory of rigs. As is customary, the +expressions $x + y$ and $r \cdot x$ are abbreviations for $(+)[x,y]$ and +$(\cdot)[r,x]$. We are again working with the modal double theory of generalized +multicategories, now instantiated with the list modality $T = +\List_{\mathrm{fun}}$. + +The left-hand side of the distributivity axiom + +$$ +\Gamma \mid [r,x,y]: [R,X,X] \vdash_P r \cdot (x + y): X +$$ + +is constructed using only the language of planar multicategory. For the +right-hand side, we proceed in the same way to build the intermediate term + +$$ +\Gamma \mid [r,x,s,y]: [R,X,R,X] \vdash_P (r \cdot x) + (s \cdot y): X. +$$ + +We must now apply the pre-composition rule to restrict the inner context. To do +so, we first form the list + +$$ +\Gamma \mid [r,x,y]: [R,X,X] \vdash_{\List_{\mathrm{fun}} \Hom_{\cal{X}}} [r,x,r,y]: [R,X,R,X] +$$ + +using the function $\sigma = [1,2,1,3]: [4] \to [3]$. Then pre-compose with this +term along the variable substitution $\gamma: \{r,s,x,y\} \to \{r,x,y\}$ sending +both $r$ and $s$ to $r$ and preserving $x$ and $y$ to derive the term + +$$ +\Gamma \mid [r,x,y]: [R,X,X] \vdash_P (r \cdot x) + (r \cdot y): X. +$$ + ## Rationale and alternatives ### Bespoke internal languages From 91c112e12be10e2ed3c576c1697ee91189803715 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 27 Apr 2026 22:09:11 -0700 Subject: [PATCH 20/81] DOC: Example of internal language of a comulticategory. --- rfc/0004.md | 123 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 29 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index afcc4599d..41929ebcb 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -411,8 +411,8 @@ original spec. In what follows, let $\dbl{T}$ be a double theory. We emphasize that, in contrast to the original version of DoubleTT, a term of type $P(X,Y)$ in the outer type theory represents a morphism *generator* from $X$ to $Y$. That's why morphism operations (unary cells in $\dbl{T}$) cannot be -applied to terms of type $P(X,Y)$. An arbitrary morphism from $X$ to $Y$ is -represented by a term $u: X \vdash_P t: Y$ in the inner type theory. +applied to terms of type $P(X,Y)$. Meanwhile, an *arbitrary* morphism from $X$ +to $Y$ is represented by a term $u: X \vdash_P t: Y$ in the inner type theory. ::: @@ -427,13 +427,13 @@ represented by a term $u: X \vdash_P t: Y$ in the inner type theory. \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} \hypo{\Gamma \mid u: X \vdash_P t: Y} \hypo{\Gamma \mid u: X \vdash_P s: Y} - \infer3{\Gamma \vdash \eqtype(t,s)\ \type} + \infer3{\Gamma \vdash \eqtype(u: X, Y, t, s)\ \type} \end{prooftree} \] ``` - Intuitively, a term of type $\eqtype(t,s)$ witnesses an equality between the - morphisms denoted by $t$ and $s$. + Intuitively, a term of this type witnesses an equality between the morphisms + denoted by $t$ and $s$. ::: {.callout-warning} #### Warning: Outer vs inner equality types @@ -809,10 +809,9 @@ is only syntactic sugar for the former. \] ``` - where the first premise includes the assumption that the proarrows $P$ and $Q$ - have a composite and in the last premise $\gamma$ is a function from the set - of variables contained in $v$ to the set of variables in $u$. **TODO**: - Elaborate + where the first premise includes the assumption that $P$ and $Q$ have a + composite and the last premise takes $\gamma$ to be a function from the set of + variables contained in $v$ to the set of variables in $u$. **TODO**: Elaborate - *Let binding*, or general *composition rule*: @@ -827,9 +826,9 @@ is only syntactic sugar for the former. \] ``` - where again the first premise includes the assumption that the proarrows $P$ - and $Q$ have a composite. This rule can be interpreted as destructuring the - value $t$ and binding it to the variables in the context $v$. + where again the first premise includes the assumption that $P$ and $Q$ have a + composite. This rule can be interpreted as destructuring the value $t$ and + binding it to the variables in the context $v$. ::: {.callout} ##### Remark: Let binding as cut rule @@ -999,16 +998,16 @@ morphisms of $\cal{X}$. For the remaining axioms of the double theory, see the page linked above. To illustrate the type theory, we construct the associativity axiom of a monoid -as an equality type +as a morphism equality type $$ -\Gamma \vdash \eqtype(m[m[x,y],z], m[x,m[y,z]])\ \type, +\Gamma \vdash \eqtype([x,y,z]: [X,X,X],\ X,\ m[m[x,y],z],\ m[x,m[y,z]])\ \type $$ in the context $$ -\Gamma \coloneqq [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([], X)] +\Gamma \coloneqq [X: \Ob_{\cal{X}},\ m: P([X, X], X),\ e: P([\,], X)] $$ that is the signature of the theory of monoids. We build the left-hand side as a @@ -1094,18 +1093,17 @@ general, though, we should not expect to have unique derivations. \newcommand{\bij}{\mathrm{bij}} -Symmetric multicategories also arise from the modal double theory of +Symmetric multicategories are also models of the modal double theory of [generalized multicategories](https://next.catcolab.org/math/dct-000F.xml), now instantiated with the list modality $T = \List_{\bij}$. To illustrate, we derive the commutativity axiom of a commutative monoid as an equality type $$ -\Gamma \vdash \eqtype(m[x,y], m[y,x])\ \type, +\Gamma \vdash \eqtype([x,y]: [X,X],\ X,\ m[x,y],\ m[y,x])\ \type, $$ where $\Gamma$ is the signature of the theory of monoids as before. We've -already derived the left-hand side, so let's derive the right-hand side as a -term in context $\Gamma \mid [x,y]: [X,X]$. +already derived the left-hand side, so let's derive the right-hand side. Start with the variables $\Gamma \mid x: X \vdash x: X$ and $\Gamma \mid y: X \vdash y: X$ and apply the list formation rule using the swap permutation @@ -1123,26 +1121,30 @@ $$ ### Cartesian multicategories -For non-planar multicategories, the list formation and post-composition rules do -not suffice to build all of the expected terms; the *pre*-composition rule is -also needed. To illustrate, using the internal language of a cartesian -multicategory, we construct the distributivity axiom of a rig +For non-planar multicategories, the list formation and post-composition rules +used above do not suffice to build all of the expected terms; the +*pre*-composition rule is also needed. To illustrate, using the internal +language of a cartesian multicategory, we construct the distributivity axiom of +a rig $$ -\Gamma \vdash \eqtype(r \cdot (x + y), (r \cdot x) + (r \cdot y))\ \type +\Gamma \vdash \eqtype([r,x,y]: [R,X,X],\ X,\ + r \cdot (x + y),\ + (r \cdot x) + (r \cdot y) +)\ \type $$ in the context $$ \Gamma \coloneqq [ -R: \Ob_{\cal{X}}, X: \Ob_{\cal{X}}, -(+): P([X,X], X), 0: P([], X), (\cdot): P([R,X], X) +R: \Ob_{\cal{X}},\ X: \Ob_{\cal{X}},\ +(+): P([X,X], X),\ 0: P([\,], X),\ (\cdot): P([R,X], X) ] $$ that is a fragment of the signature of the theory of rigs. As is customary, the -expressions $x + y$ and $r \cdot x$ are abbreviations for $(+)[x,y]$ and +expressions $x + y$ and $r \cdot x$ are merely abbreviations for $(+)[x,y]$ and $(\cdot)[r,x]$. We are again working with the modal double theory of generalized multicategories, now instantiated with the list modality $T = \List_{\mathrm{fun}}$. @@ -1154,14 +1156,14 @@ $$ $$ is constructed using only the language of planar multicategory. For the -right-hand side, we proceed in the same way to build the intermediate term +right-hand side, we proceed as before to build the intermediate term $$ \Gamma \mid [r,x,s,y]: [R,X,R,X] \vdash_P (r \cdot x) + (s \cdot y): X. $$ We must now apply the pre-composition rule to restrict the inner context. To do -so, we first form the list +so, first form the list $$ \Gamma \mid [r,x,y]: [R,X,X] \vdash_{\List_{\mathrm{fun}} \Hom_{\cal{X}}} [r,x,r,y]: [R,X,R,X] @@ -1175,6 +1177,69 @@ $$ \Gamma \mid [r,x,y]: [R,X,X] \vdash_P (r \cdot x) + (r \cdot y): X. $$ +### Comulticategories + +\newcommand{\concat}{\mathsf{concat}} + +Our first example of a categorical structure with co-multiary morphisms is a +comulticategory. This is a rather silly example as comulticategories are +maximally ill-adapted to pointful notation; moreover, since a comulticategory is +just the dual of a multicategory, any morphism expressed in the internal +language of a mulicategory could be intepreted as a morphism in a +comulticategory. Nevertheless, the example is worth considering because our type +theory *does* furnish an internal language for comulticategories and, more +importantly, it illustrates the use of features like let bindings needed in +other languages, like that for monoidal categories. We work with the modal +double theory of generalized comulticategories, which I've not written down +explicitly but which is just the loose dual of the theory of [generalized +multicategories](https://next.catcolab.org/math/dct-000F.xml), again +instantiated with the list modality $T = \List = \List_{\id}$. + +Consider the associativity axiom of a comonoid + +$$ +\Gamma \vdash \eqtype(x: X,\ [X,X,X],\ + \letin{[y, c] = \delta x} \concat [\delta y, [c]],\ + \letin{[a, y] = \delta x} \concat [[a], \delta y] +)\ \type +$$ + +in the context + +$$ +\Gamma \coloneqq [X: \Ob_{\cal{X}},\ \delta: P(X,[X,X]),\ \varepsilon: P(X,[\,])] +$$ + +that is the signature of the theory of comonoids. Here we have written $\concat$ +for the list monad multiplication $\mu_{\cal{X}}$ to avoid confusing Greek +letters in the signature and in the type theory itself. + +To construct the left-hand side, post-compose with $\delta$ to form $\Gamma \mid +y: X \vdash_P \delta y: [X,X]$ and apply the normalization axiom of a +generalized comulticategory (where the unit $\eta_{\cal{X}}$ now appears on the +*right* boundary of the restriction cell) to form $\Gamma \mid c: X +\vdash_P [c]: [X]$. Next, form the list + +$$ +\Gamma \mid [y,c]: [X,X] \vdash_{\List P} [\delta y, [c]]: [[X,X],[X]]. +$$ + +Now apply the let binding rule to the term $\Gamma \mid x: X \vdash_P \delta x: +[X,X]$ and the term above, at the composable proarrows $P$ and $\List P$, to +derive + +$$ +\Gamma \mid x: X \vdash_{P(1,\mu_{\cal{X}})} + \letin{[y,c] = \delta x} [\delta y, [c]]: [[X,X],[X]]. +$$ + +Finally, apply the restriction rule to derive the desired term + +$$ +\Gamma \mid x: X \vdash_P + \letin{[y,c] = \delta x} \concat [\delta y, [c]]: [X,X,X]. +$$ + ## Rationale and alternatives ### Bespoke internal languages From 6eecbbb4e57db180ba57329eedbf5a226c9ea5ce Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 28 Apr 2026 17:24:05 -0700 Subject: [PATCH 21/81] DOC: Example of internal language of a PROP. --- rfc/0004.md | 200 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 34 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 41929ebcb..ef59f9dee 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -792,7 +792,15 @@ is only syntactic sugar for the former. ##### Further composition rules -**TODO**: Preamble +Composition of morphisms is primarily expressed in type theory by the +post-composition rule described above. This rule should always be present. +However, one or both of the following extra rules related to composition may be +needed depending on the choice of categorical structure. The pre-composition +rule, enabling pre-composition with structural morphisms, is needed for +non-planar multiary languages, like those of symmetric or cartesian +multicategories. The let binding or "general composition" rule, enabling +destructuring of outputs, is needed for co-multiary languages, like those of +comulticategories or monoidal categories. - *Pre-composition rule*: @@ -811,9 +819,14 @@ is only syntactic sugar for the former. where the first premise includes the assumption that $P$ and $Q$ have a composite and the last premise takes $\gamma$ to be a function from the set of - variables contained in $v$ to the set of variables in $u$. **TODO**: Elaborate + variables contained in $v$ to the set of variables in $u$. Here $v[\gamma]$ + denotes the substitution along $\gamma$ of the variables in the context $v$ to + produce a new *term*, which makes sense in this type theory the contexts are a + strict subset of the terms. As usual, $s[\gamma]$ denotes the substitution + along $\gamma$ of the variables in the term $s$ to make another + term.[^variable-substitution] -- *Let binding*, or general *composition rule*: +- *Let binding*, or *general composition rule*: ```tikz \[ @@ -830,20 +843,24 @@ is only syntactic sugar for the former. composite. This rule can be interpreted as destructuring the value $t$ and binding it to the variables in the context $v$. +[^variable-substitution]: + A more careful treatment, not provided here, would define the two + substitution operations, on context and on terms, by induction on their + constructors. Still, it should be fairly obvious what it means to + simultaneously substitute one set of variables for another. + ::: {.callout} ##### Remark: Let binding as cut rule -Let binding is a necessary part of some internal languages but not others, -depending on whether morphisms have "multiary" codomains. For example, let -binding is needed in the internal languages of monoidal categories and of -PRO(P)s, but not of multicategories. - The let binding rule is none other than an explicit cut rule. So, we should obviously omit it when proving cut admissibility for a particular internal language. On the other hand, even when it's not strictly necessary, we might choose to include it on the pragmatic ground of being a standard feature of programming notation. +In contrast, the pre-composition rule is *not* a general cut rule, as it +substitutes variables with variables, not variables with arbitrary terms. + ::: ##### Lists @@ -1121,6 +1138,8 @@ $$ ### Cartesian multicategories +\newcommand{\fun}{\mathrm{fun}} + For non-planar multicategories, the list formation and post-composition rules used above do not suffice to build all of the expected terms; the *pre*-composition rule is also needed. To illustrate, using the internal @@ -1146,8 +1165,7 @@ $$ that is a fragment of the signature of the theory of rigs. As is customary, the expressions $x + y$ and $r \cdot x$ are merely abbreviations for $(+)[x,y]$ and $(\cdot)[r,x]$. We are again working with the modal double theory of generalized -multicategories, now instantiated with the list modality $T = -\List_{\mathrm{fun}}$. +multicategories, now instantiated with the list modality $T = \List_{\fun}$. The left-hand side of the distributivity axiom @@ -1181,19 +1199,22 @@ $$ \newcommand{\concat}{\mathsf{concat}} -Our first example of a categorical structure with co-multiary morphisms is a -comulticategory. This is a rather silly example as comulticategories are -maximally ill-adapted to pointful notation; moreover, since a comulticategory is -just the dual of a multicategory, any morphism expressed in the internal -language of a mulicategory could be intepreted as a morphism in a -comulticategory. Nevertheless, the example is worth considering because our type -theory *does* furnish an internal language for comulticategories and, more -importantly, it illustrates the use of features like let bindings needed in -other languages, like that for monoidal categories. We work with the modal -double theory of generalized comulticategories, which I've not written down -explicitly but which is just the loose dual of the theory of [generalized -multicategories](https://next.catcolab.org/math/dct-000F.xml), again -instantiated with the list modality $T = \List = \List_{\id}$. +Our first example of a categorical structure with "co-multiary" morphisms is a +comulticategory. This choice is rather silly as a comulticategory, having +single-input, multiple-output morphisms, is maximally ill-adapted to pointful +notation; moreover, since comulticategories are dual to multicategories, any +morphism expressed in the internal language of a mulicategory could just as well +be interpreted as a morphism in a comulticategory. Nevertheless, the example is +worth considering, first, because our type theory *does* furnish an internal +language for comulticategories, and, more importantly, because this language +illustrates features like let binding also needed in more useful languages, such +as those of PROPs or monoidal categories. + +We work with the modal double theory of *generalized comulticategories*. I've +not written down this theory explicitly, but it is just the loose dual of the +theory of [generalized +multicategories](https://next.catcolab.org/math/dct-000F.xml). The theory is +again instantiated with the list modality $T = \List = \List_{\id}$. Consider the associativity axiom of a comonoid @@ -1210,36 +1231,147 @@ $$ \Gamma \coloneqq [X: \Ob_{\cal{X}},\ \delta: P(X,[X,X]),\ \varepsilon: P(X,[\,])] $$ -that is the signature of the theory of comonoids. Here we have written $\concat$ -for the list monad multiplication $\mu_{\cal{X}}$ to avoid confusing Greek -letters in the signature and in the type theory itself. +that is the signature of the theory of comonoids. Here we write $\concat$ for +the list monad multiplication $\mu_{\cal{X}}$ to avoid confusing Greek letters +in the signature and in the type theory itself. -To construct the left-hand side, post-compose with $\delta$ to form $\Gamma \mid -y: X \vdash_P \delta y: [X,X]$ and apply the normalization axiom of a -generalized comulticategory (where the unit $\eta_{\cal{X}}$ now appears on the -*right* boundary of the restriction cell) to form $\Gamma \mid c: X -\vdash_P [c]: [X]$. Next, form the list +To construct, say, the left-hand side of the axiom, post-compose a variable with +$\delta$ to form $\Gamma \mid y: X \vdash_P \delta y: [X,X]$ and apply the +normalization axiom of a generalized comulticategory (where the unit +$\eta_{\cal{X}}$ now appears on the *right* boundary of the restriction cell) to +form $\Gamma \mid c: X \vdash_P [c]: [X]$. Combining these two terms, form the +list $$ \Gamma \mid [y,c]: [X,X] \vdash_{\List P} [\delta y, [c]]: [[X,X],[X]]. $$ Now apply the let binding rule to the term $\Gamma \mid x: X \vdash_P \delta x: -[X,X]$ and the term above, at the composable proarrows $P$ and $\List P$, to -derive +[X,X]$ and to the term above, at the composable proarrows $P$ and $\List P$, in +order to derive $$ \Gamma \mid x: X \vdash_{P(1,\mu_{\cal{X}})} \letin{[y,c] = \delta x} [\delta y, [c]]: [[X,X],[X]]. $$ -Finally, apply the restriction rule to derive the desired term +Finally, apply the restriction rule to obtain the desired term $$ \Gamma \mid x: X \vdash_P \letin{[y,c] = \delta x} \concat [\delta y, [c]]: [X,X,X]. $$ +### PROPs + +A minimal categorical structure admitting both multiary and co-multiary +morphisms is a [PRO](https://ncatlab.org/nlab/show/PRO). However, it seems that +nothing very interesting can be said in a PRO that can't already be said in a +multicategory or comulticategory. We proceed directly to the internal language +of a (multisorted) [PROP](https://ncatlab.org/nlab/show/PROP). + +PROPs are models of the modal double theory of [generalized +PROs](https://next.catcolab.org/math/dct-000E.xml), instantiated with the list +modality $T = \List_{\bij}$. The generators of this double theory are an object +$\cal{X}$, a proarrow $M: \List_{\bij} \cal{X} \proto \List_{\bij} \cal{X}$, and +a cell + +```tikz +\newcommand{\bij}{\mathrm{bij}} + +% https://q.uiver.app/#q=WzAsNCxbMCwwLCJcXExpc3Rfe1xcYmlqfV4yIFxcY2Fse1h9Il0sWzEsMCwiXFxMaXN0X3tcXGJpan1eMiBcXGNhbHtYfSJdLFswLDEsIlxcTGlzdF97XFxiaWp9IFxcY2Fse1h9Il0sWzEsMSwiXFxMaXN0X3tcXGJpan0gXFxjYWx7WH0iXSxbMCwyLCJcXG11X3tcXGNhbHtYfX0iLDJdLFswLDEsIlxcTGlzdF97XFxiaWp9IE0iLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMSwzLCJcXG11X1giXSxbMiwzLCJNIiwyLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzUsNywiXFxvdGltZXMiLDEseyJzaG9ydGVuIjp7InNvdXJjZSI6MjAsInRhcmdldCI6MjB9LCJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJub25lIn0sImhlYWQiOnsibmFtZSI6Im5vbmUifX19XV0= +\[\begin{tikzcd}[column sep=large] + {\List_{\bij}^2 \cal{X}} & {\List_{\bij}^2 \cal{X}} \\ + {\List_{\bij} \cal{X}} & {\List_{\bij} \cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "{\List_{\bij} M}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] + \arrow["{\mu_{\cal{X}}}"', from=1-1, to=2-1] + \arrow["{\mu_X}", from=1-2, to=2-2] + \arrow[""{name=1, anchor=center, inner sep=0}, "M"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-2] + \arrow["\otimes"{description}, draw=none, from=0, to=1] +\end{tikzcd}\] +``` + +Key axioms include the *composition* and *normalization* axioms: + +$$ +M \odot M = M +\qquad\text{and}\qquad +\Hom_{\cal{X}} = M(\eta_{\cal{X}}, \eta_{\cal{X}}). +$$ + +For the remaining axioms and other details, see the linked page. + +We record the most interesting axiom of a +[bimonoid](https://ncatlab.org/nlab/show/bimonoid), namely that comultiplication +commutes with multiplication (equivalently, that multiplication commutes with +comultiplication), as the morphism equality type + +$$ +\begin{aligned} +\Gamma \vdash \eqtype( + & [x,y]: [X,X],\ [X,X], \\ + & \delta\, m [x, y], \\ + & \letin{[a,b,c,d] = \concat[\delta[x], \delta[y]]} \concat [m[a,c], m[b,d]]) +\end{aligned} +$$ + +in the context + +$$ +\Gamma \coloneqq [ +X: \Ob_{\cal{X}},\ + m: M([X,X],[X]),\ e: M([\,], [X]),\ +\delta: M([X],[X,X]),\ \varepsilon: M([X],[\,]) +] +$$ + +that is the signature of the theory of bimonoids. + +The left-hand side of this equation is built in the now familiar way: first form +the list $[x,y]$ over $\List_{\bij} \Hom_{\cal{X}}$, then post-compose with $m$ +to obtain $\Gamma \mid [x,y]: [X,X] \vdash_M m[x,y]: [X]$, and post-compose +again with $\delta$ to obtain + +$$ +\Gamma \mid [x,y]: [X]^2 \vdash_M \delta\, m [x,y]: [X]^2, +$$ + +where we introduce $[X]^k$ as an abbreviation for the list $[X,\dots,X]$ with +$X$ repeated $k$ times. + +The right-hand side is derived by applying the let binding rule to the terms + +$$ +\Gamma \mid [x,y]: [X]^2 \vdash_M \concat[\delta[x], \delta[y]]: [X]^4 +\quad\text{and}\quad +\Gamma \mid [a,b,c,d]: [X]^4 \vdash_M \concat[m[a,c], m[b,d]]: [X]^2. +$$ + +For the first of these, apply the restriction cell from the normalization axiom +to the variable $x$ to make the term $\Gamma \mid [x]: [X] \vdash_{\List_{\bij} +\Hom_{\cal{X}}} [x]: [X]$, then post-compose with $\delta$ to obtain $\Gamma +\mid [x]: [X] \vdash_M \delta [x]: [X]^2$. We do the same for the variable $y$, +then form the list + +$$ +\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List_{\bij} M} + [\delta[x], \delta[y]]: [[X]^2, [X]^2]. +$$ + +Finally, apply the cell $\otimes$ representing the tensor to obtain the first of +the above terms. + +As for the second term, proceed similarly to construct the term + +$$ +\Gamma \mid [[a,c], [b,d]]: [[X]^2, [X]^2] \vdash_{\List_{\bij} M} + [m[a,c],m[b,d]]: [[X], [X]], +$$ + +apply the tensor operation, and finally precompose with the term $\Gamma \mid +[a,b,c,d] \vdash_{\List_{\bij} \Hom_{\cal{X}}} [a,c,b,d]$ induced by the +permutation $\sigma = [1,3,2,4]: [4] \xto{\cong} [4]$. + ## Rationale and alternatives ### Bespoke internal languages From b0b7ce0de57007ce5c0eeb66a1e526f94f8a2a16 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Thu, 30 Apr 2026 22:32:02 -0700 Subject: [PATCH 22/81] DOC: Example of internal language of a monoidal category. --- rfc/0004.md | 309 +++++++++++++++++++++++++++++++++++++++----- rfc/_references.bib | 12 ++ 2 files changed, 288 insertions(+), 33 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index ef59f9dee..7ecbe65ce 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -158,7 +158,7 @@ of the type theory itself. [^no-signatures]: Type theories with function types and enough inductive types to generate standard data types such as the booleans and natural numbers commonly omit a - parameterizing signature entirely, under idea that all other types and + parameterizing signature entirely, under the idea that all other types and operations should be built out of the provided ones. In this RFC, however, we are exclusively concerned with type theories that do *not* have function types, i.e., with the internal languages of categorical structures that are @@ -595,6 +595,8 @@ is only syntactic sugar for the former. ::: +##### Core rules + - *Context formation*: ```tikz @@ -666,6 +668,8 @@ is only syntactic sugar for the former. #### Terms +##### Core rules + - *Identity rule*: ```tikz @@ -726,9 +730,11 @@ is only syntactic sugar for the former. ``` Notice that the cell's name $\alpha$ does not appear on the right-hand side, - hence the need to assume that $\dbl{T}$ is flat. Two special cases are worth - recording. First, for each arrow $F: \cal{X} \to \cal{Y}$ in $\dbl{T}$, there - is an identity cell $\id_F = \Hom_F$ and hence a derived rule: + hence the need to assume that $\dbl{T}$ is flat. + + Two special cases are worth recording. First, for each arrow $F: \cal{X} \to + \cal{Y}$ in $\dbl{T}$, there is an identity cell $\id_F = \Hom_F$ and hence a + derived rule: ```tikz \[ @@ -792,39 +798,53 @@ is only syntactic sugar for the former. ##### Further composition rules -Composition of morphisms is primarily expressed in type theory by the -post-composition rule described above. This rule should always be present. -However, one or both of the following extra rules related to composition may be -needed depending on the choice of categorical structure. The pre-composition -rule, enabling pre-composition with structural morphisms, is needed for -non-planar multiary languages, like those of symmetric or cartesian -multicategories. The let binding or "general composition" rule, enabling -destructuring of outputs, is needed for co-multiary languages, like those of -comulticategories or monoidal categories. +In type theory, composition of morphisms manifests primarily as the +post-composition rule. This rule should always be present. However, one or both +of the following extra rules related to composition may be needed depending on +the categorical structure at hand. The pre-composition rule, enabling +pre-composition with structural morphisms, is needed for non-planar multiary +languages, like those of symmetric or cartesian multicategories. The let binding +or "general composition" rule, enabling destructuring of outputs, is needed for +co-multiary languages, like those of comulticategories or monoidal categories. - *Pre-composition rule*: ```tikz \[ \begin{prooftree} - \hypo{\dbl{T} \vDash (\cal{X} \xproto{P} \cal{Y} \xproto{Q} \cal{Z})} - \hypo{\Gamma \mid u: X \vdash_P t: Y} - \hypo{\Gamma \mid v: Y \vdash_Q s: Z} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\Gamma \mid u: X' \vdash_{\Hom_{\cal{X}}} t: X} + \hypo{\Gamma \mid v: X \vdash_P s: Y} \hypo{\gamma: \mathsf{var}(v) \to \mathsf{var}(u)} - \infer[no rule]1{t = v[\gamma]} - \infer4{\Gamma \mid u: X \vdash_{P \odot Q} s[\gamma]: Z} + \infer[no rule]1{\Gamma \mid u: X' \vdash t = v[\gamma]: X} + \infer4{\Gamma \mid u: X' \vdash_P s[\gamma]: Y} \end{prooftree} \] ``` - where the first premise includes the assumption that $P$ and $Q$ have a - composite and the last premise takes $\gamma$ to be a function from the set of - variables contained in $v$ to the set of variables in $u$. Here $v[\gamma]$ - denotes the substitution along $\gamma$ of the variables in the context $v$ to - produce a new *term*, which makes sense in this type theory the contexts are a - strict subset of the terms. As usual, $s[\gamma]$ denotes the substitution - along $\gamma$ of the variables in the term $s$ to make another - term.[^variable-substitution] + where in the last premise, $\gamma$ is a function from the set of variables + contained in $v$ to the set of variables in $u$. Here $v[\gamma]$ denotes the + *term* given by substituting in the *context* $v$ along $\gamma$, which makes + sense because in this type theory the contexts are a strict subset of the + terms, i.e., whenever $\Gamma \mid u: X$, the *identity* term $\Gamma \mid u: + X \vdash_{\Hom_{\cal{X}}} u: X$ is derivable. As usual, $s[\gamma]$ denotes + the term given by substituting in the term $s$ along + $\gamma$.[^variable-substitution] + + As a special case, this rule includes *$\alpha$-equivalence*. Taking $X' = X$ + and $t = u$ to be an identity term, which forces $\gamma$ to be a bijection, + there is a derived rule for *variable renaming*: + + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \hypo{\gamma: \mathsf{var}(u) \xto{\cong} \cod\gamma} + \infer[dashed]3{\Gamma \mid u[\gamma]: X \vdash_P t[\gamma]: Y} + \end{prooftree} + \] + ``` - *Let binding*, or *general composition rule*: @@ -839,14 +859,14 @@ comulticategories or monoidal categories. \] ``` - where again the first premise includes the assumption that $P$ and $Q$ have a + where the first premise includes the assumption that $P$ and $Q$ have a composite. This rule can be interpreted as destructuring the value $t$ and binding it to the variables in the context $v$. [^variable-substitution]: - A more careful treatment, not provided here, would define the two + A more careful treatment, not on offer here, would define the two substitution operations, on context and on terms, by induction on their - constructors. Still, it should be fairly obvious what it means to + constructors. Still, it should be intuitively clear what it means to simultaneously substitute one set of variables for another. ::: {.callout} @@ -1188,8 +1208,8 @@ $$ $$ using the function $\sigma = [1,2,1,3]: [4] \to [3]$. Then pre-compose with this -term along the variable substitution $\gamma: \{r,s,x,y\} \to \{r,x,y\}$ sending -both $r$ and $s$ to $r$ and preserving $x$ and $y$ to derive the term +term, with respect to the variable mapping $\gamma: \{r,s,x,y\} \to \{r,x,y\}$ +sending both $r$ and $s$ to $r$ and preserving $x$ and $y$, to derive $$ \Gamma \mid [r,x,y]: [R,X,X] \vdash_P (r \cdot x) + (r \cdot y): X. @@ -1368,10 +1388,233 @@ $$ [m[a,c],m[b,d]]: [[X], [X]], $$ -apply the tensor operation, and finally precompose with the term $\Gamma \mid +apply the tensor operation, and finally pre-compose with the term $\Gamma \mid [a,b,c,d] \vdash_{\List_{\bij} \Hom_{\cal{X}}} [a,c,b,d]$ induced by the permutation $\sigma = [1,3,2,4]: [4] \xto{\cong} [4]$. +### Monoidal categories + +At last we come to a categorical structure whose internal language has a +non-trivial type constructor. In a monoidal category, we can form the monoidal +product $X \otimes Y$ of objects $X$ and $Y$, the linear analogue of a product +type. In fact, as objects of categorical doctrines, symmetric monoidal +categories (SMCs) and (multi-sorted) PROPs are interchangeable: any theory that +can be expressed as an SMC can just as well be expressed as a PROP, and vice +versa. The difference is between their morphisms, or interpretations of +theories: a symmetric monoidal functor, even a strict one, is more general than +a morphism of PROPs. Such morphisms are, however, beyond the scope of this RFC. +So, rather than recapitulating everything above in the language of monoidal +categories, we will focus on what changes about the syntax and how the +associators and other structure isomorphisms work. + +The internal language of monoidal categories having a non-trivial type +constructor corresponds to the modal double theory of [generalized monoidal +categories](https://next.catcolab.org/math/dct-0005.xml) having a non-structural +tight morphism. This theory is better known as the theory of *monad +pseudoalgebras* or *$T$-pseudoalgebras*, where for $T$ we can take a list double +monad. For bare monoidal categories, we take the plain list monad $T = \List = +\List_{\id}$. The generators of the theory are an object $\cal{X}$, an arrow +$\otimes: \List \cal{X} \to \cal{X}$, and *associator* and *unitor* cells + +```tikz +\[ + % https://q.uiver.app/#q=WzAsNixbMCwwLCJcXExpc3ReMiBcXGNhbHtYfSJdLFswLDEsIlxcTGlzdFxcY2Fse1h9Il0sWzEsMCwiXFxMaXN0XjIgXFxjYWx7WH0iXSxbMCwyLCJcXGNhbHtYfSJdLFsxLDEsIlxcTGlzdFxcY2Fse1h9Il0sWzEsMiwiXFxjYWx7WH0iXSxbMCwyLCIiLDAseyJsZXZlbCI6Miwic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn0sImhlYWQiOnsibmFtZSI6Im5vbmUifX19XSxbMCwxLCJcXExpc3RcXG90aW1lcyIsMl0sWzEsMywiXFxvdGltZXMiLDJdLFsyLDQsIlxcbXVfe1xcY2Fse1h9fSJdLFs0LDUsIlxcb3RpbWVzIl0sWzMsNSwiIiwyLHsibGV2ZWwiOjIsInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6ImJhcnJlZCJ9LCJoZWFkIjp7Im5hbWUiOiJub25lIn19fV0sWzYsMTEsIlxcYWxwaGEiLDEseyJzaG9ydGVuIjp7InNvdXJjZSI6MjAsInRhcmdldCI6MjB9LCJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJub25lIn0sImhlYWQiOnsibmFtZSI6Im5vbmUifX19XV0= + \begin{tikzcd} + {\List^2 \cal{X}} & {\List^2 \cal{X}} \\ + {\List\cal{X}} & {\List\cal{X}} \\ + {\cal{X}} & {\cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "\shortmid"{marking}, equals, from=1-1, to=1-2] + \arrow["{\List\otimes}"', from=1-1, to=2-1] + \arrow["{\mu_{\cal{X}}}", from=1-2, to=2-2] + \arrow["\otimes"', from=2-1, to=3-1] + \arrow["\otimes", from=2-2, to=3-2] + \arrow[""{name=1, anchor=center, inner sep=0}, "\shortmid"{marking}, equals, from=3-1, to=3-2] + \arrow["\alpha"{description}, draw=none, from=0, to=1] + \end{tikzcd} + \qquad\text{and}\qquad + % https://q.uiver.app/#q=WzAsNSxbMCwwLCJcXGNhbHtYfSJdLFswLDIsIlxcY2Fse1h9Il0sWzEsMCwiXFxjYWx7WH0iXSxbMSwyLCJcXGNhbHtYfSJdLFsxLDEsIlxcTGlzdCBcXGNhbHtYfSJdLFswLDEsIiIsMCx7ImxldmVsIjoyLCJzdHlsZSI6eyJoZWFkIjp7Im5hbWUiOiJub25lIn19fV0sWzIsNCwiXFxldGFfe1xcY2Fse1h9fSJdLFs0LDMsIlxcb3RpbWVzIl0sWzAsMiwiIiwxLHsibGV2ZWwiOjIsInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6ImJhcnJlZCJ9LCJoZWFkIjp7Im5hbWUiOiJub25lIn19fV0sWzEsMywiIiwxLHsibGV2ZWwiOjIsInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6ImJhcnJlZCJ9LCJoZWFkIjp7Im5hbWUiOiJub25lIn19fV0sWzgsOSwiXFxpb3RhIiwxLHsic2hvcnRlbiI6eyJzb3VyY2UiOjIwLCJ0YXJnZXQiOjIwfSwic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoibm9uZSJ9LCJoZWFkIjp7Im5hbWUiOiJub25lIn19fV1d + \begin{tikzcd} + {\cal{X}} & {\cal{X}} \\ + & {\List \cal{X}} \\ + {\cal{X}} & {\cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "\shortmid"{marking}, equals, from=1-1, to=1-2] + \arrow[equals, from=1-1, to=3-1] + \arrow["{\eta_{\cal{X}}}", from=1-2, to=2-2] + \arrow["\otimes", from=2-2, to=3-2] + \arrow[""{name=1, anchor=center, inner sep=0}, "\shortmid"{marking}, equals, from=3-1, to=3-2] + \arrow["\iota"{description}, draw=none, from=0, to=1] + \end{tikzcd} +\] +``` + +and their loose inverses. The cells obey three coherence axioms. + +Starting with contexts, suppose $\Gamma \coloneqq [X: \Ob_{\cal{X}}, Y: +\Ob_{\cal{X}}]$ and form the context $\Gamma \mid [x,y]: [X,Y]$ over $\List +\cal{X}$ as before. Applying the arrow $\otimes: \List\cal{X} \to \cal{X}$ +yields a new context over $\cal{X}$ that appears literally as + +$$ +\Gamma \mid \otimes [x,y]: \otimes [X,Y]. +$$ + +Following the common practice for unbiased monoidal products, we abbreviate a +product $\otimes [X_1, \dots, X_k]$, with $k > 1$, as $X_1 \otimes \cdots +\otimes X_k$, and we write the empty product $\otimes [\,]$ as $I$. We then +introduce the tuple notation $(x_1,\dots,x_k)$ for the product element $\otimes +[x_1,\dots,x_k]$ for any $k \geq 0$. The context above then appears in more +familiar style as + +$$ +\Gamma \mid (x, y): X \otimes Y. +$$ + +We emphasize though that the latter is only syntactic sugar for the former. + +In the context + +$$ +\Gamma \coloneqq [ + A, B, W, X, Y: \Ob_{\cal{X}},\ + f: \Hom_{\cal{X}}(A, X \otimes W),\ + g: \Hom_{\cal{X}}(W \otimes B, Y) +], +$$ + +let's construct a term denoting the morphism[^fritz-example-morphism] that is, +rather unpleasantly, written in point-free notation as + +$$ +(f \otimes 1_B) \cdot \alpha_{X,W,B} \cdot (1_X \otimes g): A \otimes B \to X \otimes Y, +$$ + +where $\alpha_{X,W,B}$ is a component of the (biased) associator natural +isomorphism. + +[^fritz-example-morphism]: + As it happens, this morphism appears as a string diagram in [@fritz-2020, + Lemma 11.12]. + +First, form the terms + +$$ +\Gamma \mid a: A \vdash_{\Hom_{\cal{X}}} fa: X \otimes W +\qquad\text{and}\qquad +\Gamma \mid b: B \vdash_{\Hom_{\cal{X}}} b: B, +$$ + +then the list + +$$ +\Gamma \mid [a,b]: [A,B] \vdash_{\List \Hom_{\cal{X}}} [fa, b]: [X \otimes W, B], +$$ + +and then apply the cell $\Hom_{\otimes}$ induced by the tensor $\otimes: +\List\cal{X} \to \cal{X}$ to obtain + +$$ +\Gamma \mid (a,b): A \otimes B \vdash_{\Hom_{\cal{X}}} (fa, b): (X \otimes W) \otimes B. +$$ + +Now, intuitively, we want to apply the let binding rule to compose this term +with + +$$ +\Gamma \mid (x,(w,b)): X \otimes (W \otimes B) \vdash_{\Hom_{\cal{X}}} + (x,g(w,b)) \vdash X \otimes Y +$$ + +constructed similarly. But the types $(X \otimes W) \otimes B$ and $X \otimes (W +\otimes B)$ don't quite match up. + +To proceed, we must use the associators and unitors to reshape the tuples. +Semantically, this amounts to applying the following chain of isomorphisms + +$$ +(X \otimes W) \otimes B \xto{\cong} + (X \otimes W) \otimes (\otimes [B]) \xto{\cong} + (X \otimes W \otimes B) \xto{\cong} + (\otimes [X]) \otimes (W \otimes B) \xto{\cong} + X \otimes (W \otimes B). +$$ + +Let's put this plan into action. Applying the unitor cell to a variable $b$ +gives $\Gamma \mid b: B \vdash (b,): \otimes [B]$. Tensor that with the pair +$(x,w)$ to get the term + +$$ +\Gamma \mid ((x,w),b): (X \otimes W) \otimes B \vdash + ((x,w),(b,)): (X \otimes W) \otimes (\otimes [B]) +$$ + +denoting the first isomorphism. Next, form the list of lists + +$$ +\Gamma \mid [[x,w],[b]]: [[X,W],[B]] \vdash_{\List \List \Hom_{\cal{X}}} + [[x,w],[b]]: [[X,W],[B]] +$$ + +and apply the associator cell to derive the term + +$$ +\Gamma \mid ((x,w),(b,)): (X \otimes W) \otimes (\otimes [B]) \vdash + (x,w,b): X \otimes W \otimes B +$$ + +denoting the second iso. Combining the two terms using the pre-composition rule +yields a new term + +$$ +\Gamma \mid ((x,w),b): (X \otimes W) \otimes B \vdash + (x,w,b): X \otimes W \otimes B +$$ + +representing the composite of the first two isomorphisms. Repeating this +procedure for the *inverse* associator and unitor cells and applying the +pre-composition rule a final time yields a term + +$$ +\Gamma \mid ((x,w),b): (X \otimes W) \otimes B \vdash_{\Hom_{\cal{X}}} + (x,(w,b)): X \otimes (W \otimes B) +$$ + +denoting the composite of all four structure isomorphisms. Intuitively, this +term represents the function that destructures the nested tuple on the left side +of the turnstile and then restructures it into the nested tuple on the right +side. + +To complete the derivation, pre-compose the original term $(x,g(w,b))$ with the +preceding term to obtain + +$$ +\Gamma \mid ((x,w),b): (X \otimes W) \otimes B \vdash_{\Hom_{\cal{X}}} + (x,g(w,b)) \vdash X \otimes Y, +$$ + +then apply the let binding rule to derive + +$$ +\Gamma \mid (a,b): A \otimes B \vdash_{\Hom_{\cal{X}}} + \letin{((x,w),b) = (fa,b)} (x,g(w,b)): X \otimes Y. +$$ + +::: {.callout-tip} +#### Remark: Comparison with programming notation + +While this derivation was rather long, it is the job of the type checker, not +the programmer, to check that the term has a valid derivation. The term itself +is short and readable. In fact, it is nearly identical to the following +idiomatic Rust code, assuming `f` and `g` have the right signatures: + +```rust +|(a, b): (A, B)|: (X, Y) { + let (x, w) = f(a); + (x, g((w, b))) +} +``` + +::: + ## Rationale and alternatives ### Bespoke internal languages @@ -1423,4 +1666,4 @@ mathematical challenges. This document is, however, solely about textual syntax. ### Meta-theoretic properties -**TODO**: Cut admissibility +**TODO**: Flatness, soundness, completeness, cut admissibility diff --git a/rfc/_references.bib b/rfc/_references.bib index be39d2d27..2aad26a6e 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -48,6 +48,18 @@ @phdthesis{dreyer-2005 url = {https://csd.cmu.edu/sites/default/files/phd-thesis/CMU-CS-05-131.pdf} } +@article{fritz-2020, + title = {A synthetic approach to Markov kernels, conditional independence and theorems on sufficient statistics}, + author = {Fritz, Tobias}, + journal = {Advances in Mathematics}, + volume = {370}, + pages = {107239}, + year = {2020}, + doi = {10.1016/j.aim.2020.107239}, + eprinttype = {arXiv}, + eprint = {1908.07021} +} + @book{grandis-2019, title = {Higher dimensional categories: From double to multiple categories}, author = {Grandis, Marco}, From c8fb839f576ad1403e97227b3996e57be0adf41a Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Fri, 1 May 2026 17:15:06 -0700 Subject: [PATCH 23/81] DOC: Examples of internal languages of SMCs and cartesian categories. --- rfc/0004.md | 138 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 27 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 7ecbe65ce..fd85a1c5d 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -806,6 +806,8 @@ pre-composition with structural morphisms, is needed for non-planar multiary languages, like those of symmetric or cartesian multicategories. The let binding or "general composition" rule, enabling destructuring of outputs, is needed for co-multiary languages, like those of comulticategories or monoidal categories. +Both rules are needed in languages like those of symmetric or cartesian monoidal +categories. - *Pre-composition rule*: @@ -826,26 +828,11 @@ co-multiary languages, like those of comulticategories or monoidal categories. contained in $v$ to the set of variables in $u$. Here $v[\gamma]$ denotes the *term* given by substituting in the *context* $v$ along $\gamma$, which makes sense because in this type theory the contexts are a strict subset of the - terms, i.e., whenever $\Gamma \mid u: X$, the *identity* term $\Gamma \mid u: - X \vdash_{\Hom_{\cal{X}}} u: X$ is derivable. As usual, $s[\gamma]$ denotes + terms, i.e., whenever $\Gamma \mid v: X$, the *identity* term $\Gamma \mid v: + X \vdash_{\Hom_{\cal{X}}} v: X$ is derivable. As usual, $s[\gamma]$ denotes the term given by substituting in the term $s$ along $\gamma$.[^variable-substitution] - As a special case, this rule includes *$\alpha$-equivalence*. Taking $X' = X$ - and $t = u$ to be an identity term, which forces $\gamma$ to be a bijection, - there is a derived rule for *variable renaming*: - - ```tikz - \[ - \begin{prooftree} - \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} - \hypo{\Gamma \mid u: X \vdash_P t: Y} - \hypo{\gamma: \mathsf{var}(u) \xto{\cong} \cod\gamma} - \infer[dashed]3{\Gamma \mid u[\gamma]: X \vdash_P t[\gamma]: Y} - \end{prooftree} - \] - ``` - - *Let binding*, or *general composition rule*: ```tikz @@ -869,12 +856,39 @@ co-multiary languages, like those of comulticategories or monoidal categories. constructors. Still, it should be intuitively clear what it means to simultaneously substitute one set of variables for another. +::: {.callout} +#### Remark: Admissibility vs derivability of $\alpha$-equivalence + +Any type theory, including this one, should exhibit some notion of +[$\alpha$-equivalence](https://ncatlab.org/nlab/show/alpha-equivalence). Here +$\alpha$-conversion appears as the *variable renaming* rule: + +```tikz +\[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \hypo{\gamma: \mathsf{var}(u) \xto{\cong} \cod\gamma} + \infer[dashed]3{\Gamma \mid u[\gamma]: X \vdash_P t[\gamma]: Y} + \end{prooftree} +\] +``` + +Without the pre-composition rule, variable renaming is only an +[admissible](https://ncatlab.org/nlab/show/admissible+rule) rule, in that +establishing it requires an induction over all possible derivations. With the +pre-composition rule, variable renaming becomes a derivable rule, a special case +of pre-composition given by taking $X' = X$ and $t = u$ to be an identity term, +which forces $\gamma$ to be a bijection. + +::: + ::: {.callout} ##### Remark: Let binding as cut rule The let binding rule is none other than an explicit cut rule. So, we should obviously omit it when proving cut admissibility for a particular internal -language. On the other hand, even when it's not strictly necessary, we might +language. On the other hand, even when it's not logically necessary, we might choose to include it on the pragmatic ground of being a standard feature of programming notation. @@ -1164,7 +1178,7 @@ For non-planar multicategories, the list formation and post-composition rules used above do not suffice to build all of the expected terms; the *pre*-composition rule is also needed. To illustrate, using the internal language of a cartesian multicategory, we construct the distributivity axiom of -a rig +a module over a rig $$ \Gamma \vdash \eqtype([r,x,y]: [R,X,X],\ X,\ @@ -1182,10 +1196,11 @@ R: \Ob_{\cal{X}},\ X: \Ob_{\cal{X}},\ ] $$ -that is a fragment of the signature of the theory of rigs. As is customary, the -expressions $x + y$ and $r \cdot x$ are merely abbreviations for $(+)[x,y]$ and -$(\cdot)[r,x]$. We are again working with the modal double theory of generalized -multicategories, now instantiated with the list modality $T = \List_{\fun}$. +that is a fragment of the signature of the theory of modules over rigs. As is +customary, the expressions $x + y$ and $r \cdot x$ are merely abbreviations for +$(+)[x,y]$ and $(\cdot)[r,x]$. We are again working with the modal double theory +of generalized multicategories, now instantiated with the list modality $T = +\List_{\fun}$. The left-hand side of the distributivity axiom @@ -1602,16 +1617,85 @@ $$ #### Remark: Comparison with programming notation While this derivation was rather long, it is the job of the type checker, not -the programmer, to check that the term has a valid derivation. The term itself -is short and readable. In fact, it is nearly identical to the following -idiomatic Rust code, assuming `f` and `g` have the right signatures: +the programmer, to check that a given term has a valid derivation. The term +itself is short and readable. In fact, it is nearly identical to the following +idiomatic Rust code: ```rust -|(a, b): (A, B)|: (X, Y) { +fn f(a: A) -> (X, W) { ... } +fn g(input: (W, B)) -> Y { ... } + +|(a, b): (A, B)| -> (X, Y) { let (x, w) = f(a); (x, g((w, b))) } ``` +::: + +### Symmetric monoidal categories + +By now should there should be no surprises about how symmetric monoidal +categories (SMCs) arise in this framework: take the modal double theory of +[generalized monoidal categories](https://next.catcolab.org/math/dct-0005.xml), +now with the symmetric variant $T = \List_{\bij}$ of the list double monad. + +Besides the associators and unitors, SMCs have a new kind of structure +isomorphisms, the symmetries. Let's see how these appear in the internal +language. Taking $\Gamma \coloneqq [X: \Ob_{\cal{X}},\ Y: \Ob_{\cal{X}}]$ to +have two objects, we can form the list + +$$ +\Gamma \mid [x,y]: [X,Y] \vdash_{\List_{\bij} \Hom_{\cal{X}}} [y,x]: [Y,X] +$$ + +using the swap permutation $\sigma: [2] \xto{\cong} [2]$, just as we did for +symmetric multicategories. But now we can apply the cell $\Hom_{\otimes}$ +induced by the tensor $\otimes: \List_{\bij} \cal{X} \to \cal{X}$ to construct a +new term + +$$ +\Gamma \mid (x,y): X \otimes Y \vdash_{\Hom_{\cal{X}}} (y,x): Y \otimes X. +$$ + +This term denotes the symmetry isomorphism $\sigma_{X,Y}: X \otimes Y \to Y +\otimes X$. + +### Cartesian categories + +Cartesian categories, by which we mean cartesian monoidal categories, arise from +the modal double theory of generalized monoidal categories, for the cartesian +variant $T = \List_{\fun}$ of the list double monad. + +Diagonals and projections are constructed in the same manner as symmetries: one +forms the appropriate lists and then applies the cell $\Hom_{\cal{X}}$ induced +by the product operation $\otimes: \List_{\fun} \cal{X} \to \cal{X}$, +conventionally written $\times$. For example, to construct the diagonal in the +context $\Gamma \coloneqq [X: \Ob_{\cal{X}}]$, first form the list + +$$ +\Gamma \mid [x]: [X] \vdash_{\List_{\fun} \Hom_{\cal{X}}} [x,x]: [X,X] +$$ + +using the function $\sigma: [2] \xto{!} [1]$, then apply the cell +$\Hom_{\cal{X}}$, and finally pre-compose with the unitor to derive + +$$ +\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} (x,x): X \times X. +$$ + +::: {.callout-tip} +#### Remark: Comparison with standard algebraic type theory + +It's worth noting tha our internal language for cartesian products differs from +the standard presentation of product types [e.g., @shulman-2016, Figure 2.4] in +that, while it has what amounts to the standard introduction rule for product +types (pairing), it does not have the standard elimination rules (projections). +That is, given a term $t: X \times Y$, there are not terms $\pi_1(t): X$ or +$\pi_2(t): Y$ representing the two components of $t$. Rather, projecting out a +component of an arbitrary term is achieved by destructuring, so that, say, the +first component of $t$ is given by $\letin{(x, y) = t} x: X$ or, permitting the +use of anonymous variable names, $\letin{(x, \_) = t} x: X$. The latter *is*, of +course, a standard notation in many programming languages. ::: From 199e7ba3438f6c98aeb3d99a5926ba5a6a1124fd Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Fri, 1 May 2026 21:01:46 -0700 Subject: [PATCH 24/81] DOC: Prior art for RFC on internal languages of models. --- rfc/0004.md | 118 ++++++++++++++++++++++++++++++++++---------- rfc/_references.bib | 21 ++++++++ 2 files changed, 113 insertions(+), 26 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index fd85a1c5d..257645110 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -246,14 +246,15 @@ our type theory. - A **double theory** is an almost representable [virtual equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost representable VDC with units and restrictions. -- A **model** of a double theory $\dbl{T}$ is a functor of VDCs $\dbl{T} \to - \SSet$ that preserves restrictions (but not composites!); equivalently, a - model of $\dbl{T}$ is a functor of virtual equipments $\dbl{T} \to \CCat$, - i.e., a *normal* functor of VDCs that preserves restrictions. - -Double theories are usually finitely presented. A type theory for models, like -the present one, should really be parameterized by a *finite presentation* of a -theory rather than by the theory itself. However, we leave the problem of +- A **model** of a double theory $\dbl{T}$ is a functor of VDCs $M: \dbl{T} \to + \SSet$ that preserves restrictions (but not necessarily composites!); + equivalently, a model of $\dbl{T}$ is a functor of virtual equipments $M: + \dbl{T} \to \CCat$, i.e., a *normal* functor of VDCs that preserves + restrictions. + +Double theories are usually finitely presented. A type theory for models, +including this one, should really be parameterized by a *finite presentation* of +a theory rather than by the theory itself. However, we leave the problem of formalizing such presentations to future work. #### Flatness @@ -277,7 +278,7 @@ act on, the cell being applied must be unambiguously determined by its boundary. Flatness ensures that. When a double theory is given by finite presentation, proving flatness can be -nontrivial, amounting to a coherence or unbiasing theorem. +nontrivial, amounting to a coherence theorem. #### Simple and modal theories @@ -286,11 +287,18 @@ object with *at least* the structure postulated above. A **simple double theory** is a double theory with *exactly* that structure. The only other double-categorical doctrine that we need is modal double -theories. The family of list modalities in particular is essential to extract -familiar-looking internal languages for "multiary" categorical structures like -multicategories and monoidal categories. +theories. In particular, the family of list modalities plays an essential role +in extracting familiar-looking internal languages for "multiary" categorical +structures like multicategories and monoidal categories. -**Definition**. **TODO** +**Definition**. + +- A **modal double theory** is a monad $(\dbl{T},T)$ in the 2-category of + virtual equipments whose underlying object $\dbl{T}$ is almost representable. +- Let $S$ be a monad on $\SSet$ in the 2-category of VDCs. A **model** of a + modal double theory $(\dbl{T},T)$ in $(\SSet,S)$ is a strict morphism of + monads $M: (\dbl{T},T) \to (\SSet,S)$ in the 2-category of VDCs that also + preserves restrictions. #### List modalities @@ -923,7 +931,22 @@ substitutes variables with variables, not variables with arbitrary terms. well-formed, i.e., the variables appearing at any level among the contexts $\Gamma \mid u_j : X_j$ are disjoint. -- *Computation rules*: **TODO** +- *Computation rule, monad unit*: + + ```tikz + \[ + \begin{prooftree} + \hypo{\dbl{T} \vDash (P: \cal{X} \proto \cal{Y})} + \hypo{\Gamma \mid u: X \vdash_P t: Y} + \infer2{\Gamma \mid [u]: [X] \vdash_{\List_{\cat{F}} P} + \eta_{\cal{Y}}(t) = [t]: [Y]} + \end{prooftree} + \] + ``` + +- *Computation rules, monad multiplication*: extending the computation rules for + lists of objects and lists of contexts, there are computation rules governing + the monad multiplication on lists of terms. We omit the details. ## Examples @@ -1691,12 +1714,16 @@ the standard presentation of product types [e.g., @shulman-2016, Figure 2.4] in that, while it has what amounts to the standard introduction rule for product types (pairing), it does not have the standard elimination rules (projections). That is, given a term $t: X \times Y$, there are not terms $\pi_1(t): X$ or -$\pi_2(t): Y$ representing the two components of $t$. Rather, projecting out a -component of an arbitrary term is achieved by destructuring, so that, say, the -first component of $t$ is given by $\letin{(x, y) = t} x: X$ or, permitting the -use of anonymous variable names, $\letin{(x, \_) = t} x: X$. The latter *is*, of -course, a standard notation in many programming languages. +$\pi_2(t): Y$ that post-compose $t$ with projection operations. Rather, +projecting out a component of an arbitrary term $t$ is achieved by +destructuring, so that, say, the first component of $t$ is given by +$\letin{(x, y) = t} x: X$ or, permitting the use of anonymous variable names, +$\letin{(x, \_) = t} x: X$. That said, the latter *is* a standard notation in +many programming languages. In Rust, for example, one writes: +```rust +let (x, _) = t; +``` ::: ## Rationale and alternatives @@ -1738,16 +1765,55 @@ mathematical challenges. This document is, however, solely about textual syntax. ## Prior art -### Split contexts - -**TODO** - -### Two-level type theories - -**TODO** +### Frameworks for substructural types theories + +To my knowledge, the most relevant prior art is Licata-Shulman-Riley's +"fibrational framework for substructural and modal logics" [@licata-2017]. Not +only do both frameworks aim to unify a range of cartesian and substructural type +theories, there are high-level similarities in the technical approaches. The +details diverge sharply, though. + +Both frameworks are parameterized by two-dimensional categorical structures that +determine the rules of a sequent calculus. For LSR, a *cartesian +2-multicategory* (i.e., a category-enriched cartesian multicategory) plays the +role that a *modal double theory* does here. Moreover, a *locally discrete +fibration* over a cartesian 2-multicategory plays the role that a *model* of a +modal double theory does here. The latter analogy can be made tighter by +recalling that a $\SSet$-valued lax double functor is equivalent to a *discrete +double fibration* [@lambert-2021], and presumably a similar correspondence holds +for $\SSet$-valued functors of VDCs. + +A difference in philosophy between the two accounts is that LSR start from the +standard context structure of cartesian logic (i.e., the internal language of a +cartesian multicategory) and then pass to substructural logics by restricting +the use of such contexts. In contrast, we start from the trivial context +structure of a unary type theory and then move up hierarchy of substructural +logics using different versions of the list double monad on $\SSet$. An +advantage of LSR is that it hews much closer to how contexts are typically +treated in type theory. An advantage of this account is that it avoids +privileging multicategories over other categorical structures, at least not more +than pointful notation does intrinsically. + +### Split contexts and two-level type theories + +Our contexts $\Gamma \mid u: X$ are an example of a [split +context](https://ncatlab.org/nlab/show/split+context) $\Gamma \mid \Delta$, +where the judgments in $\Delta$ can depend on those in $\Gamma$ but not the +other way around. In a similar spirit, our type theory can be seen as a +[two-level type theory](https://ncatlab.org/nlab/show/two-level+type+theory) +(2LTT), and we have borrowed the terms "outer type theory" and "inner type +theory" from this area. That said, the specific work on split contexts or 2LTT +that we know of seem to have very different goals and technical execution. For +instance, 2LTT has been focused on embedding homotopy type theory in Martin-Löf +type theory. We will not attempt to survey this literature here, but see the +linked nLab pages and references therein. ## Future possibilities ### Meta-theoretic properties **TODO**: Flatness, soundness, completeness, cut admissibility + +### Validation against more theories + +**TODO** diff --git a/rfc/_references.bib b/rfc/_references.bib index 2aad26a6e..3ee0441bb 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -89,6 +89,18 @@ @article{koudenburg-2020 eprinttype = {arXiv} } +@article{lambert-2021, + title = {Discrete double fibrations}, + author = {Lambert, Michael}, + journal = {Theory and Applications of Categories}, + volume = {37}, + number = {22}, + pages = {671--708}, + year = {2021}, + eprinttype = {arXiv}, + eprint = {2101.06734} +} + @article{lambert-patterson-2024, author = {Lambert, Michael and Patterson, Evan}, title = {Representing knowledge and querying data using double-functorial semantics}, @@ -119,6 +131,15 @@ @article{libkind-myers-2025 eprinttype = {arXiv} } +@inproceedings{licata-2017, + title = {A fibrational framework for substructural and modal logics}, + author = {Licata, Daniel R. and Shulman, Michael and Riley, Mitchell}, + booktitle = {2nd International Conference on Formal Structures for Computation and Deduction (FSCD 2017)}, + pages = {25:1--25:22}, + year = {2017}, + doi = {10.4230/LIPIcs.FSCD.2017.25} +} + @article{lynch-et-al-2024, author = {Lynch, Owen and Brown, Kris and Fairbanks, James and Patterson, Evan}, title = {GATlab: Modeling and Programming with Generalized Algebraic Theories}, From 723195b76afbd7aa402eb7618dfe77861a584fab Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sat, 2 May 2026 18:09:19 -0700 Subject: [PATCH 25/81] DOC: Example of internal language of a promonad. --- rfc/0004.md | 161 ++++++++++++++++++++++++++++++++++---------- rfc/_references.bib | 9 +++ 2 files changed, 135 insertions(+), 35 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 257645110..399be2036 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -19,7 +19,7 @@ This RFC is tracked in Any pragmatic tool for programming inside categorical structures will allow morphisms to be denoted using variables and function applications, as in -conventional programming languages. In the jargon, such a notation involving +conventional programming languages. In the jargon, such a notation based on variables is called the *internal language* of the categorical structure. This RFC seeks a uniform method to construct the internal languages of different categorical structures, suitable for implementation in CatColab. The technical @@ -213,8 +213,8 @@ constructing lists of morphism terms, but not lists of morphism generators. So, a major difference with the original DoubleTT is that point-free operations on morphisms like $f \cdot g$ and $[f, g]$ are not part of the type theory. -We turn now to the mathematical specification, but the reader may find it -helpful peruse the [examples](#examples) before or along with the spec. +We turn to the mathematical specification, but the reader may find it helpful +peruse the [examples](#examples) before or along with the spec. ## Mathematical specification @@ -274,8 +274,8 @@ objects, and also on variables and terms, which represent elements of types. For example, a product operation in the double theory should act on types to form product types and on terms to form pairs. But in pointful notation, types and elements are all there is! As there is nothing else in the syntax for cells to -act on, the cell being applied must be unambiguously determined by its boundary. -Flatness ensures that. +act on, any cell being applied must be unambiguously determined by its boundary. +That's exactly what flatness says. When a double theory is given by finite presentation, proving flatness can be nontrivial, amounting to a coherence theorem. @@ -950,16 +950,7 @@ substitutes variables with variables, not variables with arbitrary terms. ## Examples -### Unary languages - -\newcommand{\Entity}{\mathsf{Entity}} -\newcommand{\AttrType}{\mathsf{AttrType}} -\newcommand{\Mapping}{\mathrm{Mapping}} -\newcommand{\Attr}{\mathrm{Attr}} - -\newcommand{\Person}{\mathrm{Person}} -\newcommand{\Dog}{\mathrm{Dog}} -\newcommand{\String}{\mathrm{String}} +### Categories Simplest among type theories are the *unary* ones---so simple, in fact, that they are rarely considered by type theorists outside of pedagogy. Yet from the @@ -977,9 +968,20 @@ As a first example, taking the double theory to be the point, i.e., to be freely generated by one object, recovers the internal language a bare category [@shulman-2016, Section 1.2]. -For an example that's a bit more interesting, consider the internal language of -a database schema, as axiomatized in idealized form by the walking proarrow, -i.e., by the freely generated double theory +### Database schemas + +\newcommand{\Entity}{\mathsf{Entity}} +\newcommand{\AttrType}{\mathsf{AttrType}} +\newcommand{\Mapping}{\mathrm{Mapping}} +\newcommand{\Attr}{\mathrm{Attr}} + +\newcommand{\Person}{\mathrm{Person}} +\newcommand{\Dog}{\mathrm{Dog}} +\newcommand{\String}{\mathrm{String}} + +For a unary type theory that's a bit more interesting, consider the internal +language of a database schema, axiomatized in idealized form by the walking +proarrow, i.e., by the freely generated double theory $$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ @@ -1000,7 +1002,7 @@ $$ \end{aligned} $$ -In the inner context $\Gamma \mid p: \Person$, we can derive the terms: +In the inner context $\Gamma \mid p: \Person$, we can then derive the terms: 1. $\Gamma \mid d: \Dog \vdash_{\Mapping} d: \Dog$ 2. $\Gamma \mid d: \Dog \vdash_{\Mapping} \text{owner}(d): \Person$ @@ -1010,6 +1012,73 @@ where step 1 uses the identity rule and steps 2 and 3 use the post-composition rule. The final term denotes the morphism that maps a dog to the first name of its owner. +### Promonads + +A [promonad](https://ncatlab.org/nlab/show/promonad) is a categorical structure +with two kinds of morphisms, one of which (sometimes called **tight**) can be +cast to the other (called **loose**). For example, a monad or a comonad on a +category $\cat{C}$ gives rise to a promonad whose tight morphisms are the +morphisms of $\cat{C}$ and loose morphisms are the Kleisli or co-Kleisli +morphisms. As an example of the example, there is a promonad whose objects are +measurable spaces, tight morphisms are measurable maps, and loose morphisms are +Markov kernels. + +Promonads are axiomatized by the simple double theory of +[promonads](https://next.catcolab.org/math/dct-0004.xml) [see also the blog post +@patterson-promonads-2024]. The theory is generated by an object $\cal{X}$, a +proarrow $P: \cal{X} \proto \cal{X}$, and a globular cell, the **unit** + +```tikz +% https://q.uiver.app/#q=WzAsNCxbMCwwLCJcXGNhbHtYfSJdLFsxLDAsIlxcY2Fse1h9Il0sWzAsMSwiXFxjYWx7WH0iXSxbMSwxLCJcXGNhbHtYfSJdLFswLDEsIlxcSG9tX3tcXGNhbHtYfX0iLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMCwyLCIiLDIseyJsZXZlbCI6Miwic3R5bGUiOnsiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dLFsxLDMsIiIsMCx7ImxldmVsIjoyLCJzdHlsZSI6eyJoZWFkIjp7Im5hbWUiOiJub25lIn19fV0sWzIsMywiUCIsMix7InN0eWxlIjp7ImJvZHkiOnsibmFtZSI6ImJhcnJlZCJ9fX1dLFs0LDcsIlxcdGhldGEiLDEseyJzaG9ydGVuIjp7InNvdXJjZSI6MjAsInRhcmdldCI6MjB9LCJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJub25lIn0sImhlYWQiOnsibmFtZSI6Im5vbmUifX19XV0= +\[\begin{tikzcd} + {\cal{X}} & {\cal{X}} \\ + {\cal{X}} & {\cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "{\Hom_{\cal{X}}}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] + \arrow[equals, from=1-1, to=2-1] + \arrow[equals, from=1-2, to=2-2] + \arrow[""{name=1, anchor=center, inner sep=0}, "P"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-2] + \arrow["\theta"{description}, draw=none, from=0, to=1] +\end{tikzcd}\] +``` + +such that the composite $P \odot P = P$ exists and subject to a couple +equations, the effect of which is to make the theory flat. + +In the internal language of a promonad, we can build unary terms while tracking +whether the morphism denoted is tight or loose. Taking the outer context + +$$ +\Gamma \coloneqq [ + X: \Ob_{\cal{X}},\ Y: \Ob_{\cal{X}},\ Z: \Ob_{\cal{X}},\ + f: \Hom_{\cal{X}}(X,Y),\ g: P(Y,Z),\ h: \Hom_{\cal{X}}(Z,X) +], +$$ + +we can derive the terms + +1. $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} x: X$ +2. $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} f(x): Y$ +3. $\Gamma \mid x: X \vdash_P g(f(x)): Z$ +3. $\Gamma \mid x: X \vdash_P h(g(f(x))): X$ + +by applying the identity rule and then repeatedly post-composing. + +We can also invoke the operation application rule with the cell $\theta$ to cast +tight morphisms to loose. For example: + +$$ +\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} f(x): Y +\qquad\leadsto\qquad +\Gamma \mid x: X \vdash_P f(x): Y. +$$ + +Then post-composing with $g$, using the composite $P \odot P = P$, we derive the +term $\Gamma \mid x: X \vdash_P g(f(x)): Z$ again but by a different means. We +see now why flatness of the double theory is important: the axioms of a promonad +ensure that the two derivations yield morphisms that are equal in the intended +categorical semantics. We will return to this point after examining the internal +language of multicategories. + ### Multicategories Perhaps the simplest multiary language is the internal language of a (planar) @@ -1135,8 +1204,8 @@ $$ \Gamma \mid [x: X, y: X, z: X] \vdash m[m[x,y],z]: X $$ -but we caution again that in the present type theory, this expression must be -understood only as an abbreviation for the previous one. +but we caution again that in our type theory, this expression can only be +understood as an abbreviation for the previous one. ::: {.callout-warning} #### Warning: Non-uniqueness of derivations @@ -1145,9 +1214,9 @@ There was another, slightly longer way we could have derived the term $m[x,y]$, parallel to how we derived the final term $m[m[x,y],z]$. Namely, after introducing the variables $x$ and $y$, apply the restriction rule to obtain terms $\Gamma \mid [v]: [X] \vdash_P v: X$ for variables $v = x,y$, then form -the list $\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List P} [x,y]: [X,X]$, and -finally proceed as before to build the term $\Gamma \mid [x,y]: [X,X] \vdash_P -m[x,y] \vdash X$. +the list $\Gamma \mid [[x],[y]]: [[X],[X]] \vdash_{\List P} [x,y]: [X,X]$ and +proceed as before to build the term $\Gamma \mid [x,y]: [X,X] \vdash_P m[x,y] +\vdash X$. By the theory's compatibility axiom for the left action of $P$ (contributing to the theory's flatness), both derivations denote the same multimorphism in any @@ -1157,9 +1226,10 @@ derivations to produce identical terms. Turning this around, we see that terms in this type theory do not have unique derivations. While that's not necessarily a problem, it can be a desideratum of a type theory that any (derivable) term has a unique derivation. For this -particular double theory, we could, at the expense of some ad hocery, achieve -uniqueness of derivations by prohibiting use of $P$'s left and right actions. In -general, though, we should not expect to have unique derivations. +particular double theory, we could achieve uniqueness of derivations by ad hocly +prohibiting use of $P$'s left and right actions. The same trick would work for +the theory of promonads. In general, though, we should not expect to have unique +derivations. ::: @@ -1796,23 +1866,44 @@ than pointful notation does intrinsically. ### Split contexts and two-level type theories -Our contexts $\Gamma \mid u: X$ are an example of a [split +Our contexts $\Gamma \mid u: X$ are a kind of [split context](https://ncatlab.org/nlab/show/split+context) $\Gamma \mid \Delta$, where the judgments in $\Delta$ can depend on those in $\Gamma$ but not the other way around. In a similar spirit, our type theory can be seen as a [two-level type theory](https://ncatlab.org/nlab/show/two-level+type+theory) (2LTT), and we have borrowed the terms "outer type theory" and "inner type theory" from this area. That said, the specific work on split contexts or 2LTT -that we know of seem to have very different goals and technical execution. For -instance, 2LTT has been focused on embedding homotopy type theory in Martin-Löf -type theory. We will not attempt to survey this literature here, but see the -linked nLab pages and references therein. +that we know of has very different goals and technical execution. Work on 2LTT, +for instance, has focused on embedding homotopy type theory in Martin-Löf type +theory. We will not attempt to survey this literature here, but see the linked +nLab pages and references therein. ## Future possibilities -### Meta-theoretic properties - -**TODO**: Flatness, soundness, completeness, cut admissibility +### Meta-theoretic results + +We have a presented a type theory and demonstrated by example that it can +reconstruct the internal languages of a range of categorical structures. +However, we have proved almost nothing about it. + +First, the type theory assumes that the parameterizing double theory is flat. +For many simple theories, like those of database schemas or promonads, flatness +is obvious, but it's generally nontrivial to see that a double theory given by +finite presentation is flat. For example, flatness of the theory of generalized +monoidal categories amounts to a coherence theorem about the associator and +unitor cells. These and other flatness results should be proved. + +As for the type theory itself, at minimum, it should be proved that the type +theory is **sound** for models of any fixed double theory. That is, every +context $\Gamma \mid u: X$ should denote a well-defined object in the model +generated by $\Gamma$, and every term $\Gamma \mid u: X \vdash_P t: Y$ should +denote a well-defined morphism in that model. The type should also be +**complete** in the sense that every morphism in the model generated by $\Gamma$ +is denoted by some term. Beyond this, it should be possible to establish the +expected meta-theoretic properties of particular internal languages. For +example, the internal languages of planar, symmetric, and cartesian +multicategories (omitting the let binding rule) should make the cut rule +admissible. ### Validation against more theories diff --git a/rfc/_references.bib b/rfc/_references.bib index 3ee0441bb..e3e0cc0dc 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -162,6 +162,15 @@ @inproceedings{patterson-et-al-2020 eprint = {2101.12046} } +@online{patterson-promonads-2024, + title={Algebras are promonads}, + author={Patterson, Evan}, + year={2024}, + month={1}, + organization={Topos Institute}, + url={https://topos.institute/blog/2024-01-29-algebras-are-promonads/} +} + @article{pollack-2002, author = {Pollack, Robert}, title = {Dependently Typed Records in Type Theory}, From 9cc6f5745d9d04a91034e12e737e9ee3f363d91f Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sat, 2 May 2026 19:54:31 -0700 Subject: [PATCH 26/81] DOC: Generalize modal double theories to have a mode theory. --- rfc/0001.md | 20 ++--- rfc/0004.md | 205 ++++++++++++++++++++++++++++++-------------- rfc/_references.bib | 30 +++++-- 3 files changed, 177 insertions(+), 78 deletions(-) diff --git a/rfc/0001.md b/rfc/0001.md index 03dae2e12..536413edd 100644 --- a/rfc/0001.md +++ b/rfc/0001.md @@ -411,16 +411,16 @@ this idea. Tim Hosgood has written up essentially the same approach to polynomial ODE systems in the [roadmap](https://github.com/ToposInstitute/CatColab-Roadmap/issues/58). -My paper with Michael Lambert on double ologs [@lambert-patterson-2024] shares ideas -with this RFC and is a useful source of intuition and examples. But there are -notable differences: here we envision semantics in $\SSet$, instead of $\Rel$ as in -the paper, and here we are interested in models given by finite presentation, -instead of explicitly by tabular data as in a database. We also currently have -no plans to implement the compact closed structure from the paper, which remains -poorly understood for weak double categories, let alone for virtual double -categories, and is the subject of ongoing research. The next section describes a -situation where the structure of a compact closed double category would be -useful, however. +My paper with Michael Lambert on double ologs [@lambert-patterson-act-2024] +shares ideas with this RFC and is a useful source of intuition and examples. But +there are notable differences: here we envision semantics in $\SSet$, instead of +$\Rel$ as in the paper, and here we are interested in models given by finite +presentation, instead of explicitly by tabular data as in a database. We also +currently have no plans to implement the compact closed structure from the +paper, which remains poorly understood for weak double categories, let alone for +virtual double categories, and is the subject of ongoing research. The next +section describes a situation where the structure of a compact closed double +category would be useful, however. ## Future possibilities diff --git a/rfc/0004.md b/rfc/0004.md index 399be2036..a931b3fb4 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -213,16 +213,31 @@ constructing lists of morphism terms, but not lists of morphism generators. So, a major difference with the original DoubleTT is that point-free operations on morphisms like $f \cdot g$ and $[f, g]$ are not part of the type theory. -We turn to the mathematical specification, but the reader may find it helpful -peruse the [examples](#examples) before or along with the spec. - ## Mathematical specification +Turning to the mathematical spec, we develop the three levels introduced above +in the logical order: first, double theories; then models of a double theory, +the subject of the outer type theory; and finally morphisms in a model, the +subject of the inner type theory. That's the only sensible way to write the +document. Still, the reader may find it easier to proceed in the opposite way, +starting with the [examples](#examples) and working backward through the three +levels of the spec. + ### Double theories -For the purposes of this RFC, we reformulate a double theory as a structure -interpolating between an arbitrary virtual double category (VDC) and a -*representable* VDC (equivalent to a genuine double category). +This RFC builds on the framework for double-categorical logic studied by the +author and collaborators for some time now. However, needs from the type theory +have led to us subtly reformulate the main definitions, so we present them again +in a self-contained if terse style. + +#### Almost representability + +\newcommand{\VDC}{\mathbf{VDC}} +\newcommand{\arVDC}{\mathbf{VDC}_{\mathrm{ar}}} + +We refine the notion of double theory to interpolate between an arbitrary +virtual double category (VDC) and a *representable* VDC (equivalent to a genuine +double category). **Definition**. A virtual double category is **almost representable** if for every multicell, every subpath of proarrows in its domain has a composite. @@ -241,18 +256,35 @@ binary composition cells; and (3) nullary composition cells, i.e., extension cells for units. This case decomposition will appear directly in the rules of our type theory. -**Definition**. +**Definition**. The **2-category of almost representable VDCs**, denoted +$\arVDC$, has as objects, almost representable VDCs; as morphisms, functors (of +VDCs) that preserve composites whenever they exist; as 2-morphisms, natural +transformations. + +Thus, this 2-category is a locally full sub-2-category of $\VDC$, the 2-category +of VDCs. -- A **double theory** is an almost representable [virtual +#### Simple theories + +The most basic notion of double theory is: + +**Definition** [cf. @lambert-patterson-act-2024, Section 3]. + +- A **simple double theory** is an almost representable [virtual equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost representable VDC with units and restrictions. -- A **model** of a double theory $\dbl{T}$ is a functor of VDCs $M: \dbl{T} \to - \SSet$ that preserves restrictions (but not necessarily composites!); +- A **model** of a simple double theory $\dbl{T}$ is a + functor[^preserving-restrictions] of VDCs $M: \dbl{T} \to \SSet$; equivalently, a model of $\dbl{T}$ is a functor of virtual equipments $M: - \dbl{T} \to \CCat$, i.e., a *normal* functor of VDCs that preserves - restrictions. + \dbl{T} \to \CCat$, i.e., a *normal* functor of VDCs. -Double theories are usually finitely presented. A type theory for models, +[^preserving-restrictions]: + A functor (of VDCs) between virtual equipments automatically preserves + restrictions, a not-so-obvious fact [@cruttwell-shulman-2010, Theorem 7.24]. + On the other hand, such a functor need not preserve any composites, including + units. Both of these properties are intended of a model of a double theory. + +In practice, double theories are finitely presented. A type theory for models, including this one, should really be parameterized by a *finite presentation* of a theory rather than by the theory itself. However, we leave the problem of formalizing such presentations to future work. @@ -280,25 +312,56 @@ That's exactly what flatness says. When a double theory is given by finite presentation, proving flatness can be nontrivial, amounting to a coherence theorem. -#### Simple and modal theories - -In what follows we use the term "double theory" somewhat loosely to refer to any -object with *at least* the structure postulated above. A **simple double -theory** is a double theory with *exactly* that structure. +#### Modal theories -The only other double-categorical doctrine that we need is modal double -theories. In particular, the family of list modalities plays an essential role -in extracting familiar-looking internal languages for "multiary" categorical -structures like multicategories and monoidal categories. +Besides simple double theories, the other double-categorical doctrine that we +will need is modal double theories. In particular, the family of list modalities +is essential to extract familiar-looking internal languages for "multiary" +categorical structures like multicategories and monoidal categories. For the +vast majority of the examples below, the following special case of the +definition suffices. The reader may wish to take this as *the* definition on a +first reading. -**Definition**. +**Definition** (special case). -- A **modal double theory** is a monad $(\dbl{T},T)$ in the 2-category of - virtual equipments whose underlying object $\dbl{T}$ is almost representable. +- A **modal double theory** is a monad $(\dbl{T},T)$ in the 2-category of almost + representable VDCs whose underlying object $\dbl{T}$ is a virtual equipment. - Let $S$ be a monad on $\SSet$ in the 2-category of VDCs. A **model** of a modal double theory $(\dbl{T},T)$ in $(\SSet,S)$ is a strict morphism of - monads $M: (\dbl{T},T) \to (\SSet,S)$ in the 2-category of VDCs that also - preserves restrictions. + monads $M: (\dbl{T},T) \to (\SSet,S)$ in the 2-category of VDCs. + +Occasionally we need a double theory equipped with multiple modalities, possibly +related to each other. Rather than defining a double theory to be equipped with +a single monad, a *mode theory* is used to axiomatize the available modalities, +which could be monads, comonads, or other things besides. + +**Definition** (general). + +- A **mode theory** is a strict monoidal category $\cat{M}$. +- A **modal double theory** with mode theory $\cat{M}$ is a 2-functor $\cal{T}: + \mathbf{B}(\cat{M}) \to \arVDC$ from the delooping of $\cat{M}$ to the + 2-category of almost representable VDCs, such that the image $\dbl{T} + \coloneqq \cal{T}(*)$ of the unique object is a virtual equipment. +- Let $\cal{S}: \mathbf{B}(\cat{M}) \to \VDC$ be a 2-functor such that + $\cal{S}(*) = \SSet$. A **model** of a modal double theory $\cal{T}$ in + $\cal{S}$ is a 2-natural transformation: + +```tikz +% https://q.uiver.app/#q=WzAsMyxbMCwxLCJcXG1hdGhiZntCfShcXG1hdGhzZntNfSkiXSxbMSwwLCJcXG1hdGhiZntWREN9X3tcXG1hdGhybXthcn19Il0sWzIsMSwiXFxtYXRoYmZ7VkRDfSJdLFsxLDIsIiIsMCx7InN0eWxlIjp7InRhaWwiOnsibmFtZSI6Imhvb2siLCJzaWRlIjoidG9wIn19fV0sWzAsMSwiXFxjYWx7VH0iXSxbMCwyLCJcXGNhbHtTfSIsMl0sWzEsNSwiIiwyLHsic2hvcnRlbiI6eyJzb3VyY2UiOjMwLCJ0YXJnZXQiOjMwfX1dXQ== +\[\begin{tikzcd}[column sep=small] + & {\mathbf{VDC}_{\mathrm{ar}}} & \\ + {\mathbf{B}(\mathsf{M})} && {\mathbf{VDC}} + \arrow[hook, from=1-2, to=2-3] + \arrow["{\cal{T}}", from=2-1, to=1-2] + \arrow[""{name=0, anchor=center, inner sep=0}, "{\cal{S}}"', from=2-1, to=2-3] + \arrow[between={0.3}{0.7}, Rightarrow, from=1-2, to=0] +\end{tikzcd}\] +``` + +This definition recovers the previous one by taking the mode theory to be the +walking monoid. Thinking of the mode theory as a 2-category with one object, +this choice corresponds to the [walking +monad](https://ncatlab.org/nlab/show/walking+monad). #### List modalities @@ -307,8 +370,8 @@ monad. Upgrading the list monad on $\Set$ to a double monad on $\SSet$ creates new degrees of freedom in how a list of arrows relates to its lists of sources and targets. -Let $\cat{F}$ be a wide subcategory of the skeleton of $\FinSet$ that is a -*faithful cartesian club* [@shulman-2016, Definition 2.6.3]. The examples of +Let $\cat{F}$ be any wide subcategory of the skeleton of $\FinSet$ that is a +*faithful cartesian club* [@shulman-2016, Definition 2.6.3]. Examples of interest are the subcategories consisting of: - identity functions @@ -419,8 +482,8 @@ original spec. In what follows, let $\dbl{T}$ be a double theory. We emphasize that, in contrast to the original version of DoubleTT, a term of type $P(X,Y)$ in the outer type theory represents a morphism *generator* from $X$ to $Y$. That's why morphism operations (unary cells in $\dbl{T}$) cannot be -applied to terms of type $P(X,Y)$. Meanwhile, an *arbitrary* morphism from $X$ -to $Y$ is represented by a term $u: X \vdash_P t: Y$ in the inner type theory. +applied to terms of type $P(X,Y)$. An *arbitrary* morphism from $X$ to $Y$ is +represented by a term $u: X \vdash_P t: Y$ in the inner type theory. ::: @@ -859,10 +922,10 @@ categories. binding it to the variables in the context $v$. [^variable-substitution]: - A more careful treatment, not on offer here, would define the two - substitution operations, on context and on terms, by induction on their - constructors. Still, it should be intuitively clear what it means to - simultaneously substitute one set of variables for another. + A more careful treatment, not on offer here, would define the two substitution + operations, on context and on terms, by induction on their constructors. + Still, it should be intuitively clear what it means to simultaneously + substitute one set of variables for another. ::: {.callout} #### Remark: Admissibility vs derivability of $\alpha$-equivalence @@ -960,9 +1023,9 @@ internal languages of categories that do not have products, genuine or virtual *simple* double theories.[^simple-type-theories] [^simple-type-theories]: - We regret that our usage of "simple" departs from the type theorist's. All - of the internal languages covered by this RFC are [simple type - theories](https://ncatlab.org/nlab/show/simple+type+theory). + We regret that our usage of "simple" departs from the type theorist's. All of + the internal languages covered by this RFC are [simple type + theories](https://ncatlab.org/nlab/show/simple+type+theory). As a first example, taking the double theory to be the point, i.e., to be freely generated by one object, recovers the internal language a bare category @@ -1600,8 +1663,8 @@ where $\alpha_{X,W,B}$ is a component of the (biased) associator natural isomorphism. [^fritz-example-morphism]: - As it happens, this morphism appears as a string diagram in [@fritz-2020, - Lemma 11.12]. + As it happens, this morphism appears as a string diagram in [@fritz-2020, + Lemma 11.12]. First, form the terms @@ -1779,17 +1842,18 @@ $$ ::: {.callout-tip} #### Remark: Comparison with standard algebraic type theory -It's worth noting tha our internal language for cartesian products differs from -the standard presentation of product types [e.g., @shulman-2016, Figure 2.4] in -that, while it has what amounts to the standard introduction rule for product -types (pairing), it does not have the standard elimination rules (projections). -That is, given a term $t: X \times Y$, there are not terms $\pi_1(t): X$ or -$\pi_2(t): Y$ that post-compose $t$ with projection operations. Rather, -projecting out a component of an arbitrary term $t$ is achieved by -destructuring, so that, say, the first component of $t$ is given by -$\letin{(x, y) = t} x: X$ or, permitting the use of anonymous variable names, -$\letin{(x, \_) = t} x: X$. That said, the latter *is* a standard notation in -many programming languages. In Rust, for example, one writes: +Our internal language for cartesian categories differs from the standard +presentation of product types [e.g., @shulman-2016, Figure 2.4] in that, while +it has the standard introduction rule for product types (pairing), it does not +have the standard elimination rules (projections). That is, given a term $t: X +\times Y$, there are no terms $\pi_1(t): X$ or $\pi_2(t): Y$ constructed by +post-composing $t$ with projection operations. Instead, projecting out a +component of an arbitrary term $t$ is a special case of destructuring, so that, +say, the first component of $t$ is given by $\letin{(x, y) = t} x: X$ or, +permitting the use of anonymous variable names, $\letin{(x, \_) = t} x: X$. + +The latter term is, however, a standard notation in many programming languages. +For example, the Rust syntax is: ```rust let (x, _) = t; @@ -1810,8 +1874,8 @@ From the "semantics first" perspective of CatColab, this approach is not unreasonable. By carefully separating the textual syntax, the data structures for models and morphisms to which the syntax compiles, and the analyses that can be run on those data structures, we could hope to retain many of the benefits of -having a uniform meta-logic, at least outside of the frontend. Indeed, this has -been our intended approach to *graphical* editors for graphs, Petri nets, and so +a uniform meta-logic, at least outside of the frontend. Indeed, this has been +our intended approach to *graphical* editors for graphs, Petri nets, and so forth, whose variety seems to defy systematization. Yet I would still rather see the ad hoc approach to internal languages as a fallback option---at best a pragmatic compromise to the vision of CatColab as an interoperable family of @@ -1820,7 +1884,7 @@ programming languages. ### String diagrams Type theory is one methodology to find canonical forms for morphisms, but it's -not the only one. Among applied category theorists, the usual recipe to produce +not the only one. Among applied category theorists, the usual recipe to find canonical forms for morphisms in monoidal categories is to use *string diagrams* [@selinger-2010] or their combinatorial counterparts, *wiring diagrams* [@patterson-et-al-2020]. Incidentally, string diagrams are a point-free syntax, @@ -1830,8 +1894,8 @@ syntax. String diagrams are not so much an alternative to type-theoretic textual notation as a complement to it. Graphical and textual syntaxes each have their pros and cons, and both should be available in CatColab. Making multiple kinds -of syntax work together seamlessly poses engineering and possibly also -mathematical challenges. This document is, however, solely about textual syntax. +of syntax work together seamlessly poses engineering challenges, if not also +mathematical ones. This document,however, is solely about textual syntax. ## Prior art @@ -1840,7 +1904,7 @@ mathematical challenges. This document is, however, solely about textual syntax. To my knowledge, the most relevant prior art is Licata-Shulman-Riley's "fibrational framework for substructural and modal logics" [@licata-2017]. Not only do both frameworks aim to unify a range of cartesian and substructural type -theories, there are high-level similarities in the technical approaches. The +theories, they share high-level features in their technical approaches. The details diverge sharply, though. Both frameworks are parameterized by two-dimensional categorical structures that @@ -1858,12 +1922,27 @@ standard context structure of cartesian logic (i.e., the internal language of a cartesian multicategory) and then pass to substructural logics by restricting the use of such contexts. In contrast, we start from the trivial context structure of a unary type theory and then move up hierarchy of substructural -logics using different versions of the list double monad on $\SSet$. An +logics by introducing different versions of the list double monad on $\SSet$. An advantage of LSR is that it hews much closer to how contexts are typically treated in type theory. An advantage of this account is that it avoids privileging multicategories over other categorical structures, at least not more than pointful notation does intrinsically. +### Mode theories + +The concept of a *mode theory* has also appeared in work by Licata and Shulman +[-@licata-2015; @licata-2017]. The comparison is easily confused as there is a +level shift between in how mode theories are invoked here and in LSR. What LSR +call the mode theory (a cartesian 2-multicategory) corresponds to a double +theory, as just explained. We use a mode theory to organize the various +modalities available to use in modal double theories and their models, +principally the list double monads and possibly also others not discussed here, +such as the codiscrete modality. To see the difference, in our framework the +mode theory and its intended semantics in $\SSet$ could be fixed for all logics, +and that's exactly what we intend to do in implementation. In contrast, for LSR, +one chooses a different mode theory for each logic of interest, like we do for +the double theory. + ### Split contexts and two-level type theories Our contexts $\Gamma \mid u: X$ are a kind of [split @@ -1872,11 +1951,11 @@ where the judgments in $\Delta$ can depend on those in $\Gamma$ but not the other way around. In a similar spirit, our type theory can be seen as a [two-level type theory](https://ncatlab.org/nlab/show/two-level+type+theory) (2LTT), and we have borrowed the terms "outer type theory" and "inner type -theory" from this area. That said, the specific work on split contexts or 2LTT -that we know of has very different goals and technical execution. Work on 2LTT, -for instance, has focused on embedding homotopy type theory in Martin-Löf type -theory. We will not attempt to survey this literature here, but see the linked -nLab pages and references therein. +theory" from this area. With that said, the specific work on split contexts or +2LTT that we know of has very different goals, leading to very different +mathematics. Work on 2LTT, for instance, has aimed to embed homotopy type theory +in Martin-Löf type theory. We will not attempt to survey this literature here, +but see the linked nLab pages and references therein. ## Future possibilities diff --git a/rfc/_references.bib b/rfc/_references.bib index e3e0cc0dc..26edbe5d7 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -102,15 +102,35 @@ @article{lambert-2021 } @article{lambert-patterson-2024, + title = {Cartesian double theories: A double-categorical framework for categorical doctrines}, author = {Lambert, Michael and Patterson, Evan}, + journal = {Advances in Mathematics}, + volume = {444}, + pages = {109630}, + year = {2024}, + doi = {10.1016/j.aim.2024.109630}, + eprinttype = {arXiv}, + eprint = {2310.05384} +} + +@inproceedings{lambert-patterson-act-2024, title = {Representing knowledge and querying data using double-functorial semantics}, + author = {Lambert, Michael and Patterson, Evan}, + booktitle = {Proceedings of the Seventh International Conference on Applied Category Theory (ACT 2024)}, + pages = {174–-189}, year = {2024}, - journal = {Electronic Proceedings in Theoretical Computer Science}, - volume = {429}, - pages = {174–189}, doi = {10.4204/EPTCS.429.9}, - eprint = {2403.19884}, - eprinttype = {arXiv} + eprinttype = {arXiv}, + eprint = {2403.19884} +} + +@inproceedings{licata-2015, + title = {Adjoint logic with a 2-category of modes}, + author = {Licata, Daniel R. and Shulman, Michael}, + booktitle = {International Symposium on Logical Foundations of Computer Science}, + pages = {219--235}, + year = {2015}, + doi = {10.1007/978-3-319-27683-0_16} } @book{leinster-2004, From e8bf382e1a7cd857a032d271edfb93c9fec277c2 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 3 May 2026 15:17:18 -0700 Subject: [PATCH 27/81] DOC: Example of internal language of a Markov category. --- rfc/0004.md | 96 ++++++++++++++++++++++++++++++++++++++------- rfc/_references.bib | 14 ++++++- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index a931b3fb4..976d85439 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -1842,7 +1842,7 @@ $$ ::: {.callout-tip} #### Remark: Comparison with standard algebraic type theory -Our internal language for cartesian categories differs from the standard +The internal language for cartesian categories here differs from the standard presentation of product types [e.g., @shulman-2016, Figure 2.4] in that, while it has the standard introduction rule for product types (pairing), it does not have the standard elimination rules (projections). That is, given a term $t: X @@ -1852,14 +1852,81 @@ component of an arbitrary term $t$ is a special case of destructuring, so that, say, the first component of $t$ is given by $\letin{(x, y) = t} x: X$ or, permitting the use of anonymous variable names, $\letin{(x, \_) = t} x: X$. -The latter term is, however, a standard notation in many programming languages. -For example, the Rust syntax is: +On the other hand, the latter term is a notation supported by many programming +languages. For example, the Rust syntax is: ```rust let (x, _) = t; ``` ::: +### Markov categories + +\newcommand{\Determ}{\mathsf{Determ}} +\newcommand{\Stoch}{\mathsf{Stoch}} + +\newcommand{\Predictors}{\mathrm{Predictors}} +\newcommand{\Coefficients}{\mathrm{Coefficients}} +\newcommand{\Dispersion}{\mathrm{Dispersion}} +\newcommand{\Response}{\mathrm{Response}} + +Our final example, the internal language for a version of a [Markov +category](https://ncatlab.org/nlab/show/Markov+category), is the most complex +logic treated here. The modal double theory for [Markov +categories](https://next.catcolab.org/math/dct-000I.xml), requiring a mode +theory more elaborate than that of a single monad, axiomatizes a Markov +category[^markov-category-definition] effectively as + +- a cartesian category (of **deterministic** maps), which maps into +- a [semicartesian + category](https://ncatlab.org/nlab/show/semicartesian%20monoidal%20category) + (of **stochastic** maps), via +- a [monoidal promonad](https://next.catcolab.org/math/dct-000G.xml). + +The double theory therefore combines several features used previously in +isolation. + +[^markov-category-definition]: + Be warned that this definition is not equivalent to, but is slightly more than + general than, the standard definition of a Markov category [@fritz-2020, + Definition 2.1]. It makes the deterministic maps be *structure* rather than + *property*. + +Complexity of the double theory aside, the internal language itself is no more +difficult to use than the preceding ones. To illustrate, we write down a simple +term denoting the sampling distribution of an abstract "surface plus noise" +regression model [@efron-2020]. Such a model has a deterministic component, the +*regression function*, and a stochastic component, the *noise distribution*. +Introducing the suggestive notation $\Determ \coloneqq \Hom_{\cal{X}}$ and +$\Stoch \coloneqq P$, this setup is captured by the context + +$$ +\begin{aligned} +\Gamma \coloneqq [\ + & \Predictors: \Ob_{\cal{X}}, \\ + & \Response: \Ob_{\cal{X}}, \\ + & \Coefficients: \Ob_{\cal{X}}, \\ + & \Dispersion: \Ob_{\cal{X}}, \\ + & f: \Determ(\Predictors \times \Coefficients,\, \Response) \\ + & \mathrm{noise}: \Stoch(\Response \otimes \Dispersion, \Response)\ +]. +\end{aligned} +$$ + +The sampling distribution, as a function of both the predicators and the +parameters, is then: + +$$ +\begin{aligned} +&\Gamma \mid (x, \beta, \phi): + \Predictors \otimes \Coefficients \otimes \Dispersion + \vdash_{\Stoch} \\ + & \qquad \text{noise}(f(x, \beta), \phi): \Response. +\end{aligned} +$$ + +Deriving this term is left as an exercise for the reader. + ## Rationale and alternatives ### Bespoke internal languages @@ -1931,17 +1998,18 @@ than pointful notation does intrinsically. ### Mode theories The concept of a *mode theory* has also appeared in work by Licata and Shulman -[-@licata-2015; @licata-2017]. The comparison is easily confused as there is a -level shift between in how mode theories are invoked here and in LSR. What LSR -call the mode theory (a cartesian 2-multicategory) corresponds to a double -theory, as just explained. We use a mode theory to organize the various -modalities available to use in modal double theories and their models, -principally the list double monads and possibly also others not discussed here, -such as the codiscrete modality. To see the difference, in our framework the -mode theory and its intended semantics in $\SSet$ could be fixed for all logics, -and that's exactly what we intend to do in implementation. In contrast, for LSR, -one chooses a different mode theory for each logic of interest, like we do for -the double theory. +[-@licata-2015; @licata-2017]. Moreover, we use essentially the same definition +of a mode theory as in the first reference (a 2-category). Yet the high-level +comparison is easily confused as there is a level shift in how mode theories are +invoked here and in LSR. What LSR call the mode theory (a cartesian +2-multicategory) corresponds to a double theory, as just explained. We use a +mode theory to organize the various modalities available in modal double +theories and their models, principally the list double monads but possibly also +others not discussed here, such as the codiscrete modality. To see the +difference, in our framework the mode theory and its intended semantics in +$\SSet$ could be fixed for all logics, and that's exactly what we intend to do +in implementation. In contrast, for LSR, one chooses a different mode theory for +each logic of interest, like we choose a double theory. ### Split contexts and two-level type theories diff --git a/rfc/_references.bib b/rfc/_references.bib index 26edbe5d7..c5c924c82 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -36,8 +36,8 @@ @article{cruttwell-shulman-2010 volume = {24}, number = {21}, pages = {580–655}, - eprint = {0907.2460}, - eprinttype = {arXiv} + eprinttype = {arXiv}, + eprint = {0907.2460} } @phdthesis{dreyer-2005, @@ -48,6 +48,16 @@ @phdthesis{dreyer-2005 url = {https://csd.cmu.edu/sites/default/files/phd-thesis/CMU-CS-05-131.pdf} } +@article{efron-2020, + title = {Prediction, estimation, and attribution}, + author = {Efron, Bradley}, + journal = {International Statistical Review}, + volume = {88}, + pages = {S28--S59}, + year = {2020}, + doi = {10.1080/01621459.2020.1762613} +} + @article{fritz-2020, title = {A synthetic approach to Markov kernels, conditional independence and theorems on sufficient statistics}, author = {Fritz, Tobias}, From 59af8fbf432edaa414dc12a1d53d8280c7f3fec4 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Sun, 3 May 2026 16:52:13 -0700 Subject: [PATCH 28/81] DOC: Future work: more double theories inducing internal languages. --- rfc/0004.md | 65 ++++++++++++++++++++++++++++++++++----------- rfc/_references.bib | 27 ++++++++++++------- 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 976d85439..c08cedc13 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -268,7 +268,7 @@ of VDCs. The most basic notion of double theory is: -**Definition** [cf. @lambert-patterson-act-2024, Section 3]. +**Definition** [cf. @lambert-patterson-2024, Section 3]. - A **simple double theory** is an almost representable [virtual equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost @@ -1972,7 +1972,7 @@ To my knowledge, the most relevant prior art is Licata-Shulman-Riley's "fibrational framework for substructural and modal logics" [@licata-2017]. Not only do both frameworks aim to unify a range of cartesian and substructural type theories, they share high-level features in their technical approaches. The -details diverge sharply, though. +details diverge significantly, though. Both frameworks are parameterized by two-dimensional categorical structures that determine the rules of a sequent calculus. For LSR, a *cartesian @@ -1986,14 +1986,17 @@ for $\SSet$-valued functors of VDCs. A difference in philosophy between the two accounts is that LSR start from the standard context structure of cartesian logic (i.e., the internal language of a -cartesian multicategory) and then pass to substructural logics by restricting -the use of such contexts. In contrast, we start from the trivial context -structure of a unary type theory and then move up hierarchy of substructural -logics by introducing different versions of the list double monad on $\SSet$. An +cartesian multicategory) and then pass to substructural logics by tracking the +use of variables through annotations on the turnstile. In contrast, we start +from the trivial context structure of unary type theory and move up the +hierarchy of substructural logics using different versions of the list double +monad on $\SSet$. The only annotation on the turnstile is the morphism type. An advantage of LSR is that it hews much closer to how contexts are typically -treated in type theory. An advantage of this account is that it avoids -privileging multicategories over other categorical structures, at least not more -than pointful notation does intrinsically. +treated in type theory. An advantage of this account is that the bookkeeping for +variable use shows up only in the *derivations* of terms, rather the terms +themselves. It also avoids privileging cartesian multicategories over other +categorical structures, at least not more than pointful notation does +intrinsically. ### Mode theories @@ -2034,11 +2037,11 @@ reconstruct the internal languages of a range of categorical structures. However, we have proved almost nothing about it. First, the type theory assumes that the parameterizing double theory is flat. -For many simple theories, like those of database schemas or promonads, flatness -is obvious, but it's generally nontrivial to see that a double theory given by -finite presentation is flat. For example, flatness of the theory of generalized -monoidal categories amounts to a coherence theorem about the associator and -unitor cells. These and other flatness results should be proved. +For the simpler double theories, like those of database schemas or promonads, +flatness is often obvious, but it's generally nontrivial to see that a double +theory given by finite presentation is flat. For example, flatness of the theory +of generalized monoidal categories amounts to a coherence theorem about the +associator and unitor cells. These and other flatness results should be proved. As for the type theory itself, at minimum, it should be proved that the type theory is **sound** for models of any fixed double theory. That is, every @@ -2054,4 +2057,36 @@ admissible. ### Validation against more theories -**TODO** +We are far from exhausting the categorical structures within reach of this +formalism. Here a few examples going beyond those presented here but still +fitting neatly in the framework. + +**Representable multicategories**. Arguably the categorical structure most +faithful to programming syntax is neither a multicategory nor a monoidal +category but a *representable* multicategory.[^representable-multicategories] In +most programming languages, functions have multiple inputs and a single output, +*and* there are product types. That's exactly what you get in a representable +multicategory. It is straightforward to axiomatize representable (generalized) +multicategories as a modal double theory extending that of generalized +multicategories; see the [math +docs](https://next.catcolab.org/math/dct-000O.xml) and references therein. + +[^representable-multicategories]: + That representable multicategories are equivalent to monoidal categories makes + them indistinguishable to the category theorist, but such a semantic + equivalence does not imply that their internal languages are equally + ergonomic. + +**Adjoint logics**. All of the double theories considered here have a single +generating object, $\cal{X}$. This restriction is by no means fundamental. We +can just as well consider categorical structures involving multiple categories +related by functors or adjunctions. Indeed, monads and adjunctions are both +axiomatized as 2-theories, hence also as simple double theories +[@lambert-patterson-2024, Section 3]. As a specific example, the categorical +semantics of mixed [linear/non-linear logic +](https://ncatlab.org/nlab/show/linear-non-linear+logic) (LNL) is a cartesian +closed category and a symmetric monoidal closed category, connected by a +symmetric monoidal adjunction [@benton-1994]. Apart from the monoidal +closedness, this structure would be straightfoward to axiomatize as a modal +double theory. It would then be interesting to unwind the type theory for LNL +furnished by this double theory. diff --git a/rfc/_references.bib b/rfc/_references.bib index c5c924c82..759d81edc 100644 --- a/rfc/_references.bib +++ b/rfc/_references.bib @@ -28,6 +28,15 @@ @article{baez-et-al-2023 eprinttype = {arXiv} } +@inproceedings{benton-1994, + title = {A mixed linear and non-linear logic: Proofs, terms and models}, + author = {Benton, P. Nick}, + booktitle = {International Workshop on Computer Science Logic}, + pages = {121--135}, + year = {1994}, + doi = {10.1007/BFb0022251} +} + @article{cruttwell-shulman-2010, author = {Shulman, Michael A. and Cruttwell, G.S.H.}, title = {A unified framework for generalized multicategories}, @@ -134,15 +143,6 @@ @inproceedings{lambert-patterson-act-2024 eprint = {2403.19884} } -@inproceedings{licata-2015, - title = {Adjoint logic with a 2-category of modes}, - author = {Licata, Daniel R. and Shulman, Michael}, - booktitle = {International Symposium on Logical Foundations of Computer Science}, - pages = {219--235}, - year = {2015}, - doi = {10.1007/978-3-319-27683-0_16} -} - @book{leinster-2004, title = {Higher operads, higher categories}, author = {Leinster, Tom}, @@ -161,6 +161,15 @@ @article{libkind-myers-2025 eprinttype = {arXiv} } +@inproceedings{licata-2015, + title = {Adjoint logic with a 2-category of modes}, + author = {Licata, Daniel R. and Shulman, Michael}, + booktitle = {International Symposium on Logical Foundations of Computer Science}, + pages = {219--235}, + year = {2015}, + doi = {10.1007/978-3-319-27683-0_16} +} + @inproceedings{licata-2017, title = {A fibrational framework for substructural and modal logics}, author = {Licata, Daniel R. and Shulman, Michael and Riley, Mitchell}, From e0daa732712e2daa245404d93dd98ff2d56fa6b4 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 11 May 2026 22:11:55 -0700 Subject: [PATCH 29/81] DOC: Assume that almost representable double theories are strict. --- rfc/0004.md | 76 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index c08cedc13..3792e5f0b 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -215,13 +215,13 @@ morphisms like $f \cdot g$ and $[f, g]$ are not part of the type theory. ## Mathematical specification -Turning to the mathematical spec, we develop the three levels introduced above -in the logical order: first, double theories; then models of a double theory, -the subject of the outer type theory; and finally morphisms in a model, the -subject of the inner type theory. That's the only sensible way to write the -document. Still, the reader may find it easier to proceed in the opposite way, -starting with the [examples](#examples) and working backward through the three -levels of the spec. +Turning to the mathematical spec, we present its three levels in the logical +order: first, double theories; then models of a double theory, the subject of +the outer type theory; and finally morphisms in a model, the subject of the +inner type theory. That is the only sensible way to write the document. Still, +the reader may find it easier to proceed in the opposite order, starting with +the [examples](#examples) and working backward through the three levels of the +spec. ### Double theories @@ -233,7 +233,8 @@ in a self-contained if terse style. #### Almost representability \newcommand{\VDC}{\mathbf{VDC}} -\newcommand{\arVDC}{\mathbf{VDC}_{\mathrm{ar}}} +\newcommand{\arVDC}{\mathbf{arVDC}} +\newcommand{\arVDCstrict}{\mathbf{arVDC}_{\mathrm{str}}} We refine the notion of double theory to interpolate between an arbitrary virtual double category (VDC) and a *representable* VDC (equivalent to a genuine @@ -256,13 +257,35 @@ binary composition cells; and (3) nullary composition cells, i.e., extension cells for units. This case decomposition will appear directly in the rules of our type theory. -**Definition**. The **2-category of almost representable VDCs**, denoted -$\arVDC$, has as objects, almost representable VDCs; as morphisms, functors (of -VDCs) that preserve composites whenever they exist; as 2-morphisms, natural -transformations. +To avoid needless complications with coherence, we will also ask that our double +theories be as strict as possible.[^strictness] -Thus, this 2-category is a locally full sub-2-category of $\VDC$, the 2-category -of VDCs. +[^strictness]: + This is consistent with @lambert-patterson-2024, who, while using genuine + rather than virtual double categories for double theories, still assume them + to be strict. Thanks to tslil clingman for noticing the complications that + arise in the type theory when we do not assume strictness. + +**Definition**. An almost representable VDC is **strict** when it is equipped +with a strictly associative and unital choice of composites, for all subpaths of +proarrows in the domain of a multicell. + +Whenever we write loose composites or identities in a double theory, we will +mean the chosen ones. + +**Definition**. + +- The **2-category of almost representable VDCs**, denoted $\arVDC$, has as + objects, almost representable VDCs; as morphisms, functors (of VDCs) that + preserve composites whenever they exist; and as 2-morphisms, natural + transformations. +- The **2-category of strict almost representable VDCs**, denoted + $\arVDCstrict$, has as objects, strict almost representable VDCs; as + morphisms, functors that strictly preserve the chosen composites; and as + 2-morphisms, natural transformations. + +Notice that $\arVDC$ is a locally full sub-2-category of $\VDC$, the 2-category +of VDCs, whereas $\arVDCstrict$ merely has a forgetful functor to $\VDC$. #### Simple theories @@ -270,9 +293,9 @@ The most basic notion of double theory is: **Definition** [cf. @lambert-patterson-2024, Section 3]. -- A **simple double theory** is an almost representable [virtual - equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., an almost - representable VDC with units and restrictions. +- A **simple double theory** is a strict almost representable [virtual + equipment](https://ncatlab.org/nlab/show/virtual+equipment), i.e., a strict + almost representable VDC having all units and restrictions. - A **model** of a simple double theory $\dbl{T}$ is a functor[^preserving-restrictions] of VDCs $M: \dbl{T} \to \SSet$; equivalently, a model of $\dbl{T}$ is a functor of virtual equipments $M: @@ -324,34 +347,35 @@ first reading. **Definition** (special case). -- A **modal double theory** is a monad $(\dbl{T},T)$ in the 2-category of almost - representable VDCs whose underlying object $\dbl{T}$ is a virtual equipment. +- A **modal double theory** is a monad $(\dbl{T},T)$ in $\arVDCstrict$, the + 2-category of strict almost representable VDCs, whose underlying object + $\dbl{T}$ is a virtual equipment. - Let $S$ be a monad on $\SSet$ in the 2-category of VDCs. A **model** of a modal double theory $(\dbl{T},T)$ in $(\SSet,S)$ is a strict morphism of monads $M: (\dbl{T},T) \to (\SSet,S)$ in the 2-category of VDCs. Occasionally we need a double theory equipped with multiple modalities, possibly -related to each other. Rather than defining a double theory to be equipped with -a single monad, a *mode theory* is used to axiomatize the available modalities, +related to each other. Generalizing the idea of a double theory equipped with a +single monad, a *mode theory* is used to axiomatize the available modalities, which could be monads, comonads, or other things besides. **Definition** (general). - A **mode theory** is a strict monoidal category $\cat{M}$. - A **modal double theory** with mode theory $\cat{M}$ is a 2-functor $\cal{T}: - \mathbf{B}(\cat{M}) \to \arVDC$ from the delooping of $\cat{M}$ to the - 2-category of almost representable VDCs, such that the image $\dbl{T} + \mathbf{B}(\cat{M}) \to \arVDCstrict$ from the delooping of $\cat{M}$ to the + 2-category of strict almost representable VDCs, such that the image $\dbl{T} \coloneqq \cal{T}(*)$ of the unique object is a virtual equipment. - Let $\cal{S}: \mathbf{B}(\cat{M}) \to \VDC$ be a 2-functor such that $\cal{S}(*) = \SSet$. A **model** of a modal double theory $\cal{T}$ in $\cal{S}$ is a 2-natural transformation: ```tikz -% https://q.uiver.app/#q=WzAsMyxbMCwxLCJcXG1hdGhiZntCfShcXG1hdGhzZntNfSkiXSxbMSwwLCJcXG1hdGhiZntWREN9X3tcXG1hdGhybXthcn19Il0sWzIsMSwiXFxtYXRoYmZ7VkRDfSJdLFsxLDIsIiIsMCx7InN0eWxlIjp7InRhaWwiOnsibmFtZSI6Imhvb2siLCJzaWRlIjoidG9wIn19fV0sWzAsMSwiXFxjYWx7VH0iXSxbMCwyLCJcXGNhbHtTfSIsMl0sWzEsNSwiIiwyLHsic2hvcnRlbiI6eyJzb3VyY2UiOjMwLCJ0YXJnZXQiOjMwfX1dXQ== +% https://q.uiver.app/#q=WzAsMyxbMCwxLCJcXG1hdGhiZntCfShcXG1hdGhzZntNfSkiXSxbMSwwLCJcXG1hdGhiZnthclZEQ31fe1xcbWF0aHJte3N0cn19Il0sWzIsMSwiXFxtYXRoYmZ7VkRDfSJdLFswLDEsIlxcY2Fse1R9Il0sWzAsMiwiXFxjYWx7U30iLDJdLFsxLDJdLFsxLDQsIiIsMix7InNob3J0ZW4iOnsic291cmNlIjozMCwidGFyZ2V0IjozMH19XV0= \[\begin{tikzcd}[column sep=small] - & {\mathbf{VDC}_{\mathrm{ar}}} & \\ + & {\mathbf{arVDC}_{\mathrm{str}}} & \\ {\mathbf{B}(\mathsf{M})} && {\mathbf{VDC}} - \arrow[hook, from=1-2, to=2-3] + \arrow[from=1-2, to=2-3] \arrow["{\cal{T}}", from=2-1, to=1-2] \arrow[""{name=0, anchor=center, inner sep=0}, "{\cal{S}}"', from=2-1, to=2-3] \arrow[between={0.3}{0.7}, Rightarrow, from=1-2, to=0] From c5fef478d36863af3d0ac07f7fefacf0e231180d Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 11 May 2026 22:36:02 -0700 Subject: [PATCH 30/81] DOC: Example of using internal language with record types. --- rfc/0004.md | 87 +++++++++++++++++++++++++++++++++-------- rfc/_macros.qmd | 8 ++-- rfc/_templates/tikz.tex | 8 ++-- 3 files changed, 77 insertions(+), 26 deletions(-) diff --git a/rfc/0004.md b/rfc/0004.md index 3792e5f0b..6a89f87b4 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -4,10 +4,16 @@ title: | author: Evan Patterson date: 2026-04-10 bibliography: _references.bib +tikz-preamble: | + \newcommand{\eqtype}{\mathsf{eq}} + \newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} --- {{< include _macros.qmd >}} +\newcommand{\eqtype}{\mathsf{eq}} +\newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} + ::: {.callout-note} ### RFC status @@ -285,7 +291,8 @@ mean the chosen ones. 2-morphisms, natural transformations. Notice that $\arVDC$ is a locally full sub-2-category of $\VDC$, the 2-category -of VDCs, whereas $\arVDCstrict$ merely has a forgetful functor to $\VDC$. +of VDCs, whereas $\arVDCstrict$ merely has a locally fully faithful 2-functor to +$\VDC$. #### Simple theories @@ -394,15 +401,11 @@ monad. Upgrading the list monad on $\Set$ to a double monad on $\SSet$ creates new degrees of freedom in how a list of arrows relates to its lists of sources and targets. -Let $\cat{F}$ be any wide subcategory of the skeleton of $\FinSet$ that is a -*faithful cartesian club* [@shulman-2016, Definition 2.6.3]. Examples of -interest are the subcategories consisting of: - -- identity functions -- bijections -- injections -- surjections -- arbitrary functions +One such degree of freedom is captured by the following data. Let $\cat{F}$ be +any wide subcategory of the skeleton of $\FinSet$ that is a *faithful cartesian +club* [@shulman-2016, Definition 2.6.3]. Examples of interest are the +subcategories consisting of identity functions, bijections, injections, +surjections, or all functions. There is a double monad $\List_{\cat{F}}: \SSet \to \SSet$ that is the ordinary list monad in degree zero, @@ -419,10 +422,10 @@ consist of a function $\sigma: [m] \to [n]$ together with a list $\vec{p} = $$ p_i: a_{\sigma(i)} \proto b_i, \qquad i = 1,\dots,m. $$ -This double monad is a monad in the 2-category of VDCs, functors of VDCs, and -natural transformations, or, equivalently since $\SSet$ is representable, in the -2-category of double categories, lax double functors, and (tight) natural -transformations. For details and generalizations, see the [math +This double monad is more precisely a monad in the 2-category $\VDC$, or, +equivalently since $\SSet$ is representable, in the 2-category of double +categories, lax double functors, and (tight) natural transformations. For +details and generalizations, see the [math docs](https://next.catcolab.org/math/mdt-0007.xml). By convention, rules below that reference the double monad $\List$ without @@ -435,8 +438,8 @@ The outer type theory, a dependent type theory, has the standard judgments for contexts, types in context, and terms in context, as well as for equalities between types and between terms. We are agnostic as to whether substitutions are implicit or explicit. Moreover, the outer type theory has dependent record types -and singleton types, though neither are actually needed for present purposes. -For details, see [RFC-0002](0002.md) and references therein. +and singleton types, though neither feature is actually needed for this RFC. For +details, see [RFC-0002](0002.md) and references therein. We state the special rules for DoubleTT in full, as there are changes from the original spec. In what follows, let $\dbl{T}$ be a double theory. @@ -1410,6 +1413,58 @@ $$ \Gamma \mid [r,x,y]: [R,X,X] \vdash_P (r \cdot x) + (r \cdot y): X. $$ +::: {.callout-tip} +#### Compositional signatures via record types + +Alternatively, we can use record types to compositionally construct the +signature of the theory of modules. Defining the record types + +$$ +\begin{aligned} +\mathtt{SigRig} &\coloneqq \{ + X: \Ob_{\cal{X}},\ + \mathtt{add}: P([X,X], X),\ + \mathtt{zero}: P([], X),\ + \mathtt{mul}: P([X,X], X)\ + \mathtt{one}: P([], X) +\} \\ +\mathtt{SigMod} &\coloneqq \{ + R: \mathtt{SigRig},\ + X: \Ob_{\cal{X}}, + \mathtt{add}: P([X,X], X),\ + \mathtt{zero}: P([], X),\ + \mathtt{mul}: P([R.X, X], X) +\} +\end{aligned} +$$ + +for the signatures of rigs and of modules over rigs, the distributivity axiom +for scalar multiplication becomes + +$$ +\begin{aligned} +[M: \mathtt{SigMod}] \vdash \eqtype( + & [r,x,y]: [M.R.X, M.X, M.X], \\ + & M.\mathtt{mul}[r, M.\mathtt{add}[x, y]], \\ + & M.\mathtt{add}[M.\mathtt{mul}[r, x], M.\mathtt{mul}[r, y]] +). +\end{aligned} +$$ + +Similarly, the associativity axiom for scalar multiplication is + +$$ +\begin{aligned} +[M: \mathtt{SigMod}] \vdash \eqtype( + & [r,s,x]: [M.R.X, M.R.X, M.X], \\ + & M.\mathtt{mul}[M.R.\mathtt{mul}[r, s], x], \\ + & M.\mathtt{mul}[r, M.\mathtt{mul}[s, x]] +). +\end{aligned} +$$ + +::: + ### Comulticategories \newcommand{\concat}{\mathsf{concat}} diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index 16bd7cbf0..ba611574c 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -30,12 +30,10 @@ \newcommand{\proTo}{\mathrel{\mkern3mu\vcenter{\hbox{$\shortmid$}}\mkern-10mu{\Rightarrow}}} -\newcommand{\List}{\mathop{\mathrm{List}}} -\newcommand{\Disc}{\mathop{\mathrm{Disc}}} -\newcommand{\coDisc}{\mathop{\mathrm{coDisc}}} +\newcommand{\List}{\operatorname{List}} +\newcommand{\Disc}{\operatorname{Disc}} +\newcommand{\coDisc}{\operatorname{coDisc}} \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} -\newcommand{\eqtype}{\mathsf{eq}} -\newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} diff --git a/rfc/_templates/tikz.tex b/rfc/_templates/tikz.tex index d6146f6eb..07b31b253 100644 --- a/rfc/_templates/tikz.tex +++ b/rfc/_templates/tikz.tex @@ -40,15 +40,13 @@ \newcommand{\proTo}{\mathrel{\mkern3mu\vcenter{\hbox{$\shortmid$}}\mkern-10mu{\Rightarrow}}} % Monads -\newcommand{\List}{\mathop{\mathrm{List}}} -\newcommand{\Disc}{\mathop{\mathrm{Disc}}} -\newcommand{\coDisc}{\mathop{\mathrm{coDisc}}} +\newcommand{\List}{\operatorname{List}} +\newcommand{\Disc}{\operatorname{Disc}} +\newcommand{\coDisc}{\operatorname{coDisc}} % Type theory \newcommand{\context}{\mathsf{cx}} \newcommand{\type}{\mathsf{ty}} -\newcommand{\eqtype}{\mathsf{eq}} -\newcommand{\letin}[1]{\mathsf{let}\;{#1}\;\mathsf{in}\;} @OPTIONAL_PREAMBLE From 5cc08f60c87e796314b3b0ad089acd6368ff19f9 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Tue, 12 May 2026 22:49:49 -0700 Subject: [PATCH 31/81] DOC: Rename "inner context" -> "object term" in RFC-0004. --- rfc/0002.md | 4 +- rfc/0004.md | 144 +++++++++++++++++++----------------- rfc/chicago-author-date.csl | 2 +- 3 files changed, 80 insertions(+), 70 deletions(-) diff --git a/rfc/0002.md b/rfc/0002.md index 260fc272d..dd614ccf2 100644 --- a/rfc/0002.md +++ b/rfc/0002.md @@ -450,7 +450,7 @@ The following rules are specific to DoubleTT. #### Hom types - *Hom type former*: For each object in the double theory, there is a - family of Hom types, parameterized by pairs of object terms: + family of Hom types, parameterized by pairs of objects: ```tikz \[ @@ -507,7 +507,7 @@ The following rules are specific to DoubleTT. #### Morphism types - *Morphism type former*: For each proarrow in the double theory, there - is a family of morphism types, parameterized by pairs of object terms: + is a family of morphism types, parameterized by pairs of objects: ```tikz \[ diff --git a/rfc/0004.md b/rfc/0004.md index 6a89f87b4..61a8bac9f 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -191,24 +191,32 @@ standard dependent type theory. The type theory has record types (equivalent to dependent sums but more ergonomic) and singleton types (to enable model composition); it does *not* have dependent products, equality types, or universes. To this base is added specialized rules for the object and morphisms -types of the double theory. All this follows the original DoubleTT, with an -important exception to be described shortly. +types of the double theory. All of this follows the original DoubleTT, with an +important exception described below. New here are two additional kinds of judgments: -1. **contexts** over an object type in the theory, and -2. **terms in context**, or just **terms**, over a morphism type in the theory. +1. **object terms** over an object type in the theory, and +2. **morphism terms**, or just **terms**, over a morphism type in the theory. + +Morphism terms are the usual terms of an internal language. Object terms are +less conventional, playing the role that contexts usually do. In both simple and dependent type theory, contexts are lists of typed variables, with the list structure belonging to the *meta*-theory. We depart from this tradition since our internal languages encompass both unary type theories [@shulman-2016, Chapter 1] and multiary ("simple") type theories [@shulman-2016, -Chapter 2]; a context may or may not be a list, depending on which object type -it is over. Instead, the list modality of a modal double theory supplies the -list structure needed for multiary internal languages. So, again, a structure -usually external to the type theory is internalized. The price paid is that -operations like flattening nested lists, usually swept under the meta-theoretic -rug, must now be explicitly handled within in the type theory. +Chapter 2]. Depending on which object type it is over, an object term may or may +not be a list. To avoid confusion, we therefore avoid calling object terms +"contexts."[^contexts-vs-object-terms] Here the list structure is supplied by a +modality available in modal double theories. So, again, a structure usually +external to the type theory is internalized. The price paid is that operations +like flattening nested lists, usually swept under the meta-theoretic rug, must +now be explicitly handled within in the type theory. + +[^contexts-vs-object-terms]: + Thanks to Mitchell Riley for pointing out that the "contexts" in the inner + type theory are more like terms. Another design goal is that the type theory for morphisms be **cut-free**. That is, composition of morphisms should be not a primitive operation but an @@ -517,7 +525,7 @@ represented by a term $u: X \vdash_P t: Y$ in the inner type theory. #### Morphism equality types - *Morphism equality type former*: For each proarrow $P$ in the double theory, - there is a type of equalities between *inner* terms over $P$: + there is a type of equalities between morphism terms over $P$: ```tikz \[ @@ -538,9 +546,9 @@ represented by a term $u: X \vdash_P t: Y$ in the inner type theory. The type above is not an equality type in the usual sense of dependent type theory [@angiuli-gratzer-2025]. Indeed, the outer type theory does *not* have -equality types, i.e., there is no type of equalities between a pair of outer +equality types, i.e., there is no type of equalities between a pair of *outer* terms of the same type. Rather, there is an type of equalities between a pair of -inner terms (morphisms) with the same source and target. +*inner* terms (morphism terms) with the same domain and codomain. ::: @@ -638,50 +646,51 @@ We now assume that $\dbl{T}$ is a *flat* double theory. The inner type theory introduces two new judgments, both depending on contexts from the outer type theory. -1. *Contexts*: Presupposing that $\cal{X}$ is an object type of $\dbl{T}$ and - that $\Gamma \vdash X: \Ob_{\cal{X}}$ is an object, the judgment +1. *Object terms*: Presupposing that $\cal{X}$ is an object type of $\dbl{T}$ + and that $\Gamma \vdash X: \Ob_{\cal{X}}$ is an object, the judgment $$ \Gamma \mid u: X $$ - asserts that "$u$ is an (inner) context of type $X$, in (outer) context - $\Gamma$ and over $\cal{X}$". + asserts that "$u$ is an (object) term of type $X$, in context $\Gamma$ and + over $\cal{X}$". -2. *Terms*: Presupposing that $P: \cal{X} \proto \cal{Y}$ is a morphism type of - $\dbl{T}$, that $\Gamma \vdash X: \Ob_{\cal{X}}$ and $\Gamma \vdash Y: - \Ob_{\cal{Y}}$ are objects, and that $\Gamma \mid u: X$ is a context of type - $X$, the judgment +2. *Morphism terms*: Presupposing that $P: \cal{X} \proto \cal{Y}$ is a morphism + type of $\dbl{T}$, that $\Gamma \vdash X: \Ob_{\cal{X}}$ and $\Gamma \vdash + Y: \Ob_{\cal{Y}}$ are objects, and that $\Gamma \mid u: X$ is an object term + of type $X$, the judgment $$ \Gamma \mid u: X \vdash_P t: Y $$ - asserts that "$t$ is an (inner) term of type $Y$, in context $\Gamma \mid u: - X$ and over $P$". + asserts that "$t$ is a (morphism) term of type $Y$ with domain $u: X$, in + context $\Gamma$ and over $P$". When $\cal{X} = \cal{Y}$ and $P = \Hom_{\cal{X}}$, the subscript $P$ on the turnstile can be dropped. -In addition, there are judgments of equality between inner contexts and between -inner terms. +In addition, there are judgments of equality between object terms and between +morphism terms. -#### Contexts +#### Object terms ::: {.callout-warning} -#### Warning: Contexts vs lists +#### Warning: Contexts vs object terms Contexts are typically defined inductively to be lists of typed variables (i.e., -variable-type pairs), but the inner contexts of this type theory are constructed -differently. First of all, contexts need not be lists at all. Only over an -object type of form $\List\cal{X}$ can we build contexts like +variable-type pairs), but despite playing an analogous role, the object terms of +this type theory are structured differently. First of all, object terms need not +be lists at all. Only over an object type of form $\List\cal{X}$ can we build +object terms like $$ \Gamma \mid [x_1, x_2, x_3] : [X_1, X_2, X_3], $$ -where the $x_i$ are variables and the $X_i$ are objects of type $\cal{X}$ in -context $\Gamma$. Especially in examples, we will be lulled into writing this +where the $x_i$ are variables and the $X_i$ are objects over $\cal{X}$ in +context $\Gamma$. Especially in examples, we might be lulled into writing this more conventionally as $$ @@ -695,7 +704,7 @@ is only syntactic sugar for the former. ##### Core rules -- *Context formation*: +- *Formation rule*: ```tikz \[ @@ -707,7 +716,7 @@ is only syntactic sugar for the former. \] ``` - where $x$ is a variable assumed not to appear in the outer context $\Gamma$. + where $x$ is a variable assumed not to appear in the context $\Gamma$. - *Operation application*: @@ -764,7 +773,7 @@ is only syntactic sugar for the former. - *Computation rules, monad multiplication*: like the above rule, mirrors the the computation rules for the list modality in the outer type theory. -#### Terms +#### Morphism terms ##### Core rules @@ -780,8 +789,8 @@ is only syntactic sugar for the former. \] ``` - where $x$ is a variable assumed not to appear in the outer context $\Gamma$. - This rule can be interpreted as applying the nullary composition cell out of + where $x$ is a variable assumed not to appear in the context $\Gamma$. This + rule can be interpreted as applying the nullary composition cell out of $\cal{X}$. - *Post-composition rule*: @@ -922,13 +931,13 @@ categories. \] ``` - where in the last premise, $\gamma$ is a function from the set of variables - contained in $v$ to the set of variables in $u$. Here $v[\gamma]$ denotes the - *term* given by substituting in the *context* $v$ along $\gamma$, which makes - sense because in this type theory the contexts are a strict subset of the - terms, i.e., whenever $\Gamma \mid v: X$, the *identity* term $\Gamma \mid v: - X \vdash_{\Hom_{\cal{X}}} v: X$ is derivable. As usual, $s[\gamma]$ denotes - the term given by substituting in the term $s$ along + where $\gamma$ is a function from the set of variables contained in $v$ to the + set of variables contained in $u$. Here $v[\gamma]$ denotes the *morphism* + term given by substituting in the *object* term $v$ along $\gamma$, which + makes sense because object terms are included in morphism terms in that, + whenever $\Gamma \mid v: X$, the *identity* term $\Gamma \mid v: X + \vdash_{\Hom_{\cal{X}}} v: X$ is derivable. As usual, $s[\gamma]$ denotes the + (morphism) term given by substituting in the (morphism) term $s$ along $\gamma$.[^variable-substitution] - *Let binding*, or *general composition rule*: @@ -946,13 +955,13 @@ categories. where the first premise includes the assumption that $P$ and $Q$ have a composite. This rule can be interpreted as destructuring the value $t$ and - binding it to the variables in the context $v$. + binding it to the variables in $v$. [^variable-substitution]: - A more careful treatment, not on offer here, would define the two substitution - operations, on context and on terms, by induction on their constructors. - Still, it should be intuitively clear what it means to simultaneously - substitute one set of variables for another. + A more careful treatment, not on offer here, would define the substitution + operations on both kinds of terms by induction on their constructors. Still, + it should be intuitively clear what it means to simultaneously substitute one + set of variables for another. ::: {.callout} #### Remark: Admissibility vs derivability of $\alpha$-equivalence @@ -1017,9 +1026,9 @@ substitutes variables with variables, not variables with arbitrary terms. \] ``` - where it is implicitly assumed that the context in the conclusion is - well-formed, i.e., the variables appearing at any level among the contexts - $\Gamma \mid u_j : X_j$ are disjoint. + where it is implicitly assumed that the domain of the morphism term in the + conclusion is well-formed, i.e., the variables appearing at any level among + the object terms $\Gamma \mid u_j : X_j$ are disjoint. - *Computation rule, monad unit*: @@ -1035,8 +1044,9 @@ substitutes variables with variables, not variables with arbitrary terms. ``` - *Computation rules, monad multiplication*: extending the computation rules for - lists of objects and lists of contexts, there are computation rules governing - the monad multiplication on lists of terms. We omit the details. + lists of objects and lists of object terms, there are computation rules that + govern the monad multiplication for lists of morphism terms. We omit the + details. ## Examples @@ -1076,8 +1086,8 @@ proarrow, i.e., by the freely generated double theory $$\dbl{T} \coloneqq \big\{\Entity \xproto{\Attr} \AttrType\big\}.$$ Writing $\Mapping \coloneqq \Hom_{\Entity}$ for the type of mappings (foreign -keys) between entities, we can form the outer context representing a simple -schema involving people and dogs: +keys) between entities, we can form the context representing a simple schema +involving people and dogs: $$ \begin{aligned} @@ -1092,7 +1102,7 @@ $$ \end{aligned} $$ -In the inner context $\Gamma \mid p: \Person$, we can then derive the terms: +We can then derive the terms with domain $\Gamma \mid p: \Person$: 1. $\Gamma \mid d: \Dog \vdash_{\Mapping} d: \Dog$ 2. $\Gamma \mid d: \Dog \vdash_{\Mapping} \text{owner}(d): \Person$ @@ -1135,7 +1145,7 @@ such that the composite $P \odot P = P$ exists and subject to a couple equations, the effect of which is to make the theory flat. In the internal language of a promonad, we can build unary terms while tracking -whether the morphism denoted is tight or loose. Taking the outer context +whether the morphism denoted is tight or loose. Taking the context $$ \Gamma \coloneqq [ @@ -1173,9 +1183,9 @@ language of multicategories. Perhaps the simplest multiary language is the internal language of a (planar) multicategory. In this highly substructural language, variables must be used -exactly once and in the same order that they appear in the context. The type -theory here should be compared with the *cut-free type theory for -multicategories* in [@shulman-2016, Section 2.4.1]. +exactly once and in the same order that they are declared. The type theory here +should be compared with the *cut-free type theory for multicategories* in +[@shulman-2016, Section 2.4.1]. Let's begin by reviewing the modal double theory of [generalized multicategories](https://next.catcolab.org/math/dct-000F.xml), to be @@ -1244,7 +1254,7 @@ $$ $$ that is the signature of the theory of monoids. We build the left-hand side as a -term in context $\Gamma \mid [x,y,z]: [X,X,X]$. The right-hand side is similar. +term with domain $\Gamma \mid [x,y,z]: [X,X,X]$. The right-hand side is similar. To start, use the identity rule to form the term $\Gamma \mid x: X \vdash_{\Hom_{\cal{X}}} x: X$ and similarly for a second variable $y$. Combining @@ -1398,8 +1408,8 @@ $$ \Gamma \mid [r,x,s,y]: [R,X,R,X] \vdash_P (r \cdot x) + (s \cdot y): X. $$ -We must now apply the pre-composition rule to restrict the inner context. To do -so, first form the list +We must now apply the pre-composition rule to restrict the domain. To do so, +first form the list $$ \Gamma \mid [r,x,y]: [R,X,X] \vdash_{\List_{\mathrm{fun}} \Hom_{\cal{X}}} [r,x,r,y]: [R,X,R,X] @@ -1700,9 +1710,9 @@ $\otimes: \List \cal{X} \to \cal{X}$, and *associator* and *unitor* cells and their loose inverses. The cells obey three coherence axioms. Starting with contexts, suppose $\Gamma \coloneqq [X: \Ob_{\cal{X}}, Y: -\Ob_{\cal{X}}]$ and form the context $\Gamma \mid [x,y]: [X,Y]$ over $\List +\Ob_{\cal{X}}]$ and form the object term $\Gamma \mid [x,y]: [X,Y]$ over $\List \cal{X}$ as before. Applying the arrow $\otimes: \List\cal{X} \to \cal{X}$ -yields a new context over $\cal{X}$ that appears literally as +yields a new object term over $\cal{X}$ that appears literally as $$ \Gamma \mid \otimes [x,y]: \otimes [X,Y]. @@ -1712,7 +1722,7 @@ Following the common practice for unbiased monoidal products, we abbreviate a product $\otimes [X_1, \dots, X_k]$, with $k > 1$, as $X_1 \otimes \cdots \otimes X_k$, and we write the empty product $\otimes [\,]$ as $I$. We then introduce the tuple notation $(x_1,\dots,x_k)$ for the product element $\otimes -[x_1,\dots,x_k]$ for any $k \geq 0$. The context above then appears in more +[x_1,\dots,x_k]$ for any $k \geq 0$. The object term above then appears in more familiar style as $$ diff --git a/rfc/chicago-author-date.csl b/rfc/chicago-author-date.csl index 6dacc76b6..6896c5f64 100644 --- a/rfc/chicago-author-date.csl +++ b/rfc/chicago-author-date.csl @@ -4101,7 +4101,7 @@ - + From 71e62c36bfc94584793efe22968e844e63dd4bf8 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 13 May 2026 12:35:36 +0100 Subject: [PATCH 32/81] BUILD: Make deployment set a status on the `main` commit (#1271) --- .github/workflows/deploy.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c927b3cc6..89c28aef4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,6 +11,7 @@ env: permissions: deployments: write + statuses: write jobs: create_deployment: @@ -289,3 +290,18 @@ jobs: description: 'Backend deployment failed' state: 'failure' deployment-id: ${{ needs.create_backend_deployment.outputs.deployment_id }} + + - name: Post commit status + uses: actions/github-script@v7 + with: + script: | + const state = '${{ needs.deploy_backend.result }}' === 'success' ? 'success' : 'failure'; + await github.rest.repos.createCommitStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + sha: '${{ github.event.workflow_run.head_sha }}', + state: state, + target_url: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}', + description: state === 'success' ? 'Backend deployed to AWS' : 'Backend deployment failed', + context: 'deploy / backend' + }); From b398fd16b5485126b1664d51ecea81ca4d393a5f Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 13 May 2026 13:35:33 +0100 Subject: [PATCH 33/81] FIX: Commit Manifest.toml for algjulia-interop (#1273) --- packages/algjulia-interop/Manifest.toml | 652 ++++++++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 packages/algjulia-interop/Manifest.toml diff --git a/packages/algjulia-interop/Manifest.toml b/packages/algjulia-interop/Manifest.toml new file mode 100644 index 000000000..2929feb0a --- /dev/null +++ b/packages/algjulia-interop/Manifest.toml @@ -0,0 +1,652 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.12.5" +manifest_format = "2.0" +project_hash = "ae3caef2a0866469616d51a6bb9ac744f063ad55" + +[[deps.ACSets]] +deps = ["AlgebraicInterfaces", "Base64", "CompTime", "DataStructures", "JSON3", "MLStyle", "OrderedCollections", "PEG", "Permutations", "Pkg", "PrettyTables", "Random", "Reexport", "SHA", "StaticArrays", "StructEquality", "StructTypes", "Tables"] +git-tree-sha1 = "dabea268e85bfa2d87019772d1e5b5115542450f" +uuid = "227ef7b5-1206-438b-ac65-934d6da304b8" +version = "0.2.28" + + [deps.ACSets.extensions] + DataFramesACSetsExt = "DataFrames" + NautyACSetsExt = "nauty_jll" + XLSXACSetsExt = "XLSX" + + [deps.ACSets.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" + nauty_jll = "55c6dc9b-343a-50ca-8ff2-b71adb3733d5" + +[[deps.AlgebraicInterfaces]] +git-tree-sha1 = "28cca88bde1068ccf57f950e639df67975517c25" +uuid = "23cfdc9f-0504-424a-be1f-4892b28e2f0c" +version = "0.1.4" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.2" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" + +[[deps.BitFlags]] +git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" +uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" +version = "0.1.9" + +[[deps.CatColabInterop]] +deps = ["ACSets", "Catlab", "HTTP", "MLStyle", "Oxygen", "Reexport", "StructTypes"] +path = "." +uuid = "9ecda8fb-39ab-46a2-a496-7285fa6368c1" +version = "0.1.1" + +[[deps.Catlab]] +deps = ["ACSets", "AlgebraicInterfaces", "Colors", "CompTime", "Compose", "DataStructures", "GATlab", "GeneralizedGenerated", "JSON3", "LightXML", "LinearAlgebra", "Logging", "MLStyle", "PEG", "PrettyTables", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StructEquality", "StructTypes", "Tables"] +git-tree-sha1 = "fadf5ec3e429cc88be1178b6baf687b99bb3177c" +uuid = "134e5e36-593f-5add-ad60-77f754baafbe" +version = "0.17.5" + + [deps.Catlab.extensions] + CatlabConvexExt = "Convex" + CatlabDataFramesExt = "DataFrames" + CatlabGraphsExt = "Graphs" + CatlabGraphvizExt = "Graphviz_jll" + CatlabMetaGraphsExt = "MetaGraphs" + CatlabSCSExt = "SCS" + CatlabTikzPicturesExt = "TikzPictures" + + [deps.Catlab.weakdeps] + Convex = "f65535da-76fb-5f13-bab9-19810c17039a" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" + Graphviz_jll = "3c863552-8265-54e4-a6dc-903eb78fde85" + MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5" + SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" + TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.8" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "67e11ee83a43eb71ddc950302c53bf33f0690dfe" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.12.1" +weakdeps = ["StyledStrings"] + + [deps.ColorTypes.extensions] + StyledStringsExt = "StyledStrings" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "37ea44092930b1811e666c3bc38065d7d87fcc74" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.13.1" + +[[deps.Combinatorics]] +git-tree-sha1 = "c761b00e7755700f9cdf5b02039939d1359330e1" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.1.0" + +[[deps.CompTime]] +deps = ["MLStyle", "MacroTools"] +git-tree-sha1 = "8c05059bc293a17f71cae4cd58b1fc18d4ede271" +uuid = "0fb5dd42-039a-4ca4-a1d7-89a96eae6d39" +version = "0.1.2" + +[[deps.Compat]] +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.18.1" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.3.0+1" + +[[deps.Compose]] +deps = ["Base64", "Colors", "DataStructures", "Dates", "IterTools", "JSON", "LinearAlgebra", "Measures", "Printf", "Random", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "b137aa32bfe5b89996f8f87825b64ac41b9f2e16" +uuid = "a81c6b42-2e10-5240-aca2-a61377ecd94b" +version = "0.9.6" + +[[deps.ConcurrentUtilities]] +deps = ["Serialization", "Sockets"] +git-tree-sha1 = "21d088c496ea22914fe80906eb5bce65755e5ec8" +uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" +version = "2.5.1" + +[[deps.Crayons]] +git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.1.1" + +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.22" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.7.0" + +[[deps.ExceptionUnwrapping]] +deps = ["Test"] +git-tree-sha1 = "d36f682e590a83d63d1c7dbd287573764682d12a" +uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" +version = "0.1.11" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" + +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.5" + +[[deps.GATlab]] +deps = ["AlgebraicInterfaces", "Colors", "Crayons", "DataStructures", "JSON", "MLStyle", "Markdown", "Random", "Reexport", "StructEquality", "UUIDs"] +git-tree-sha1 = "70ba9859a1a62ff8af931d7ab0bc5df65e090ac1" +uuid = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" +version = "0.2.2" + +[[deps.GeneralizedGenerated]] +deps = ["DataStructures", "JuliaVariables", "MLStyle", "Serialization"] +git-tree-sha1 = "60f1fa1696129205873c41763e7d0920ac7d6f1f" +uuid = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" +version = "0.3.3" + +[[deps.HTTP]] +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "51059d23c8bb67911a2e6fd5130229113735fc7e" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "1.11.0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" + +[[deps.IterTools]] +git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.10.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.8.0" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.JSON3]] +deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] +git-tree-sha1 = "411eccfe8aba0814ffa0fdf4860913ed09c34975" +uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" +version = "1.14.3" + + [deps.JSON3.extensions] + JSON3ArrowExt = ["ArrowTypes"] + + [deps.JSON3.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + +[[deps.JuliaSyntaxHighlighting]] +deps = ["StyledStrings"] +uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" +version = "1.12.0" + +[[deps.JuliaVariables]] +deps = ["MLStyle", "NameResolution"] +git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" +uuid = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" +version = "0.2.4" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.4.0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.15.0+0" + +[[deps.LibGit2]] +deps = ["LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.9.0+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "OpenSSL_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.3+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.18.0+0" + +[[deps.LightXML]] +deps = ["Libdl", "XML2_jll"] +git-tree-sha1 = "aa971a09f0f1fe92fe772713a564aa48abe510df" +uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" +version = "0.9.3" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.12.0" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" + +[[deps.LoggingExtras]] +deps = ["Dates", "Logging"] +git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" +uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" +version = "1.2.0" + +[[deps.MIMEs]] +git-tree-sha1 = "c64d943587f7187e751162b3b84445bbbd79f691" +uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" +version = "1.1.0" + +[[deps.MLStyle]] +git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" +uuid = "d8e11817-5142-5d16-987a-aa16d5891078" +version = "0.4.17" + +[[deps.MacroTools]] +git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.16" + +[[deps.Markdown]] +deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" + +[[deps.MbedTLS]] +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] +git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "1.1.10" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "ff69a2b1330bcb730b9ac1ab7dd680176f5896b8" +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.1010+0" + +[[deps.Measures]] +git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.3" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2025.11.4" + +[[deps.NameResolution]] +deps = ["PrettyPrint"] +git-tree-sha1 = "1a0fa0e9613f46c9b8c11eee38ebb4f590013c5e" +uuid = "71a1bf82-56d0-4bbc-8a3c-48b961074391" +version = "0.1.5" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.3.0" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.29+0" + +[[deps.OpenSSL]] +deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"] +git-tree-sha1 = "1d1aaa7d449b58415f97d2839c318b70ffb525a0" +uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" +version = "1.6.1" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.5.4+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "05868e21324cede2207c6f0f466b4bfef6d5e7ee" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.8.1" + +[[deps.Oxygen]] +deps = ["DataStructures", "Dates", "HTTP", "JSON3", "MIMEs", "Reexport", "RelocatableFolders", "Sockets", "Statistics", "StructTypes"] +git-tree-sha1 = "c1f679c1e604d83388f397027b827e08af3c9984" +uuid = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" +version = "1.7.5" + + [deps.Oxygen.extensions] + BonitoExt = "Bonito" + CairoMakieExt = "CairoMakie" + MustacheExt = "Mustache" + OteraEngineExt = "OteraEngine" + ProtoBufExt = "ProtoBuf" + TimeZonesExt = "TimeZones" + WGLMakieExt = ["WGLMakie", "Bonito"] + + [deps.Oxygen.weakdeps] + Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" + CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" + Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" + OteraEngine = "b2d7f28f-acd6-4007-8b26-bc27716e5513" + ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" + TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" + WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" + +[[deps.PEG]] +git-tree-sha1 = "7ddadb443e48ae0293de19737c8defdc4c6ad89a" +uuid = "12d937ae-5f68-53be-93c9-3a6f997a20a8" +version = "1.0.4" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "5d5e0a78e971354b1c7bff0655d11fdc1b0e12c8" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.4" + +[[deps.Permutations]] +deps = ["Combinatorics", "LinearAlgebra", "Random"] +git-tree-sha1 = "b1f03a4943c62552a12c7f95965b76c3f91cf5b7" +uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +version = "0.4.23" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.12.1" +weakdeps = ["REPL"] + + [deps.Pkg.extensions] + REPLExt = "REPL" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "edbeefc7a4889f528644251bdb5fc9ab5348bc2c" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.3.4" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "8b770b60760d4451834fe79dd483e318eee709c4" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.5.2" + +[[deps.PrettyPrint]] +git-tree-sha1 = "632eb4abab3449ab30c5e1afaa874f0b98b586e4" +uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" +version = "0.2.0" + +[[deps.PrettyTables]] +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] +git-tree-sha1 = "624de6279ab7d94fc9f672f0068107eb6619732c" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "3.3.2" + + [deps.PrettyTables.extensions] + PrettyTablesTypstryExt = "Typstry" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.REPL]] +deps = ["InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.1" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.3.0" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" + +[[deps.SimpleBufferStream]] +git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" +uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" +version = "1.2.0" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.12.0" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "246a8bb2e6667f832eea063c3a56aef96429a3db" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.18" + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [deps.StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.4" + +[[deps.Statistics]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.1" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] + +[[deps.StringManipulation]] +deps = ["PrecompileTools"] +git-tree-sha1 = "d05693d339e37d6ab134c5ab53c29fce5ee5d7d5" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.4.4" + +[[deps.StructEquality]] +deps = ["Compat"] +git-tree-sha1 = "192a9f1de3cfef80ab1a4ba7b150bb0e11ceedcf" +uuid = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c" +version = "2.1.0" + +[[deps.StructTypes]] +deps = ["Dates", "UUIDs"] +git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" +uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +version = "1.11.0" + +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.8.3+2" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "f2c1efbc8f3a609aadf318094f8fc5204bdaf344" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.12.1" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[deps.TranscodingStreams]] +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.11.3" + +[[deps.URIs]] +git-tree-sha1 = "bef26fb046d031353ef97a82e3fdb6afe7f21b1a" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.6.1" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "5c959b708667b34cb758e8d7c6f8e69b94c32deb" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.15.1+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.3.1+2" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.15.0+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.64.0+1" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.7.0+0" From 2b729c4a5b76f192ac93498c3878a05210cbf0ce Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Wed, 13 May 2026 20:55:54 -0700 Subject: [PATCH 34/81] BUILD: Remove Project/Manifest toml files for `test` (#1274) --- packages/algjulia-interop/.gitignore | 4 + packages/algjulia-interop/Manifest.toml | 4 +- packages/algjulia-interop/Project.toml | 4 + packages/algjulia-interop/test/Manifest.toml | 652 ------------------- packages/algjulia-interop/test/Project.toml | 8 - 5 files changed, 10 insertions(+), 662 deletions(-) delete mode 100644 packages/algjulia-interop/test/Manifest.toml delete mode 100644 packages/algjulia-interop/test/Project.toml diff --git a/packages/algjulia-interop/.gitignore b/packages/algjulia-interop/.gitignore index daa3c70fe..57e3341df 100644 --- a/packages/algjulia-interop/.gitignore +++ b/packages/algjulia-interop/.gitignore @@ -22,3 +22,7 @@ docs/site/ # Local preferences LocalPreferences.toml + +# Julia +test/Manifest.toml +test/Project.toml diff --git a/packages/algjulia-interop/Manifest.toml b/packages/algjulia-interop/Manifest.toml index 2929feb0a..c1bbb7e0d 100644 --- a/packages/algjulia-interop/Manifest.toml +++ b/packages/algjulia-interop/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.12.5" +julia_version = "1.12.6" manifest_format = "2.0" -project_hash = "ae3caef2a0866469616d51a6bb9ac744f063ad55" +project_hash = "9451d3fd8333dba6c13ebb67dfb76723fc303e64" [[deps.ACSets]] deps = ["AlgebraicInterfaces", "Base64", "CompTime", "DataStructures", "JSON3", "MLStyle", "OrderedCollections", "PEG", "Permutations", "Pkg", "PrettyTables", "Random", "Reexport", "SHA", "StaticArrays", "StructEquality", "StructTypes", "Tables"] diff --git a/packages/algjulia-interop/Project.toml b/packages/algjulia-interop/Project.toml index 8e8512424..14fde7c13 100644 --- a/packages/algjulia-interop/Project.toml +++ b/packages/algjulia-interop/Project.toml @@ -8,17 +8,21 @@ authors = ["CatColab team"] ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" Oxygen = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] ACSets = "0.2.26" Catlab = "0.17.2" HTTP = "1.10.19" +JSON3 = "1.14.3" MLStyle = "0.4.17" Oxygen = "1.7.5" Reexport = "1.2.2" StructTypes = "1.11.0" +Test = "1.11.0" julia = "1.11" diff --git a/packages/algjulia-interop/test/Manifest.toml b/packages/algjulia-interop/test/Manifest.toml deleted file mode 100644 index d63984100..000000000 --- a/packages/algjulia-interop/test/Manifest.toml +++ /dev/null @@ -1,652 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.12.5" -manifest_format = "2.0" -project_hash = "8e27b017675e11d4ba34f579de046e8ab3cb258a" - -[[deps.ACSets]] -deps = ["AlgebraicInterfaces", "Base64", "CompTime", "DataStructures", "JSON3", "MLStyle", "OrderedCollections", "PEG", "Permutations", "Pkg", "PrettyTables", "Random", "Reexport", "SHA", "StaticArrays", "StructEquality", "StructTypes", "Tables"] -git-tree-sha1 = "dabea268e85bfa2d87019772d1e5b5115542450f" -uuid = "227ef7b5-1206-438b-ac65-934d6da304b8" -version = "0.2.28" - - [deps.ACSets.extensions] - DataFramesACSetsExt = "DataFrames" - NautyACSetsExt = "nauty_jll" - XLSXACSetsExt = "XLSX" - - [deps.ACSets.weakdeps] - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" - nauty_jll = "55c6dc9b-343a-50ca-8ff2-b71adb3733d5" - -[[deps.AlgebraicInterfaces]] -git-tree-sha1 = "28cca88bde1068ccf57f950e639df67975517c25" -uuid = "23cfdc9f-0504-424a-be1f-4892b28e2f0c" -version = "0.1.4" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[deps.BitFlags]] -git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" -uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.9" - -[[deps.CatColabInterop]] -deps = ["ACSets", "Catlab", "HTTP", "MLStyle", "Oxygen", "Reexport", "StructTypes"] -path = ".." -uuid = "9ecda8fb-39ab-46a2-a496-7285fa6368c1" -version = "0.1.1" - -[[deps.Catlab]] -deps = ["ACSets", "AlgebraicInterfaces", "Colors", "CompTime", "Compose", "DataStructures", "GATlab", "GeneralizedGenerated", "JSON3", "LightXML", "LinearAlgebra", "Logging", "MLStyle", "PEG", "PrettyTables", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StructEquality", "StructTypes", "Tables"] -git-tree-sha1 = "fadf5ec3e429cc88be1178b6baf687b99bb3177c" -uuid = "134e5e36-593f-5add-ad60-77f754baafbe" -version = "0.17.5" - - [deps.Catlab.extensions] - CatlabConvexExt = "Convex" - CatlabDataFramesExt = "DataFrames" - CatlabGraphsExt = "Graphs" - CatlabGraphvizExt = "Graphviz_jll" - CatlabMetaGraphsExt = "MetaGraphs" - CatlabSCSExt = "SCS" - CatlabTikzPicturesExt = "TikzPictures" - - [deps.Catlab.weakdeps] - Convex = "f65535da-76fb-5f13-bab9-19810c17039a" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" - Graphviz_jll = "3c863552-8265-54e4-a6dc-903eb78fde85" - MetaGraphs = "626554b9-1ddb-594c-aa3c-2596fe9399a5" - SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" - TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.8" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "67e11ee83a43eb71ddc950302c53bf33f0690dfe" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.12.1" -weakdeps = ["StyledStrings"] - - [deps.ColorTypes.extensions] - StyledStringsExt = "StyledStrings" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "37ea44092930b1811e666c3bc38065d7d87fcc74" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.13.1" - -[[deps.Combinatorics]] -git-tree-sha1 = "c761b00e7755700f9cdf5b02039939d1359330e1" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.1.0" - -[[deps.CompTime]] -deps = ["MLStyle", "MacroTools"] -git-tree-sha1 = "8c05059bc293a17f71cae4cd58b1fc18d4ede271" -uuid = "0fb5dd42-039a-4ca4-a1d7-89a96eae6d39" -version = "0.1.2" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.18.1" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.3.0+1" - -[[deps.Compose]] -deps = ["Base64", "Colors", "DataStructures", "Dates", "IterTools", "JSON", "LinearAlgebra", "Measures", "Printf", "Random", "Requires", "Statistics", "UUIDs"] -git-tree-sha1 = "b137aa32bfe5b89996f8f87825b64ac41b9f2e16" -uuid = "a81c6b42-2e10-5240-aca2-a61377ecd94b" -version = "0.9.6" - -[[deps.ConcurrentUtilities]] -deps = ["Serialization", "Sockets"] -git-tree-sha1 = "21d088c496ea22914fe80906eb5bce65755e5ec8" -uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.5.1" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "4e1fe97fdaed23e9dc21d4d664bea76b65fc50a0" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.22" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.7.0" - -[[deps.ExceptionUnwrapping]] -deps = ["Test"] -git-tree-sha1 = "d36f682e590a83d63d1c7dbd287573764682d12a" -uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" -version = "0.1.11" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.5" - -[[deps.GATlab]] -deps = ["AlgebraicInterfaces", "Colors", "Crayons", "DataStructures", "JSON", "MLStyle", "Markdown", "Random", "Reexport", "StructEquality", "UUIDs"] -git-tree-sha1 = "70ba9859a1a62ff8af931d7ab0bc5df65e090ac1" -uuid = "f0ffcf3b-d13a-433e-917c-cc44ccf5ead2" -version = "0.2.2" - -[[deps.GeneralizedGenerated]] -deps = ["DataStructures", "JuliaVariables", "MLStyle", "Serialization"] -git-tree-sha1 = "60f1fa1696129205873c41763e7d0920ac7d6f1f" -uuid = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" -version = "0.3.3" - -[[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "51059d23c8bb67911a2e6fd5130229113735fc7e" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.11.0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[deps.IterTools]] -git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.10.0" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.8.0" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JSON3]] -deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] -git-tree-sha1 = "411eccfe8aba0814ffa0fdf4860913ed09c34975" -uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -version = "1.14.3" - - [deps.JSON3.extensions] - JSON3ArrowExt = ["ArrowTypes"] - - [deps.JSON3.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - -[[deps.JuliaSyntaxHighlighting]] -deps = ["StyledStrings"] -uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" -version = "1.12.0" - -[[deps.JuliaVariables]] -deps = ["MLStyle", "NameResolution"] -git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" -uuid = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" -version = "0.2.4" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.4.0" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.15.0+0" - -[[deps.LibGit2]] -deps = ["LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.9.0+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "OpenSSL_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.3+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.18.0+0" - -[[deps.LightXML]] -deps = ["Libdl", "XML2_jll"] -git-tree-sha1 = "aa971a09f0f1fe92fe772713a564aa48abe510df" -uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" -version = "0.9.3" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.12.0" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[deps.LoggingExtras]] -deps = ["Dates", "Logging"] -git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" -uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.2.0" - -[[deps.MIMEs]] -git-tree-sha1 = "c64d943587f7187e751162b3b84445bbbd79f691" -uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" -version = "1.1.0" - -[[deps.MLStyle]] -git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" -uuid = "d8e11817-5142-5d16-987a-aa16d5891078" -version = "0.4.17" - -[[deps.MacroTools]] -git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.16" - -[[deps.Markdown]] -deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[deps.MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.10" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ff69a2b1330bcb730b9ac1ab7dd680176f5896b8" -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.1010+0" - -[[deps.Measures]] -git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" -uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.3" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2025.11.4" - -[[deps.NameResolution]] -deps = ["PrettyPrint"] -git-tree-sha1 = "1a0fa0e9613f46c9b8c11eee38ebb4f590013c5e" -uuid = "71a1bf82-56d0-4bbc-8a3c-48b961074391" -version = "0.1.5" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.3.0" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.29+0" - -[[deps.OpenSSL]] -deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "1d1aaa7d449b58415f97d2839c318b70ffb525a0" -uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.6.1" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.5.4+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "05868e21324cede2207c6f0f466b4bfef6d5e7ee" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.8.1" - -[[deps.Oxygen]] -deps = ["DataStructures", "Dates", "HTTP", "JSON3", "MIMEs", "Reexport", "RelocatableFolders", "Sockets", "Statistics", "StructTypes"] -git-tree-sha1 = "c1f679c1e604d83388f397027b827e08af3c9984" -uuid = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" -version = "1.7.5" - - [deps.Oxygen.extensions] - BonitoExt = "Bonito" - CairoMakieExt = "CairoMakie" - MustacheExt = "Mustache" - OteraEngineExt = "OteraEngine" - ProtoBufExt = "ProtoBuf" - TimeZonesExt = "TimeZones" - WGLMakieExt = ["WGLMakie", "Bonito"] - - [deps.Oxygen.weakdeps] - Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" - CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" - Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" - OteraEngine = "b2d7f28f-acd6-4007-8b26-bc27716e5513" - ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" - TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" - WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008" - -[[deps.PEG]] -git-tree-sha1 = "7ddadb443e48ae0293de19737c8defdc4c6ad89a" -uuid = "12d937ae-5f68-53be-93c9-3a6f997a20a8" -version = "1.0.4" - -[[deps.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "5d5e0a78e971354b1c7bff0655d11fdc1b0e12c8" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.4" - -[[deps.Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "b1f03a4943c62552a12c7f95965b76c3f91cf5b7" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.23" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.12.1" -weakdeps = ["REPL"] - - [deps.Pkg.extensions] - REPLExt = "REPL" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "07a921781cab75691315adc645096ed5e370cb77" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.3.3" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "8b770b60760d4451834fe79dd483e318eee709c4" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.5.2" - -[[deps.PrettyPrint]] -git-tree-sha1 = "632eb4abab3449ab30c5e1afaa874f0b98b586e4" -uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" -version = "0.2.0" - -[[deps.PrettyTables]] -deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "624de6279ab7d94fc9f672f0068107eb6619732c" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "3.3.2" - - [deps.PrettyTables.extensions] - PrettyTablesTypstryExt = "Typstry" - - [deps.PrettyTables.weakdeps] - Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[deps.REPL]] -deps = ["InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.1" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.Scratch]] -deps = ["Dates"] -git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.3.0" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[deps.SimpleBufferStream]] -git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" -uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" -version = "1.2.0" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.12.0" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "246a8bb2e6667f832eea063c3a56aef96429a3db" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.18" - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - - [deps.StaticArrays.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.4" - -[[deps.Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[deps.StringManipulation]] -deps = ["PrecompileTools"] -git-tree-sha1 = "d05693d339e37d6ab134c5ab53c29fce5ee5d7d5" -uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.4.4" - -[[deps.StructEquality]] -deps = ["Compat"] -git-tree-sha1 = "192a9f1de3cfef80ab1a4ba7b150bb0e11ceedcf" -uuid = "6ec83bb0-ed9f-11e9-3b4c-2b04cb4e219c" -version = "2.1.0" - -[[deps.StructTypes]] -deps = ["Dates", "UUIDs"] -git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" -uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -version = "1.11.0" - -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.8.3+2" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "f2c1efbc8f3a609aadf318094f8fc5204bdaf344" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.1" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[deps.URIs]] -git-tree-sha1 = "bef26fb046d031353ef97a82e3fdb6afe7f21b1a" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.6.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "5c959b708667b34cb758e8d7c6f8e69b94c32deb" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.15.1+0" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.3.1+2" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.15.0+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.64.0+1" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.7.0+0" diff --git a/packages/algjulia-interop/test/Project.toml b/packages/algjulia-interop/test/Project.toml deleted file mode 100644 index 84cb3f020..000000000 --- a/packages/algjulia-interop/test/Project.toml +++ /dev/null @@ -1,8 +0,0 @@ -[deps] -ACSets = "227ef7b5-1206-438b-ac65-934d6da304b8" -CatColabInterop = "9ecda8fb-39ab-46a2-a496-7285fa6368c1" -Catlab = "134e5e36-593f-5add-ad60-77f754baafbe" -HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" -JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -Oxygen = "df9a0d86-3283-4920-82dc-4555fc0d1d8b" -StructTypes = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" From 83e9de9e1fbfd9166f520a632ef0e50ccc1a448f Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Mon, 11 May 2026 15:59:02 +0100 Subject: [PATCH 35/81] BUILD: Update oxlint --- oxlint.config.ts | 6 + package.json | 4 +- packages/ui-components/src/util/types.ts | 1 + pnpm-lock.yaml | 226 +++++++++++------------ 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/oxlint.config.ts b/oxlint.config.ts index 0c2049db0..03009023d 100644 --- a/oxlint.config.ts +++ b/oxlint.config.ts @@ -75,6 +75,12 @@ export default defineConfig({ // Ternary-as-statement (`condition ? doA() : doB()`) is idiomatic in // this codebase for concise branching in callbacks. "no-unused-expressions": ["warn", { allowTernary: true }], + // Requires adding explicit `return undefined` in places seems to add too much noise + "consistent-return": "off", + // We use _ prefix in places + "no-underscore-dangle": "off", + // We have some tests that just see if anything breaks when things are run + "vitest/expect-expect": "off", // Allow underscore-prefixed names for intentionally unused bindings // (e.g., `_e` in catch blocks, `_unused` destructured fields). "no-unused-vars": [ diff --git a/package.json b/package.json index 6cf556014..70fc4e722 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "devDependencies": { "depcheck": "^1.4.7", "oxfmt": "^0.37.0", - "oxlint": "^1.51.0", - "oxlint-tsgolint": "^0.16.0", + "oxlint": "^1.62.0", + "oxlint-tsgolint": "^0.22.1", "stylelint": "^17.4.0", "stylelint-config-standard": "^40.0.0", "tsx": "^4.16.5", diff --git a/packages/ui-components/src/util/types.ts b/packages/ui-components/src/util/types.ts index 829da28ac..0b105fb42 100644 --- a/packages/ui-components/src/util/types.ts +++ b/packages/ui-components/src/util/types.ts @@ -4,4 +4,5 @@ Call this function as no effect. The minimal version of: */ +// oxlint-disable-next-line typescript-eslint/no-unnecessary-type-parameters -- compile-time assertion helper export function assertTypelevel<_T extends true>() {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c00edf2bb..f9c464668 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,11 +20,11 @@ importers: specifier: ^0.37.0 version: 0.37.0 oxlint: - specifier: ^1.51.0 - version: 1.52.0(oxlint-tsgolint@0.16.0) + specifier: ^1.62.0 + version: 1.62.0(oxlint-tsgolint@0.22.1) oxlint-tsgolint: - specifier: ^0.16.0 - version: 0.16.0 + specifier: ^0.22.1 + version: 0.22.1 stylelint: specifier: ^17.4.0 version: 17.4.0(typescript@5.6.2) @@ -427,154 +427,154 @@ packages: cpu: [x64] os: [win32] - '@oxlint-tsgolint/darwin-arm64@0.16.0': - resolution: {integrity: sha512-WQt5lGwRPJBw7q2KNR0mSPDAaMmZmVvDlEEti96xLO7ONhyomQc6fBZxxwZ4qTFedjJnrHX94sFelZ4OKzS7UQ==} + '@oxlint-tsgolint/darwin-arm64@0.22.1': + resolution: {integrity: sha512-4150Lpgc1YM09GcjA6GSrra1JoPjC7aOpfywLjWEY4vW0Sd1qKzqHF1WRaiw0/qUZ40OATYdv3aRd7ipPkWQbw==} cpu: [arm64] os: [darwin] - '@oxlint-tsgolint/darwin-x64@0.16.0': - resolution: {integrity: sha512-VJo29XOzdkalvCTiE2v6FU3qZlgHaM8x8hUEVJGPU2i5W+FlocPpmn00+Ld2n7Q0pqIjyD5EyvZ5UmoIEJMfqg==} + '@oxlint-tsgolint/darwin-x64@0.22.1': + resolution: {integrity: sha512-vFWcPWYOgZs4HWcgS1EjUZg33NLcNfEYU49KGImmCfZWkflENrmBYV4HN/C0YeAPum6ZZ/goPSvQrB/cOD+NfA==} cpu: [x64] os: [darwin] - '@oxlint-tsgolint/linux-arm64@0.16.0': - resolution: {integrity: sha512-MPfqRt1+XRHv9oHomcBMQ3KpTE+CSkZz14wUxDQoqTNdUlV0HWdzwIE9q65I3D9YyxEnqpM7j4qtDQ3apqVvbQ==} + '@oxlint-tsgolint/linux-arm64@0.22.1': + resolution: {integrity: sha512-6LiUpP0Zir3+29FvBm7Y28q/dBjSHqTZ5MhG1Ckw4fGhI4cAvbcwXaKvbjx1TP7rRmBNOoq/M5xdpHjTb+GAew==} cpu: [arm64] os: [linux] - '@oxlint-tsgolint/linux-x64@0.16.0': - resolution: {integrity: sha512-XQSwVUsnwLokMhe1TD6IjgvW5WMTPzOGGkdFDtXWQmlN2YeTw94s/NN0KgDrn2agM1WIgAenEkvnm0u7NgwEyw==} + '@oxlint-tsgolint/linux-x64@0.22.1': + resolution: {integrity: sha512-fuX1hEQfpHauUbXADsfqVhRzrUrGabzGXbj5wsp2vKhV5uk/Rze8Mba9GdjFGECzvXudMGqHqxB4r6jGRdhxVA==} cpu: [x64] os: [linux] - '@oxlint-tsgolint/win32-arm64@0.16.0': - resolution: {integrity: sha512-EWdlspQiiFGsP2AiCYdhg5dTYyAlj6y1nRyNI2dQWq4Q/LITFHiSRVPe+7m7K7lcsZCEz2icN/bCeSkZaORqIg==} + '@oxlint-tsgolint/win32-arm64@0.22.1': + resolution: {integrity: sha512-8SZidAj+jrbZf9ZjBEYW0tiNZ+KasqB2zgW26qdiPpQSF/DzURnPmXz651IeA9YsmbVdHGIooEHUmev6QJdquA==} cpu: [arm64] os: [win32] - '@oxlint-tsgolint/win32-x64@0.16.0': - resolution: {integrity: sha512-1ufk8cgktXJuJZHKF63zCHAkaLMwZrEXnZ89H2y6NO85PtOXqu4zbdNl0VBpPP3fCUuUBu9RvNqMFiv0VsbXWA==} + '@oxlint-tsgolint/win32-x64@0.22.1': + resolution: {integrity: sha512-QweSk9H5lFh5Y+WUf2Kq/OAN88V6+62ZwGhP38gqdRotI90luXSMkruFTj7Q2rYrzH4ZVNaSqx7NY8JpSfIzqg==} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.52.0': - resolution: {integrity: sha512-fW2pmR1VzFEdcvOYeSiv+R7CqffOjr9Bv5QmZaHuHJ4ZCqouaF6o48N/hJ3H1n9Zd8PCMFgJkeqUvUsVce01mw==} + '@oxlint/binding-android-arm-eabi@1.62.0': + resolution: {integrity: sha512-pKsthNECyvJh8lPTICz6VcwVy2jOqdhhsp1rlxCkhgZR47aKvXPmaRWQDv+zlXpRae4qm1MaaTnutkaOk5aofg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.52.0': - resolution: {integrity: sha512-ptuJljIB+klNi8//qxXyGD51NLJXY9lv40Olc7l3/pEyjejWwXGvGMO0GM6f0JsjmbnDL+VkX7RVQNhByaX8WA==} + '@oxlint/binding-android-arm64@1.62.0': + resolution: {integrity: sha512-b1AUNViByvgmR2xJDubvLIr+dSuu3uraG7bsAoKo+xrpspPvu6RIn6Fhr2JUhobfep3jwUTy18Huco6GkwdvGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.52.0': - resolution: {integrity: sha512-5d079Uw43BHVZzOwm3uJI2PgSbsZJTpfHDq2jMOR6rRjGiEBlgasaEvAA26VBqpkO1++/59ZCKLBnEpkro3zIg==} + '@oxlint/binding-darwin-arm64@1.62.0': + resolution: {integrity: sha512-iG+Tvf70UJ6otfwFYIHk36Sjq9cpPP5YLxkoggANNRtzgi3Tj3g8q6Ybqi6AtkU3+yg9QwF7bDCkCS6bbL4PCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.52.0': - resolution: {integrity: sha512-vRTjnhPEHAyfUhO9w6GM1VkxeVXFcDs+huyB5YNMw+Py+6PRYDFFrrOEr0rZYcoGtSH25ScozZV8I1UXrzaDjQ==} + '@oxlint/binding-darwin-x64@1.62.0': + resolution: {integrity: sha512-oOWI6YPPr5AJUx+yIDlxmuUbQjS5gZX3OH3QisawYvsZgLiQVvZtR0rPBcJTxLWqt2ClrWg0DlSrlUiG5SQNHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.52.0': - resolution: {integrity: sha512-vFthhhciRAliAjoKMsvi7UkkQp/EtMNhmCRYBuKsNiTH0k4H3SFfbuWWr80Q7+uTXijfBP91KO/EeF48RggC7A==} + '@oxlint/binding-freebsd-x64@1.62.0': + resolution: {integrity: sha512-dLP33T7VLCmLVv4cvjkVX+rmkcwNk2UfxmsZPNur/7BQHoQR60zJ7XLiRvNUawlzn0u8ngCa3itjEG73MAMa/w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.52.0': - resolution: {integrity: sha512-qX3K4mKbju54ojUa8nigVxxZAUDBGu5MGzpoXvWmiw+7hafoQKaLAoTm94EqRlv9v27p864GQBgc4g3qYtMXXA==} + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': + resolution: {integrity: sha512-fl//LWNks6qo9chNY60UDYyIwtp7a5cEx4Y/rHPjaarhuwqx6jtbzEpD5V5AqmdL4a6Y5D8zeXg5HF2Cr0QmSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.52.0': - resolution: {integrity: sha512-x5D5/EUS9U4kndPncLB6mDfCsv7i8XcRLu0DZyTngXvyqapc96WwmyyOG2j8Dt26aE8Ykgh6AhsHp9bQtoBUAw==} + '@oxlint/binding-linux-arm-musleabihf@1.62.0': + resolution: {integrity: sha512-i5vkAuxvueTODV3J2dL61/TXewDHhMFKvtD156cIsk7GsdfiAu7zW7kY0NJXhKeFHeiMZIh7eFNjkPYH6J47HQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.52.0': - resolution: {integrity: sha512-2Ep1tnGLuGG7lUkKG/nilIJ0/T2rebEcATxMJ7afuhD6Z2Sc9dDcpX00IngAMyR9l6hXrvaOw9YA5HUAJVSENg==} + '@oxlint/binding-linux-arm64-gnu@1.62.0': + resolution: {integrity: sha512-QwN19LLuIGuOjEflSeJkZmOTfBdBMlTmW8xbMf8TZhjd//cxVNYQPq75q7oKZBJc6hRx3gY7sX0Egc8cEIFZYg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.52.0': - resolution: {integrity: sha512-54wxvb1Pztz0GMgTLUG9HsH8uhZSL4UbG7n4PDxWIRT9TygTVYKfD6D7iasYdKg6ZpWB5Y86VMxgjSJpR/Y7bQ==} + '@oxlint/binding-linux-arm64-musl@1.62.0': + resolution: {integrity: sha512-8eCy3FCDuWUM5hWujAv6heMvfZPbcCOU3SdQUAkixZLu5bSzOkNfirJiLGoQFO943xceOKkiQRMQNzH++jM3WA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.52.0': - resolution: {integrity: sha512-A82Zks1lJyLclrj8n2tJPHOw2ieZXCaBctnCarS1BRlPQMC1Y98vWCLqgvg9ssWy5ZAja0IjUHN1cYsp53mrqA==} + '@oxlint/binding-linux-ppc64-gnu@1.62.0': + resolution: {integrity: sha512-NjQ7K7tpTPDe9J+yq8p/s/J0E7lRCkK2uDBDqvT4XIT6f4Z0tlnr59OBg/WcrmVHER1AbrcfyxhGTXgcG8ytWg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.52.0': - resolution: {integrity: sha512-ci89Ou+u9vnA0r4eQqGm/KPEkpea+QEtZCLKkrOAD/K5ZBwjS8ToID6aMgsDbIOJUNBGufsmX0iCC7EWrNKQFA==} + '@oxlint/binding-linux-riscv64-gnu@1.62.0': + resolution: {integrity: sha512-oKZed9gmSwze29dEt3/Wnsv6l/Ygw/FUst+8Kfpv2SGeS/glEoTGZAMQw37SVyzFV76UTHJN2snGgxK2t2+8ow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.52.0': - resolution: {integrity: sha512-3/+DVDWajFSu69TaYnKkoUgMEcHR3puO8TcBu3fPCKRhbLjgwDiYIVRdvQX0QaSjkNPJARmpYq7vlPHWNo2cUA==} + '@oxlint/binding-linux-riscv64-musl@1.62.0': + resolution: {integrity: sha512-gBjBxQ+9lGpAYq+ELqw0w8QXsBnkZclFc7GRX2r0LnEVn3ZTEqeIKpKcGjucmp76Q53bvJD0i4qBWBhcfhSfGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.52.0': - resolution: {integrity: sha512-BU7CbceOh00NDmY1IYr72qZoj4sJVHB9DCL2tIq2vyNllNJIpZWTxqlzdqmC4FViXWMy8kZNkOa+SdauH+EcoQ==} + '@oxlint/binding-linux-s390x-gnu@1.62.0': + resolution: {integrity: sha512-Ew2Kxs9EQ9/mbAIJ2hvocMC0wsOu6YKzStI2eFBDt+Td5O8seVC/oxgRIHqCcl5sf5ratA1nozQBAuv7tphkHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.52.0': - resolution: {integrity: sha512-JUVZ6TKYl1yArS3xGsNLQlZxgVpjNKtZFja6VxSTDy2ToN7H58PiDRcxWoN2XoIcWlHSvK7pkIPFNOyzdEJ23A==} + '@oxlint/binding-linux-x64-gnu@1.62.0': + resolution: {integrity: sha512-5z25jcAA0gfKyVwz71A0VXgaPlocPoTAxhlv/hgoK6tlCrfoNuw7haWbDHvGMfjXhdic4EqVXGRv5XsTqFnbRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.52.0': - resolution: {integrity: sha512-IatLKG6UUbIbTBjBZ9SIAYp4SIvOpYIXPXn9cMLqWxh9HrHsu0fLNL+VQ67y4vdlIleYLeuIHkAp3M6saIN1RQ==} + '@oxlint/binding-linux-x64-musl@1.62.0': + resolution: {integrity: sha512-IWpHmMB6ZDllPvqWDkG6AmXrN7JF5e/c4g/0PuURsmlK+vHoYZPB70rr4u1bn3I4LsKCSpqqfveyx6UCOC8wdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.52.0': - resolution: {integrity: sha512-CWgJ6FepHryuc/lgQWStFf3lcvEkbFLSa9zqO0D0QLVfrdg43I4XItKpL/bnfm4n7obzwgG8j8sBggdoxJQKfw==} + '@oxlint/binding-openharmony-arm64@1.62.0': + resolution: {integrity: sha512-fjlSxxrD5pA594vkyikCS9MnPRjQawW6/BLgyTYkO+73wwPlYjkcZ7LSd974l0Q2zkHQmu4DPvJFLYA7o8xrxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.52.0': - resolution: {integrity: sha512-EuNAbPpctu8jYMZnvYh53Xw3YVY2nIi9bQlyMjY0eKiJxDv8ikHrAfcVcwTQW9xa5tp0eiMkmW7iHPP5CYUC9Q==} + '@oxlint/binding-win32-arm64-msvc@1.62.0': + resolution: {integrity: sha512-EiFXr8loNS0Ul3Gu80+9nr1T8jRmnKocqmHHg16tj5ZqTgUXyb97l2rrspVHdDluyFn9JfR4PoJFdNzw4paHww==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.52.0': - resolution: {integrity: sha512-wu3fquQttzSXwyy8DfdOG3Kyb17yAbRhwPlly7NHSXkrffAEAmZ6+o38tCNgsReGLugbn/wbq4uS4nEQubCq+A==} + '@oxlint/binding-win32-ia32-msvc@1.62.0': + resolution: {integrity: sha512-IgOFvL73li1bFgab+hThXYA0N2Xms2kV2MvZN95cebV+fmrZ9AVui1JSxfeeqRLo3CpPxKZlzhyq4G0cnaAvIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.52.0': - resolution: {integrity: sha512-wikx9I9J9/lPOZlrCCNgm8YjWkia8NZfhWd1TTvZTMguyChbw/oA2VEM6Fzx+kkpA+1qu5Mo7nrLdOXEJavw8g==} + '@oxlint/binding-win32-x64-msvc@1.62.0': + resolution: {integrity: sha512-6hMpyDWQ2zGA1OXFKBrdYMUveUCO8UJhkO6JdwZPd78xIdHZNhjx+pib+4fC2Cljuhjyl0QwA2F3df/bs4Bp6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1154,16 +1154,16 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint-tsgolint@0.16.0: - resolution: {integrity: sha512-4RuJK2jP08XwqtUu+5yhCbxEauCm6tv2MFHKEMsjbosK2+vy5us82oI3VLuHwbNyZG7ekZA26U2LLHnGR4frIA==} + oxlint-tsgolint@0.22.1: + resolution: {integrity: sha512-YUSGSLUnoolsu8gxISEDio3q1rtsCozwfOzASUn3DT2mR2EeQ93uEEnen7s+6LpF+lyTQFln1pQfqwBh/fsVEg==} hasBin: true - oxlint@1.52.0: - resolution: {integrity: sha512-InLldD+6+3iHJGIrtU1W37UIpsg+xoGCemkZCuSQhxUO3evMX+L872ONvbECyRza9k7ScMCukJIK3Al/2ZMDnQ==} + oxlint@1.62.0: + resolution: {integrity: sha512-1uFkg6HakjsGIpW9wNdeW4/2LOHW9MEkoWjZUTUfQtIHyLIZPYt00w3Sg+H3lH+206FgBPHBbW5dVE5l2ExECQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.15.0' + oxlint-tsgolint: '>=0.18.0' peerDependenciesMeta: oxlint-tsgolint: optional: true @@ -1727,79 +1727,79 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.37.0': optional: true - '@oxlint-tsgolint/darwin-arm64@0.16.0': + '@oxlint-tsgolint/darwin-arm64@0.22.1': optional: true - '@oxlint-tsgolint/darwin-x64@0.16.0': + '@oxlint-tsgolint/darwin-x64@0.22.1': optional: true - '@oxlint-tsgolint/linux-arm64@0.16.0': + '@oxlint-tsgolint/linux-arm64@0.22.1': optional: true - '@oxlint-tsgolint/linux-x64@0.16.0': + '@oxlint-tsgolint/linux-x64@0.22.1': optional: true - '@oxlint-tsgolint/win32-arm64@0.16.0': + '@oxlint-tsgolint/win32-arm64@0.22.1': optional: true - '@oxlint-tsgolint/win32-x64@0.16.0': + '@oxlint-tsgolint/win32-x64@0.22.1': optional: true - '@oxlint/binding-android-arm-eabi@1.52.0': + '@oxlint/binding-android-arm-eabi@1.62.0': optional: true - '@oxlint/binding-android-arm64@1.52.0': + '@oxlint/binding-android-arm64@1.62.0': optional: true - '@oxlint/binding-darwin-arm64@1.52.0': + '@oxlint/binding-darwin-arm64@1.62.0': optional: true - '@oxlint/binding-darwin-x64@1.52.0': + '@oxlint/binding-darwin-x64@1.62.0': optional: true - '@oxlint/binding-freebsd-x64@1.52.0': + '@oxlint/binding-freebsd-x64@1.62.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.52.0': + '@oxlint/binding-linux-arm-gnueabihf@1.62.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.52.0': + '@oxlint/binding-linux-arm-musleabihf@1.62.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.52.0': + '@oxlint/binding-linux-arm64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.52.0': + '@oxlint/binding-linux-arm64-musl@1.62.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.52.0': + '@oxlint/binding-linux-ppc64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.52.0': + '@oxlint/binding-linux-riscv64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.52.0': + '@oxlint/binding-linux-riscv64-musl@1.62.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.52.0': + '@oxlint/binding-linux-s390x-gnu@1.62.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.52.0': + '@oxlint/binding-linux-x64-gnu@1.62.0': optional: true - '@oxlint/binding-linux-x64-musl@1.52.0': + '@oxlint/binding-linux-x64-musl@1.62.0': optional: true - '@oxlint/binding-openharmony-arm64@1.52.0': + '@oxlint/binding-openharmony-arm64@1.62.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.52.0': + '@oxlint/binding-win32-arm64-msvc@1.62.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.52.0': + '@oxlint/binding-win32-ia32-msvc@1.62.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.52.0': + '@oxlint/binding-win32-x64-msvc@1.62.0': optional: true '@shikijs/core@1.21.0': @@ -2422,37 +2422,37 @@ snapshots: '@oxfmt/binding-win32-ia32-msvc': 0.37.0 '@oxfmt/binding-win32-x64-msvc': 0.37.0 - oxlint-tsgolint@0.16.0: + oxlint-tsgolint@0.22.1: optionalDependencies: - '@oxlint-tsgolint/darwin-arm64': 0.16.0 - '@oxlint-tsgolint/darwin-x64': 0.16.0 - '@oxlint-tsgolint/linux-arm64': 0.16.0 - '@oxlint-tsgolint/linux-x64': 0.16.0 - '@oxlint-tsgolint/win32-arm64': 0.16.0 - '@oxlint-tsgolint/win32-x64': 0.16.0 - - oxlint@1.52.0(oxlint-tsgolint@0.16.0): + '@oxlint-tsgolint/darwin-arm64': 0.22.1 + '@oxlint-tsgolint/darwin-x64': 0.22.1 + '@oxlint-tsgolint/linux-arm64': 0.22.1 + '@oxlint-tsgolint/linux-x64': 0.22.1 + '@oxlint-tsgolint/win32-arm64': 0.22.1 + '@oxlint-tsgolint/win32-x64': 0.22.1 + + oxlint@1.62.0(oxlint-tsgolint@0.22.1): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.52.0 - '@oxlint/binding-android-arm64': 1.52.0 - '@oxlint/binding-darwin-arm64': 1.52.0 - '@oxlint/binding-darwin-x64': 1.52.0 - '@oxlint/binding-freebsd-x64': 1.52.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.52.0 - '@oxlint/binding-linux-arm-musleabihf': 1.52.0 - '@oxlint/binding-linux-arm64-gnu': 1.52.0 - '@oxlint/binding-linux-arm64-musl': 1.52.0 - '@oxlint/binding-linux-ppc64-gnu': 1.52.0 - '@oxlint/binding-linux-riscv64-gnu': 1.52.0 - '@oxlint/binding-linux-riscv64-musl': 1.52.0 - '@oxlint/binding-linux-s390x-gnu': 1.52.0 - '@oxlint/binding-linux-x64-gnu': 1.52.0 - '@oxlint/binding-linux-x64-musl': 1.52.0 - '@oxlint/binding-openharmony-arm64': 1.52.0 - '@oxlint/binding-win32-arm64-msvc': 1.52.0 - '@oxlint/binding-win32-ia32-msvc': 1.52.0 - '@oxlint/binding-win32-x64-msvc': 1.52.0 - oxlint-tsgolint: 0.16.0 + '@oxlint/binding-android-arm-eabi': 1.62.0 + '@oxlint/binding-android-arm64': 1.62.0 + '@oxlint/binding-darwin-arm64': 1.62.0 + '@oxlint/binding-darwin-x64': 1.62.0 + '@oxlint/binding-freebsd-x64': 1.62.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.62.0 + '@oxlint/binding-linux-arm-musleabihf': 1.62.0 + '@oxlint/binding-linux-arm64-gnu': 1.62.0 + '@oxlint/binding-linux-arm64-musl': 1.62.0 + '@oxlint/binding-linux-ppc64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-gnu': 1.62.0 + '@oxlint/binding-linux-riscv64-musl': 1.62.0 + '@oxlint/binding-linux-s390x-gnu': 1.62.0 + '@oxlint/binding-linux-x64-gnu': 1.62.0 + '@oxlint/binding-linux-x64-musl': 1.62.0 + '@oxlint/binding-openharmony-arm64': 1.62.0 + '@oxlint/binding-win32-arm64-msvc': 1.62.0 + '@oxlint/binding-win32-ia32-msvc': 1.62.0 + '@oxlint/binding-win32-x64-msvc': 1.62.0 + oxlint-tsgolint: 0.22.1 parent-module@1.0.1: dependencies: From a38f216922bc4661b0e335a9208bd634664def6a Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Mon, 11 May 2026 16:04:00 +0100 Subject: [PATCH 36/81] BUILD: Update stylelint --- package.json | 2 +- pnpm-lock.yaml | 168 ++++++++++++++++++++++++++----------------------- 2 files changed, 92 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index 70fc4e722..f0853878b 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "oxfmt": "^0.37.0", "oxlint": "^1.62.0", "oxlint-tsgolint": "^0.22.1", - "stylelint": "^17.4.0", + "stylelint": "^17.9.1", "stylelint-config-standard": "^40.0.0", "tsx": "^4.16.5", "typedoc": "^0.26.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9c464668..7f5585b1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,11 +26,11 @@ importers: specifier: ^0.22.1 version: 0.22.1 stylelint: - specifier: ^17.4.0 - version: 17.4.0(typescript@5.6.2) + specifier: ^17.9.1 + version: 17.9.1(typescript@5.6.2) stylelint-config-standard: specifier: ^40.0.0 - version: 40.0.0(stylelint@17.4.0(typescript@5.6.2)) + version: 40.0.0(stylelint@17.9.1(typescript@5.6.2)) tsx: specifier: ^4.16.5 version: 4.19.1 @@ -80,11 +80,11 @@ packages: '@cacheable/memory@2.0.8': resolution: {integrity: sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==} - '@cacheable/utils@2.4.0': - resolution: {integrity: sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==} + '@cacheable/utils@2.4.1': + resolution: {integrity: sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==} - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -96,8 +96,13 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.0': - resolution: {integrity: sha512-H4tuz2nhWgNKLt1inYpoVCfbJbMwX/lQKp3g69rrrIMIYlFD9+zTykOKhNR8uGrAmbS/kT9n6hTFkmDkxLgeTA==} + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true '@csstools/css-tokenizer@4.0.0': resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} @@ -631,8 +636,8 @@ packages: '@vue/shared@3.5.10': resolution: {integrity: sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==} - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -685,8 +690,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - cacheable@2.3.3: - resolution: {integrity: sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==} + cacheable@2.3.4: + resolution: {integrity: sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew==} callsite@1.0.0: resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} @@ -867,11 +872,11 @@ packages: resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} engines: {node: '>= 10.13.0'} - flat-cache@6.1.20: - resolution: {integrity: sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==} + flat-cache@6.1.22: + resolution: {integrity: sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug==} - flatted@3.4.1: - resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -916,8 +921,8 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globby@16.1.1: - resolution: {integrity: sha512-dW7vl+yiAJSp6aCekaVnVJxurRv7DCOLyXqEG3RYMYUg7AuJ2jCqPkZTA8ooqC2vtnkaMcV5WfFBMuEnTu1OQg==} + globby@16.2.0: + resolution: {integrity: sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==} engines: {node: '>=20'} globjoin@0.1.4: @@ -931,8 +936,8 @@ packages: resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} engines: {node: '>=12'} - hashery@1.5.0: - resolution: {integrity: sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==} + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} engines: {node: '>=20'} hasown@2.0.2: @@ -952,6 +957,9 @@ packages: hookified@1.15.1: resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + html-tags@5.1.0: resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} engines: {node: '>=20.10'} @@ -971,13 +979,13 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} @@ -1132,8 +1140,8 @@ packages: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1217,8 +1225,8 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.13: + resolution: {integrity: sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==} engines: {node: ^10 || ^12 || >=14} property-information@6.5.0: @@ -1228,8 +1236,8 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} - qified@0.6.0: - resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} + qified@0.9.1: + resolution: {integrity: sha512-n7mar4T0xQ+39dE2vGTAlbxUEpndwPANH0kDef1/MYsB8Bba9wshkybIRx74qgcvKQPEWErf9AqAdYjhzY2Ilg==} engines: {node: '>=20'} queue-microtask@1.2.3: @@ -1316,8 +1324,8 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} stringify-entities@4.0.4: @@ -1343,8 +1351,8 @@ packages: peerDependencies: stylelint: ^17.0.0 - stylelint@17.4.0: - resolution: {integrity: sha512-3kQ2/cHv3Zt8OBg+h2B8XCx9evEABQIrv4hh3uXahGz/ZEHrTR80zxBiK2NfXNaSoyBzxO1pjsz1Vhdzwn5XSw==} + stylelint@17.9.1: + resolution: {integrity: sha512-THTmnAPJTrg/JhkTWZlSyrO+HUYMx6ELthIHeMyD2WOKqXIJUFQv2Yxn91bvUrZdbBJaW2dUuQdPST2wcQ6C3g==} engines: {node: '>=20.19.0'} hasBin: true @@ -1525,17 +1533,17 @@ snapshots: '@cacheable/memory@2.0.8': dependencies: - '@cacheable/utils': 2.4.0 + '@cacheable/utils': 2.4.1 '@keyv/bigmap': 1.3.1(keyv@5.6.0) hookified: 1.15.1 keyv: 5.6.0 - '@cacheable/utils@2.4.0': + '@cacheable/utils@2.4.1': dependencies: - hashery: 1.5.0 + hashery: 1.5.1 keyv: 5.6.0 - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -1544,7 +1552,9 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.0': {} + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 '@csstools/css-tokenizer@4.0.0': {} @@ -1652,7 +1662,7 @@ snapshots: '@keyv/bigmap@1.3.1(keyv@5.6.0)': dependencies: - hashery: 1.5.0 + hashery: 1.5.1 hookified: 1.15.1 keyv: 5.6.0 @@ -1879,7 +1889,7 @@ snapshots: '@vue/shared@3.5.10': {} - ajv@8.18.0: + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.0 @@ -1927,13 +1937,13 @@ snapshots: dependencies: fill-range: 7.1.1 - cacheable@2.3.3: + cacheable@2.3.4: dependencies: '@cacheable/memory': 2.0.8 - '@cacheable/utils': 2.4.0 + '@cacheable/utils': 2.4.1 hookified: 1.15.1 keyv: 5.6.0 - qified: 0.6.0 + qified: 0.9.1 callsite@1.0.0: {} @@ -1988,7 +1998,7 @@ snapshots: cosmiconfig@9.0.1(typescript@5.6.2): dependencies: env-paths: 2.2.1 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: @@ -2118,7 +2128,7 @@ snapshots: file-entry-cache@11.1.2: dependencies: - flat-cache: 6.1.20 + flat-cache: 6.1.22 fill-range@7.1.1: dependencies: @@ -2131,13 +2141,13 @@ snapshots: micromatch: 4.0.8 resolve-dir: 1.0.1 - flat-cache@6.1.20: + flat-cache@6.1.22: dependencies: - cacheable: 2.3.3 - flatted: 3.4.1 + cacheable: 2.3.4 + flatted: 3.4.2 hookified: 1.15.1 - flatted@3.4.1: {} + flatted@3.4.2: {} fsevents@2.3.3: optional: true @@ -2182,7 +2192,7 @@ snapshots: globals@11.12.0: {} - globby@16.1.1: + globby@16.2.0: dependencies: '@sindresorhus/merge-streams': 4.0.0 fast-glob: 3.3.3 @@ -2197,7 +2207,7 @@ snapshots: has-flag@5.0.1: {} - hashery@1.5.0: + hashery@1.5.1: dependencies: hookified: 1.15.1 @@ -2229,6 +2239,8 @@ snapshots: hookified@1.15.1: {} + hookified@2.2.0: {} + html-tags@5.1.0: {} html-void-elements@3.0.0: {} @@ -2242,9 +2254,12 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-meta-resolve@4.2.0: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 - imurmurhash@0.1.4: {} + import-meta-resolve@4.2.0: {} ini@1.3.8: {} @@ -2388,7 +2403,7 @@ snapshots: arrify: 2.0.1 minimatch: 3.1.2 - nanoid@3.3.11: {} + nanoid@3.3.12: {} nanoid@3.3.7: {} @@ -2481,9 +2496,9 @@ snapshots: dependencies: semver-compare: 1.0.0 - postcss-safe-parser@7.0.1(postcss@8.5.8): + postcss-safe-parser@7.0.1(postcss@8.5.13): dependencies: - postcss: 8.5.8 + postcss: 8.5.13 postcss-selector-parser@7.1.1: dependencies: @@ -2498,9 +2513,9 @@ snapshots: picocolors: 1.1.0 source-map-js: 1.2.1 - postcss@8.5.8: + postcss@8.5.13: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -2508,9 +2523,9 @@ snapshots: punycode.js@2.3.1: {} - qified@0.6.0: + qified@0.9.1: dependencies: - hookified: 1.15.1 + hookified: 2.2.0 queue-microtask@1.2.3: {} @@ -2584,7 +2599,7 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@8.2.0: + string-width@8.2.1: dependencies: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 @@ -2602,20 +2617,20 @@ snapshots: dependencies: ansi-regex: 6.2.2 - stylelint-config-recommended@18.0.0(stylelint@17.4.0(typescript@5.6.2)): + stylelint-config-recommended@18.0.0(stylelint@17.9.1(typescript@5.6.2)): dependencies: - stylelint: 17.4.0(typescript@5.6.2) + stylelint: 17.9.1(typescript@5.6.2) - stylelint-config-standard@40.0.0(stylelint@17.4.0(typescript@5.6.2)): + stylelint-config-standard@40.0.0(stylelint@17.9.1(typescript@5.6.2)): dependencies: - stylelint: 17.4.0(typescript@5.6.2) - stylelint-config-recommended: 18.0.0(stylelint@17.4.0(typescript@5.6.2)) + stylelint: 17.9.1(typescript@5.6.2) + stylelint-config-recommended: 18.0.0(stylelint@17.9.1(typescript@5.6.2)) - stylelint@17.4.0(typescript@5.6.2): + stylelint@17.9.1(typescript@5.6.2): dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-syntax-patches-for-csstree': 1.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) @@ -2629,23 +2644,22 @@ snapshots: fastest-levenshtein: 1.0.16 file-entry-cache: 11.1.2 global-modules: 2.0.0 - globby: 16.1.1 + globby: 16.2.0 globjoin: 0.1.4 html-tags: 5.1.0 ignore: 7.0.5 import-meta-resolve: 4.2.0 - imurmurhash: 0.1.4 is-plain-object: 5.0.0 mathml-tag-names: 4.0.0 meow: 14.1.0 micromatch: 4.0.8 normalize-path: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.8 - postcss-safe-parser: 7.0.1(postcss@8.5.8) + postcss: 8.5.13 + postcss-safe-parser: 7.0.1(postcss@8.5.13) postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - string-width: 8.2.0 + string-width: 8.2.1 supports-hyperlinks: 4.4.0 svg-tags: 1.0.0 table: 6.9.0 @@ -2671,7 +2685,7 @@ snapshots: table@6.9.0: dependencies: - ajv: 8.18.0 + ajv: 8.20.0 lodash.truncate: 4.4.2 slice-ansi: 4.0.0 string-width: 4.2.3 From c74799c47945c0b89d5dc3a91ceaf0b236aeb78b Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Mon, 11 May 2026 16:10:26 +0100 Subject: [PATCH 37/81] BUILD: Update typescript to 6.0 --- packages/backend/pkg/package.json | 2 +- packages/backend/pkg/pnpm-lock.yaml | 10 +- packages/document-methods/package.json | 2 +- packages/document-methods/pnpm-lock.yaml | 10 +- packages/frontend/default.nix | 3 +- packages/frontend/package.json | 2 +- packages/frontend/pnpm-lock.yaml | 117 +++++++------ packages/frontend/src/vite-env.d.ts | 7 + packages/gaios/package.json | 2 +- packages/gaios/pnpm-lock.yaml | 100 ++++++----- packages/ui-components/package.json | 2 +- packages/ui-components/pnpm-lock.yaml | 159 +++++++++++------- .../src/expandable_list.stories.tsx | 4 +- 13 files changed, 250 insertions(+), 170 deletions(-) diff --git a/packages/backend/pkg/package.json b/packages/backend/pkg/package.json index 424094301..d20ccad18 100644 --- a/packages/backend/pkg/package.json +++ b/packages/backend/pkg/package.json @@ -9,6 +9,6 @@ }, "dependencies": { "@qubit-rs/client": "^0.4.5", - "typescript": "^5.6.2" + "typescript": "^6.0.3" } } diff --git a/packages/backend/pkg/pnpm-lock.yaml b/packages/backend/pkg/pnpm-lock.yaml index dece28881..e542947de 100644 --- a/packages/backend/pkg/pnpm-lock.yaml +++ b/packages/backend/pkg/pnpm-lock.yaml @@ -17,16 +17,16 @@ importers: specifier: ^0.4.5 version: 0.4.5 typescript: - specifier: ^5.6.2 - version: 5.6.2 + specifier: ^6.0.3 + version: 6.0.3 packages: '@qubit-rs/client@0.4.5': resolution: {integrity: sha512-L7pBAUs1sPWCkuoRq7rdwqXy1+iLOmOVhADxj+QoNY24yu4J1aiTUSlJJllK9rXRxfvkjRNEPSeUE5H+U3ncug==} - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -34,4 +34,4 @@ snapshots: '@qubit-rs/client@0.4.5': {} - typescript@5.6.2: {} + typescript@6.0.3: {} diff --git a/packages/document-methods/package.json b/packages/document-methods/package.json index 0e9129293..16963d70e 100644 --- a/packages/document-methods/package.json +++ b/packages/document-methods/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@types/uuid": "^10.0.0", - "typescript": "^5.2.2", + "typescript": "^6.0.3", "wasm-pack": "^0.14.0" } } diff --git a/packages/document-methods/pnpm-lock.yaml b/packages/document-methods/pnpm-lock.yaml index eac366654..57ec8280b 100644 --- a/packages/document-methods/pnpm-lock.yaml +++ b/packages/document-methods/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^10.0.0 version: 10.0.0 typescript: - specifier: ^5.2.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 wasm-pack: specifier: ^0.14.0 version: 0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56) @@ -126,8 +126,8 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -236,7 +236,7 @@ snapshots: tiny-invariant@1.3.3: {} - typescript@5.9.3: {} + typescript@6.0.3: {} uuid@13.0.0: {} diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index b0420c8e7..c1a4dc99a 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -37,7 +37,8 @@ let pnpmDeps = pkgs.fetchPnpmDeps { # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-LpQ4TxbVpdXPe6MGdtg/TQX7n4b0QAkgkj0ju9U+gZc="; + hash = "sha256-heS7iYxu9mOAHa4yQ6d1/j1SO8LPA7Vj1evLmzqiVqo="; + pname = name; fetcherVersion = 2; # Only includes package.json and pnpm-lock.yaml files to ensure consistent hashing in different diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d9b441644..af14ba0d3 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -88,7 +88,7 @@ "solid-mdx": "^0.0.7", "typed-css-modules": "^0.9.1", "typedoc": "^0.26.8", - "typescript": "^5.2.2", + "typescript": "^6.0.3", "vite": "^7.2.2", "vite-plugin-solid": "^2.11.10", "vite-plugin-wasm": "^3.5.0", diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index 57705f9b8..c09876f37 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -54,7 +54,7 @@ importers: version: 3.1.0(acorn@8.16.0)(rollup@4.53.2) '@modular-forms/solid': specifier: ^0.24.1 - version: 0.24.1(solid-js@1.9.10)(typescript@5.6.2) + version: 0.24.1(solid-js@1.9.10)(typescript@6.0.3) '@qubit-rs/client': specifier: ^0.4.5 version: 0.4.5 @@ -181,7 +181,7 @@ importers: version: 24.10.0 eslint-plugin-solid: specifier: ^0.14.5 - version: 0.14.5(eslint@9.39.4)(typescript@5.6.2) + version: 0.14.5(eslint@9.39.4)(typescript@6.0.3) rehype-katex: specifier: ^7.0.1 version: 7.0.1 @@ -199,10 +199,10 @@ importers: version: 0.9.1 typedoc: specifier: ^0.26.8 - version: 0.26.8(typescript@5.6.2) + version: 0.26.8(typescript@6.0.3) typescript: - specifier: ^5.2.2 - version: 5.6.2 + specifier: ^6.0.3 + version: 6.0.3 vite: specifier: ^7.2.2 version: 7.2.2(@types/node@24.10.0)(yaml@2.5.1) @@ -826,12 +826,16 @@ packages: engines: {node: '>=6'} hasBin: true - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -1305,8 +1309,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -1384,6 +1388,9 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} @@ -1750,8 +1757,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.1: - resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} @@ -2861,8 +2868,8 @@ packages: peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x - typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -3536,7 +3543,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 @@ -3913,13 +3920,18 @@ snapshots: protobufjs: 7.4.0 yargs: 17.7.2 - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -4019,10 +4031,10 @@ snapshots: - acorn - supports-color - '@modular-forms/solid@0.24.1(solid-js@1.9.10)(typescript@5.6.2)': + '@modular-forms/solid@0.24.1(solid-js@1.9.10)(typescript@6.0.3)': dependencies: solid-js: 1.9.10 - valibot: 1.0.0-beta.0(typescript@5.6.2) + valibot: 1.0.0-beta.0(typescript@6.0.3) transitivePeerDependencies: - typescript @@ -4305,12 +4317,12 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/project-service@8.57.0(typescript@5.6.2)': + '@typescript-eslint/project-service@8.57.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.6.2) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@6.0.3) '@typescript-eslint/types': 8.57.0 debug: 4.4.3 - typescript: 5.6.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4319,35 +4331,35 @@ snapshots: '@typescript-eslint/types': 8.57.0 '@typescript-eslint/visitor-keys': 8.57.0 - '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.6.2)': + '@typescript-eslint/tsconfig-utils@8.57.0(typescript@6.0.3)': dependencies: - typescript: 5.6.2 + typescript: 6.0.3 '@typescript-eslint/types@8.57.0': {} - '@typescript-eslint/typescript-estree@8.57.0(typescript@5.6.2)': + '@typescript-eslint/typescript-estree@8.57.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.57.0(typescript@5.6.2) - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.6.2) + '@typescript-eslint/project-service': 8.57.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@6.0.3) '@typescript-eslint/types': 8.57.0 '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.4 tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.6.2) - typescript: 5.6.2 + ts-api-utils: 2.4.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@5.6.2)': + '@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@typescript-eslint/scope-manager': 8.57.0 '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.6.2) + '@typescript-eslint/typescript-estree': 8.57.0(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.6.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4411,7 +4423,7 @@ snapshots: acorn@8.16.0: {} - ajv@6.14.0: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -4490,6 +4502,11 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -4719,16 +4736,16 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@5.6.2): + eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@6.0.3): dependencies: - '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@5.6.2) + '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 estraverse: 5.3.0 is-html: 2.0.0 kebab-case: 1.0.2 known-css-properties: 0.30.0 style-to-object: 1.0.9 - typescript: 5.6.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4753,11 +4770,11 @@ snapshots: '@eslint/eslintrc': 3.3.5 '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.14.0 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -4932,10 +4949,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.1 + flatted: 3.4.2 keyv: 4.5.4 - flatted@3.4.1: {} + flatted@3.4.2: {} follow-redirects@1.16.0: {} @@ -5990,7 +6007,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.14 minimatch@9.0.5: dependencies: @@ -6567,9 +6584,9 @@ snapshots: trough@2.2.0: {} - ts-api-utils@2.4.0(typescript@5.6.2): + ts-api-utils@2.4.0(typescript@6.0.3): dependencies: - typescript: 5.6.2 + typescript: 6.0.3 ts-pattern@5.4.0: {} @@ -6597,16 +6614,16 @@ snapshots: postcss-modules-values: 4.0.0(postcss@8.4.47) yargs: 17.7.2 - typedoc@0.26.8(typescript@5.6.2): + typedoc@0.26.8(typescript@6.0.3): dependencies: lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 shiki: 1.21.0 - typescript: 5.6.2 + typescript: 6.0.3 yaml: 2.5.1 - typescript@5.6.2: {} + typescript@6.0.3: {} uc.micro@2.1.0: {} @@ -6728,9 +6745,9 @@ snapshots: kleur: 4.1.5 sade: 1.8.1 - valibot@1.0.0-beta.0(typescript@5.6.2): + valibot@1.0.0-beta.0(typescript@6.0.3): optionalDependencies: - typescript: 5.6.2 + typescript: 6.0.3 validate-html-nesting@1.2.2: {} diff --git a/packages/frontend/src/vite-env.d.ts b/packages/frontend/src/vite-env.d.ts index 93a008f03..419a52433 100644 --- a/packages/frontend/src/vite-env.d.ts +++ b/packages/frontend/src/vite-env.d.ts @@ -11,3 +11,10 @@ interface ImportMetaEnv { interface ImportMeta { readonly env: ImportMetaEnv; } + +declare module "*.mdx" { + import type { Component } from "solid-js"; + + const MDXComponent: Component; + export default MDXComponent; +} diff --git a/packages/gaios/package.json b/packages/gaios/package.json index 966261953..97102b6df 100644 --- a/packages/gaios/package.json +++ b/packages/gaios/package.json @@ -39,7 +39,7 @@ "eslint-plugin-solid": "^0.14.5", "nodemon": "^3.1.9", "pushwork": "^1.1.8", - "typescript": "^5.2.2", + "typescript": "^6.0.3", "vite": "^5.3.4", "vite-plugin-css-injected-by-js": "^3.5.2", "vite-plugin-solid": "^2.10.2", diff --git a/packages/gaios/pnpm-lock.yaml b/packages/gaios/pnpm-lock.yaml index b5b1cc9e7..32ff11c4f 100644 --- a/packages/gaios/pnpm-lock.yaml +++ b/packages/gaios/pnpm-lock.yaml @@ -58,7 +58,7 @@ importers: version: 10.4.20(postcss@8.5.1) eslint-plugin-solid: specifier: ^0.14.5 - version: 0.14.5(eslint@9.39.4)(typescript@5.9.2) + version: 0.14.5(eslint@9.39.4)(typescript@6.0.3) nodemon: specifier: ^3.1.9 version: 3.1.9 @@ -66,8 +66,8 @@ importers: specifier: ^1.1.8 version: 1.1.8 typescript: - specifier: ^5.2.2 - version: 5.9.2 + specifier: ^6.0.3 + version: 6.0.3 vite: specifier: ^5.3.4 version: 5.4.14(@types/node@18.19.75)(lightningcss@1.29.1) @@ -412,12 +412,16 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -582,6 +586,9 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -650,8 +657,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -716,6 +723,9 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} @@ -1521,8 +1531,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -1949,7 +1959,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 @@ -1970,13 +1980,18 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -2100,6 +2115,8 @@ snapshots: '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} + '@types/json-schema@7.0.15': {} '@types/node@18.19.75': @@ -2116,12 +2133,12 @@ snapshots: '@types/uuid@10.0.0': {} - '@typescript-eslint/project-service@8.58.1(typescript@5.9.2)': + '@typescript-eslint/project-service@8.58.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.3) '@typescript-eslint/types': 8.58.1 debug: 4.4.3 - typescript: 5.9.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2130,35 +2147,35 @@ snapshots: '@typescript-eslint/types': 8.58.1 '@typescript-eslint/visitor-keys': 8.58.1 - '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.58.1(typescript@6.0.3)': dependencies: - typescript: 5.9.2 + typescript: 6.0.3 '@typescript-eslint/types@8.58.1': {} - '@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.58.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.58.1(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.2) + '@typescript-eslint/project-service': 8.58.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.3) '@typescript-eslint/types': 8.58.1 '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.7.4 tinyglobby: 0.2.16 - ts-api-utils: 2.5.0(typescript@5.9.2) - typescript: 5.9.2 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.2)': + '@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@typescript-eslint/scope-manager': 8.58.1 '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2184,7 +2201,7 @@ snapshots: acorn@8.16.0: {} - ajv@6.14.0: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -2254,6 +2271,11 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -2414,16 +2436,16 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@5.9.2): + eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@6.0.3): dependencies: - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.2) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 estraverse: 5.3.0 is-html: 2.0.0 kebab-case: 1.0.2 known-css-properties: 0.30.0 style-to-object: 1.0.14 - typescript: 5.9.2 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2448,11 +2470,11 @@ snapshots: '@eslint/eslintrc': 3.3.5 '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.6 - ajv: 6.14.0 + '@types/estree': 1.0.8 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -2741,7 +2763,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.14 minimatch@9.0.9: dependencies: @@ -3041,15 +3063,15 @@ snapshots: touch@3.1.1: {} - ts-api-utils@2.5.0(typescript@5.9.2): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.9.2 + typescript: 6.0.3 type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typescript@5.9.2: {} + typescript@6.0.3: {} undefsafe@2.0.5: {} diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 54de9d312..2c695c0d4 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -46,7 +46,7 @@ "playwright": "^1.56.1", "storybook": "^10.0.2", "storybook-solidjs-vite": "^10.0.5", - "typescript": "^5.2.2", + "typescript": "^6.0.3", "vite": "^7.2.2", "vite-plugin-solid": "^2.11.10", "vitest": "^4.0.8" diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 4a1aa1d64..17a39bb40 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -85,7 +85,7 @@ importers: version: 4.0.8(@vitest/browser@4.0.8(vite@7.2.2(@types/node@24.10.0))(vitest@4.0.8))(vitest@4.0.8) eslint-plugin-solid: specifier: ^0.14.5 - version: 0.14.5(eslint@9.39.4)(typescript@5.9.3) + version: 0.14.5(eslint@9.39.4)(typescript@6.0.3) playwright: specifier: ^1.56.1 version: 1.56.1 @@ -94,10 +94,10 @@ importers: version: 10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)) storybook-solidjs-vite: specifier: ^10.0.5 - version: 10.0.5(@testing-library/jest-dom@6.9.1)(esbuild@0.25.11)(rollup@4.52.5)(solid-js@1.9.10)(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)) + version: 10.0.5(@testing-library/jest-dom@6.9.1)(esbuild@0.25.11)(rollup@4.52.5)(solid-js@1.9.10)(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)))(typescript@6.0.3)(vite@7.2.2(@types/node@24.10.0)) typescript: - specifier: ^5.2.2 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 vite: specifier: ^7.2.2 version: 7.2.2(@types/node@24.10.0) @@ -117,6 +117,10 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} @@ -182,8 +186,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': @@ -449,12 +453,16 @@ packages: '@github/relative-time-element@5.0.0': resolution: {integrity: sha512-L/2r0DNR/rMbmHWcsdmhtOiy2gESoGOhItNFD4zJ3nZfHl79Dx3N18Vfx/pYr2lruMOdk1cJZb4wEumm+Dxm1w==} - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -909,8 +917,13 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -982,8 +995,8 @@ packages: resolution: {integrity: sha512-/tk9kky/d8T8CTXIQYASLyhAxR5VwL3zct1oAoVTaOUHwrmsGnfbRwNdEq+vOl2BN8i3PcDdP0o4Q+jjKQoFbQ==} hasBin: true - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1059,6 +1072,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1216,8 +1232,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.1: - resolution: {integrity: sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==} + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} @@ -1759,8 +1775,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -1930,6 +1946,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} '@babel/core@7.28.5': @@ -2012,7 +2034,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.28.4': {} + '@babel/runtime@7.29.2': {} '@babel/template@7.27.2': dependencies: @@ -2199,7 +2221,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 @@ -2233,13 +2255,18 @@ snapshots: '@github/relative-time-element@5.0.0': {} - '@humanfs/core@0.19.1': {} + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.8': dependencies: - '@humanfs/core': 0.19.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 + '@humanfs/types@0.15.0': {} + '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -2253,13 +2280,13 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@joshwooding/vite-plugin-react-docgen-typescript@0.6.2(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.6.2(typescript@6.0.3)(vite@7.2.2(@types/node@24.10.0))': dependencies: glob: 10.4.5 - react-docgen-typescript: 2.4.0(typescript@5.9.3) + react-docgen-typescript: 2.4.0(typescript@6.0.3) vite: 7.2.2(@types/node@24.10.0) optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -2466,8 +2493,8 @@ snapshots: '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -2532,14 +2559,14 @@ snapshots: '@types/react@19.2.2': dependencies: - csstype: 3.1.3 + csstype: 3.2.3 - '@typescript-eslint/project-service@8.57.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.57.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@6.0.3) '@typescript-eslint/types': 8.57.0 debug: 4.4.3 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2548,35 +2575,35 @@ snapshots: '@typescript-eslint/types': 8.57.0 '@typescript-eslint/visitor-keys': 8.57.0 - '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.57.0(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 '@typescript-eslint/types@8.57.0': {} - '@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.57.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.57.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/project-service': 8.57.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@6.0.3) '@typescript-eslint/types': 8.57.0 '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3 minimatch: 10.2.4 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.4.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@typescript-eslint/scope-manager': 8.57.0 '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.57.0(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2703,13 +2730,15 @@ snapshots: '@vitest/pretty-format': 4.0.8 tinyrainbow: 3.0.3 - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn@8.15.0: {} - ajv@6.14.0: + acorn@8.16.0: {} + + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -2772,7 +2801,7 @@ snapshots: baseline-browser-mapping@2.8.22: {} - brace-expansion@1.1.12: + brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 @@ -2838,6 +2867,8 @@ snapshots: csstype@3.1.3: {} + csstype@3.2.3: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2897,16 +2928,16 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@5.9.3): + eslint-plugin-solid@0.14.5(eslint@9.39.4)(typescript@6.0.3): dependencies: - '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 estraverse: 5.3.0 is-html: 2.0.0 kebab-case: 1.0.2 known-css-properties: 0.30.0 style-to-object: 1.0.14 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -2931,11 +2962,11 @@ snapshots: '@eslint/eslintrc': 3.3.5 '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 + '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.14.0 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -2962,8 +2993,8 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 esprima@4.0.1: {} @@ -3009,10 +3040,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.1 + flatted: 3.4.2 keyv: 4.5.4 - flatted@3.4.1: {} + flatted@3.4.2: {} foreground-child@3.3.1: dependencies: @@ -3198,7 +3229,7 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.14 minimatch@9.0.5: dependencies: @@ -3295,9 +3326,9 @@ snapshots: punycode@2.3.1: {} - react-docgen-typescript@2.4.0(typescript@5.9.3): + react-docgen-typescript@2.4.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 + typescript: 6.0.3 react-dom@19.2.0(react@19.2.0): dependencies: @@ -3424,9 +3455,9 @@ snapshots: std-env@3.10.0: {} - storybook-solidjs-vite@10.0.5(@testing-library/jest-dom@6.9.1)(esbuild@0.25.11)(rollup@4.52.5)(solid-js@1.9.10)(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)))(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)): + storybook-solidjs-vite@10.0.5(@testing-library/jest-dom@6.9.1)(esbuild@0.25.11)(rollup@4.52.5)(solid-js@1.9.10)(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)))(typescript@6.0.3)(vite@7.2.2(@types/node@24.10.0)): dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.2(typescript@5.9.3)(vite@7.2.2(@types/node@24.10.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.6.2(typescript@6.0.3)(vite@7.2.2(@types/node@24.10.0)) '@storybook/builder-vite': 10.0.2(esbuild@0.25.11)(rollup@4.52.5)(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0)))(vite@7.2.2(@types/node@24.10.0)) '@storybook/global': 5.0.0 solid-js: 1.9.10 @@ -3434,7 +3465,7 @@ snapshots: vite: 7.2.2(@types/node@24.10.0) vite-plugin-solid: 2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.2(@types/node@24.10.0)) optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - '@testing-library/jest-dom' - esbuild @@ -3517,9 +3548,9 @@ snapshots: totalist@3.0.1: {} - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 + typescript: 6.0.3 ts-dedent@2.2.0: {} @@ -3529,7 +3560,7 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript@5.9.3: {} + typescript@6.0.3: {} undici-types@7.16.0: {} diff --git a/packages/ui-components/src/expandable_list.stories.tsx b/packages/ui-components/src/expandable_list.stories.tsx index dacb06fbb..651447ce6 100644 --- a/packages/ui-components/src/expandable_list.stories.tsx +++ b/packages/ui-components/src/expandable_list.stories.tsx @@ -179,7 +179,9 @@ export const WithKatex: Story = {
} + renderItem={(item) => + typeof item === "string" ? : item + } />
); From 7c7149d50be4f9d003fdbe43692f581168bcd61b Mon Sep 17 00:00:00 2001 From: Kaspar Date: Fri, 15 May 2026 17:39:37 +0100 Subject: [PATCH 38/81] ENH: Reset active input in editors when focus is lost (#1277) --- .../src/diagram/morphism_cell_editor.tsx | 9 ++++++++- .../src/model/contribution_cell_editor.tsx | 9 ++++++++- .../src/model/instantiation_cell_editor.tsx | 17 +++++++++++++++++ .../frontend/src/model/morphism_cell_editor.tsx | 9 ++++++++- .../string_diagram_morphism_cell_editor.tsx | 7 +++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/diagram/morphism_cell_editor.tsx b/packages/frontend/src/diagram/morphism_cell_editor.tsx index aafcf5606..481291f77 100644 --- a/packages/frontend/src/diagram/morphism_cell_editor.tsx +++ b/packages/frontend/src/diagram/morphism_cell_editor.tsx @@ -1,4 +1,4 @@ -import { createSignal, useContext } from "solid-js"; +import { createEffect, createSignal, useContext } from "solid-js"; import invariant from "tiny-invariant"; import { v7 } from "uuid"; @@ -25,6 +25,13 @@ export function DiagramMorphismCellEditor(props: { const [activeInput, setActiveInput] = createSignal("mor"); + // Reset to default on deactivation so re-entry lands on the morphism input. + createEffect(() => { + if (!props.isActive) { + setActiveInput("mor"); + } + }); + const domType = () => props.theory.theory.src(props.decl.morType); const codType = () => props.theory.theory.tgt(props.decl.morType); diff --git a/packages/frontend/src/model/contribution_cell_editor.tsx b/packages/frontend/src/model/contribution_cell_editor.tsx index 6288755f7..673b48434 100644 --- a/packages/frontend/src/model/contribution_cell_editor.tsx +++ b/packages/frontend/src/model/contribution_cell_editor.tsx @@ -1,4 +1,4 @@ -import { createMemo, createSignal, useContext, Switch, Match } from "solid-js"; +import { createEffect, createMemo, createSignal, useContext, Switch, Match } from "solid-js"; import invariant from "tiny-invariant"; import { NameInput } from "catcolab-ui-components"; @@ -34,6 +34,13 @@ export default function ContributionCellEditor( const [activeInput, setActiveInput] = createSignal("name"); + // Reset to default on deactivation so re-entry lands on the name input. + createEffect(() => { + if (!props.isActive) { + setActiveInput("name"); + } + }); + const morTypeMeta = () => props.theory.modelMorTypeMeta(props.morphism.morType); const domType = createMemo(() => { diff --git a/packages/frontend/src/model/instantiation_cell_editor.tsx b/packages/frontend/src/model/instantiation_cell_editor.tsx index 9b034e5a8..d76331cc9 100644 --- a/packages/frontend/src/model/instantiation_cell_editor.tsx +++ b/packages/frontend/src/model/instantiation_cell_editor.tsx @@ -77,6 +77,16 @@ export function InstantiationCellEditor(props: { setActiveIndex(index); }); + // Reset to default on deactivation so re-entry lands on the name input. + createEffect(() => { + if (!props.isActive) { + batch(() => { + setActiveComponent("name"); + setActiveIndex(0); + }); + } + }); + const insertSpecializationAtTop = () => { props.modifyInstantiation((inst) => { inst.specializations.unshift({ id: null, ob: null }); @@ -307,6 +317,13 @@ function SpecializationEditor( const [activeInput, setActiveInput] = createSignal("id"); + // Reset to default on deactivation so re-entry lands on the id input. + createEffect(() => { + if (!props.isActive) { + setActiveInput("id"); + } + }); + const obType = () => { const id = props.specialization.id; if (id) { diff --git a/packages/frontend/src/model/morphism_cell_editor.tsx b/packages/frontend/src/model/morphism_cell_editor.tsx index a3aeb4ebe..a48d12f21 100644 --- a/packages/frontend/src/model/morphism_cell_editor.tsx +++ b/packages/frontend/src/model/morphism_cell_editor.tsx @@ -1,4 +1,4 @@ -import { createMemo, createSignal, useContext } from "solid-js"; +import { createEffect, createMemo, createSignal, useContext } from "solid-js"; import invariant from "tiny-invariant"; import { NameInput } from "catcolab-ui-components"; @@ -18,6 +18,13 @@ export default function MorphismCellEditor(props: MorphismEditorProps) { const [activeInput, setActiveInput] = createSignal("name"); + // Reset to default on deactivation so re-entry lands on the name input. + createEffect(() => { + if (!props.isActive) { + setActiveInput("name"); + } + }); + const morTypeMeta = () => props.theory.modelMorTypeMeta(props.morphism.morType); const domType = createMemo(() => { diff --git a/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx b/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx index 7b98c26ff..d777367b0 100644 --- a/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx +++ b/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx @@ -134,6 +134,13 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro const [active, setActive] = createSignal({ zone: "name" }); + // Reset to default on deactivation so re-entry lands on the name input. + createEffect(() => { + if (!props.isActive) { + setActive({ zone: "name" }); + } + }); + // Track which wire indices have non-empty text (including incomplete input). const domInputTexts = new Map(); const codInputTexts = new Map(); From d04712d1f1fb73b1966a16f139d0c3b23a1ccf51 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Fri, 15 May 2026 17:52:57 +0100 Subject: [PATCH 39/81] BUILD: Update wasm-pack to 0.15 and remove patch (#1281) --- dev-docs/pnpm-lock.yaml | 5 - packages/backend/pkg/pnpm-lock.yaml | 5 - packages/document-methods/package.json | 2 +- packages/document-methods/pnpm-lock.yaml | 220 +++++------------------ packages/frontend/default.nix | 6 +- packages/frontend/package.json | 2 +- packages/frontend/pnpm-lock.yaml | 203 ++++----------------- packages/gaios/pnpm-lock.yaml | 5 - packages/ui-components/pnpm-lock.yaml | 5 - patches/wasm-pack@0.14.0.patch | 13 -- pnpm-lock.yaml | 5 - pnpm-workspace.yaml | 5 +- 12 files changed, 85 insertions(+), 391 deletions(-) delete mode 100644 patches/wasm-pack@0.14.0.patch diff --git a/dev-docs/pnpm-lock.yaml b/dev-docs/pnpm-lock.yaml index a9bc6e643..a36a548cf 100644 --- a/dev-docs/pnpm-lock.yaml +++ b/dev-docs/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../patches/wasm-pack@0.14.0.patch - importers: .: diff --git a/packages/backend/pkg/pnpm-lock.yaml b/packages/backend/pkg/pnpm-lock.yaml index e542947de..d276c483f 100644 --- a/packages/backend/pkg/pnpm-lock.yaml +++ b/packages/backend/pkg/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../../../patches/wasm-pack@0.14.0.patch - importers: .: diff --git a/packages/document-methods/package.json b/packages/document-methods/package.json index 16963d70e..a796f16ca 100644 --- a/packages/document-methods/package.json +++ b/packages/document-methods/package.json @@ -23,6 +23,6 @@ "devDependencies": { "@types/uuid": "^10.0.0", "typescript": "^6.0.3", - "wasm-pack": "^0.14.0" + "wasm-pack": "^0.15.0" } } diff --git a/packages/document-methods/pnpm-lock.yaml b/packages/document-methods/pnpm-lock.yaml index 57ec8280b..8ed08b845 100644 --- a/packages/document-methods/pnpm-lock.yaml +++ b/packages/document-methods/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../../patches/wasm-pack@0.14.0.patch - importers: .: @@ -30,98 +25,33 @@ importers: specifier: ^6.0.3 version: 6.0.3 wasm-pack: - specifier: ^0.14.0 - version: 0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56) + specifier: ^0.15.0 + version: 0.15.0 packages: + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - binary-install@1.1.2: - resolution: {integrity: sha512-ZS2cqFHPZOy4wLxvzqfQvDjCOifn+7uCPqNmYRIBM/03+yllON+4fNnsD0VJdW0p97y+E+dTRNPStWNqMBq+9g==} - engines: {node: '>=10'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + tar@7.5.14: + resolution: {integrity: sha512-/7sHKgQO3JLP9ESlwTYUUftHUadOURUqq23xs1vjcnp8Vss6k0wCfzulyEtk5g91pjvnuriimGlyG7k6msrzRw==} + engines: {node: '>=18'} tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -135,104 +65,38 @@ packages: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true - wasm-pack@0.14.0: - resolution: {integrity: sha512-7uKj+483b6ETTnuWHK3zKNB3Ca3M159tPZ5shyXxI4j7i9Lk82rL2ck/L6E9O5VMWk9JgowdtTBOSfWmGBRFtw==} + wasm-pack@0.15.0: + resolution: {integrity: sha512-DdqtGWc3+iFx+7lL7QU5LBWs7qMnwQSWxF0htSfE15sNa3roVwHjAkTm2JXgueU2GGfSwNqbq2EzyC2b/biKDA==} + engines: {node: '>=16'} hasBin: true - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} snapshots: - '@types/uuid@10.0.0': {} - - axios@0.26.1: + '@isaacs/fs-minipass@4.0.1': dependencies: - follow-redirects: 1.15.11 - transitivePeerDependencies: - - debug - - balanced-match@1.0.2: {} + minipass: 7.1.3 - binary-install@1.1.2: - dependencies: - axios: 0.26.1 - rimraf: 3.0.2 - tar: 6.2.1 - transitivePeerDependencies: - - debug - - brace-expansion@1.1.14: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - chownr@2.0.0: {} - - concat-map@0.0.1: {} - - follow-redirects@1.15.11: {} - - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs.realpath@1.0.0: {} - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.14 - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp@1.0.4: {} + '@types/uuid@10.0.0': {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 + chownr@3.0.0: {} - path-is-absolute@1.0.1: {} + minipass@7.1.3: {} - rimraf@3.0.2: + minizlib@3.1.0: dependencies: - glob: 7.2.3 + minipass: 7.1.3 - tar@6.2.1: + tar@7.5.14: dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 tiny-invariant@1.3.3: {} @@ -240,12 +104,8 @@ snapshots: uuid@13.0.0: {} - wasm-pack@0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56): + wasm-pack@0.15.0: dependencies: - binary-install: 1.1.2 - transitivePeerDependencies: - - debug - - wrappy@1.0.2: {} + tar: 7.5.14 - yallist@4.0.0: {} + yallist@5.0.0: {} diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index c1a4dc99a..79c6b963b 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -19,7 +19,7 @@ let ../../.npmrc ../../pnpm-workspace.yaml ../../pnpm-lock.yaml - ../../patches + (lib.fileset.maybeMissing ../../patches) ../../packages/frontend ../../packages/ui-components ../../packages/document-methods @@ -37,7 +37,7 @@ let pnpmDeps = pkgs.fetchPnpmDeps { # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-heS7iYxu9mOAHa4yQ6d1/j1SO8LPA7Vj1evLmzqiVqo="; + hash = "sha256-tIcRNotCTct15zr6DypDALXS+tyQy4bstldHLX5HYys="; pname = name; fetcherVersion = 2; @@ -49,7 +49,7 @@ let ../../.npmrc ../../pnpm-workspace.yaml ../../pnpm-lock.yaml - ../../patches + (lib.fileset.maybeMissing ../../patches) ../../packages/frontend/package.json ../../packages/frontend/pnpm-lock.yaml ../../packages/ui-components/package.json diff --git a/packages/frontend/package.json b/packages/frontend/package.json index af14ba0d3..7ff9b4bc7 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -93,7 +93,7 @@ "vite-plugin-solid": "^2.11.10", "vite-plugin-wasm": "^3.5.0", "vitest": "^4.0.8", - "wasm-pack": "^0.14.0", + "wasm-pack": "^0.15.0", "web-worker": "^1.5.0" } } diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index c09876f37..f76fce41d 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../../patches/wasm-pack@0.14.0.patch - importers: .: @@ -216,8 +211,8 @@ importers: specifier: ^4.0.8 version: 4.0.8(@types/debug@4.1.12)(@types/node@24.10.0)(yaml@2.5.1) wasm-pack: - specifier: ^0.14.0 - version: 0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56) + specifier: ^0.15.0 + version: 0.15.0 web-worker: specifier: ^1.5.0 version: 1.5.0 @@ -850,6 +845,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1347,9 +1346,6 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - axios@0.26.1: - resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} - babel-plugin-jsx-dom-expressions@0.39.2: resolution: {integrity: sha512-rCkSYFuLl5/XD+BXjZk1XxFAsIBgNe9WZ7xBHjQV1dBliI64kO+EWktAD3b6Bj/SXk+LpVXFyMVydhnI35svWQ==} peerDependencies: @@ -1377,17 +1373,9 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - binary-install@1.1.2: - resolution: {integrity: sha512-ZS2cqFHPZOy4wLxvzqfQvDjCOifn+7uCPqNmYRIBM/03+yllON+4fNnsD0VJdW0p97y+E+dTRNPStWNqMBq+9g==} - engines: {node: '>=10'} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - bind-event-listener@3.0.0: resolution: {integrity: sha512-PJvH288AWQhKs2v9zyfYdPzlPqf5bXbGMmhmUIY9x4dAUGIWgomO771oBQNwJnMQSnUIXhKu6sgzpBRXTlvb8Q==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} @@ -1462,9 +1450,9 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} @@ -1760,26 +1748,10 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - follow-redirects@1.16.0: - resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - foreground-child@3.3.0: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1805,10 +1777,6 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1916,13 +1884,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inline-style-parser@0.1.1: resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} @@ -2349,26 +2310,13 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} @@ -2406,9 +2354,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - oniguruma-to-js@0.4.3: resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==} @@ -2447,10 +2392,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2647,11 +2588,6 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rollup@4.53.2: resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2801,10 +2737,9 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + tar@7.5.14: + resolution: {integrity: sha512-/7sHKgQO3JLP9ESlwTYUUftHUadOURUqq23xs1vjcnp8Vss6k0wCfzulyEtk5g91pjvnuriimGlyG7k6msrzRw==} + engines: {node: '>=18'} tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -3088,8 +3023,9 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - wasm-pack@0.14.0: - resolution: {integrity: sha512-7uKj+483b6ETTnuWHK3zKNB3Ca3M159tPZ5shyXxI4j7i9Lk82rL2ck/L6E9O5VMWk9JgowdtTBOSfWmGBRFtw==} + wasm-pack@0.15.0: + resolution: {integrity: sha512-DdqtGWc3+iFx+7lL7QU5LBWs7qMnwQSWxF0htSfE15sNa3roVwHjAkTm2JXgueU2GGfSwNqbq2EzyC2b/biKDA==} + engines: {node: '>=16'} hasBin: true web-namespaces@2.0.1: @@ -3128,9 +3064,6 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -3153,8 +3086,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} yaml@2.5.1: resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} @@ -3945,6 +3879,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 @@ -4455,12 +4393,6 @@ snapshots: astring@1.9.0: {} - axios@0.26.1: - dependencies: - follow-redirects: 1.16.0 - transitivePeerDependencies: - - debug - babel-plugin-jsx-dom-expressions@0.39.2(@babel/core@7.25.7): dependencies: '@babel/core': 7.25.7 @@ -4487,21 +4419,8 @@ snapshots: binary-extensions@2.3.0: {} - binary-install@1.1.2: - dependencies: - axios: 0.26.1 - rimraf: 3.0.2 - tar: 6.2.1 - transitivePeerDependencies: - - debug - bind-event-listener@3.0.0: {} - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 @@ -4592,7 +4511,7 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chownr@2.0.0: {} + chownr@3.0.0: {} cliui@8.0.1: dependencies: @@ -4954,19 +4873,11 @@ snapshots: flatted@3.4.2: {} - follow-redirects@1.16.0: {} - foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -4991,15 +4902,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - globals@11.12.0: {} globals@14.0.0: {} @@ -5206,13 +5108,6 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - inline-style-parser@0.1.1: {} inline-style-parser@0.2.4: {} @@ -6013,20 +5908,11 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: + minizlib@3.1.0: dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp@1.0.4: {} + minipass: 7.1.2 mkdirp@3.0.1: {} @@ -6049,10 +5935,6 @@ snapshots: normalize-path@3.0.0: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - oniguruma-to-js@0.4.3: dependencies: regex: 4.3.2 @@ -6101,8 +5983,6 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-scurry@1.11.1: @@ -6381,10 +6261,6 @@ snapshots: resolve-from@4.0.0: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rollup@4.53.2: dependencies: '@types/estree': 1.0.8 @@ -6552,14 +6428,13 @@ snapshots: dependencies: has-flag: 4.0.0 - tar@6.2.1: + tar@7.5.14: dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 tiny-invariant@1.3.3: {} @@ -6853,11 +6728,9 @@ snapshots: w3c-keyname@2.2.8: {} - wasm-pack@0.14.0(patch_hash=3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56): + wasm-pack@0.15.0: dependencies: - binary-install: 1.1.2 - transitivePeerDependencies: - - debug + tar: 7.5.14 web-namespaces@2.0.1: {} @@ -6894,8 +6767,6 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 - wrappy@1.0.2: {} - ws@8.18.3: {} xstate@5.20.1: {} @@ -6904,7 +6775,7 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: {} + yallist@5.0.0: {} yaml@2.5.1: {} diff --git a/packages/gaios/pnpm-lock.yaml b/packages/gaios/pnpm-lock.yaml index 32ff11c4f..a23fbaa4e 100644 --- a/packages/gaios/pnpm-lock.yaml +++ b/packages/gaios/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../../patches/wasm-pack@0.14.0.patch - importers: .: diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 17a39bb40..57b133330 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: ../../patches/wasm-pack@0.14.0.patch - importers: .: diff --git a/patches/wasm-pack@0.14.0.patch b/patches/wasm-pack@0.14.0.patch deleted file mode 100644 index a3a2cf447..000000000 --- a/patches/wasm-pack@0.14.0.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/binary.js b/binary.js -index 7f0472a1f80325d4b75860f0ec9f784779a90c01..f9d1fae49b5bb6dd6baa1a8e268c8aa930fe299a 100644 ---- a/binary.js -+++ b/binary.js -@@ -31,7 +31,7 @@ const getPlatform = () => { - const getBinary = () => { - const platform = getPlatform(); - const version = require("./package.json").version; -- const author = "drager"; -+ const author = "wasm-bindgen"; - const name = "wasm-pack"; - const url = `https://github.com/${author}/${name}/releases/download/v${version}/${name}-v${version}-${platform}.tar.gz`; - return new Binary(platform === windows ? "wasm-pack.exe" : "wasm-pack", url, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f5585b1c..d3b51e6c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - wasm-pack@0.14.0: - hash: 3c7b8af86d6b541704193ec3130f1444612e1187cf4f53aff0ed0570b58d5e56 - path: patches/wasm-pack@0.14.0.patch - importers: .: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 53374b52d..9942e7d55 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,7 @@ # 9 days, in minutes minimumReleaseAge: 12960 +minimumReleaseAgeExclude: + - wasm-pack packages: - "dev-docs" - "packages/backend/pkg" @@ -7,5 +9,4 @@ packages: - "packages/document-methods" - "packages/ui-components" - "packages/gaios" -patchedDependencies: - wasm-pack@0.14.0: patches/wasm-pack@0.14.0.patch + From 1dd4ccd85baf6aced7d13cd2dbfde36c3b34ab91 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Fri, 15 May 2026 19:04:44 +0100 Subject: [PATCH 40/81] FIX: Mitigate invalid date being passed to relative_time (#1278) --- packages/ui-components/src/relative_time.tsx | 31 ++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/ui-components/src/relative_time.tsx b/packages/ui-components/src/relative_time.tsx index 672c01ac2..b0f8e0d31 100644 --- a/packages/ui-components/src/relative_time.tsx +++ b/packages/ui-components/src/relative_time.tsx @@ -1,3 +1,5 @@ +import { Show } from "solid-js"; + // oxlint-disable-next-line no-unassigned-import -- side-effect import registers the custom element import "@github/relative-time-element"; @@ -19,9 +21,34 @@ declare module "solid-js" { } } +const warnedTimestamps = new Set(); + +function toIsoStringSafe(ts: unknown): string | undefined { + if (typeof ts !== "number" || !Number.isFinite(ts)) { + if (!warnedTimestamps.has(ts)) { + warnedTimestamps.add(ts); + console.warn("RelativeTime: invalid timestamp", ts); + } + return undefined; + } + const d = new Date(ts); + if (Number.isNaN(d.getTime())) { + if (!warnedTimestamps.has(ts)) { + warnedTimestamps.add(ts); + console.warn("RelativeTime: timestamp produced invalid Date", ts); + } + return undefined; + } + return d.toISOString(); +} + /** Display a timestamp as relative text, with the exact time as a tooltip. Auto-updates. */ export function RelativeTime(props: { timestamp: number }) { - const datetime = () => new Date(props.timestamp).toISOString(); + const datetime = () => toIsoStringSafe(props.timestamp); - return ; + return ( + + ); } From c0568af1c114ba397c8e2198ed34caeb39099c02 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Tue, 19 May 2026 19:59:20 +0100 Subject: [PATCH 41/81] DOC: Tighten up docs on hash mismatch (#1138) Co-authored-by: Jason Moggridge --- dev-docs/fixing-hash-mismatches.md | 56 ++++++++++++++++-------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/dev-docs/fixing-hash-mismatches.md b/dev-docs/fixing-hash-mismatches.md index 43e806c83..b180d3649 100644 --- a/dev-docs/fixing-hash-mismatches.md +++ b/dev-docs/fixing-hash-mismatches.md @@ -4,41 +4,28 @@ title: "Fixing hash mismatches in Nix" ### Fixing Hash Mismatches in Nix -Nix uses **fixed-output derivations** to fetch external dependencies (from npm, crates.io, GitHub, etc.). -These derivations require a cryptographic hash to verify that the fetched content matches what's expected, -ensuring reproducibility and security. +#### pnpm Dependencies in the Frontend -When a fixed-output dependency changes, so will its hash, causing a hash mismatch error in Nix. -This generally happens in two scenarios: -1. Intentional updates: You upgrade a package version -2. Upstream changes: The upstream package was modified without a version change +This only applies to the `frontend` package. The pnpm hash is located in `packages/frontend/default.nix` +within the `pkgs.fetchPnpmDeps` block. -If you encounter a hash mismatch without updating anything it should probably be investigated: it means -the external source changed unexpectedly. - -#### pnpm Dependencies - -This only applies to the `frontend` package. - -The following error occurs when a dependency has changed but the Nix hash has not: +When a pnpm dependency has changed but the Nix hash has not, running `nix build .#frontend` will fail +with a hash mismatch that looks like this: ``` -> ERR_PNPM_NO_OFFLINE_TARBALL  A package is missing from the store but cannot download it in offline mode. The missing package may be downloaded from https://registry.npmjs.org/@automerge/prosemirror/-/prosemirror-0.2.0-alpha.0.tgz. -> ERROR: pnpm failed to install dependencies +error: hash mismatch in fixed-output derivation '/nix/store/4wpp80j18vvm232ii1ajl2kqnbfgvzq2-frontend-pnpm-deps.drv': + specified: sha256-vuBwhtNTTRbpgPZS+AQDybASYM9rwWYG8l0bscVQUso= + got: sha256-sxczRF8IsYqQzmAAv+IiFeWJygqHCVnSk8fEuy5d1JM= ``` -In this case you can follow the instructions given below the error message: +This can be fixed by replacing the `specified` hash in `packages/frontend/default.nix` with the `got` hash. + +You may also see a pnpm-specific error like this: ``` -> If you see ERR_PNPM_NO_OFFLINE_TARBALL above this, follow these to fix the issue: -> 1. Set pnpmDeps.hash to "" (empty string) -> 2. Build the derivation and wait for it to fail with a hash mismatch -> 3. Copy the 'got: sha256-' value back into the pnpmDeps.hash field +> ERR_PNPM_NO_OFFLINE_TARBALL A package is missing from the store but cannot download it in offline mode. +> ERROR: pnpm failed to install dependencies ``` -The hash is located in `packages/frontend/default.nix` within the `pkgs.fetchPnpmDeps` block. -You can search for the text "hash" to find it quickly. - -The frontend package can be built by running the command `nix build .#frontend` in the repository root. -This will build the minimum needed to print the hash mismatch described in the instructions. +This will be accompanied by the hash mismatch above and can be fixed the same way. #### Other Dependencies @@ -58,3 +45,18 @@ Currently all hashes except for `pnpm` are defined in `flake.nix` in the repo ro You may encounter hash mismatches for these dependencies: - wasm-bindgen-cli - rust-toolchain + + +#### Explanation + +Nix uses **fixed-output derivations** to fetch external dependencies (from npm, crates.io, GitHub, etc.). +These derivations require a cryptographic hash to verify that the fetched content matches what's expected, +ensuring reproducibility and security. + +When a fixed-output dependency changes, so will its hash, causing a hash mismatch error in Nix. +This generally happens in two scenarios: +1. Intentional updates: You upgrade a package version +2. Upstream changes: The upstream package was modified without a version change + +If you encounter a hash mismatch without updating anything it should probably be investigated: it means +the external source changed unexpectedly. From bc90390bc1740d2a3ef52d03934b103ef07cc4be Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Wed, 20 May 2026 10:45:00 -0700 Subject: [PATCH 42/81] ENH: Code view component (#1285) Co-authored-by: Kaspar Bumke --- packages/frontend/default.nix | 2 +- packages/frontend/src/stdlib/analyses/sql.tsx | 6 +- packages/ui-components/package.json | 1 + packages/ui-components/pnpm-lock.yaml | 331 ++++++++++++++++++ .../ui-components/src/code_view.stories.tsx | 91 +++++ packages/ui-components/src/code_view.tsx | 26 ++ packages/ui-components/src/index.ts | 1 + packages/ui-components/src/panel.css | 1 + 8 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 packages/ui-components/src/code_view.stories.tsx create mode 100644 packages/ui-components/src/code_view.tsx diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index 79c6b963b..cc090e4ec 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -37,7 +37,7 @@ let pnpmDeps = pkgs.fetchPnpmDeps { # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-tIcRNotCTct15zr6DypDALXS+tyQy4bstldHLX5HYys="; + hash = "sha256-hmjA8fRTgeVYksCp7PVBUTY7Y6huasNCx8KRofapC2g="; pname = name; fetcherVersion = 2; diff --git a/packages/frontend/src/stdlib/analyses/sql.tsx b/packages/frontend/src/stdlib/analyses/sql.tsx index 0ae68af1c..e01ca25b0 100644 --- a/packages/frontend/src/stdlib/analyses/sql.tsx +++ b/packages/frontend/src/stdlib/analyses/sql.tsx @@ -4,7 +4,7 @@ import Copy from "lucide-solid/icons/copy"; import Download from "lucide-solid/icons/download"; import { For, Match, Show, Switch } from "solid-js"; -import { BlockTitle, ErrorAlert, IconButton } from "catcolab-ui-components"; +import { CodeView, BlockTitle, ErrorAlert, IconButton } from "catcolab-ui-components"; import type { ModelAnalysisProps } from "../../analysis"; import * as SQL from "./sql_types.ts"; @@ -105,7 +105,9 @@ export default function SQLSchemaAnalysis( actions={SQLHeader(sql())} settingsPane={BackendConfig()} /> -
{sql()}
+ + + )} diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 2c695c0d4..a20951061 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -27,6 +27,7 @@ "@solid-primitives/destructure": "^0.2.1", "katex": "^0.16.22", "lucide-solid": "^0.471.0", + "shiki": "^4.0.2", "solid-js": "^1.9.10" }, "devDependencies": { diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 57b133330..9ba62674b 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: lucide-solid: specifier: ^0.471.0 version: 0.471.0(solid-js@1.9.10) + shiki: + specifier: ^4.0.2 + version: 4.0.2 solid-js: specifier: ^1.9.10 version: 1.9.10 @@ -634,6 +637,37 @@ packages: cpu: [x64] os: [win32] + '@shikijs/core@4.0.2': + resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.0.2': + resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.0.2': + resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} + engines: {node: '>=20'} + + '@shikijs/langs@4.0.2': + resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.0.2': + resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==} + engines: {node: '>=20'} + + '@shikijs/themes@4.0.2': + resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} + engines: {node: '>=20'} + + '@shikijs/types@4.0.2': + resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@solid-primitives/active-element@2.1.3': resolution: {integrity: sha512-9t5K4aR2naVDj950XU8OjnLgOg94a8k5wr6JNOPK+N5ESLsJDq42c1ZP8UKpewi1R+wplMMxiM6OPKRzbxJY7A==} peerDependencies: @@ -778,12 +812,18 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} @@ -793,6 +833,9 @@ packages: '@types/react@19.2.2': resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@typescript-eslint/project-service@8.57.0': resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -830,6 +873,9 @@ packages: resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@vitest/browser-playwright@4.0.8': resolution: {integrity: sha512-MUi0msIAPXcA2YAuVMcssrSYP/yylxLt347xyTC6+ODl0c4XQFs0d2AN3Pc3iTa0pxIGmogflUV6eogXpPbJeA==} peerDependencies: @@ -1012,6 +1058,9 @@ packages: caniuse-lite@1.0.30001752: resolution: {integrity: sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -1024,6 +1073,12 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1047,6 +1102,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} @@ -1090,6 +1148,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -1267,6 +1328,12 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + html-entities@2.3.3: resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} @@ -1277,6 +1344,9 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1426,10 +1496,28 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -1467,6 +1555,12 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1549,6 +1643,9 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1578,6 +1675,15 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1617,6 +1723,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shiki@4.0.2: + resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} + engines: {node: '>=20'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -1667,6 +1777,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -1701,6 +1814,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1753,6 +1869,9 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -1778,6 +1897,21 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -1795,6 +1929,12 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-plugin-solid@2.11.10: resolution: {integrity: sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw==} peerDependencies: @@ -1931,6 +2071,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@adobe/css-tools@4.4.4': {} @@ -2381,6 +2524,46 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true + '@shikijs/core@4.0.2': + dependencies: + '@shikijs/primitive': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/primitive@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/themes@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/types@4.0.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@solid-primitives/active-element@2.1.3(solid-js@1.9.10)': dependencies: '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) @@ -2542,10 +2725,18 @@ snapshots: '@types/estree@1.0.8': {} + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/mdx@2.0.13': {} '@types/node@24.10.0': @@ -2556,6 +2747,8 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/unist@3.0.3': {} + '@typescript-eslint/project-service@8.57.0(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@6.0.3) @@ -2607,6 +2800,8 @@ snapshots: '@typescript-eslint/types': 8.57.0 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.1': {} + '@vitest/browser-playwright@4.0.8(playwright@1.56.1)(vite@7.2.2(@types/node@24.10.0))(vitest@4.0.8)': dependencies: '@vitest/browser': 4.0.8(vite@7.2.2(@types/node@24.10.0))(vitest@4.0.8) @@ -2821,6 +3016,8 @@ snapshots: caniuse-lite@1.0.30001752: {} + ccount@2.0.1: {} + chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -2836,6 +3033,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + check-error@2.1.1: {} chromatic@12.2.0: {} @@ -2846,6 +3047,8 @@ snapshots: color-name@1.1.4: {} + comma-separated-tokens@2.0.3: {} + commander@8.3.0: {} concat-map@0.0.1: {} @@ -2874,6 +3077,10 @@ snapshots: dequal@2.0.3: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -3073,12 +3280,32 @@ snapshots: has-flag@4.0.0: {} + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + html-entities@2.3.3: {} html-escaper@2.0.2: {} html-tags@3.3.1: {} + html-void-elements@3.0.0: {} + ignore@5.3.2: {} import-fresh@3.3.1: @@ -3212,10 +3439,39 @@ snapshots: dependencies: semver: 7.7.3 + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + merge-anything@5.1.7: dependencies: is-what: 4.1.16 + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + min-indent@1.0.1: {} minimatch@10.2.4: @@ -3242,6 +3498,14 @@ snapshots: node-releases@2.0.27: {} + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3319,6 +3583,8 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + property-information@7.1.0: {} + punycode@2.3.1: {} react-docgen-typescript@2.4.0(typescript@6.0.3): @@ -3347,6 +3613,16 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + resolve-from@4.0.0: {} rollup@4.52.5: @@ -3395,6 +3671,17 @@ snapshots: shebang-regex@3.0.0: {} + shiki@4.0.2: + dependencies: + '@shikijs/core': 4.0.2 + '@shikijs/engine-javascript': 4.0.2 + '@shikijs/engine-oniguruma': 4.0.2 + '@shikijs/langs': 4.0.2 + '@shikijs/themes': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -3446,6 +3733,8 @@ snapshots: source-map@0.6.1: {} + space-separated-tokens@2.0.2: {} + stackback@0.0.2: {} std-env@3.10.0: {} @@ -3502,6 +3791,11 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.2 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -3543,6 +3837,8 @@ snapshots: totalist@3.0.1: {} + trim-lines@3.0.1: {} + ts-api-utils@2.4.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -3559,6 +3855,29 @@ snapshots: undici-types@7.16.0: {} + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@2.0.1: {} unplugin@2.3.10: @@ -3578,6 +3897,16 @@ snapshots: dependencies: punycode: 2.3.1 + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite-plugin-solid@2.11.10(@testing-library/jest-dom@6.9.1)(solid-js@1.9.10)(vite@7.2.2(@types/node@24.10.0)): dependencies: '@babel/core': 7.28.5 @@ -3678,3 +4007,5 @@ snapshots: yallist@3.1.1: {} yocto-queue@0.1.0: {} + + zwitch@2.0.4: {} diff --git a/packages/ui-components/src/code_view.stories.tsx b/packages/ui-components/src/code_view.stories.tsx new file mode 100644 index 000000000..98fd56d3c --- /dev/null +++ b/packages/ui-components/src/code_view.stories.tsx @@ -0,0 +1,91 @@ +import { bundledLanguagesInfo, bundledThemesInfo } from "shiki"; +import { For } from "solid-js"; +import type { Meta, StoryObj } from "storybook-solidjs-vite"; + +import { CodeView } from "./code_view"; + +const langs = bundledLanguagesInfo.map((l) => l.id); +const themes = bundledThemesInfo.map((t) => t.id); + +const meta: Meta = { + title: "Misc/CodeView", + component: CodeView, + tags: ["autodocs"], + argTypes: { + text: { + control: "text", + description: "The source code to display", + }, + lang: { + control: "select", + options: langs, + description: "The lang for syntax highlighting", + }, + theme: { + control: "select", + options: themes, + description: "The theme for syntax highlighting", + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const SQL: Story = { + // excluding from autodocs and dev seems to be the way to have this + // component as the first thing in the docs and only there + tags: ["!autodocs", "!dev"], + args: { + lang: "sql", + text: `SELECT p.name, COUNT(o.id) AS order_count +FROM products p +LEFT JOIN orders o ON o.product_id = p.id +WHERE p.active = TRUE +GROUP BY p.name +ORDER BY order_count DESC;`, + }, +}; + +export const Typescript: Story = { + args: { + lang: "typescript", + text: `function greet(name: string): string { + return \`Hello, \${name}!\`; +}`, + }, +}; + +const sampleTs = `function greet(name: string): string { + return \`Hello, \${name}!\`; +}`; + +export const Themes: Story = { + render: () => ( +
+ + {(theme) => ( +
+

{theme}

+ +
+ )} +
+
+ ), +}; diff --git a/packages/ui-components/src/code_view.tsx b/packages/ui-components/src/code_view.tsx new file mode 100644 index 000000000..c1c5fb69c --- /dev/null +++ b/packages/ui-components/src/code_view.tsx @@ -0,0 +1,26 @@ +import { type BundledLanguage, type BundledTheme, codeToHtml } from "shiki"; +import { createResource } from "solid-js"; + +export type CodeViewProps = { + text: string; + lang: BundledLanguage; + theme?: BundledTheme; +}; + +export const CodeView = (props: CodeViewProps) => { + const [html] = createResource( + () => ({ text: props.text, lang: props.lang, theme: props.theme }), + ({ text, lang, theme }) => + codeToHtml(text, { + lang, + theme: theme ?? "min-light", + }), + ); + + return ( + // shiki uses hast-util-to-html which escapes html entities so no need + // for extra sanitization and setting innerHTML should be safe + // oxlint-disable-next-line solid/no-innerhtml +
+ ); +}; diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index de93d87b7..7544a6536 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -2,6 +2,7 @@ export * from "./alert"; export * from "./block_title"; export * from "./settings_disclosure"; export * from "./button"; +export * from "./code_view"; export * from "./completions"; export * from "./dialog"; export * from "./document_type_icon"; diff --git a/packages/ui-components/src/panel.css b/packages/ui-components/src/panel.css index d166b0998..2ec4c8a7f 100644 --- a/packages/ui-components/src/panel.css +++ b/packages/ui-components/src/panel.css @@ -3,6 +3,7 @@ flex-direction: row; align-items: center; width: 100%; + gap: 4px; & .filler { flex: 1; From 5daca23948a2af34ff3de4dd819351316148ad0e Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 20 May 2026 18:47:25 +0100 Subject: [PATCH 43/81] CLEANUP: `createResource` reactivity issues (#1286) * CLEANUP: Use onMount instead of createResource for auth * CLEANUP: Input tracking of createResource in document_page_sidebar * CLEANUP: Input tracking for createResource in elk_svg --- packages/frontend/src/App.tsx | 25 +++++++---------- .../src/page/document_page_sidebar.tsx | 27 ++++++++++++------- .../frontend/src/visualization/elk_svg.tsx | 16 +++++++---- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 4c93e3437..df312edd4 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -5,7 +5,7 @@ import { Navigate, type RouteDefinition, Router, type RouteSectionProps } from " import { type FirebaseOptions, initializeApp } from "firebase/app"; import { getAuth, signOut } from "firebase/auth"; import { FirebaseProvider } from "solid-firebase"; -import { createResource, createSignal, ErrorBoundary, lazy, Show } from "solid-js"; +import { createResource, createSignal, ErrorBoundary, lazy, onMount, Show } from "solid-js"; import invariant from "tiny-invariant"; import * as uuid from "uuid"; @@ -31,20 +31,15 @@ const Root = (props: RouteSectionProps) => { const firebaseApp = initializeApp(firebaseOptions); const api = new Api({ serverUrl, repoUrl, firebaseApp }); - const [isSessionInvalid] = createResource( - // oxlint-disable-next-line solid/reactivity -- createResource fetcher - async () => { - const result = await api.rpc.validate_session.query(); - if (result.tag === "Err") { - await signOut(getAuth(firebaseApp)); - return true; - } - return false; - }, - { - initialValue: false, - }, - ); + const [isSessionInvalid, setIsSessionInvalid] = createSignal(false); + + onMount(async () => { + const result = await api.rpc.validate_session.query(); + if (result.tag === "Err") { + await signOut(getAuth(firebaseApp)); + setIsSessionInvalid(true); + } + }); const theories = stdTheories; const models = createModelLibraryWithApi(api, theories); diff --git a/packages/frontend/src/page/document_page_sidebar.tsx b/packages/frontend/src/page/document_page_sidebar.tsx index 29c34f6b7..d798722b7 100644 --- a/packages/frontend/src/page/document_page_sidebar.tsx +++ b/packages/frontend/src/page/document_page_sidebar.tsx @@ -114,12 +114,22 @@ function DocumentsTreeNode(props: { .map((rel) => uuidStringify(rel.refId)); }); - // oxlint-disable-next-line solid/reactivity -- createResource fetcher - const [childDocs] = createResource(childRefIds, async (refIds) => { + const childDocSource = createMemo(() => { + const refIds = childRefIds(); + return { + createdAtByRefId: Object.fromEntries( + refIds.map((refId) => [refId, userState.documents[refId]?.createdAt ?? 0]), + ), + isParentOwnerless: props.doc.docRef.permissions.anyone === "Own", + refIds, + }; + }); + + const [childDocs] = createResource(childDocSource, async (source) => { // Individual failures are skipped to prevent one corrupt document // from crashing the entire sidebar. const childDocs = await Promise.all( - refIds.map(async (refId) => { + source.refIds.map(async (refId) => { try { return await api.getLiveDoc(refId); } catch (e) { @@ -132,21 +142,18 @@ function DocumentsTreeNode(props: { (doc): doc is NonNullable => doc !== null, ); - const isParentOwnerless = props.doc.docRef.permissions.anyone === "Own"; - // Don't show ownerless children or deleted documents const filtered = loadedChildDocs.filter( (childDoc) => !childDoc.docRef.isDeleted && - (isParentOwnerless || childDoc.docRef.permissions.anyone !== "Own"), + (source.isParentOwnerless || childDoc.docRef.permissions.anyone !== "Own"), ); // Sort by createdAt descending (newest first) - const docs = userState.documents; filtered.sort((a, b) => { - const aInfo = a.docRef.refId ? docs[a.docRef.refId] : undefined; - const bInfo = b.docRef.refId ? docs[b.docRef.refId] : undefined; - return (bInfo?.createdAt ?? 0) - (aInfo?.createdAt ?? 0); + const aCreatedAt = a.docRef.refId ? (source.createdAtByRefId[a.docRef.refId] ?? 0) : 0; + const bCreatedAt = b.docRef.refId ? (source.createdAtByRefId[b.docRef.refId] ?? 0) : 0; + return bCreatedAt - aCreatedAt; }); return filtered; diff --git a/packages/frontend/src/visualization/elk_svg.tsx b/packages/frontend/src/visualization/elk_svg.tsx index e98bf9e21..d5b9a8e2b 100644 --- a/packages/frontend/src/visualization/elk_svg.tsx +++ b/packages/frontend/src/visualization/elk_svg.tsx @@ -40,14 +40,20 @@ export function ElkLayout(props: { () => { const elk = elkResource(); const graph = props.graph; + const args = props.args; + const elkToLayout = props.elkToLayout; if (elk && graph) { - return [elk, graph] as const; + return [elk, graph, args, elkToLayout] as const; } }, - // oxlint-disable-next-line solid/reactivity -- createResource fetcher - async ([elk, graph]: readonly [ELK, ElkNode]): Promise => { - const elkNode = await elk.layout(graph, props.args); - return props.elkToLayout(elkNode); + async ([elk, graph, args, elkToLayout]: readonly [ + ELK, + ElkNode, + ElkLayoutArguments | undefined, + (e: ElkNode) => T, + ]): Promise => { + const elkNode = await elk.layout(graph, args); + return elkToLayout(elkNode); }, ); From b9dedfb10cf1c44c02366e2d2b4913accaa95c3e Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 15:52:52 +0100 Subject: [PATCH 44/81] ENH: Hierarchical FocusHandle --- .../frontend/src/analysis/analysis_editor.tsx | 8 +- .../src/components/document_picker.tsx | 9 +- .../frontend/src/diagram/diagram_editor.tsx | 8 +- .../src/diagram/morphism_cell_editor.tsx | 53 +++----- .../src/diagram/object_cell_editor.tsx | 29 ++--- .../src/model/contribution_cell_editor.tsx | 48 +++---- .../model/contribution_monomial_editor.tsx | 6 +- packages/frontend/src/model/editors.ts | 5 +- .../src/model/instantiation_cell_editor.tsx | 123 ++++++------------ packages/frontend/src/model/model_editor.tsx | 10 +- .../src/model/morphism_cell_editor.tsx | 52 +++----- .../frontend/src/model/object_cell_editor.tsx | 3 +- .../frontend/src/model/object_list_editor.tsx | 39 +++--- .../string_diagram_morphism_cell_editor.tsx | 36 ++--- .../frontend/src/notebook/notebook_cell.tsx | 18 +-- .../frontend/src/notebook/notebook_editor.tsx | 59 ++++----- packages/frontend/src/page/document_page.tsx | 39 +++++- packages/ui-components/src/index.ts | 1 + packages/ui-components/src/text_input.tsx | 8 +- packages/ui-components/src/util/focus.ts | 45 +++++++ 20 files changed, 295 insertions(+), 304 deletions(-) create mode 100644 packages/ui-components/src/util/focus.ts diff --git a/packages/frontend/src/analysis/analysis_editor.tsx b/packages/frontend/src/analysis/analysis_editor.tsx index bbd48238c..74888b8ba 100644 --- a/packages/frontend/src/analysis/analysis_editor.tsx +++ b/packages/frontend/src/analysis/analysis_editor.tsx @@ -3,6 +3,7 @@ import { Dynamic } from "solid-js/web"; import invariant from "tiny-invariant"; import { Nb } from "catcolab-document-methods"; +import { type FocusHandle } from "catcolab-ui-components"; import { type CellConstructor, type FormalCellEditorProps, NotebookEditor } from "../notebook"; import type { AnalysisMeta, DiagramAnalysisMeta, ModelAnalysisMeta } from "../theory"; import { LiveAnalysisContext } from "./context"; @@ -16,7 +17,10 @@ import type { Analysis } from "./types"; /** Notebook editor for analyses of models of double theories. */ -export function AnalysisNotebookEditor(props: { liveAnalysis: LiveAnalysisDoc }) { +export function AnalysisNotebookEditor(props: { + liveAnalysis: LiveAnalysisDoc; + focus: FocusHandle; +}) { const liveDoc = () => props.liveAnalysis.liveDoc; const cellConstructors = () => { @@ -39,7 +43,7 @@ export function AnalysisNotebookEditor(props: { liveAnalysis: LiveAnalysisDoc }) changeNotebook={(f) => liveDoc().changeDoc((doc) => f(doc.notebook))} formalCellEditor={AnalysisCellEditor} cellConstructors={cellConstructors()} - noShortcuts={true} + focus={props.focus} /> ); diff --git a/packages/frontend/src/components/document_picker.tsx b/packages/frontend/src/components/document_picker.tsx index d049e07cb..ad976b452 100644 --- a/packages/frontend/src/components/document_picker.tsx +++ b/packages/frontend/src/components/document_picker.tsx @@ -55,6 +55,7 @@ export function DocumentPicker( "docType", "placeholder", "isActive", + "focus", "filterCompletions", ]); @@ -68,10 +69,13 @@ export function DocumentPicker( ); const [editMode, setEditMode] = createSignal(false); - const enableEditMode = () => setEditMode(true); + const enableEditMode = () => { + props.focus?.setFocused(true); + setEditMode(true); + }; const disableEditMode = () => setEditMode(false); - createEffect(() => setEditMode(props.isActive ?? false)); + createEffect(() => setEditMode(props.focus?.hasFocus() ?? props.isActive ?? false)); const DocLink = (linkProps: ComponentProps<"a">) => ( @@ -123,6 +127,7 @@ export function DocumentPicker( }> { props.setRefId(refId); diff --git a/packages/frontend/src/diagram/diagram_editor.tsx b/packages/frontend/src/diagram/diagram_editor.tsx index e4ded5756..12e3e93ab 100644 --- a/packages/frontend/src/diagram/diagram_editor.tsx +++ b/packages/frontend/src/diagram/diagram_editor.tsx @@ -4,6 +4,7 @@ import invariant from "tiny-invariant"; import { Diagram, Nb } from "catcolab-document-methods"; import type { DiagramJudgment, DiagramMorDecl, DiagramObDecl } from "catcolab-document-types"; +import { type FocusHandle } from "catcolab-ui-components"; import { LiveModelContext } from "../model"; import { type CellConstructor, type FormalCellEditorProps, NotebookEditor } from "../notebook"; import type { InstanceTypeMeta } from "../theory"; @@ -14,7 +15,7 @@ import { DiagramObjectCellEditor } from "./object_cell_editor"; /** Notebook editor for a diagram in a model. */ -export function DiagramNotebookEditor(props: { liveDiagram: LiveDiagramDoc }) { +export function DiagramNotebookEditor(props: { liveDiagram: LiveDiagramDoc; focus: FocusHandle }) { const liveDoc = () => props.liveDiagram.liveDoc; const liveModel = () => props.liveDiagram.liveModel; @@ -39,6 +40,7 @@ export function DiagramNotebookEditor(props: { liveDiagram: LiveDiagramDoc }) { cellConstructors={cellConstructors()} cellLabel={judgmentLabel} duplicateCell={Diagram.duplicateDiagramJudgment} + focus={props.focus} /> ); @@ -59,7 +61,7 @@ function DiagramCellEditor(props: FormalCellEditorProps) { modifyDecl={(f) => props.changeContent((content) => f(content as DiagramObDecl)) } - isActive={props.isActive} + focus={props.focus} actions={props.actions} theory={theory()} /> @@ -72,7 +74,7 @@ function DiagramCellEditor(props: FormalCellEditorProps) { modifyDecl={(f) => props.changeContent((content) => f(content as DiagramMorDecl)) } - isActive={props.isActive} + focus={props.focus} actions={props.actions} theory={theory()} /> diff --git a/packages/frontend/src/diagram/morphism_cell_editor.tsx b/packages/frontend/src/diagram/morphism_cell_editor.tsx index 481291f77..bd698c0d3 100644 --- a/packages/frontend/src/diagram/morphism_cell_editor.tsx +++ b/packages/frontend/src/diagram/morphism_cell_editor.tsx @@ -1,7 +1,8 @@ -import { createEffect, createSignal, useContext } from "solid-js"; +import { useContext } from "solid-js"; import invariant from "tiny-invariant"; import { v7 } from "uuid"; +import { type FocusHandle, useChildFocus } from "catcolab-ui-components"; import type { DiagramMorDecl } from "catlog-wasm"; import { BasicMorInput } from "../model/morphism_input"; import type { CellActions } from "../notebook"; @@ -16,21 +17,15 @@ import "./morphism_cell_editor.css"; export function DiagramMorphismCellEditor(props: { decl: DiagramMorDecl; modifyDecl: (f: (decl: DiagramMorDecl) => void) => void; - isActive: boolean; + focus: FocusHandle; actions: CellActions; theory: Theory; }) { const liveDiagram = useContext(LiveDiagramContext); invariant(liveDiagram, "Live diagram should be provided as context"); - const [activeInput, setActiveInput] = createSignal("mor"); - - // Reset to default on deactivation so re-entry lands on the morphism input. - createEffect(() => { - if (!props.isActive) { - setActiveInput("mor"); - } - }); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted cell. + const focus = useChildFocus(props.focus, { default: "mor" }); const domType = () => props.theory.theory.src(props.decl.morType); const codType = () => props.theory.theory.tgt(props.decl.morType); @@ -61,15 +56,11 @@ export function DiagramMorphismCellEditor(props: { obType={domType()} generateId={v7} isInvalid={domInvalid()} - isActive={props.isActive && activeInput() === "dom"} - deleteForward={() => setActiveInput("mor")} - exitBackward={() => setActiveInput("mor")} - exitForward={() => setActiveInput("cod")} - exitRight={() => setActiveInput("mor")} - hasFocused={() => { - setActiveInput("dom"); - props.actions.hasFocused?.(); - }} + focus={focus.childFocus("dom")} + deleteForward={() => focus.setActiveChild("mor")} + exitBackward={() => focus.setActiveChild("mor")} + exitForward={() => focus.setActiveChild("cod")} + exitRight={() => focus.setActiveChild("mor")} />
@@ -82,19 +73,15 @@ export function DiagramMorphismCellEditor(props: { }} morType={props.decl.morType} placeholder={props.theory.modelMorTypeMeta(props.decl.morType)?.name} - isActive={props.isActive && activeInput() === "mor"} + focus={focus.childFocus("mor")} deleteBackward={props.actions.deleteBackward} deleteForward={props.actions.deleteForward} exitBackward={props.actions.activateAbove} - exitForward={() => setActiveInput("dom")} + exitForward={() => focus.setActiveChild("dom")} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - exitLeft={() => setActiveInput("dom")} - exitRight={() => setActiveInput("cod")} - hasFocused={() => { - setActiveInput("mor"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("dom")} + exitRight={() => focus.setActiveChild("cod")} />
@@ -112,15 +99,11 @@ export function DiagramMorphismCellEditor(props: { obType={codType()} generateId={v7} isInvalid={codInvalid()} - isActive={props.isActive && activeInput() === "cod"} - deleteBackward={() => setActiveInput("mor")} - exitBackward={() => setActiveInput("dom")} + focus={focus.childFocus("cod")} + deleteBackward={() => focus.setActiveChild("mor")} + exitBackward={() => focus.setActiveChild("dom")} exitForward={props.actions.activateBelow} - exitLeft={() => setActiveInput("mor")} - hasFocused={() => { - setActiveInput("cod"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("mor")} />
); diff --git a/packages/frontend/src/diagram/object_cell_editor.tsx b/packages/frontend/src/diagram/object_cell_editor.tsx index 9fab4fe87..7f00403de 100644 --- a/packages/frontend/src/diagram/object_cell_editor.tsx +++ b/packages/frontend/src/diagram/object_cell_editor.tsx @@ -1,6 +1,4 @@ -import { createSignal } from "solid-js"; - -import { NameInput } from "catcolab-ui-components"; +import { NameInput, type FocusHandle, useChildFocus } from "catcolab-ui-components"; import type { DiagramObDecl } from "catlog-wasm"; import { ObInput } from "../model/object_input"; import type { CellActions } from "../notebook"; @@ -12,11 +10,12 @@ import "./object_cell_editor.css"; export function DiagramObjectCellEditor(props: { decl: DiagramObDecl; modifyDecl: (f: (decl: DiagramObDecl) => void) => void; - isActive: boolean; + focus: FocusHandle; actions: CellActions; theory: Theory; }) { - const [activeInput, setActiveInput] = createSignal("name"); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted cell. + const focus = useChildFocus(props.focus, { default: "name" }); return (
@@ -32,13 +31,9 @@ export function DiagramObjectCellEditor(props: { deleteForward={props.actions.deleteForward} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - exitRight={() => setActiveInput("overOb")} - exitForward={() => setActiveInput("overOb")} - isActive={props.isActive && activeInput() === "name"} - hasFocused={() => { - setActiveInput("name"); - props.actions.hasFocused?.(); - }} + exitRight={() => focus.setActiveChild("overOb")} + exitForward={() => focus.setActiveChild("overOb")} + focus={focus.childFocus("name")} /> setActiveInput("name")} - exitBackward={() => setActiveInput("name")} - isActive={props.isActive && activeInput() === "overOb"} - hasFocused={() => { - setActiveInput("overOb"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("name")} + exitBackward={() => focus.setActiveChild("name")} + focus={focus.childFocus("overOb")} />
); diff --git a/packages/frontend/src/model/contribution_cell_editor.tsx b/packages/frontend/src/model/contribution_cell_editor.tsx index 673b48434..7e5d35149 100644 --- a/packages/frontend/src/model/contribution_cell_editor.tsx +++ b/packages/frontend/src/model/contribution_cell_editor.tsx @@ -1,7 +1,7 @@ -import { createEffect, createMemo, createSignal, useContext, Switch, Match } from "solid-js"; +import { createMemo, useContext, Switch, Match } from "solid-js"; import invariant from "tiny-invariant"; -import { NameInput } from "catcolab-ui-components"; +import { NameInput, useChildFocus } from "catcolab-ui-components"; import type { Ob } from "catlog-wasm"; import { LiveModelContext } from "./context"; import { ContributionMonomialEditor } from "./contribution_monomial_editor"; @@ -32,14 +32,8 @@ export default function ContributionCellEditor( const liveModel = useContext(LiveModelContext); invariant(liveModel, "Live model should be provided as context"); - const [activeInput, setActiveInput] = createSignal("name"); - - // Reset to default on deactivation so re-entry lands on the name input. - createEffect(() => { - if (!props.isActive) { - setActiveInput("name"); - } - }); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted cell. + const focus = useChildFocus(props.focus, { default: "name" }); const morTypeMeta = () => props.theory.modelMorTypeMeta(props.morphism.morType); @@ -104,19 +98,15 @@ export default function ContributionCellEditor( mor.name = name; }); }} - isActive={props.isActive && activeInput() === "name"} + focus={focus.childFocus("name")} deleteBackward={props.actions.deleteBackward} deleteForward={props.actions.deleteForward} exitBackward={props.actions.activateAbove} - exitForward={() => setActiveInput("cod")} + exitForward={() => focus.setActiveChild("cod")} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - exitLeft={() => setActiveInput("cod")} - exitRight={() => setActiveInput("dom")} - hasFocused={() => { - setActiveInput("name"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("cod")} + exitRight={() => focus.setActiveChild("dom")} />
:
@@ -138,15 +128,11 @@ export default function ContributionCellEditor( obType={codType()} applyOp={morTypeMeta()?.codomain?.apply} isInvalid={errors().some((err) => err.tag === "Cod" || err.tag === "CodType")} - isActive={props.isActive && activeInput() === "cod"} - deleteForward={() => setActiveInput("name")} + focus={focus.childFocus("cod")} + deleteForward={() => focus.setActiveChild("name")} exitBackward={props.actions.activateAbove} - exitForward={() => setActiveInput("dom")} - exitLeft={() => setActiveInput("name")} - hasFocused={() => { - setActiveInput("cod"); - props.actions.hasFocused?.(); - }} + exitForward={() => focus.setActiveChild("dom")} + exitLeft={() => focus.setActiveChild("name")} />
@@ -164,15 +150,11 @@ export default function ContributionCellEditor( setOb={setDomOb} obType={domType()} isInvalid={errors().some((err) => err.tag === "Dom" || err.tag === "DomType")} - isActive={props.isActive && activeInput() === "dom"} - deleteBackward={() => setActiveInput("name")} - exitBackward={() => setActiveInput("name")} + focus={focus.childFocus("dom")} + deleteBackward={() => focus.setActiveChild("name")} + exitBackward={() => focus.setActiveChild("name")} exitForward={props.actions.activateBelow} exitRight={props.actions.activateBelow} - hasFocused={() => { - setActiveInput("dom"); - props.actions.hasFocused?.(); - }} />
diff --git a/packages/frontend/src/model/contribution_monomial_editor.tsx b/packages/frontend/src/model/contribution_monomial_editor.tsx index 25d3680da..0c2343637 100644 --- a/packages/frontend/src/model/contribution_monomial_editor.tsx +++ b/packages/frontend/src/model/contribution_monomial_editor.tsx @@ -59,11 +59,12 @@ export function ContributionMonomialEditor(props: ContributionMonomialEditorProp return ( ob === null)} + when={(props.focus?.hasFocus() ?? props.isActive) || obList().some((ob) => ob === null)} fallback={
{ + props.focus?.setFocused(true); props.hasFocused?.(); evt.preventDefault(); }} @@ -91,14 +92,13 @@ export function ContributionMonomialEditor(props: ContributionMonomialEditorProp obType={props.obType} placeholder={props.placeholder} isInvalid={props.isInvalid} - isActive={props.isActive} + focus={props.focus} deleteBackward={props.deleteBackward} deleteForward={props.deleteForward} exitBackward={props.exitBackward} exitForward={props.exitForward} exitLeft={props.exitLeft} exitRight={props.exitRight} - hasFocused={props.hasFocused} insertKey={props.insertKey ?? ","} startDelimiter={
{"["}
} endDelimiter={
{"]"}
} diff --git a/packages/frontend/src/model/editors.ts b/packages/frontend/src/model/editors.ts index 516f19850..131d8595a 100644 --- a/packages/frontend/src/model/editors.ts +++ b/packages/frontend/src/model/editors.ts @@ -1,5 +1,6 @@ import type { Component } from "solid-js"; +import type { FocusHandle } from "catcolab-ui-components"; import type { MorDecl, ObDecl } from "catlog-wasm"; import type { CellActions } from "../notebook"; import type { Theory } from "../theory"; @@ -19,7 +20,7 @@ export type EditorVariantOverrides = { export type ObjectEditorProps = { object: ObDecl; modifyObject: (f: (decl: ObDecl) => void) => void; - isActive: boolean; + focus: FocusHandle; actions: CellActions; theory: Theory; }; @@ -28,7 +29,7 @@ export type ObjectEditorProps = { export type MorphismEditorProps = { morphism: MorDecl; modifyMorphism: (f: (decl: MorDecl) => void) => void; - isActive: boolean; + focus: FocusHandle; actions: CellActions; theory: Theory; }; diff --git a/packages/frontend/src/model/instantiation_cell_editor.tsx b/packages/frontend/src/model/instantiation_cell_editor.tsx index d76331cc9..09f4c1916 100644 --- a/packages/frontend/src/model/instantiation_cell_editor.tsx +++ b/packages/frontend/src/model/instantiation_cell_editor.tsx @@ -1,17 +1,13 @@ import type { DocInfo } from "catcolab-api/src/user_state"; -import { - batch, - createEffect, - createSignal, - Index, - Show, - splitProps, - untrack, - useContext, -} from "solid-js"; +import { batch, createEffect, Index, Show, splitProps, untrack, useContext } from "solid-js"; import invariant from "tiny-invariant"; -import { NameInput, type TextInputOptions } from "catcolab-ui-components"; +import { + type FocusHandle, + NameInput, + type TextInputOptions, + useChildFocus, +} from "catcolab-ui-components"; import type { DblModel, InstantiatedModel, Ob, SpecializeModel } from "catlog-wasm"; import { useApi } from "../api"; import { DocumentPicker, IdInput, IdInputPlaceholder } from "../components"; @@ -27,7 +23,7 @@ import "./instantiation_cell_editor.css"; export function InstantiationCellEditor(props: { instantiation: InstantiatedModel; modifyInstantiation: (f: (inst: InstantiatedModel) => void) => void; - isActive: boolean; + focus: FocusHandle; actions: CellActions; }) { const api = useApi(); @@ -67,26 +63,19 @@ export function InstantiationCellEditor(props: { invariant(models); const instantiated = models.useElaboratedModel(refId); - const [activeComponent, setActiveComponent] = createSignal( - refId() == null ? "model" : "name", - ); - const [activeIndex, setActiveIndex] = createSignal(0); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted cell. + const focus = useChildFocus(props.focus, { + default: refId() == null ? "model" : "name", + }); + const specializationFocus = useChildFocus(focus.childFocus("specializations"), { + default: 0, + }); const activateIndex = (index: number) => batch(() => { - setActiveComponent("specializations"); - setActiveIndex(index); + focus.setActiveChild("specializations"); + specializationFocus.setActiveChild(index); }); - // Reset to default on deactivation so re-entry lands on the name input. - createEffect(() => { - if (!props.isActive) { - batch(() => { - setActiveComponent("name"); - setActiveIndex(0); - }); - } - }); - const insertSpecializationAtTop = () => { props.modifyInstantiation((inst) => { inst.specializations.unshift({ id: null, ob: null }); @@ -156,7 +145,7 @@ export function InstantiationCellEditor(props: { // Clean up empty rows when the cell becomes inactive. createEffect(() => { - if (!props.isActive) { + if (!props.focus.hasFocus()) { untrack(() => pruneEmptySpecializations()); } }); @@ -185,13 +174,9 @@ export function InstantiationCellEditor(props: { deleteForward={props.actions.deleteForward} exitUp={props.actions.activateAbove} exitDown={exitDownFromTop} - exitRight={() => setActiveComponent("model")} - exitForward={() => setActiveComponent("model")} - isActive={props.isActive && activeComponent() === "name"} - hasFocused={() => { - setActiveComponent("name"); - props.actions.hasFocused(); - }} + exitRight={() => focus.setActiveChild("model")} + exitForward={() => focus.setActiveChild("model")} + focus={focus.childFocus("name")} /> setActiveComponent("name")} + deleteBackward={() => focus.setActiveChild("name")} exitUp={props.actions.activateAbove} exitDown={exitDownFromTop} - exitLeft={() => setActiveComponent("name")} - exitBackward={() => setActiveComponent("name")} - isActive={props.isActive && activeComponent() === "model"} - hasFocused={() => { - setActiveComponent("model"); - props.actions.hasFocused(); - }} + exitLeft={() => focus.setActiveChild("name")} + exitBackward={() => focus.setActiveChild("name")} + focus={focus.childFocus("model")} />
    idInputTexts.set(i, text)} instantiatedModel={instantiated()?.validatedModel.model ?? null} - isActive={ - props.isActive && - activeComponent() === "specializations" && - activeIndex() === i - } - hasFocused={() => { - activateIndex(i); - props.actions.hasFocused(); - }} + focus={specializationFocus.childFocus(i)} createBelow={() => { props.modifyInstantiation((inst) => { const spec = { id: null, ob: null }; @@ -262,7 +235,7 @@ export function InstantiationCellEditor(props: { props.modifyInstantiation((inst) => inst.specializations.splice(i, 1), ); - i === 0 ? setActiveComponent("name") : activateIndex(i - 1); + i === 0 ? focus.setActiveChild("name") : activateIndex(i - 1); }} exitDown={() => { if (i >= props.instantiation.specializations.length - 1) { @@ -272,7 +245,7 @@ export function InstantiationCellEditor(props: { } }} exitUp={() => { - i === 0 ? setActiveComponent("name") : activateIndex(i - 1); + i === 0 ? focus.setActiveChild("name") : activateIndex(i - 1); }} /> @@ -283,7 +256,7 @@ export function InstantiationCellEditor(props: { class="model-specialization-add" onMouseDown={(evt) => { appendSpecialization(); - props.actions.hasFocused(); + props.focus.setFocused(true); evt.preventDefault(); }} > @@ -308,21 +281,13 @@ function SpecializationEditor( instantiatedModel: DblModel | null; /** Called when the displayed text of the id input changes. */ onIdTextChange?: (text: string) => void; - } & Pick< - TextInputOptions, - "isActive" | "hasFocused" | "createBelow" | "deleteBackward" | "exitDown" | "exitUp" - >, + focus: FocusHandle; + } & Pick, ) { const [inputProps, props] = splitProps(allProps, ["createBelow", "exitDown", "exitUp"]); - const [activeInput, setActiveInput] = createSignal("id"); - - // Reset to default on deactivation so re-entry lands on the id input. - createEffect(() => { - if (!props.isActive) { - setActiveInput("id"); - } - }); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted row. + const focus = useChildFocus(props.focus, { default: "id" }); const obType = () => { const id = props.specialization.id; @@ -348,14 +313,10 @@ function SpecializationEditor( completions={props.instantiatedModel?.obGenerators()} idToLabel={(id) => props.instantiatedModel?.obGeneratorLabel(id)} labelToId={(label) => props.instantiatedModel?.obGeneratorWithLabel(label)} - isActive={props.isActive && activeInput() === "id"} - hasFocused={() => { - setActiveInput("id"); - props.hasFocused?.(); - }} + focus={focus.childFocus("id")} deleteBackward={props.deleteBackward} - exitForward={() => setActiveInput("ob")} - exitRight={() => setActiveInput("ob")} + exitForward={() => focus.setActiveChild("ob")} + exitRight={() => focus.setActiveChild("ob")} {...inputProps} /> @@ -370,14 +331,10 @@ function SpecializationEditor( }); }} obType={obType()} - isActive={props.isActive && activeInput() === "ob"} - hasFocused={() => { - setActiveInput("ob"); - props.hasFocused?.(); - }} - deleteBackward={() => setActiveInput("id")} - exitBackward={() => setActiveInput("id")} - exitLeft={() => setActiveInput("id")} + focus={focus.childFocus("ob")} + deleteBackward={() => focus.setActiveChild("id")} + exitBackward={() => focus.setActiveChild("id")} + exitLeft={() => focus.setActiveChild("id")} {...inputProps} /> )} diff --git a/packages/frontend/src/model/model_editor.tsx b/packages/frontend/src/model/model_editor.tsx index f5d4250a9..8236b2e21 100644 --- a/packages/frontend/src/model/model_editor.tsx +++ b/packages/frontend/src/model/model_editor.tsx @@ -4,6 +4,7 @@ import invariant from "tiny-invariant"; import { Model, Nb } from "catcolab-document-methods"; import type { InstantiatedModel, ModelJudgment, MorDecl, ObDecl } from "catcolab-document-types"; +import { type FocusHandle } from "catcolab-ui-components"; import { type CellConstructor, type FormalCellEditorProps, NotebookEditor } from "../notebook"; import { TheoryLibraryContext, type ModelTypeMeta, type Theory } from "../theory"; import { LiveModelContext } from "./context"; @@ -12,7 +13,7 @@ import { InstantiationCellEditor } from "./instantiation_cell_editor"; /** Notebook editor for a model of a double theory. */ -export function ModelNotebookEditor(props: { liveModel: LiveModelDoc }) { +export function ModelNotebookEditor(props: { liveModel: LiveModelDoc; focus: FocusHandle }) { const liveDoc = () => props.liveModel.liveDoc; const cellConstructors = () => { @@ -34,6 +35,7 @@ export function ModelNotebookEditor(props: { liveModel: LiveModelDoc }) { cellConstructors={cellConstructors()} cellLabel={judgmentLabel} duplicateCell={Model.duplicateModelJudgment} + focus={props.focus} /> ); @@ -65,7 +67,7 @@ export function ModelCellEditor(props: FormalCellEditorProps) { modifyObject={(f: (decl: ObDecl) => void) => props.changeContent((content) => f(content as ObDecl)) } - isActive={props.isActive} + focus={props.focus} actions={props.actions} theory={theory()} /> @@ -85,7 +87,7 @@ export function ModelCellEditor(props: FormalCellEditorProps) { modifyMorphism={(f: (decl: MorDecl) => void) => props.changeContent((content) => f(content as MorDecl)) } - isActive={props.isActive} + focus={props.focus} actions={props.actions} theory={theory()} /> @@ -98,7 +100,7 @@ export function ModelCellEditor(props: FormalCellEditorProps) { modifyInstantiation={(f) => props.changeContent((content) => f(content as InstantiatedModel)) } - isActive={props.isActive} + focus={props.focus} actions={props.actions} /> diff --git a/packages/frontend/src/model/morphism_cell_editor.tsx b/packages/frontend/src/model/morphism_cell_editor.tsx index a48d12f21..b7d459044 100644 --- a/packages/frontend/src/model/morphism_cell_editor.tsx +++ b/packages/frontend/src/model/morphism_cell_editor.tsx @@ -1,7 +1,7 @@ -import { createEffect, createMemo, createSignal, useContext } from "solid-js"; +import { createMemo, useContext } from "solid-js"; import invariant from "tiny-invariant"; -import { NameInput } from "catcolab-ui-components"; +import { NameInput, useChildFocus } from "catcolab-ui-components"; import { removeProxyAndCopy } from "../util/remove_proxy_and_copy"; import { LiveModelContext } from "./context"; import type { MorphismEditorProps } from "./editors"; @@ -16,14 +16,8 @@ export default function MorphismCellEditor(props: MorphismEditorProps) { const liveModel = useContext(LiveModelContext); invariant(liveModel, "Live model should be provided as context"); - const [activeInput, setActiveInput] = createSignal("name"); - - // Reset to default on deactivation so re-entry lands on the name input. - createEffect(() => { - if (!props.isActive) { - setActiveInput("name"); - } - }); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted cell. + const focus = useChildFocus(props.focus, { default: "name" }); const morTypeMeta = () => props.theory.modelMorTypeMeta(props.morphism.morType); @@ -81,15 +75,11 @@ export default function MorphismCellEditor(props: MorphismEditorProps) { obType={domType()} applyOp={morTypeMeta()?.domain?.apply} isInvalid={errors().some((err) => err.tag === "Dom" || err.tag === "DomType")} - isActive={props.isActive && activeInput() === "dom"} - deleteForward={() => setActiveInput("name")} - exitBackward={() => setActiveInput("name")} - exitForward={() => setActiveInput("cod")} - exitRight={() => setActiveInput("name")} - hasFocused={() => { - setActiveInput("dom"); - props.actions.hasFocused?.(); - }} + focus={focus.childFocus("dom")} + deleteForward={() => focus.setActiveChild("name")} + exitBackward={() => focus.setActiveChild("name")} + exitForward={() => focus.setActiveChild("cod")} + exitRight={() => focus.setActiveChild("name")} />
    @@ -102,19 +92,15 @@ export default function MorphismCellEditor(props: MorphismEditorProps) { mor.name = name; }); }} - isActive={props.isActive && activeInput() === "name"} + focus={focus.childFocus("name")} deleteBackward={props.actions.deleteBackward} deleteForward={props.actions.deleteForward} exitBackward={props.actions.activateAbove} - exitForward={() => setActiveInput("dom")} + exitForward={() => focus.setActiveChild("dom")} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - exitLeft={() => setActiveInput("dom")} - exitRight={() => setActiveInput("cod")} - hasFocused={() => { - setActiveInput("name"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("dom")} + exitRight={() => focus.setActiveChild("cod")} />
    @@ -133,15 +119,11 @@ export default function MorphismCellEditor(props: MorphismEditorProps) { obType={codType()} applyOp={morTypeMeta()?.codomain?.apply} isInvalid={errors().some((err) => err.tag === "Cod" || err.tag === "CodType")} - isActive={props.isActive && activeInput() === "cod"} - deleteBackward={() => setActiveInput("name")} - exitBackward={() => setActiveInput("dom")} + focus={focus.childFocus("cod")} + deleteBackward={() => focus.setActiveChild("name")} + exitBackward={() => focus.setActiveChild("dom")} exitForward={props.actions.activateBelow} - exitLeft={() => setActiveInput("name")} - hasFocused={() => { - setActiveInput("cod"); - props.actions.hasFocused?.(); - }} + exitLeft={() => focus.setActiveChild("name")} />
    diff --git a/packages/frontend/src/model/object_cell_editor.tsx b/packages/frontend/src/model/object_cell_editor.tsx index 46c5e1520..c1e2e92d0 100644 --- a/packages/frontend/src/model/object_cell_editor.tsx +++ b/packages/frontend/src/model/object_cell_editor.tsx @@ -23,14 +23,13 @@ export default function ObjectCellEditor(props: ObjectEditorProps) { ob.name = name; }); }} - isActive={props.isActive} + focus={props.focus} deleteBackward={props.actions.deleteBackward} deleteForward={props.actions.deleteForward} exitBackward={props.actions.activateAbove} exitForward={props.actions.activateBelow} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - hasFocused={props.actions.hasFocused} /> ); diff --git a/packages/frontend/src/model/object_list_editor.tsx b/packages/frontend/src/model/object_list_editor.tsx index 73975ab4f..a074aeb43 100644 --- a/packages/frontend/src/model/object_list_editor.tsx +++ b/packages/frontend/src/model/object_list_editor.tsx @@ -1,7 +1,6 @@ import { batch, createEffect, - createSignal, Index, type JSX, mergeProps, @@ -11,7 +10,7 @@ import { } from "solid-js"; import invariant from "tiny-invariant"; -import type { TextInputOptions } from "catcolab-ui-components"; +import { type FocusHandle, type TextInputOptions, useChildFocus } from "catcolab-ui-components"; import type { Ob, QualifiedName } from "catlog-wasm"; import { ObIdInput } from "../components"; import { removeProxyAndCopy } from "../util/remove_proxy_and_copy"; @@ -44,7 +43,17 @@ export function ObListEditor(originalProps: ObListEditorProps) { const liveModel = useContext(LiveModelContext); invariant(liveModel, "Live model should be provided as context"); - const [activeIndex, setActiveIndex] = createSignal(0); + const parentFocus: FocusHandle = { + hasFocus: () => props.focus?.hasFocus() ?? !!props.isActive, + setFocused: (focused) => { + if (props.focus) { + props.focus.setFocused(focused); + } else if (focused) { + props.hasFocused?.(); + } + }, + }; + const focus = useChildFocus(parentFocus, { default: 0 }); // Track which indices have non-empty text (including incomplete input). const inputTexts = new Map(); @@ -73,7 +82,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { updateObList((objects) => { objects.splice(i, 0, null); }); - setActiveIndex(i); + focus.setActiveChild(i); }); }; @@ -89,7 +98,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { // Insert into new object into empty list when focus is gained. createEffect(() => { - if (props.isActive && untrack(obList).length === 0) { + if (parentFocus.hasFocus() && untrack(obList).length === 0) { insertNewOb(0); } }); @@ -104,7 +113,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { // Clean up when the component becomes inactive. createEffect(() => { - if (!props.isActive) { + if (!parentFocus.hasFocus()) { untrack(() => deactivate()); } }); @@ -115,7 +124,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { onMouseDown={(evt) => { if (obList().length === 0) { insertNewOb(0); - props.hasFocused?.(); + parentFocus.setFocused(true); evt.preventDefault(); } }} @@ -139,7 +148,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { liveModel().elaboratedModel()?.obGeneratorWithLabel(label) } completions={completions()} - isActive={props.isActive && activeIndex() === i} + focus={focus.childFocus(i)} deleteBackward={() => batch(() => { updateObList((objects) => { @@ -148,7 +157,7 @@ export function ObListEditor(originalProps: ObListEditorProps) { if (i === 0) { props.deleteBackward?.(); } else { - setActiveIndex(i - 1); + focus.setActiveChild(i - 1); } }) } @@ -168,14 +177,14 @@ export function ObListEditor(originalProps: ObListEditorProps) { if (i === 0) { props.exitLeft?.(); } else { - setActiveIndex(i - 1); + focus.setActiveChild(i - 1); } }} exitRight={() => { if (i === obList().length - 1) { props.exitRight?.(); } else { - setActiveIndex(i + 1); + focus.setActiveChild(i + 1); } }} interceptKeyDown={(evt) => { @@ -184,16 +193,12 @@ export function ObListEditor(originalProps: ObListEditorProps) { return true; } else if (evt.key === "Home" && !evt.shiftKey) { // TODO: Should move to beginning of input. - setActiveIndex(0); + focus.setActiveChild(0); } else if (evt.key === "End" && !evt.shiftKey) { - setActiveIndex(obList().length - 1); + focus.setActiveChild(obList().length - 1); } return false; }} - hasFocused={() => { - setActiveIndex(i); - props.hasFocused?.(); - }} /> )} diff --git a/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx b/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx index d777367b0..9731338aa 100644 --- a/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx +++ b/packages/frontend/src/model/string_diagram_morphism_cell_editor.tsx @@ -1,7 +1,7 @@ import { Index, createEffect, createMemo, createSignal, untrack, useContext } from "solid-js"; import invariant from "tiny-invariant"; -import { NameInput } from "catcolab-ui-components"; +import { type FocusHandle, NameInput } from "catcolab-ui-components"; import type { Ob, ObOp, ObType, QualifiedName } from "catlog-wasm"; import { ObIdInput } from "../components"; import { removeProxyAndCopy } from "../util/remove_proxy_and_copy"; @@ -34,7 +34,7 @@ function WireColumn(props: { exitFirstBackward: (() => void) | undefined; /** Called when tabbing forward from the last wire. */ exitLastForward: (() => void) | undefined; - hasFocused: (() => void) | undefined; + setFocused: () => void; }) { const liveModel = useContext(LiveModelContext); invariant(liveModel, "Live model should be provided as context"); @@ -90,7 +90,7 @@ function WireColumn(props: { }} hasFocused={() => { props.activateWire(i); - props.hasFocused?.(); + props.setFocused(); }} /> ); @@ -110,7 +110,7 @@ function WireColumn(props: { class={`${styles.wire} ${styles.addWire}`} onMouseDown={(evt) => { props.insertWire(props.obs.length); - props.hasFocused?.(); + props.setFocused(); evt.preventDefault(); }} > @@ -136,7 +136,7 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro // Reset to default on deactivation so re-entry lands on the name input. createEffect(() => { - if (!props.isActive) { + if (!props.focus.hasFocus()) { setActive({ zone: "name" }); } }); @@ -241,13 +241,23 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro // Clean up when the cell becomes inactive. createEffect(() => { - if (!props.isActive) { + if (!props.focus.hasFocus()) { untrack(() => deactivate()); } }); const completions = () => liveModel().elaboratedModel()?.obGeneratorsWithType(elementObType()); + const nameFocus: FocusHandle = { + hasFocus: () => props.focus.hasFocus() && active()?.zone === "name", + setFocused: (focused) => { + if (focused) { + setActive({ zone: "name" }); + props.focus.setFocused(true); + } + }, + }; + const errors = () => { const validated = liveModel().validatedModel(); if (validated?.tag !== "Invalid") { @@ -265,7 +275,7 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro completions={completions()} isActive={(i) => { const a = active(); - return props.isActive && a?.zone === "dom" && a.index === i; + return props.focus.hasFocus() && a?.zone === "dom" && a.index === i; }} onTextChange={(i, text) => domInputTexts.set(i, text)} insertWire={insertDom} @@ -285,7 +295,7 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro insertCod(0); } }} - hasFocused={props.actions.hasFocused} + setFocused={() => props.focus.setFocused(true)} />
    { - setActive({ zone: "name" }); - props.actions.hasFocused?.(); - }} />
    { const a = active(); - return props.isActive && a?.zone === "cod" && a.index === i; + return props.focus.hasFocus() && a?.zone === "cod" && a.index === i; }} onTextChange={(i, text) => codInputTexts.set(i, text)} insertWire={insertCod} @@ -356,7 +362,7 @@ export default function StringDiagramMorphismCellEditor(props: MorphismEditorPro } }} exitLastForward={props.actions.activateBelow} - hasFocused={props.actions.hasFocused} + setFocused={() => props.focus.setFocused(true)} /> ); diff --git a/packages/frontend/src/notebook/notebook_cell.tsx b/packages/frontend/src/notebook/notebook_cell.tsx index 07b6cf7db..f6794ba07 100644 --- a/packages/frontend/src/notebook/notebook_cell.tsx +++ b/packages/frontend/src/notebook/notebook_cell.tsx @@ -17,7 +17,7 @@ import type { EditorView } from "prosemirror-view"; import { createEffect, createSignal, type JSX, onCleanup, Show } from "solid-js"; import type { Uuid } from "catcolab-document-types"; -import { type Completion, Completions, IconButton } from "catcolab-ui-components"; +import { type Completion, Completions, type FocusHandle, IconButton } from "catcolab-ui-components"; import { RichTextEditor } from "../components"; import { CellTypePopover } from "./notebook_editor"; @@ -25,11 +25,8 @@ import "./notebook_cell.css"; /** Props available to all notebook cell editors. */ export type CellEditorProps = { - /** Is the cell requested to be active? - - When this prop changes to `true`, the cell is authorizeed to grab the focus. - */ - isActive: boolean; + /** Focus state for this cell. */ + focus: FocusHandle; /** Actions invokable within the cell. */ actions: CellActions; @@ -61,9 +58,6 @@ export type CellActions = { /** Move this cell down, if possible. */ moveDown: () => void; - - /** The cell has received focus. */ - hasFocused: () => void; }; const cellDragDataKey = Symbol("notebook-cell"); @@ -99,6 +93,7 @@ the cell is rendered by its children. export function NotebookCell(props: { cellId: Uuid; index: number; + focus: FocusHandle; actions: CellActions; children: JSX.Element; tag?: string; @@ -235,6 +230,7 @@ export function NotebookCell(props: {
    { const view = editorView(); - if (props.isActive && view) { + if (props.focus.hasFocus() && view) { view.focus(); } }); @@ -315,7 +311,7 @@ export function RichTextCellEditor( deleteForward={props.actions.deleteForward} exitUp={props.actions.activateAbove} exitDown={props.actions.activateBelow} - onFocus={props.actions.hasFocused} + onFocus={() => props.focus.setFocused(true)} /> ); } diff --git a/packages/frontend/src/notebook/notebook_editor.tsx b/packages/frontend/src/notebook/notebook_editor.tsx index 6187fbd67..8dc6015b7 100644 --- a/packages/frontend/src/notebook/notebook_editor.tsx +++ b/packages/frontend/src/notebook/notebook_editor.tsx @@ -23,10 +23,12 @@ import { type Completion, Completions, type CompletionsRef, + type FocusHandle, IconButton, type KbdKey, keyEventHasModifier, type ModifierKey, + useChildFocus, } from "catcolab-ui-components"; import { materializeFromAutomerge } from "../util/materialize_from_automerge"; import { @@ -86,17 +88,16 @@ export function NotebookEditor(props: { formalCellEditor: Component>; cellConstructors?: CellConstructor[]; cellLabel?: (content: T) => string | undefined; + focus: FocusHandle; /** Called to duplicate an existing cell. If omitted, a deep copy is performed. */ duplicateCell?: (content: T) => T; - - // FIXME: Remove this option once we fix focus management. - noShortcuts?: boolean; }) { - const [activeCell, setActiveCell] = createSignal(null); + // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted notebook. + const cellFocus = useChildFocus(props.focus); const [currentDropTarget, setCurrentDropTarget] = createSignal(null); // Which create-cell popover (if any) the editor has requested to open. @@ -113,13 +114,13 @@ export function NotebookEditor(props: { description, shortcut: shortcut && [cellShortcutModifier, ...shortcut], onComplete: () => { - const [i, n] = [activeCell(), props.notebook.cellOrder.length]; + const [i, n] = [cellFocus.activeChild(), props.notebook.cellOrder.length]; const cellIndex = i != null ? Math.min(i + 1, n) : n; props.changeNotebook((nb) => { Nb.insertCellAtIndex(nb, cc.construct(), cellIndex); }); // Defer so the popover fully closes before we focus the new cell. - requestAnimationFrame(() => setActiveCell(cellIndex)); + requestAnimationFrame(() => cellFocus.setActiveChild(cellIndex)); }, }; }); @@ -148,7 +149,7 @@ export function NotebookEditor(props: { Nb.insertCellAtIndex(nb, cc.construct(), index); }); // Defer so the popover fully closes before we focus the new cell. - requestAnimationFrame(() => setActiveCell(index)); + requestAnimationFrame(() => cellFocus.setActiveChild(index)); }, }; }); @@ -167,14 +168,14 @@ export function NotebookEditor(props: { }); // Defer so the popover fully closes before we focus the new cell. requestAnimationFrame(() => { - setActiveCell(Nb.numCells(props.notebook) - 1); + cellFocus.setActiveChild(Nb.numCells(props.notebook) - 1); }); }, }; }); makeEventListener(window, "keydown", (evt) => { - if (props.noShortcuts) { + if (!props.focus.hasFocus()) { return; } if (keyEventHasModifier(evt, cellShortcutModifier)) { @@ -195,7 +196,7 @@ export function NotebookEditor(props: { // create-cell popover. The popover is rendered by that anchor, so // it is positioned automatically and doesn't require any DOM // queries here. - const cellIndex = activeCell(); + const cellIndex = cellFocus.activeChild(); setCreatePopoverTarget(cellIndex != null ? cellIndex : "append"); evt.preventDefault(); // Stop the same event from reaching `CellTypePopover`'s window @@ -255,21 +256,12 @@ export function NotebookEditor(props: { }); return ( -
    { - const container = evt.currentTarget; - setTimeout(() => { - if (!container.contains(document.activeElement)) { - setActiveCell(null); - } - }, 0); - }} - > +
    setCreatePopoverTarget(open ? "append" : null)} > @@ -281,17 +273,17 @@ export function NotebookEditor(props: {
      {(cellId, i) => { - const isActive = () => activeCell() === i(); + const focus = () => cellFocus.childFocus(i()); const cellActions: CellActions = { activateAbove() { if (i() > 0) { - setActiveCell(i() - 1); + cellFocus.setActiveChild(i() - 1); } }, activateBelow() { if (i() < Nb.numCells(props.notebook) - 1) { - setActiveCell(i() + 1); + cellFocus.setActiveChild(i() + 1); } }, deleteBackward() { @@ -299,14 +291,14 @@ export function NotebookEditor(props: { props.changeNotebook((nb) => { Nb.deleteCellAtIndex(nb, index); }); - setActiveCell(index - 1); + cellFocus.setActiveChild(index - 1); }, deleteForward() { const index = i(); props.changeNotebook((nb) => { Nb.deleteCellAtIndex(nb, index); }); - setActiveCell(index); + cellFocus.setActiveChild(index); }, moveUp() { // oxlint-disable-next-line solid/reactivity -- event handler @@ -320,9 +312,6 @@ export function NotebookEditor(props: { Nb.moveCellDown(nb, i()); }); }, - hasFocused() { - setActiveCell(i()); - }, }; const cell = props.notebook.cellContents[cellId]; @@ -345,7 +334,7 @@ export function NotebookEditor(props: { props.changeNotebook((nb) => { Nb.insertCellAtIndex(nb, newCell, index + 1); }); - setActiveCell(index + 1); + cellFocus.setActiveChild(index + 1); }; } @@ -354,6 +343,7 @@ export function NotebookEditor(props: { (props: { cellId={cell.id} handle={props.handle} path={[...props.path, "cellContents", cell.id]} - isActive={isActive()} + focus={focus()} actions={cellActions} /> @@ -391,7 +381,7 @@ export function NotebookEditor(props: { ), ) } - isActive={isActive()} + focus={focus()} actions={cellActions} /> )} @@ -407,6 +397,7 @@ export function NotebookEditor(props: {
      setCreatePopoverTarget(open ? "append" : null)} @@ -428,6 +419,7 @@ Up/Down to move, Enter to select, Escape to close). */ export function CellTypePopover(props: { completions: Completion[]; + focus?: FocusHandle; tooltip?: string; /** Whether the button is visible. Defaults to `true`. The button always remains visible while the popover is open. */ @@ -454,6 +446,9 @@ export function CellTypePopover(props: { if (!isOpen()) { return; } + if (props.focus && !props.focus.hasFocus()) { + return; + } const ref = completionsRef(); if (evt.key === "ArrowDown") { ref?.nextPresumptive(); diff --git a/packages/frontend/src/page/document_page.tsx b/packages/frontend/src/page/document_page.tsx index 441734f45..d266a4d0f 100644 --- a/packages/frontend/src/page/document_page.tsx +++ b/packages/frontend/src/page/document_page.tsx @@ -17,7 +17,15 @@ import { } from "solid-js"; import invariant from "tiny-invariant"; -import { Button, IconButton, ResizableHandle, WarningBanner } from "catcolab-ui-components"; +import { + Button, + type FocusHandle, + IconButton, + ResizableHandle, + rootFocus, + useChildFocus, + WarningBanner, +} from "catcolab-ui-components"; import { getLiveAnalysis, type LiveAnalysisDoc } from "../analysis"; import { AnalysisNotebookEditor } from "../analysis/analysis_editor"; import { AnalysisInfo } from "../analysis/analysis_info"; @@ -63,6 +71,7 @@ export default function DocumentPage() { const params = useParams(); const navigate = useNavigate(); const isSidePanelOpen = () => !!params.subkind && !!params.subref; + const paneFocus = useChildFocus<"primary" | "secondary">(rootFocus, { default: "primary" }); // Redirect if primary and secondary refs match createEffect(() => { @@ -101,6 +110,7 @@ export default function DocumentPage() { ); const closeSidePanel = () => { + paneFocus.setActiveChild("primary"); navigate(`/${params.kind}/${params.ref}`); }; @@ -120,6 +130,7 @@ export default function DocumentPage() { // expand the second panel context?.expand(1); } else { + paneFocus.setActiveChild("primary"); // collapse the second panel context?.collapse(1); // Set the first panel to be the full size @@ -194,6 +205,8 @@ export default function DocumentPage() { setResizableContext={setResizableContext} primaryHistoryOpen={primaryHistoryOpen()} secondaryHistoryOpen={secondaryHistoryOpen()} + primaryFocus={paneFocus.childFocus("primary")} + secondaryFocus={paneFocus.childFocus("secondary")} /> )} @@ -313,6 +326,8 @@ function ResizablePanels(props: { setResizableContext: (context: ContextValue) => void; primaryHistoryOpen: boolean; secondaryHistoryOpen: boolean; + primaryFocus: FocusHandle; + secondaryFocus: FocusHandle; }) { return ( @@ -329,6 +344,7 @@ function ResizablePanels(props: { refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} historySidebarOpen={props.primaryHistoryOpen} + focus={props.primaryFocus} /> @@ -347,6 +363,7 @@ function ResizablePanels(props: { refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} historySidebarOpen={props.secondaryHistoryOpen} + focus={props.secondaryFocus} /> )} @@ -365,6 +382,7 @@ export function DocumentPane(props: { refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; historySidebarOpen: boolean; + focus: FocusHandle; }) { const api = useApi(); const [isDeleted, setIsDeleted] = createSignal(false); @@ -404,7 +422,7 @@ export function DocumentPane(props: { // oxlint-disable solid/reactivity -- Context.Provider value getter is reactive return ( props.docRef.refId}> -
      +
      props.focus.setFocused(true)}>
      - {(liveModel) => } + {(liveModel) => ( + + )} {(liveDiagram) => ( - + )} {(liveAnalysis) => ( - + )} diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index 7544a6536..cbff9ec8a 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -23,6 +23,7 @@ export * from "./relative_time"; export * from "./resizable"; export * from "./spinner"; export * from "./text_input"; +export * from "./util/focus"; export * from "./util/keyboard"; export * from "./virtual_list"; export * from "./warning_banner"; diff --git a/packages/ui-components/src/text_input.tsx b/packages/ui-components/src/text_input.tsx index 0d70d21cf..73533c845 100644 --- a/packages/ui-components/src/text_input.tsx +++ b/packages/ui-components/src/text_input.tsx @@ -5,6 +5,7 @@ import { type ComponentProps, createEffect, createSignal, type JSX, splitProps } void focus; import { type Completion, Completions, type CompletionsRef } from "./completions"; +import type { FocusHandle } from "./util/focus"; import { assertTypelevel } from "./util/types"; /** Props for `TextInput` component. */ @@ -16,6 +17,9 @@ type TextInputProps = Omit, "onKeyDown"> & /** Optional props available to a `TextInput` component. */ export type TextInputOptions = TextInputActions & { + /** Focus state for this input. */ + focus?: FocusHandle; + /** Whether the input is active: allowed to the grab the focus. */ isActive?: boolean; @@ -103,6 +107,7 @@ type TextInputActions = { // XXX: Need the list of options as a *value* to split props. const TEXT_INPUT_OPTIONS = [ + "focus", "isActive", "hasFocused", "hasBlurred", @@ -143,7 +148,7 @@ export function TextInput(allProps: TextInputProps) { let ref!: HTMLInputElement; createEffect(() => { - if (options.isActive && document.activeElement !== ref) { + if ((options.focus?.hasFocus() ?? options.isActive) && document.activeElement !== ref) { ref.focus(); // Move cursor to end of input. ref.selectionStart = ref.selectionEnd = ref.value.length; @@ -224,6 +229,7 @@ export function TextInput(allProps: TextInputProps) { value={props.text} use:focus={(isFocused) => { if (isFocused) { + options.focus?.setFocused(true); options.hasFocused?.(); if ( options.completions != null && diff --git a/packages/ui-components/src/util/focus.ts b/packages/ui-components/src/util/focus.ts new file mode 100644 index 000000000..556a5c6c3 --- /dev/null +++ b/packages/ui-components/src/util/focus.ts @@ -0,0 +1,45 @@ +import { createEffect, createSignal, type Accessor } from "solid-js"; + +/** Focus state passed down a component tree. */ +export type FocusHandle = { + hasFocus: Accessor; + setFocused: (focused: boolean) => void; +}; + +/** Root focus handle for a tree that should always remember its last focus. */ +export const rootFocus: FocusHandle = { + hasFocus: () => true, + setFocused: () => {}, +}; + +/** Track which immediate child of a focused parent has focus. */ +export function useChildFocus( + parent: FocusHandle, + options?: { default?: K }, +): { + activeChild: Accessor; + setActiveChild: (child: K | null) => void; + childFocus: (child: K) => FocusHandle; +} { + const [activeChild, setActiveChild] = createSignal(options?.default ?? null); + + createEffect(() => { + if (!parent.hasFocus()) { + setActiveChild(() => options?.default ?? null); + } + }); + + const childFocus = (child: K): FocusHandle => ({ + hasFocus: () => parent.hasFocus() && activeChild() === child, + setFocused: (focused) => { + if (focused) { + setActiveChild(() => child); + parent.setFocused(true); + } else if (activeChild() === child) { + setActiveChild(() => options?.default ?? null); + } + }, + }); + + return { activeChild, setActiveChild, childFocus }; +} From 0e9dc9d167ecfee18a1718b75643621d08aa0258 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 15:59:28 +0100 Subject: [PATCH 45/81] ENH: Keyboard shortcuts for undo/redo --- packages/frontend/src/page/document_page.tsx | 26 ++++++++++++++++++++ packages/ui-components/src/util/keyboard.ts | 17 +++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/frontend/src/page/document_page.tsx b/packages/frontend/src/page/document_page.tsx index d266a4d0f..1989ab6af 100644 --- a/packages/frontend/src/page/document_page.tsx +++ b/packages/frontend/src/page/document_page.tsx @@ -1,4 +1,5 @@ import Resizable, { type ContextValue } from "@corvu/resizable"; +import { makeEventListener } from "@solid-primitives/event-listener"; import { Title } from "@solidjs/meta"; import { useNavigate, useParams } from "@solidjs/router"; import ChevronsRight from "lucide-solid/icons/chevrons-right"; @@ -21,6 +22,8 @@ import { Button, type FocusHandle, IconButton, + keyEventHasModifier, + primaryModifier, ResizableHandle, rootFocus, useChildFocus, @@ -419,6 +422,29 @@ export function DocumentPane(props: { const history = useSnapshotHistory(() => props.docRef.refId); + makeEventListener(window, "keydown", (evt) => { + if (!props.focus.hasFocus()) { + return; + } + const key = evt.key.toUpperCase(); + const hasPrimary = keyEventHasModifier(evt, primaryModifier); + if (!hasPrimary || evt.altKey) { + return; + } + + if (key === "Z" && !evt.shiftKey && history.canUndo()) { + history.onUndo(); + return evt.preventDefault(); + } + if ( + ((key === "Z" && evt.shiftKey) || (key === "Y" && !evt.shiftKey)) && + history.canRedo() + ) { + history.onRedo(); + return evt.preventDefault(); + } + }); + // oxlint-disable solid/reactivity -- Context.Provider value getter is reactive return ( props.docRef.refId}> diff --git a/packages/ui-components/src/util/keyboard.ts b/packages/ui-components/src/util/keyboard.ts index 0f44b3807..f1bcdbceb 100644 --- a/packages/ui-components/src/util/keyboard.ts +++ b/packages/ui-components/src/util/keyboard.ts @@ -9,6 +9,23 @@ The types `ModifierKey` and `KbdKey` are borrowed from export type ModifierKey = "Alt" | "Control" | "Meta" | "Shift"; export type KbdKey = ModifierKey | (string & {}); +/** Platform-appropriate primary modifier key for editor shortcuts. + +Uses Meta (Cmd) on Mac and Control elsewhere, matching native app convention. + */ +export const primaryModifier: ModifierKey = navigator.userAgent.includes("Mac") + ? "Meta" + : "Control"; + +/** Platform-appropriate secondary modifier key for editor shortcuts. + +Uses Control on Mac, where Alt/Option remaps keys, and Alt elsewhere, where +Control tends to already be bound. + */ +export const secondaryModifier: ModifierKey = navigator.userAgent.includes("Mac") + ? "Control" + : "Alt"; + /** Returns whether the modifier key is active in the keyboard event. */ export function keyEventHasModifier(evt: KeyboardEvent, key: ModifierKey): boolean { switch (key) { From 87d438d9d259b54aea879fdb9fa8461a685fc893 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 16:12:26 +0100 Subject: [PATCH 46/81] ENH: Indicate focus in sidebar --- packages/frontend/src/page/document_page.tsx | 2 ++ .../src/page/document_page_sidebar.tsx | 26 +++++++++++++++++-- packages/frontend/src/page/sidebar_layout.css | 6 ++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/page/document_page.tsx b/packages/frontend/src/page/document_page.tsx index 1989ab6af..6f418055b 100644 --- a/packages/frontend/src/page/document_page.tsx +++ b/packages/frontend/src/page/document_page.tsx @@ -192,6 +192,8 @@ export default function DocumentPage() { } : undefined; })()} + primaryFocus={paneFocus.childFocus("primary")} + secondaryFocus={paneFocus.childFocus("secondary")} refetchPrimaryDoc={refetchPrimaryDoc} refetchSecondaryDoc={refetchSecondaryDoc} /> diff --git a/packages/frontend/src/page/document_page_sidebar.tsx b/packages/frontend/src/page/document_page_sidebar.tsx index d798722b7..5e6bfd8ad 100644 --- a/packages/frontend/src/page/document_page_sidebar.tsx +++ b/packages/frontend/src/page/document_page_sidebar.tsx @@ -2,7 +2,7 @@ import { useNavigate } from "@solidjs/router"; import { createMemo, createResource, For, Show, useContext } from "solid-js"; import { stringify as uuidStringify } from "uuid"; -import { DocumentTypeIcon } from "catcolab-ui-components"; +import { DocumentTypeIcon, type FocusHandle } from "catcolab-ui-components"; import type { Document, Link } from "catlog-wasm"; import { type Api, type LiveDocWithRef, useApi } from "../api"; import { TheoryLibraryContext } from "../theory"; @@ -12,6 +12,8 @@ import { DocumentMenu } from "./document_menu"; export function DocumentSidebar(props: { primaryDoc?: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; + primaryFocus: FocusHandle; + secondaryFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -21,6 +23,8 @@ export function DocumentSidebar(props: { @@ -59,6 +63,8 @@ async function getDocParent(doc: Document, api: Api): Promise void; refetchSecondaryDoc: () => void; }) { @@ -78,6 +84,8 @@ function RelatedDocuments(props: { indent={1} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} + primaryFocus={props.primaryFocus} + secondaryFocus={props.secondaryFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -92,6 +100,8 @@ function DocumentsTreeNode(props: { indent: number; primaryDoc: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; + primaryFocus: FocusHandle; + secondaryFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -166,6 +176,8 @@ function DocumentsTreeNode(props: { indent={props.indent} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} + primaryFocus={props.primaryFocus} + secondaryFocus={props.secondaryFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -176,6 +188,8 @@ function DocumentsTreeNode(props: { indent={props.indent + 1} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} + primaryFocus={props.primaryFocus} + secondaryFocus={props.secondaryFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -190,6 +204,8 @@ function DocumentsTreeLeaf(props: { indent: number; primaryDoc: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; + primaryFocus: FocusHandle; + secondaryFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -200,6 +216,11 @@ function DocumentsTreeLeaf(props: { const clickedRefId = createMemo(() => props.doc.docRef.refId); const primaryRefId = createMemo(() => props.primaryDoc.docRef.refId); const secondaryRefId = createMemo(() => props.secondaryDoc?.docRef.refId); + const isPrimary = () => clickedRefId() === primaryRefId(); + const isSecondary = () => clickedRefId() === secondaryRefId(); + const isFocused = () => + (isPrimary() && props.primaryFocus.hasFocus()) || + (isSecondary() && props.secondaryFocus.hasFocus()); const iconLetters = createMemo(() => { const doc = props.doc.liveDoc.doc; @@ -236,7 +257,8 @@ function DocumentsTreeLeaf(props: { onClick={handleClick} class="related-document" classList={{ - active: props.doc.docRef.refId === props.primaryDoc.docRef.refId, + active: isPrimary() || isSecondary(), + focused: isFocused(), }} style={{ "padding-left": `${props.indent * 16}px` }} > diff --git a/packages/frontend/src/page/sidebar_layout.css b/packages/frontend/src/page/sidebar_layout.css index a97e7344f..2cd8eeb57 100644 --- a/packages/frontend/src/page/sidebar_layout.css +++ b/packages/frontend/src/page/sidebar_layout.css @@ -122,7 +122,7 @@ .related-document { padding: 5px 8px; height: 30px; - border-radius: 6px; + border-left: 3px solid transparent; transition: background 20ms; display: flex; justify-content: space-between; @@ -173,6 +173,10 @@ background: var(--color-hover-bg-dark); } } + + &.focused { + border-left-color: var(--color-alert-question); + } } .resizeable-handle { From 3130fa831096ed8b5af3d0b595d1df8ab170acdf Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 16:25:54 +0100 Subject: [PATCH 47/81] ENH: Opening history sidebar grabs focus --- packages/frontend/src/page/document_page.tsx | 27 +++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/page/document_page.tsx b/packages/frontend/src/page/document_page.tsx index 6f418055b..8db9ed496 100644 --- a/packages/frontend/src/page/document_page.tsx +++ b/packages/frontend/src/page/document_page.tsx @@ -175,6 +175,8 @@ export default function DocumentPage() { closeSidePanel={closeSidePanel} togglePrimaryHistorySidebar={togglePrimaryHistorySidebar} toggleSecondaryHistorySidebar={toggleSecondaryHistorySidebar} + primaryFocus={paneFocus.childFocus("primary")} + secondaryFocus={paneFocus.childFocus("secondary")} /> } sidebarContents={ @@ -230,6 +232,8 @@ function SplitPaneToolbar(props: { maximizeSidePanel: () => void; togglePrimaryHistorySidebar: () => void; toggleSecondaryHistorySidebar: () => void; + primaryFocus: FocusHandle; + secondaryFocus: FocusHandle; }) { const secondaryPanelSize = () => props.panelSizes?.[1]; const primaryPanelSize = () => props.panelSizes?.[0]; @@ -239,7 +243,13 @@ function SplitPaneToolbar(props: { - + { + props.primaryFocus.setFocused(true); + props.togglePrimaryHistorySidebar(); + }} + tooltip="Toggle history" + > @@ -250,7 +260,10 @@ function SplitPaneToolbar(props: { style={{ left: `${(primaryPanelSize() ?? 0) * 100}%` }} > { + props.primaryFocus.setFocused(true); + props.togglePrimaryHistorySidebar(); + }} tooltip="Toggle history" > @@ -267,6 +280,7 @@ function SplitPaneToolbar(props: { closeSidePanel={props.closeSidePanel} maximizeSidePanel={props.maximizeSidePanel} toggleHistorySidebar={props.toggleSecondaryHistorySidebar} + focus={props.secondaryFocus} /> )} @@ -281,6 +295,7 @@ function SecondaryToolbar(props: { closeSidePanel: () => void; maximizeSidePanel: () => void; toggleHistorySidebar: () => void; + focus: FocusHandle; }) { return ( <> @@ -306,7 +321,13 @@ function SecondaryToolbar(props: { > {(secondary) => (
      - + { + props.focus.setFocused(true); + props.toggleHistorySidebar(); + }} + tooltip="Toggle history" + > Date: Tue, 19 May 2026 16:29:50 +0100 Subject: [PATCH 48/81] ENH: Set focused on side-bar click on doc --- packages/frontend/src/page/document_page_sidebar.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/frontend/src/page/document_page_sidebar.tsx b/packages/frontend/src/page/document_page_sidebar.tsx index 5e6bfd8ad..7b3f3204a 100644 --- a/packages/frontend/src/page/document_page_sidebar.tsx +++ b/packages/frontend/src/page/document_page_sidebar.tsx @@ -239,14 +239,17 @@ function DocumentsTreeLeaf(props: { const handleClick = async () => { // If clicking on primary or secondary doc, navigate to just that doc if (clickedRefId() === primaryRefId() || clickedRefId() === secondaryRefId()) { + props.primaryFocus.setFocused(true); navigate(`/${createLinkPart(props.doc)}`); } else { // Otherwise, open it as a side panel or put on the left if it is a parent doc const clickedDoc = props.doc; const parentOfPrimary = await getDocParent(props.primaryDoc.liveDoc.doc, api); if (parentOfPrimary && clickedDoc.docRef.refId === parentOfPrimary.docRef.refId) { + props.primaryFocus.setFocused(true); navigate(`/${createLinkPart(clickedDoc)}/${createLinkPart(props.primaryDoc)}`); } else { + props.secondaryFocus.setFocused(true); navigate(`/${createLinkPart(props.primaryDoc)}/${createLinkPart(clickedDoc)}`); } } From 6014239bc4c882641f10d8e81bede4076d60a1e6 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 16:31:58 +0100 Subject: [PATCH 49/81] REFACTOR: Include "pane" in the focus names --- packages/frontend/src/page/document_page.tsx | 34 +++++++-------- .../src/page/document_page_sidebar.tsx | 42 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/packages/frontend/src/page/document_page.tsx b/packages/frontend/src/page/document_page.tsx index 8db9ed496..5ea18b825 100644 --- a/packages/frontend/src/page/document_page.tsx +++ b/packages/frontend/src/page/document_page.tsx @@ -175,8 +175,8 @@ export default function DocumentPage() { closeSidePanel={closeSidePanel} togglePrimaryHistorySidebar={togglePrimaryHistorySidebar} toggleSecondaryHistorySidebar={toggleSecondaryHistorySidebar} - primaryFocus={paneFocus.childFocus("primary")} - secondaryFocus={paneFocus.childFocus("secondary")} + primaryPaneFocus={paneFocus.childFocus("primary")} + secondaryPaneFocus={paneFocus.childFocus("secondary")} /> } sidebarContents={ @@ -194,8 +194,8 @@ export default function DocumentPage() { } : undefined; })()} - primaryFocus={paneFocus.childFocus("primary")} - secondaryFocus={paneFocus.childFocus("secondary")} + primaryPaneFocus={paneFocus.childFocus("primary")} + secondaryPaneFocus={paneFocus.childFocus("secondary")} refetchPrimaryDoc={refetchPrimaryDoc} refetchSecondaryDoc={refetchSecondaryDoc} /> @@ -212,8 +212,8 @@ export default function DocumentPage() { setResizableContext={setResizableContext} primaryHistoryOpen={primaryHistoryOpen()} secondaryHistoryOpen={secondaryHistoryOpen()} - primaryFocus={paneFocus.childFocus("primary")} - secondaryFocus={paneFocus.childFocus("secondary")} + primaryPaneFocus={paneFocus.childFocus("primary")} + secondaryPaneFocus={paneFocus.childFocus("secondary")} /> )} @@ -232,8 +232,8 @@ function SplitPaneToolbar(props: { maximizeSidePanel: () => void; togglePrimaryHistorySidebar: () => void; toggleSecondaryHistorySidebar: () => void; - primaryFocus: FocusHandle; - secondaryFocus: FocusHandle; + primaryPaneFocus: FocusHandle; + secondaryPaneFocus: FocusHandle; }) { const secondaryPanelSize = () => props.panelSizes?.[1]; const primaryPanelSize = () => props.panelSizes?.[0]; @@ -245,7 +245,7 @@ function SplitPaneToolbar(props: { { - props.primaryFocus.setFocused(true); + props.primaryPaneFocus.setFocused(true); props.togglePrimaryHistorySidebar(); }} tooltip="Toggle history" @@ -261,7 +261,7 @@ function SplitPaneToolbar(props: { > { - props.primaryFocus.setFocused(true); + props.primaryPaneFocus.setFocused(true); props.togglePrimaryHistorySidebar(); }} tooltip="Toggle history" @@ -280,7 +280,7 @@ function SplitPaneToolbar(props: { closeSidePanel={props.closeSidePanel} maximizeSidePanel={props.maximizeSidePanel} toggleHistorySidebar={props.toggleSecondaryHistorySidebar} - focus={props.secondaryFocus} + secondaryPaneFocus={props.secondaryPaneFocus} /> )} @@ -295,7 +295,7 @@ function SecondaryToolbar(props: { closeSidePanel: () => void; maximizeSidePanel: () => void; toggleHistorySidebar: () => void; - focus: FocusHandle; + secondaryPaneFocus: FocusHandle; }) { return ( <> @@ -323,7 +323,7 @@ function SecondaryToolbar(props: {
      { - props.focus.setFocused(true); + props.secondaryPaneFocus.setFocused(true); props.toggleHistorySidebar(); }} tooltip="Toggle history" @@ -352,8 +352,8 @@ function ResizablePanels(props: { setResizableContext: (context: ContextValue) => void; primaryHistoryOpen: boolean; secondaryHistoryOpen: boolean; - primaryFocus: FocusHandle; - secondaryFocus: FocusHandle; + primaryPaneFocus: FocusHandle; + secondaryPaneFocus: FocusHandle; }) { return ( @@ -370,7 +370,7 @@ function ResizablePanels(props: { refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} historySidebarOpen={props.primaryHistoryOpen} - focus={props.primaryFocus} + focus={props.primaryPaneFocus} /> @@ -389,7 +389,7 @@ function ResizablePanels(props: { refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} historySidebarOpen={props.secondaryHistoryOpen} - focus={props.secondaryFocus} + focus={props.secondaryPaneFocus} /> )} diff --git a/packages/frontend/src/page/document_page_sidebar.tsx b/packages/frontend/src/page/document_page_sidebar.tsx index 7b3f3204a..64f249fe9 100644 --- a/packages/frontend/src/page/document_page_sidebar.tsx +++ b/packages/frontend/src/page/document_page_sidebar.tsx @@ -12,8 +12,8 @@ import { DocumentMenu } from "./document_menu"; export function DocumentSidebar(props: { primaryDoc?: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; - primaryFocus: FocusHandle; - secondaryFocus: FocusHandle; + primaryPaneFocus: FocusHandle; + secondaryPaneFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -23,8 +23,8 @@ export function DocumentSidebar(props: { @@ -63,8 +63,8 @@ async function getDocParent(doc: Document, api: Api): Promise void; refetchSecondaryDoc: () => void; }) { @@ -84,8 +84,8 @@ function RelatedDocuments(props: { indent={1} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} - primaryFocus={props.primaryFocus} - secondaryFocus={props.secondaryFocus} + primaryPaneFocus={props.primaryPaneFocus} + secondaryPaneFocus={props.secondaryPaneFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -100,8 +100,8 @@ function DocumentsTreeNode(props: { indent: number; primaryDoc: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; - primaryFocus: FocusHandle; - secondaryFocus: FocusHandle; + primaryPaneFocus: FocusHandle; + secondaryPaneFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -176,8 +176,8 @@ function DocumentsTreeNode(props: { indent={props.indent} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} - primaryFocus={props.primaryFocus} - secondaryFocus={props.secondaryFocus} + primaryPaneFocus={props.primaryPaneFocus} + secondaryPaneFocus={props.secondaryPaneFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -188,8 +188,8 @@ function DocumentsTreeNode(props: { indent={props.indent + 1} primaryDoc={props.primaryDoc} secondaryDoc={props.secondaryDoc} - primaryFocus={props.primaryFocus} - secondaryFocus={props.secondaryFocus} + primaryPaneFocus={props.primaryPaneFocus} + secondaryPaneFocus={props.secondaryPaneFocus} refetchPrimaryDoc={props.refetchPrimaryDoc} refetchSecondaryDoc={props.refetchSecondaryDoc} /> @@ -204,8 +204,8 @@ function DocumentsTreeLeaf(props: { indent: number; primaryDoc: LiveDocWithRef; secondaryDoc?: LiveDocWithRef; - primaryFocus: FocusHandle; - secondaryFocus: FocusHandle; + primaryPaneFocus: FocusHandle; + secondaryPaneFocus: FocusHandle; refetchPrimaryDoc: () => void; refetchSecondaryDoc: () => void; }) { @@ -219,8 +219,8 @@ function DocumentsTreeLeaf(props: { const isPrimary = () => clickedRefId() === primaryRefId(); const isSecondary = () => clickedRefId() === secondaryRefId(); const isFocused = () => - (isPrimary() && props.primaryFocus.hasFocus()) || - (isSecondary() && props.secondaryFocus.hasFocus()); + (isPrimary() && props.primaryPaneFocus.hasFocus()) || + (isSecondary() && props.secondaryPaneFocus.hasFocus()); const iconLetters = createMemo(() => { const doc = props.doc.liveDoc.doc; @@ -239,17 +239,17 @@ function DocumentsTreeLeaf(props: { const handleClick = async () => { // If clicking on primary or secondary doc, navigate to just that doc if (clickedRefId() === primaryRefId() || clickedRefId() === secondaryRefId()) { - props.primaryFocus.setFocused(true); + props.primaryPaneFocus.setFocused(true); navigate(`/${createLinkPart(props.doc)}`); } else { // Otherwise, open it as a side panel or put on the left if it is a parent doc const clickedDoc = props.doc; const parentOfPrimary = await getDocParent(props.primaryDoc.liveDoc.doc, api); if (parentOfPrimary && clickedDoc.docRef.refId === parentOfPrimary.docRef.refId) { - props.primaryFocus.setFocused(true); + props.primaryPaneFocus.setFocused(true); navigate(`/${createLinkPart(clickedDoc)}/${createLinkPart(props.primaryDoc)}`); } else { - props.secondaryFocus.setFocused(true); + props.secondaryPaneFocus.setFocused(true); navigate(`/${createLinkPart(props.primaryDoc)}/${createLinkPart(clickedDoc)}`); } } From 76d6337c9b9dda359527e68c2e2581e250d72518 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 16:44:53 +0100 Subject: [PATCH 50/81] FIX: Keep focus on completion escape key press --- packages/ui-components/src/text_input.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ui-components/src/text_input.tsx b/packages/ui-components/src/text_input.tsx index 73533c845..c3aeaef2b 100644 --- a/packages/ui-components/src/text_input.tsx +++ b/packages/ui-components/src/text_input.tsx @@ -161,7 +161,10 @@ export function TextInput(allProps: TextInputProps) { const onKeyDown: JSX.EventHandler = (evt) => { const remaining = completionsRef()?.remainingCompletions() ?? []; const value = evt.currentTarget.value; - if (options.interceptKeyDown?.(evt)) { + if (evt.key === "Escape" && isCompletionsOpen()) { + setCompletionsOpen(false); + evt.stopPropagation(); + } else if (options.interceptKeyDown?.(evt)) { } else if (options.deleteBackward && evt.key === "Backspace" && !value) { options.deleteBackward(); } else if (options.deleteForward && evt.key === "Delete" && !value) { From 16fa8a2703ca1de9cc08457f1120643ba9a2ff68 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 17:09:17 +0100 Subject: [PATCH 51/81] FIX: Use uuid to track active cell rather than index --- .../frontend/src/notebook/notebook_editor.tsx | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/notebook/notebook_editor.tsx b/packages/frontend/src/notebook/notebook_editor.tsx index 8dc6015b7..b53d281f3 100644 --- a/packages/frontend/src/notebook/notebook_editor.tsx +++ b/packages/frontend/src/notebook/notebook_editor.tsx @@ -18,7 +18,7 @@ import { import invariant from "tiny-invariant"; import { Nb } from "catcolab-document-methods"; -import type { Cell, Notebook } from "catcolab-document-types"; +import type { Cell, Notebook, Uuid } from "catcolab-document-types"; import { type Completion, Completions, @@ -44,10 +44,10 @@ import "./notebook_editor.css"; /** Identifies which create-cell popover, if any, the editor wants open. -Either an index of an existing cell (open the "create below" popover anchored -to that cell) or `"append"` (open the popover for the end-of-notebook button). +Either the id of an existing cell (open the "create below" popover anchored to +that cell) or `"append"` (open the popover for the end-of-notebook button). */ -type CreatePopoverTarget = number | "append" | null; +type CreatePopoverTarget = Uuid | "append" | null; /** Constructor for a cell in a notebook. @@ -97,7 +97,7 @@ export function NotebookEditor(props: { duplicateCell?: (content: T) => T; }) { // oxlint-disable-next-line solid/reactivity -- Focus handles are stable for a mounted notebook. - const cellFocus = useChildFocus(props.focus); + const cellFocus = useChildFocus(props.focus); const [currentDropTarget, setCurrentDropTarget] = createSignal(null); // Which create-cell popover (if any) the editor has requested to open. @@ -114,13 +114,20 @@ export function NotebookEditor(props: { description, shortcut: shortcut && [cellShortcutModifier, ...shortcut], onComplete: () => { - const [i, n] = [cellFocus.activeChild(), props.notebook.cellOrder.length]; - const cellIndex = i != null ? Math.min(i + 1, n) : n; + const activeCellId = cellFocus.activeChild(); + const activeIndex = activeCellId + ? props.notebook.cellOrder.indexOf(activeCellId) + : -1; + const cellIndex = + activeIndex >= 0 + ? Math.min(activeIndex + 1, props.notebook.cellOrder.length) + : props.notebook.cellOrder.length; + const newCell = cc.construct(); props.changeNotebook((nb) => { - Nb.insertCellAtIndex(nb, cc.construct(), cellIndex); + Nb.insertCellAtIndex(nb, newCell, cellIndex); }); // Defer so the popover fully closes before we focus the new cell. - requestAnimationFrame(() => cellFocus.setActiveChild(cellIndex)); + requestAnimationFrame(() => cellFocus.setActiveChild(newCell.id)); }, }; }); @@ -145,11 +152,12 @@ export function NotebookEditor(props: { shortcut: shortcut && [cellShortcutModifier, ...shortcut], onComplete: () => { const index = i + 1; + const newCell = cc.construct(); props.changeNotebook((nb) => { - Nb.insertCellAtIndex(nb, cc.construct(), index); + Nb.insertCellAtIndex(nb, newCell, index); }); // Defer so the popover fully closes before we focus the new cell. - requestAnimationFrame(() => cellFocus.setActiveChild(index)); + requestAnimationFrame(() => cellFocus.setActiveChild(newCell.id)); }, }; }); @@ -163,13 +171,12 @@ export function NotebookEditor(props: { description, shortcut: shortcut && [cellShortcutModifier, ...shortcut], onComplete: () => { + const newCell = cc.construct(); props.changeNotebook((nb) => { - Nb.appendCell(nb, cc.construct()); + Nb.appendCell(nb, newCell); }); // Defer so the popover fully closes before we focus the new cell. - requestAnimationFrame(() => { - cellFocus.setActiveChild(Nb.numCells(props.notebook) - 1); - }); + requestAnimationFrame(() => cellFocus.setActiveChild(newCell.id)); }, }; }); @@ -196,8 +203,8 @@ export function NotebookEditor(props: { // create-cell popover. The popover is rendered by that anchor, so // it is positioned automatically and doesn't require any DOM // queries here. - const cellIndex = cellFocus.activeChild(); - setCreatePopoverTarget(cellIndex != null ? cellIndex : "append"); + const cellId = cellFocus.activeChild(); + setCreatePopoverTarget(cellId ?? "append"); evt.preventDefault(); // Stop the same event from reaching `CellTypePopover`'s window // keydown listener, which would otherwise see the popover as @@ -273,32 +280,41 @@ export function NotebookEditor(props: {
        {(cellId, i) => { - const focus = () => cellFocus.childFocus(i()); + const focus = () => cellFocus.childFocus(cellId); const cellActions: CellActions = { activateAbove() { if (i() > 0) { - cellFocus.setActiveChild(i() - 1); + cellFocus.setActiveChild( + props.notebook.cellOrder[i() - 1] ?? null, + ); } }, activateBelow() { if (i() < Nb.numCells(props.notebook) - 1) { - cellFocus.setActiveChild(i() + 1); + cellFocus.setActiveChild( + props.notebook.cellOrder[i() + 1] ?? null, + ); } }, deleteBackward() { const index = i(); + const nextActiveId = props.notebook.cellOrder[index - 1] ?? null; props.changeNotebook((nb) => { Nb.deleteCellAtIndex(nb, index); }); - cellFocus.setActiveChild(index - 1); + cellFocus.setActiveChild(nextActiveId); }, deleteForward() { const index = i(); + const nextActiveId = + props.notebook.cellOrder[index + 1] ?? + props.notebook.cellOrder[index - 1] ?? + null; props.changeNotebook((nb) => { Nb.deleteCellAtIndex(nb, index); }); - cellFocus.setActiveChild(index); + cellFocus.setActiveChild(nextActiveId); }, moveUp() { // oxlint-disable-next-line solid/reactivity -- event handler @@ -334,7 +350,7 @@ export function NotebookEditor(props: { props.changeNotebook((nb) => { Nb.insertCellAtIndex(nb, newCell, index + 1); }); - cellFocus.setActiveChild(index + 1); + cellFocus.setActiveChild(newCell.id); }; } @@ -351,9 +367,9 @@ export function NotebookEditor(props: { : undefined } createCompletions={createBelowCommands(i())} - popoverOpen={createPopoverTarget() === i()} + popoverOpen={createPopoverTarget() === cellId} setPopoverOpen={(open) => - setCreatePopoverTarget(open ? i() : null) + setCreatePopoverTarget(open ? cellId : null) } currentDropTarget={currentDropTarget()} setCurrentDropTarget={setCurrentDropTarget} From bbfa8c0f2062831460c68dad168e62e914887cf5 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Fri, 15 May 2026 15:27:03 +0100 Subject: [PATCH 52/81] ENH: Add keyboard shortcut indication to undo/redo button tooltip --- .../frontend/src/page/history_sidebar.tsx | 5 +-- packages/ui-components/src/util/keyboard.ts | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/page/history_sidebar.tsx b/packages/frontend/src/page/history_sidebar.tsx index 50b1db463..de635b1da 100644 --- a/packages/frontend/src/page/history_sidebar.tsx +++ b/packages/frontend/src/page/history_sidebar.tsx @@ -1,3 +1,4 @@ +import { formatShortcut, primaryModifier } from "catcolab-ui-components"; import { HistoryNavigator } from "catcolab-ui-components"; import type { SnapshotHistory } from "./use_snapshot_history"; @@ -10,8 +11,8 @@ export function HistorySidebar(props: { history: SnapshotHistory }) { onUndo={props.history.onUndo} onRedo={props.history.onRedo} onSelect={props.history.navigate} - undoTooltip="Undo" - redoTooltip="Redo" + undoTooltip={`Undo (${formatShortcut([primaryModifier, "Z"])})`} + redoTooltip={`Redo (${formatShortcut([primaryModifier, "Shift", "Z"])} or ${formatShortcut([primaryModifier, "Y"])})`} /> ); } diff --git a/packages/ui-components/src/util/keyboard.ts b/packages/ui-components/src/util/keyboard.ts index f1bcdbceb..23762a188 100644 --- a/packages/ui-components/src/util/keyboard.ts +++ b/packages/ui-components/src/util/keyboard.ts @@ -41,3 +41,36 @@ export function keyEventHasModifier(evt: KeyboardEvent, key: ModifierKey): boole throw new Error(`Key is not a modifier: ${key}`); } } + +/** Format a modifier key for display to the user (e.g. "Cmd" on Mac, "Ctrl" elsewhere). */ +function formatModifierKey(key: ModifierKey, isMac: boolean): string { + switch (key) { + case "Meta": + return isMac ? "Cmd" : "Win"; + case "Control": + return isMac ? "⌃" : "Ctrl"; + case "Shift": + return "Shift"; + case "Alt": + return isMac ? "⌥" : "Alt"; + } +} + +/** Format a keyboard shortcut for display to the user. + * + * Takes an array of keys (modifiers followed by the main key) and returns + * a human-readable string like "Cmd+Z" or "Ctrl+Shift+Z". + */ +export function formatShortcut(keys: KbdKey[]): string { + if (keys.length === 0) { + return ""; + } + const isMac = navigator.userAgent.includes("Mac"); + const parts = keys.map((key) => { + if (key === "Meta" || key === "Control" || key === "Alt" || key === "Shift") { + return formatModifierKey(key as ModifierKey, isMac); + } + return key; + }); + return parts.join("+"); +} From d61510d858b7b365facabada0586cb911fdbedfb Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Tue, 19 May 2026 17:31:55 +0100 Subject: [PATCH 53/81] FIX: Update gaios for FocusHandle --- packages/gaios/src/model_tool.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/gaios/src/model_tool.tsx b/packages/gaios/src/model_tool.tsx index cc04d9d8c..4d14fcfe0 100644 --- a/packages/gaios/src/model_tool.tsx +++ b/packages/gaios/src/model_tool.tsx @@ -11,6 +11,7 @@ import { ModelNotebookEditor } from "../../frontend/src/model/model_editor"; import { ModelDocumentHead } from "../../frontend/src/model/model_info"; import { stdTheories } from "../../frontend/src/stdlib"; import { TheoryLibraryContext } from "../../frontend/src/theory"; +import { rootFocus } from "../../ui-components/src/util/focus"; import type { ModelDoc } from "./model_datatype"; import "../../ui-components/src/global.css"; @@ -54,7 +55,10 @@ export function renderModelTool(handle: DocHandle, element: ToolElemen - + )} From ba10615efc87642eea2a29dd4f8ca6db11733d63 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Thu, 21 May 2026 17:44:23 +0100 Subject: [PATCH 54/81] REFACTOR: Rename color-alert-question to color-indicator --- packages/frontend/src/page/sidebar_layout.css | 2 +- packages/ui-components/src/alert.css | 2 +- packages/ui-components/src/colors.css | 2 +- packages/ui-components/src/colors.stories.tsx | 12 ++++++------ .../ui-components/src/history_navigator.module.css | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/frontend/src/page/sidebar_layout.css b/packages/frontend/src/page/sidebar_layout.css index 2cd8eeb57..beb0e063b 100644 --- a/packages/frontend/src/page/sidebar_layout.css +++ b/packages/frontend/src/page/sidebar_layout.css @@ -175,7 +175,7 @@ } &.focused { - border-left-color: var(--color-alert-question); + border-left-color: var(--color-indicator); } } diff --git a/packages/ui-components/src/alert.css b/packages/ui-components/src/alert.css index 86366848f..612aa106b 100644 --- a/packages/ui-components/src/alert.css +++ b/packages/ui-components/src/alert.css @@ -34,5 +34,5 @@ } .alert-question { - --alert-color: var(--color-alert-question); + --alert-color: var(--color-accent-info); } diff --git a/packages/ui-components/src/colors.css b/packages/ui-components/src/colors.css index 01847c714..3ecc055f4 100644 --- a/packages/ui-components/src/colors.css +++ b/packages/ui-components/src/colors.css @@ -111,7 +111,7 @@ /* ======================================== * Information Colors * ======================================== */ - --color-alert-question: cornflowerblue; + --color-accent-info: cornflowerblue; --color-alert-note: teal; /* ======================================== diff --git a/packages/ui-components/src/colors.stories.tsx b/packages/ui-components/src/colors.stories.tsx index 10cf413d7..6cc254720 100644 --- a/packages/ui-components/src/colors.stories.tsx +++ b/packages/ui-components/src/colors.stories.tsx @@ -456,7 +456,7 @@ export const AllColors: Story = { - Alert Question Variant (alert.css) + Information Accent (alert.css, history_navigator.module.css)

        - Question alerts use --color-alert-question (cornflowerblue) for the accent - color and heading text. + Informational accents use --color-accent-info (cornflowerblue), including + question alert headings and the history navigator's active marker.

        @@ -3172,7 +3172,7 @@ export const InformationColorUsage: Story = { "font-size": "0.875rem", }} > -

        --alert-color: var(--color-alert-question);
        +
        --alert-color: var(--color-accent-info);
        border-left: 4px solid var(--alert-color);
        color: var(--alert-color); {/* heading */}
      @@ -3231,7 +3231,7 @@ export const InformationColorUsage: Story = { gap: "1.5rem", }} > - +
      diff --git a/packages/ui-components/src/history_navigator.module.css b/packages/ui-components/src/history_navigator.module.css index 71197aa97..11734d272 100644 --- a/packages/ui-components/src/history_navigator.module.css +++ b/packages/ui-components/src/history_navigator.module.css @@ -58,7 +58,7 @@ width: 10px; height: 10px; border-radius: 50%; - background-color: var(--color-alert-question); + background-color: var(--color-accent-info); } .timeCell { From a842bc6aaa7d2f2b968ed6cfdc4723a246e8c937 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Thu, 21 May 2026 17:54:23 +0100 Subject: [PATCH 55/81] ENH: Line up button colors with topos primary --- packages/ui-components/src/alert.css | 2 +- packages/ui-components/src/colors.css | 14 +++++------ packages/ui-components/src/colors.stories.tsx | 23 ++++++++++--------- .../src/history_navigator.module.css | 2 +- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/ui-components/src/alert.css b/packages/ui-components/src/alert.css index 612aa106b..86366848f 100644 --- a/packages/ui-components/src/alert.css +++ b/packages/ui-components/src/alert.css @@ -34,5 +34,5 @@ } .alert-question { - --alert-color: var(--color-accent-info); + --alert-color: var(--color-alert-question); } diff --git a/packages/ui-components/src/colors.css b/packages/ui-components/src/colors.css index 3ecc055f4..d6887c268 100644 --- a/packages/ui-components/src/colors.css +++ b/packages/ui-components/src/colors.css @@ -78,12 +78,12 @@ * Positive Colors * From lightest to darkest * ======================================== */ - --color-icon-button-positive-hover: #efe; - --color-icon-button-positive-active: #dfd; - --color-button-positive-hover: #3cb371; - --color-button-positive-base: #2e8b57; - --color-icon-button-positive-text: #0a0; - --color-button-positive-border: darkgreen; + --color-icon-button-positive-hover: #e8f7f6; + --color-icon-button-positive-active: #d4efed; + --color-button-positive-hover: var(--color-topos-primary-hover); + --color-button-positive-base: var(--color-topos-primary); + --color-icon-button-positive-text: var(--color-topos-primary); + --color-button-positive-border: var(--color-topos-primary); /* ======================================== * Danger Colors @@ -111,7 +111,7 @@ /* ======================================== * Information Colors * ======================================== */ - --color-accent-info: cornflowerblue; + --color-indicator: cornflowerblue; --color-alert-note: teal; /* ======================================== diff --git a/packages/ui-components/src/colors.stories.tsx b/packages/ui-components/src/colors.stories.tsx index 6cc254720..614af3fe2 100644 --- a/packages/ui-components/src/colors.stories.tsx +++ b/packages/ui-components/src/colors.stories.tsx @@ -456,7 +456,7 @@ export const AllColors: Story = {

      - Positive buttons use --color-button-positive-base for background and - --color-button-positive-border for border. On hover, they transition to - --color-button-positive-hover. + Positive buttons use the Topos primary palette: --color-button-positive-base + for background and --color-button-positive-border for border. On hover, they + transition to --color-button-positive-hover.

      @@ -2635,8 +2635,8 @@ export const PositiveColorUsage: Story = {

      Positive icon buttons use transparent background by default. On hover, they - use --color-icon-button-positive-hover for background and - --color-icon-button-positive-text for color. On active, they use + use Topos-primary-tinted --color-icon-button-positive-hover for background + and --color-icon-button-positive-text for color. On active, they use --color-icon-button-positive-active.

      - Information Accent (alert.css, history_navigator.module.css) + Indicator (alert.css, history_navigator.module.css)

      - Informational accents use --color-accent-info (cornflowerblue), including - question alert headings and the history navigator's active marker. + Indicators use --color-indicator (cornflowerblue), with + --color-alert-question as the alert-specific alias for question headings.

      @@ -3172,7 +3172,7 @@ export const InformationColorUsage: Story = { "font-size": "0.875rem", }} > -

      --alert-color: var(--color-accent-info);
      +
      --alert-color: var(--color-alert-question);
      border-left: 4px solid var(--alert-color);
      color: var(--alert-color); {/* heading */}
      @@ -3231,7 +3231,8 @@ export const InformationColorUsage: Story = { gap: "1.5rem", }} > - + +
      diff --git a/packages/ui-components/src/history_navigator.module.css b/packages/ui-components/src/history_navigator.module.css index 11734d272..ee5f1b1b7 100644 --- a/packages/ui-components/src/history_navigator.module.css +++ b/packages/ui-components/src/history_navigator.module.css @@ -58,7 +58,7 @@ width: 10px; height: 10px; border-radius: 50%; - background-color: var(--color-accent-info); + background-color: var(--color-indicator); } .timeCell { From 9fffad5cab4a71544a20108d3896a4729b8a6f28 Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Thu, 21 May 2026 17:57:56 +0100 Subject: [PATCH 56/81] ENH: Use topos-primary for color-alert-note --- packages/ui-components/src/colors.css | 3 ++- packages/ui-components/src/colors.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/ui-components/src/colors.css b/packages/ui-components/src/colors.css index d6887c268..fb8387c83 100644 --- a/packages/ui-components/src/colors.css +++ b/packages/ui-components/src/colors.css @@ -112,7 +112,8 @@ * Information Colors * ======================================== */ --color-indicator: cornflowerblue; - --color-alert-note: teal; + --color-alert-question: var(--color-indicator); + --color-alert-note: var(--color-topos-primary); /* ======================================== * Rich Text Editor Colors diff --git a/packages/ui-components/src/colors.stories.tsx b/packages/ui-components/src/colors.stories.tsx index 614af3fe2..519501825 100644 --- a/packages/ui-components/src/colors.stories.tsx +++ b/packages/ui-components/src/colors.stories.tsx @@ -3189,8 +3189,8 @@ export const InformationColorUsage: Story = { Alert Note Variant (alert.css)

      - Note alerts use --color-alert-note (teal) for the accent color and heading - text. + Note alerts use --color-alert-note (Topos primary) for the accent color and + heading text.

      From d833f6c70308cd9ffc1d1542b17aac79574e04ff Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Thu, 21 May 2026 18:08:00 +0100 Subject: [PATCH 57/81] REFACTOR: Use button positive instead of topos color name for buttons --- packages/frontend/src/page/home_page.css | 32 ------------------- packages/frontend/src/page/home_page.tsx | 24 +++++++++----- packages/ui-components/src/button.tsx | 6 ++-- packages/ui-components/src/colors.stories.tsx | 15 --------- 4 files changed, 19 insertions(+), 58 deletions(-) diff --git a/packages/frontend/src/page/home_page.css b/packages/frontend/src/page/home_page.css index 61ca7a2df..3e0af0ace 100644 --- a/packages/frontend/src/page/home_page.css +++ b/packages/frontend/src/page/home_page.css @@ -65,13 +65,7 @@ .home-nav-button { margin-top: 20px; padding: 10px 20px; - background: var(--color-topos-primary); - border: none; - border-radius: 5px; - font-family: var(--main-font); font-size: 18px; - color: var(--color-background); - cursor: pointer; } .quick-actions { @@ -205,31 +199,5 @@ } .home-nav-button.get-started { - border: 1px solid var(--color-background); - border-radius: 5px; - cursor: pointer; font-weight: bold; - color: var(--color-background); -} - -.home-nav-button.outline { - background: var(--color-background); - color: var(--color-gray-850); - border: 1px solid var(--color-gray-700); - border-radius: 5px; - cursor: pointer; -} - -.home-nav-button.outline:hover:not(:disabled) { - background-color: var(--color-hover-button-light); -} - -.home-nav-button:hover:not(:disabled) { - background-color: var(--color-topos-primary-hover); -} - -/* stylelint-disable-next-line no-descending-specificity -- :disabled and :hover:not(:disabled) are mutually exclusive */ -.home-nav-button:disabled { - opacity: 0.6; - cursor: not-allowed; } diff --git a/packages/frontend/src/page/home_page.tsx b/packages/frontend/src/page/home_page.tsx index a14b6e3e5..87c0124c6 100644 --- a/packages/frontend/src/page/home_page.tsx +++ b/packages/frontend/src/page/home_page.tsx @@ -11,6 +11,7 @@ import LogInIcon from "lucide-solid/icons/log-in"; import { useAuth, useFirebaseApp } from "solid-firebase"; import { createSignal, Match, Show, Switch } from "solid-js"; +import { Button } from "catcolab-ui-components"; import { useApi } from "../api"; import { createModel } from "../model/document"; import { stdTheories } from "../stdlib"; @@ -57,12 +58,13 @@ export default function HomePage() {

      - +
      @@ -74,28 +76,34 @@ export default function HomePage() {
      - + - + - +
      diff --git a/packages/ui-components/src/button.tsx b/packages/ui-components/src/button.tsx index 80561c110..62f6ef3ef 100644 --- a/packages/ui-components/src/button.tsx +++ b/packages/ui-components/src/button.tsx @@ -12,7 +12,7 @@ export function Button( children: JSX.Element; } & ComponentProps<"button">, ) { - const [props, buttonProps] = splitProps(allProps, ["variant", "children"]); + const [props, buttonProps] = splitProps(allProps, ["variant", "children", "class"]); const variantClass = () => { switch (props.variant) { @@ -29,9 +29,9 @@ export function Button( return ( diff --git a/packages/ui-components/src/colors.stories.tsx b/packages/ui-components/src/colors.stories.tsx index 519501825..9019b66a0 100644 --- a/packages/ui-components/src/colors.stories.tsx +++ b/packages/ui-components/src/colors.stories.tsx @@ -532,21 +532,6 @@ export const ButtonColorsStudy: Story = { Disabled
      -
      From 4c3a593e3f82d49965562f799d8e21597e03c31f Mon Sep 17 00:00:00 2001 From: Kaspar Bumke Date: Thu, 21 May 2026 18:30:54 +0100 Subject: [PATCH 59/81] CLEANUP: Put foldable stories in the right category --- packages/ui-components/src/foldable.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-components/src/foldable.stories.tsx b/packages/ui-components/src/foldable.stories.tsx index 48ef5a003..5759f8623 100644 --- a/packages/ui-components/src/foldable.stories.tsx +++ b/packages/ui-components/src/foldable.stories.tsx @@ -7,7 +7,7 @@ import { Foldable } from "./foldable"; import { IconButton } from "./icon_button"; const meta = { - title: "Foldable", + title: "Layout/Foldable", component: Foldable, } satisfies Meta; From d77ac106bfd517ee69a1a895144cb1b60aed6717 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Fri, 22 May 2026 11:05:07 -0700 Subject: [PATCH 60/81] BUILD: Upgrade ECharts from v5.5 to v6.1 (#1288) Co-authored-by: Kaspar Bumke --- packages/frontend/default.nix | 2 +- packages/frontend/package.json | 4 +- packages/frontend/pnpm-lock.yaml | 106 ++++++++++++++++++------------- pnpm-workspace.yaml | 1 + 4 files changed, 65 insertions(+), 48 deletions(-) diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index cc090e4ec..0fbccecf1 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -37,7 +37,7 @@ let pnpmDeps = pkgs.fetchPnpmDeps { # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-hmjA8fRTgeVYksCp7PVBUTY7Y6huasNCx8KRofapC2g="; + hash = "sha256-INfSKk7wf37D3HVs8QXzx9YV0VwuDmfwnnppviLlXNQ="; pname = name; fetcherVersion = 2; diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 7ff9b4bc7..c22d2ff4e 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -52,8 +52,8 @@ "catcolab-document-types": "link:../document-types/pkg", "catcolab-ui-components": "link:../ui-components", "catlog-wasm": "link:../catlog-wasm/dist/pkg-browser", - "echarts": "^5.5.1", - "echarts-solid": "^0.2.0", + "echarts": "^6.1.0", + "echarts-solid": "github:ToposInstitute/echarts-solid#kb/echarts-v6-dist", "elkjs": "^0.11.0", "fast-equals": "^5.2.2", "fast-json-patch": "^3.1.1", diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index f76fce41d..af36bf556 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -93,11 +93,11 @@ importers: specifier: link:../catlog-wasm/dist/pkg-browser version: link:../catlog-wasm/dist/pkg-browser echarts: - specifier: ^5.5.1 - version: 5.5.1 + specifier: ^6.1.0 + version: 6.1.0 echarts-solid: - specifier: ^0.2.0 - version: 0.2.0(echarts@5.5.1)(solid-js@1.9.10) + specifier: github:ToposInstitute/echarts-solid#kb/echarts-v6-dist + version: https://codeload.github.com/ToposInstitute/echarts-solid/tar.gz/fa9fd59f51067c3ad3f6aaddb6c62eadad738f8a(echarts@6.1.0)(solid-js@1.9.10) elkjs: specifier: ^0.11.0 version: 0.11.0 @@ -1094,28 +1094,33 @@ packages: peerDependencies: solid-js: ^1.6.12 + '@solid-primitives/event-listener@2.4.5': + resolution: {integrity: sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA==} + peerDependencies: + solid-js: ^1.6.12 + '@solid-primitives/map@0.7.2': resolution: {integrity: sha512-sXK/rS68B4oq3XXNyLrzVhLtT1pnimmMUahd2FqhtYUuyQsCfnW058ptO1s+lWc2k8F/3zQSNVkZ2ifJjlcNbQ==} peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/refs@1.0.8': - resolution: {integrity: sha512-+jIsWG8/nYvhaCoG2Vg6CJOLgTmPKFbaCrNQKWfChalgUf9WrVxWw0CdJb3yX15n5lUcQ0jBo6qYtuVVmBLpBw==} + '@solid-primitives/refs@1.1.3': + resolution: {integrity: sha512-aam02fjNKpBteewF/UliPSQCVJsIIGOLEWQOh+ll6R/QePzBOOBMcC4G+5jTaO75JuUS1d/14Q1YXT3X0Ow6iA==} peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/resize-observer@2.0.26': - resolution: {integrity: sha512-KbPhwal6ML9OHeUTZszBbt6PYSMj89d4wVCLxlvDYL4U0+p+xlCEaqz6v9dkCwm/0Lb+Wed7W5T1dQZCP3JUUw==} + '@solid-primitives/resize-observer@2.1.5': + resolution: {integrity: sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw==} peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/rootless@1.4.5': - resolution: {integrity: sha512-GFJE9GC3ojx0aUKqAUZmQPyU8fOVMtnVNrkdk2yS4kd17WqVSpXpoTmo9CnOwA+PG7FTzdIkogvfLQSLs4lrww==} + '@solid-primitives/rootless@1.5.3': + resolution: {integrity: sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA==} peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/static-store@0.0.8': - resolution: {integrity: sha512-ZecE4BqY0oBk0YG00nzaAWO5Mjcny8Fc06CdbXadH9T9lzq/9GefqcSe/5AtdXqjvY/DtJ5C6CkcjPZO0o/eqg==} + '@solid-primitives/static-store@0.1.3': + resolution: {integrity: sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ==} peerDependencies: solid-js: ^1.6.12 @@ -1129,13 +1134,13 @@ packages: peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/utils@6.2.3': - resolution: {integrity: sha512-CqAwKb2T5Vi72+rhebSsqNZ9o67buYRdEJrIFzRXz3U59QqezuuxPsyzTSVCacwS5Pf109VRsgCJQoxKRoECZQ==} + '@solid-primitives/utils@6.3.2': + resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==} peerDependencies: solid-js: ^1.6.12 - '@solid-primitives/utils@6.3.2': - resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==} + '@solid-primitives/utils@6.4.0': + resolution: {integrity: sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A==} peerDependencies: solid-js: ^1.6.12 @@ -1185,6 +1190,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} @@ -1540,15 +1548,16 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - echarts-solid@0.2.0: - resolution: {integrity: sha512-ilGFJ1zOUza4asiBoNu47TQYtL1WlUwp/BL9TGoDwAXbZczyqUl5RaDEQHRHTZiMh/4t0lenXw0QHRnGToOYkg==} + echarts-solid@https://codeload.github.com/ToposInstitute/echarts-solid/tar.gz/fa9fd59f51067c3ad3f6aaddb6c62eadad738f8a: + resolution: {tarball: https://codeload.github.com/ToposInstitute/echarts-solid/tar.gz/fa9fd59f51067c3ad3f6aaddb6c62eadad738f8a} + version: 0.2.0 engines: {node: '>=18', pnpm: '>=8.6.0'} peerDependencies: - echarts: ^5.5.1 + echarts: ^6.1.0 solid-js: ^1.8.22 - echarts@5.5.1: - resolution: {integrity: sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==} + echarts@6.1.0: + resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==} electron-to-chromium@1.5.33: resolution: {integrity: sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==} @@ -3107,8 +3116,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zrender@5.6.0: - resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==} + zrender@6.1.0: + resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -4123,32 +4132,37 @@ snapshots: '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) solid-js: 1.9.10 + '@solid-primitives/event-listener@2.4.5(solid-js@1.9.10)': + dependencies: + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) + solid-js: 1.9.10 + '@solid-primitives/map@0.7.2(solid-js@1.9.10)': dependencies: '@solid-primitives/trigger': 1.2.2(solid-js@1.9.10) solid-js: 1.9.10 - '@solid-primitives/refs@1.0.8(solid-js@1.9.10)': + '@solid-primitives/refs@1.1.3(solid-js@1.9.10)': dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.9.10) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) solid-js: 1.9.10 - '@solid-primitives/resize-observer@2.0.26(solid-js@1.9.10)': + '@solid-primitives/resize-observer@2.1.5(solid-js@1.9.10)': dependencies: - '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.10) - '@solid-primitives/rootless': 1.4.5(solid-js@1.9.10) - '@solid-primitives/static-store': 0.0.8(solid-js@1.9.10) - '@solid-primitives/utils': 6.2.3(solid-js@1.9.10) + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.10) + '@solid-primitives/rootless': 1.5.3(solid-js@1.9.10) + '@solid-primitives/static-store': 0.1.3(solid-js@1.9.10) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) solid-js: 1.9.10 - '@solid-primitives/rootless@1.4.5(solid-js@1.9.10)': + '@solid-primitives/rootless@1.5.3(solid-js@1.9.10)': dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.9.10) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) solid-js: 1.9.10 - '@solid-primitives/static-store@0.0.8(solid-js@1.9.10)': + '@solid-primitives/static-store@0.1.3(solid-js@1.9.10)': dependencies: - '@solid-primitives/utils': 6.2.3(solid-js@1.9.10) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) solid-js: 1.9.10 '@solid-primitives/timer@1.4.2(solid-js@1.9.10)': @@ -4157,14 +4171,14 @@ snapshots: '@solid-primitives/trigger@1.2.2(solid-js@1.9.10)': dependencies: - '@solid-primitives/utils': 6.3.2(solid-js@1.9.10) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.10) solid-js: 1.9.10 - '@solid-primitives/utils@6.2.3(solid-js@1.9.10)': + '@solid-primitives/utils@6.3.2(solid-js@1.9.10)': dependencies: solid-js: 1.9.10 - '@solid-primitives/utils@6.3.2(solid-js@1.9.10)': + '@solid-primitives/utils@6.4.0(solid-js@1.9.10)': dependencies: solid-js: 1.9.10 @@ -4221,6 +4235,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + '@types/hast@2.3.10': dependencies: '@types/unist': 2.0.11 @@ -4582,17 +4598,17 @@ snapshots: eastasianwidth@0.2.0: {} - echarts-solid@0.2.0(echarts@5.5.1)(solid-js@1.9.10): + echarts-solid@https://codeload.github.com/ToposInstitute/echarts-solid/tar.gz/fa9fd59f51067c3ad3f6aaddb6c62eadad738f8a(echarts@6.1.0)(solid-js@1.9.10): dependencies: - '@solid-primitives/refs': 1.0.8(solid-js@1.9.10) - '@solid-primitives/resize-observer': 2.0.26(solid-js@1.9.10) - echarts: 5.5.1 + '@solid-primitives/refs': 1.1.3(solid-js@1.9.10) + '@solid-primitives/resize-observer': 2.1.5(solid-js@1.9.10) + echarts: 6.1.0 solid-js: 1.9.10 - echarts@5.5.1: + echarts@6.1.0: dependencies: tslib: 2.3.0 - zrender: 5.6.0 + zrender: 6.1.0 electron-to-chromium@1.5.33: {} @@ -4692,7 +4708,7 @@ snapshots: '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -6793,7 +6809,7 @@ snapshots: yocto-queue@0.1.0: {} - zrender@5.6.0: + zrender@6.1.0: dependencies: tslib: 2.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9942e7d55..0745ec4cb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ minimumReleaseAge: 12960 minimumReleaseAgeExclude: - wasm-pack + - echarts packages: - "dev-docs" - "packages/backend/pkg" From 27d3ed9afd948e9e9611480fd8b158c4eae45ae0 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Tue, 26 May 2026 06:28:19 -0400 Subject: [PATCH 61/81] ENH: Adding Julia to the CI Workflow (#1292) --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15dbf1b93..ec4971efa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,26 @@ env: CARGO_TERM_COLOR: always jobs: + julia_tests: + name: julia tests + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - stable + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Julia + uses: julia-actions/setup-julia@v2 + with: + version: '1' + + - name: Tests + run : | + julia --project=packages/algjulia-interop -e 'import Pkg; Pkg.test()' + catlog_tests: name: catlog tests runs-on: ubuntu-latest From a1eb8e68177d1713e28b0978112540bca8d02de5 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 27 May 2026 13:52:37 +0100 Subject: [PATCH 62/81] Use pnpm2nix-nzbr (#1263) * BUILD: Use FliegendeWurst/pnpm2nix-nzbr * BUILD: MacOS support for pnpm2nix workflow * BUILD: Refactor out craneArgs for callPackage in flake.nix * BUILD: Use pkgs.format.yaml for pnpm lock files in nix --- CONTRIBUTING.md | 4 - dev-docs/fixing-hash-mismatches.md | 62 --------- dev-docs/typedoc.json | 3 +- flake.lock | 57 +++++++- flake.nix | 208 +++++++++++++++++------------ packages/frontend/README.md | 13 -- packages/frontend/default.nix | 149 ++++++++++++++++----- 7 files changed, 292 insertions(+), 204 deletions(-) delete mode 100644 dev-docs/fixing-hash-mismatches.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2e4b80355..647ab70dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,8 +69,4 @@ cargo clippy Try to remember to run these commands before making a PR. (If you forget, the CI will remind you.) -## Developer documentation -Additional documentation for developers: - -- [Fixing Hash Mismatches in Nix](./dev-docs/fixing-hash-mismatches.md) diff --git a/dev-docs/fixing-hash-mismatches.md b/dev-docs/fixing-hash-mismatches.md deleted file mode 100644 index b180d3649..000000000 --- a/dev-docs/fixing-hash-mismatches.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: "Fixing hash mismatches in Nix" ---- - -### Fixing Hash Mismatches in Nix - -#### pnpm Dependencies in the Frontend - -This only applies to the `frontend` package. The pnpm hash is located in `packages/frontend/default.nix` -within the `pkgs.fetchPnpmDeps` block. - -When a pnpm dependency has changed but the Nix hash has not, running `nix build .#frontend` will fail -with a hash mismatch that looks like this: -``` -error: hash mismatch in fixed-output derivation '/nix/store/4wpp80j18vvm232ii1ajl2kqnbfgvzq2-frontend-pnpm-deps.drv': - specified: sha256-vuBwhtNTTRbpgPZS+AQDybASYM9rwWYG8l0bscVQUso= - got: sha256-sxczRF8IsYqQzmAAv+IiFeWJygqHCVnSk8fEuy5d1JM= -``` - -This can be fixed by replacing the `specified` hash in `packages/frontend/default.nix` with the `got` hash. - -You may also see a pnpm-specific error like this: -``` -> ERR_PNPM_NO_OFFLINE_TARBALL A package is missing from the store but cannot download it in offline mode. -> ERROR: pnpm failed to install dependencies -``` - -This will be accompanied by the hash mismatch above and can be fixed the same way. - - -#### Other Dependencies - -Dependencies other than `pnpm` will have hash mismatch errors that look like this: -``` -error: hash mismatch in fixed-output derivation '/nix/store/9ydq26vqirys8i3p9yx2ljxj8l9ynlgs-wasm-bindgen-cli-0.2.105.tar.gz.drv': - specified: sha256-M6WuGl7EruNopHZbqBpucu4RWz44/MSdv6f0zkYw+44= - got: sha256-zLPFFgnqAWq5R2KkaTGAYqVQswfBEYm9x3OPjx8DJRY= -``` - -These can be fixed by finding the `specified` hash in the Nix configs and replacing it with the `got` hash. -Currently all hashes except for `pnpm` are defined in `flake.nix` in the repo root. - -##### Dependencies with Hash Mismatches - -You may encounter hash mismatches for these dependencies: -- wasm-bindgen-cli -- rust-toolchain - - -#### Explanation - -Nix uses **fixed-output derivations** to fetch external dependencies (from npm, crates.io, GitHub, etc.). -These derivations require a cryptographic hash to verify that the fetched content matches what's expected, -ensuring reproducibility and security. - -When a fixed-output dependency changes, so will its hash, causing a hash mismatch error in Nix. -This generally happens in two scenarios: -1. Intentional updates: You upgrade a package version -2. Upstream changes: The upstream package was modified without a version change - -If you encounter a hash mismatch without updating anything it should probably be investigated: it means -the external source changed unexpectedly. diff --git a/dev-docs/typedoc.json b/dev-docs/typedoc.json index bdbdc0cc5..848e735a3 100644 --- a/dev-docs/typedoc.json +++ b/dev-docs/typedoc.json @@ -2,6 +2,5 @@ "entryPoints": ["./index.ts"], "out": "output", "name": "CatColab: for developers", - "readme": "../CONTRIBUTING.md", - "projectDocuments": ["fixing-hash-mismatches.md"] + "readme": "../CONTRIBUTING.md" } diff --git a/flake.lock b/flake.lock index 900ca5784..12b4e35b8 100644 --- a/flake.lock +++ b/flake.lock @@ -115,6 +115,24 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -234,6 +252,27 @@ "type": "github" } }, + "pnpm2nix-nzbr": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1749022118, + "narHash": "sha256-7Qzmy1snKbxFBKoqUrfyxxmEB8rPxDdV7PQwRiAR01o=", + "owner": "FliegendeWurst", + "repo": "pnpm2nix-nzbr", + "rev": "35f88a41d29839b3989f31871263451c8e092cb1", + "type": "github" + }, + "original": { + "owner": "FliegendeWurst", + "repo": "pnpm2nix-nzbr", + "type": "github" + } + }, "root": { "inputs": { "agenix": "agenix", @@ -241,7 +280,8 @@ "deploy-rs": "deploy-rs", "fenix": "fenix", "nixos-generators": "nixos-generators", - "nixpkgs": "nixpkgs_4" + "nixpkgs": "nixpkgs_4", + "pnpm2nix-nzbr": "pnpm2nix-nzbr" } }, "rust-analyzer-src": { @@ -291,6 +331,21 @@ "type": "github" } }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "utils": { "inputs": { "systems": "systems_2" diff --git a/flake.nix b/flake.nix index 92eddc969..1619af6b4 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,11 @@ }; nixos-generators.url = "github:nix-community/nixos-generators"; + + pnpm2nix-nzbr = { + url = "github:FliegendeWurst/pnpm2nix-nzbr"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = @@ -70,19 +75,37 @@ pkgsLinux = nixpkgsFor linuxSystem; rustToolchainLinux = rustToolchainFor linuxSystem; - craneLib = (crane.mkLib pkgsLinux).overrideToolchain rustToolchainLinux; + # Per-system crane library + prebuilt cargo dependency layer. The + # frontend package is exposed for every system in `devShellSystems` so + # macOS developers can `nix build .#frontend` against the same wasm/api + # chain linux developers use; that requires per-system cargoArtifacts. + craneLibFor = + system: + let + pkgs = nixpkgsFor system; + in + (crane.mkLib pkgs).overrideToolchain (rustToolchainFor system); - cargoArtifacts = craneLib.buildDepsOnly { - src = craneLib.cleanCargoSource ./.; - strictDeps = true; - nativeBuildInputs = [ - pkgsLinux.pkg-config - ]; + cargoArtifactsFor = + system: + let + pkgs = nixpkgsFor system; + craneLib = craneLibFor system; + in + craneLib.buildDepsOnly { + src = craneLib.cleanCargoSource ./.; + strictDeps = true; + nativeBuildInputs = [ + pkgs.pkg-config + ]; - buildInputs = [ - pkgsLinux.openssl - ]; - }; + buildInputs = [ + pkgs.openssl + ]; + }; + + craneLib = craneLibFor linuxSystem; + cargoArtifacts = cargoArtifactsFor linuxSystem; # Generate devShells for each system devShellForSystem = @@ -194,90 +217,103 @@ # Example of how to build and test individual package built by nix: # nix build .#packages.x86_64-linux.automerge # node ./result/main.cjs - packages = { - x86_64-linux = { - catcolabApi = pkgsLinux.callPackage ./infrastructure/catcolab-api.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; - - backend = pkgsLinux.callPackage ./packages/backend/default.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; - - migrator = pkgsLinux.callPackage ./packages/migrator/default.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; - - catlog-wasm-browser = pkgsLinux.callPackage ./packages/catlog-wasm/default.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; - - document-types-wasm = pkgsLinux.callPackage ./packages/document-types/default.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; + # + # The frontend and its rust-derived dependencies (catcolabApi, + # catlog-wasm-browser, document-types-wasm) are exposed for every + # `devShellSystems` entry so macOS developers can `nix build .#frontend` + # natively. Linux-only outputs (backend, migrator, julia-fhs, rust-docs, + # catcolab-vm) stay under packages.x86_64-linux only. + packages = + let + frontendChainFor = + system: + let + pkgs = nixpkgsFor system; + craneArgs = { + craneLib = craneLibFor system; + cargoArtifacts = cargoArtifactsFor system; + inherit pkgs; + }; + frontendPackage = pkgs.callPackage ./packages/frontend/default.nix { + inherit inputs self; + rustToolchain = rustToolchainFor system; + pnpm2nix = inputs.pnpm2nix-nzbr; + }; + in + { + catcolabApi = pkgs.callPackage ./infrastructure/catcolab-api.nix craneArgs; + catlog-wasm-browser = pkgs.callPackage ./packages/catlog-wasm/default.nix craneArgs; + document-types-wasm = pkgs.callPackage ./packages/document-types/default.nix craneArgs; + frontend = frontendPackage.package; + frontend-tests = frontendPackage.tests; + }; - julia-fhs = (pkgsLinux.callPackage ./infrastructure/julia.nix { }).julia-fhs; + linuxOnlyPackages = { + backend = pkgsLinux.callPackage ./packages/backend/default.nix { + inherit craneLib cargoArtifacts; + pkgs = pkgsLinux; + }; - frontend = - (pkgsLinux.callPackage ./packages/frontend/default.nix { - inherit inputs rustToolchainLinux self; - }).package; + migrator = pkgsLinux.callPackage ./packages/migrator/default.nix { + inherit craneLib cargoArtifacts; + pkgs = pkgsLinux; + }; - frontend-tests = - (pkgsLinux.callPackage ./packages/frontend/default.nix { - inherit inputs rustToolchainLinux self; - }).tests; + julia-fhs = (pkgsLinux.callPackage ./infrastructure/julia.nix { }).julia-fhs; - rust-docs = pkgsLinux.callPackage ./infrastructure/rust-docs.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - }; + rust-docs = pkgsLinux.callPackage ./infrastructure/rust-docs.nix { + inherit craneLib cargoArtifacts; + pkgs = pkgsLinux; + }; - rust-docs-check = pkgsLinux.callPackage ./infrastructure/rust-docs.nix { - inherit craneLib cargoArtifacts; - pkgs = pkgsLinux; - checkMode = true; - }; + rust-docs-check = pkgsLinux.callPackage ./infrastructure/rust-docs.nix { + inherit craneLib cargoArtifacts; + pkgs = pkgsLinux; + checkMode = true; + }; - # VMs built with `nixos-rebuild build-vm` (like `nix build - # .#nixosConfigurations.catcolab-vm.config.system.build.vm`) are not the same - # as "traditional" VMs, which causes deploy-rs to fail when deploying to them. - # https://github.com/serokell/deploy-rs/issues/85#issuecomment-885782350 - # - # This is worked around by creating a full featured VM image. - # - # use: - # nix build .#catcolab-vm - # cp result/catcolab-vm.qcow2 catcolab-vm.qcow2 - # db-utils vm start - # deploy -s .#catcolab-vm - catcolab-vm = pkgsLinux.stdenv.mkDerivation { - name = "catcolab-vm"; - src = nixos-generators.nixosGenerate { - system = "x86_64-linux"; - format = "qcow"; - - modules = [ - ./infrastructure/hosts/catcolab-vm - ]; - - specialArgs = { - inherit inputs self; - rustToolchain = rustToolchainLinux; + # VMs built with `nixos-rebuild build-vm` (like `nix build + # .#nixosConfigurations.catcolab-vm.config.system.build.vm`) are not the same + # as "traditional" VMs, which causes deploy-rs to fail when deploying to them. + # https://github.com/serokell/deploy-rs/issues/85#issuecomment-885782350 + # + # This is worked around by creating a full featured VM image. + # + # use: + # nix build .#catcolab-vm + # cp result/catcolab-vm.qcow2 catcolab-vm.qcow2 + # db-utils vm start + # deploy -s .#catcolab-vm + catcolab-vm = pkgsLinux.stdenv.mkDerivation { + name = "catcolab-vm"; + src = nixos-generators.nixosGenerate { + system = "x86_64-linux"; + format = "qcow"; + + modules = [ + ./infrastructure/hosts/catcolab-vm + ]; + + specialArgs = { + inherit inputs self; + rustToolchain = rustToolchainLinux; + }; }; + installPhase = '' + mkdir -p $out + cp $src/nixos.qcow2 $out/catcolab-vm.qcow2 + ''; }; - installPhase = '' - mkdir -p $out - cp $src/nixos.qcow2 $out/catcolab-vm.qcow2 - ''; }; - }; - }; + in + builtins.listToAttrs ( + map (system: { + name = system; + value = + frontendChainFor system + // pkgsLinux.lib.optionalAttrs (system == linuxSystem) linuxOnlyPackages; + }) devShellSystems + ); # Create a NixOS configuration for each host nixosConfigurations = { diff --git a/packages/frontend/README.md b/packages/frontend/README.md index 1403ad7bd..16dd48bf3 100644 --- a/packages/frontend/README.md +++ b/packages/frontend/README.md @@ -31,16 +31,3 @@ where `$MODE` is replaced with one of the following: Running the command above builds the Wasm and other local dependencies (by running `pnpm run build:deps`) before launching the Vite preview server. - -## Troubleshooting - -### Nix Hash Mismatches - -If this package fails to build in Nix with the error: - -``` -> ERROR: pnpm failed to install dependencies -``` - -Refer to the "pnpm Dependencies" section in [Fixing Hash Mismatches in -Nix](../../dev-docs/fixing-hash-mismatches.md). diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index 0fbccecf1..21d0fafaa 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -2,6 +2,7 @@ pkgs, self, lib, + pnpm2nix, ... }: let @@ -9,8 +10,60 @@ let name = packageJson.name; version = packageJson.version; + # pnpm2nix-nzbr exposes a `processLockfile` function in lockfile.nix that, given a + # pnpm-lock.yaml, produces: + # - dependencyTarballs: a list of FODs (fetchurl/fetchGit/...) for each + # dependency tarball referenced by the lockfile. + # - patchedLockfile: an in-memory copy of the lockfile with every + # `resolution.tarball` rewritten to point at the local nix store tarball. + # + # We use these to drive an offline `pnpm install --frozen-lockfile`, which + # avoids having to maintain a monolithic `pnpmDeps.hash` by hand: every + # individual tarball is hashed via the integrity field already present in + # the lockfile. + lockfileLib = pkgs.callPackage (pnpm2nix + "/lockfile.nix") { }; + + processLock = lockfile: lockfileLib.processLockfile { + registry = "https://registry.npmjs.org"; + noDevDependencies = false; + inherit lockfile; + }; + + # The frontend package and every sibling workspace member it pulls in via + # `link:` need their lockfiles processed: each lockfile is rewritten to + # reference nix-store tarballs, and the union of all dependency tarballs + # is fed to `pnpm store add` so the offline install can resolve everything. + # Because `.npmrc` sets `shared-workspace-lockfile=false`, each workspace + # member has its own pnpm-lock.yaml that must be processed independently. + lockfilesToProcess = { + "packages/frontend" = ../frontend/pnpm-lock.yaml; + "packages/ui-components" = ../ui-components/pnpm-lock.yaml; + "packages/document-methods" = ../document-methods/pnpm-lock.yaml; + "packages/backend/pkg" = ../backend/pkg/pnpm-lock.yaml; + }; + + processedLocks = lib.mapAttrs (_: processLock) lockfilesToProcess; + + yamlFormat = pkgs.formats.yaml { }; + + patchedLockfiles = lib.mapAttrs ( + relPath: processed: + yamlFormat.generate "pnpm-lock-${builtins.replaceStrings [ "/" ] [ "-" ] relPath}.yaml" + processed.patchedLockfile + ) processedLocks; + + allTarballs = lib.unique ( + lib.concatLists (lib.mapAttrsToList (_: p: p.dependencyTarballs) processedLocks) + ); + + # File containing the space-separated list of all dependency tarball paths, + # consumed by `pnpm store add` to seed the offline store. + dependencyTarballsFile = pkgs.runCommand "${name}-dependency-tarballs" { } '' + echo ${lib.concatStringsSep " " allTarballs} > $out + ''; + commonAttrs = { - version = version; + inherit version; # Filter source to only include packages needed for frontend build # This prevents unnecessary rebuilds when unrelated files change src = lib.fileset.toSource { @@ -28,39 +81,48 @@ let }; nativeBuildInputs = with pkgs; [ - pnpm.configHook + nodejs_24 + pnpm ]; buildInputs = with pkgs; [ nodejs_24 ]; - pnpmDeps = pkgs.fetchPnpmDeps { - # see ../../dev-docs/fixing-hash-mismatches.md - hash = "sha256-INfSKk7wf37D3HVs8QXzx9YV0VwuDmfwnnppviLlXNQ="; - - pname = name; - fetcherVersion = 2; - # Only includes package.json and pnpm-lock.yaml files to ensure consistent hashing in different - # environments - src = lib.fileset.toSource { - root = ../../.; - fileset = lib.fileset.unions [ - ../../.npmrc - ../../pnpm-workspace.yaml - ../../pnpm-lock.yaml - (lib.fileset.maybeMissing ../../patches) - ../../packages/frontend/package.json - ../../packages/frontend/pnpm-lock.yaml - ../../packages/ui-components/package.json - ../../packages/ui-components/pnpm-lock.yaml - ../../packages/document-methods/package.json - ../../packages/document-methods/pnpm-lock.yaml - ../../packages/backend/pkg/package.json - ../../packages/backend/pkg/pnpm-lock.yaml - ]; - }; - }; + # Drive pnpm install ourselves, using the lockfile-derived nix store + # tarballs from pnpm2nix-nzbr instead of pnpm.configHook + fetchPnpmDeps. + # This phase runs from the unpacked source root (monorepo subset). + configurePhase = '' + runHook preConfigure + + export HOME=$NIX_BUILD_TOP + export npm_config_nodedir=${pkgs.nodejs_24} + + # Replace each workspace member's lockfile with the version whose tarball + # references point at /nix/store paths so pnpm doesn't hit the network. + ${lib.concatStringsSep "\n" ( + lib.mapAttrsToList (relPath: patched: '' + cp -fv ${patched} ${relPath}/pnpm-lock.yaml + '') patchedLockfiles + )} + + # Pre-populate the local pnpm content-addressable store with every + # tarball referenced by the patched lockfiles. + store=$(pnpm store path) + mkdir -p "$(dirname "$store")" + pnpm store add $(cat ${dependencyTarballsFile}) + + # Install dependencies for every workspace member. Recursive install + # handles the `link:` deps between members correctly. + pnpm install \ + --recursive \ + --ignore-scripts \ + --force \ + --frozen-lockfile \ + --prefer-offline + + runHook postConfigure + ''; }; package = pkgs.stdenv.mkDerivation ( @@ -69,29 +131,37 @@ let pname = name; buildPhase = '' + runHook preBuild + # Set up catlog-wasm before TypeScript build needs it mkdir -p packages/catlog-wasm/dist/pkg-browser - cp -r ${self.packages.x86_64-linux.catlog-wasm-browser}/* packages/catlog-wasm/dist/pkg-browser/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.catlog-wasm-browser}/* packages/catlog-wasm/dist/pkg-browser/ # Set up document-types wasm output mkdir -p packages/document-types/pkg - cp -r ${self.packages.x86_64-linux.document-types-wasm}/* packages/document-types/pkg/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.document-types-wasm}/* packages/document-types/pkg/ # Set up generated API bindings mkdir -p packages/backend/pkg/src - cp -r ${self.packages.x86_64-linux.catcolabApi}/src packages/backend/pkg/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.catcolabApi}/src packages/backend/pkg/ cd packages/frontend # Generate CSS module type declarations - npm run build:tcm + pnpm run build:tcm # Build with development mode to use .env.development configuration - npm run build -- --mode development + pnpm run build -- --mode development cd - + + runHook postBuild ''; installPhase = '' + runHook preInstall + mkdir -p $out cp -r packages/frontend/dist/* $out + + runHook postInstall ''; } ); @@ -109,21 +179,26 @@ let dontStrip = true; dontPatchShebangs = true; + # No build step; we just package up the tree with node_modules. + dontBuild = true; + installPhase = '' + runHook preInstall + # for vitest to work we need to basically recreate the development environment. We achieve this # by setting of up a copy of the packages structure. mkdir -p $out/packages mkdir -p $out/packages/catlog-wasm/dist/pkg-browser - cp -r ${self.packages.x86_64-linux.catlog-wasm-browser}/* $out/packages/catlog-wasm/dist/pkg-browser/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.catlog-wasm-browser}/* $out/packages/catlog-wasm/dist/pkg-browser/ # Set up document-types wasm output before copying to $out mkdir -p packages/document-types/pkg - cp -r ${self.packages.x86_64-linux.document-types-wasm}/* packages/document-types/pkg/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.document-types-wasm}/* packages/document-types/pkg/ # Bindings must be copied into source tree BEFORE the cp below copies backend to $out mkdir -p packages/backend/pkg/src - cp -r ${self.packages.x86_64-linux.catcolabApi}/src packages/backend/pkg/ + cp -r ${self.packages.${pkgs.stdenv.hostPlatform.system}.catcolabApi}/src packages/backend/pkg/ cp -r packages/backend $out/packages/ cp -r packages/frontend $out/packages/ @@ -183,6 +258,8 @@ let # Wrap the script to ensure nodejs is in PATH makeWrapper $out/bin/.${name}-tests-unwrapped $out/bin/${name}-tests \ --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.nodejs_24 ]} + + runHook postInstall ''; meta.mainProgram = "${name}-tests"; From e1bb1d4843f44e432b15d049053ac3d0329b14b5 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 27 May 2026 16:38:21 +0100 Subject: [PATCH 63/81] Move ui-component tests to own GitHub Actions workflow (#1296) * BUILD: Move ui-component tests to own GitHub Actions workflow * BUILD: Fix/workaround issues with installing chromium in CI --- .github/workflows/ci.yml | 25 ----------- .github/workflows/ui-components-tests.yml | 53 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/ui-components-tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec4971efa..20c78bb7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,31 +101,6 @@ jobs: run: | nix build .#rust-docs-check - ui_components_tests: - name: ui-components tests - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup NodeJS - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: "pnpm" - - - name: Install dependencies - run: pnpm install - - - name: Install Playwright browser - run: pnpm --filter ./packages/ui-components exec playwright install chromium - - - name: Run ui-components tests - run: pnpm --filter ./packages/ui-components run test - npm_checks: name: npm checks runs-on: ubuntu-latest diff --git a/.github/workflows/ui-components-tests.yml b/.github/workflows/ui-components-tests.yml new file mode 100644 index 000000000..73df839b7 --- /dev/null +++ b/.github/workflows/ui-components-tests.yml @@ -0,0 +1,53 @@ +name: ui-components-tests + +on: + push: + branches: + - main + paths: + - "packages/ui-components/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - ".github/workflows/ui-components-tests.yml" + pull_request: + paths: + - "packages/ui-components/**" + - "package.json" + - "pnpm-lock.yaml" + - "pnpm-workspace.yaml" + - ".github/workflows/ui-components-tests.yml" + +jobs: + ui-components-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: "pnpm" + + - name: Cache Playwright browsers + uses: actions/cache@v4 + id: playwright-cache + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('packages/ui-components/pnpm-lock.yaml') }} + + - name: Install dependencies + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" + run: pnpm install + + - name: Install Playwright browser + run: pnpm --filter ./packages/ui-components exec playwright install --with-deps chromium-headless-shell + + - name: Run ui-components tests + run: pnpm --filter ./packages/ui-components run test From 9613b85d69f5f611ca40001c90aba93f8709a7d1 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Wed, 27 May 2026 17:02:05 +0100 Subject: [PATCH 64/81] BUILD: Move Julia tests to own Github Actions workflow (#1295) --- .github/workflows/ci.yml | 20 -------------------- .github/workflows/julia-tests.yml | 29 +++++++++++++++++++++++++++++ packages/algjulia-interop/README.md | 2 +- 3 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/julia-tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20c78bb7e..0f1402532 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,26 +10,6 @@ env: CARGO_TERM_COLOR: always jobs: - julia_tests: - name: julia tests - runs-on: ubuntu-latest - strategy: - matrix: - toolchain: - - stable - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Setup Julia - uses: julia-actions/setup-julia@v2 - with: - version: '1' - - - name: Tests - run : | - julia --project=packages/algjulia-interop -e 'import Pkg; Pkg.test()' - catlog_tests: name: catlog tests runs-on: ubuntu-latest diff --git a/.github/workflows/julia-tests.yml b/.github/workflows/julia-tests.yml new file mode 100644 index 000000000..63eead5e6 --- /dev/null +++ b/.github/workflows/julia-tests.yml @@ -0,0 +1,29 @@ +name: julia-tests + +on: + push: + branches: + - main + paths: + - 'packages/algjulia-interop/**' + - '.github/workflows/julia-tests.yml' + pull_request: + paths: + - 'packages/algjulia-interop/**' + - '.github/workflows/julia-tests.yml' + +jobs: + julia-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Julia + uses: julia-actions/setup-julia@v2 + with: + version: '1' + + - name: Tests + run: | + julia --project=packages/algjulia-interop -e 'import Pkg; Pkg.test()' diff --git a/packages/algjulia-interop/README.md b/packages/algjulia-interop/README.md index dd37a4707..0517bb484 100644 --- a/packages/algjulia-interop/README.md +++ b/packages/algjulia-interop/README.md @@ -3,7 +3,7 @@ This small package makes functionality from [AlgebraicJulia](https://www.algebraicjulia.org/) available to CatColab. At this time, only a [Catlab.jl](https://github.com/AlgebraicJulia/Catlab.jl) service is -provided. Other packages (e.g. Decapodes.jl) will be added in the future. +provided. Other packages (e.g. [Decapodes.jl](https://github.com/AlgebraicJulia/Decapodes.jl))) will be added in the future. ## Usage From bb0c66190dca39b41fb189bc157c149c24f4f220 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Wed, 27 May 2026 22:19:56 -0700 Subject: [PATCH 65/81] ENH: Enable RFCs to build as PDFs. (#1291) --- .github/workflows/build.yml | 1 + rfc/0004.md | 16 +++++++--------- rfc/_macros.qmd | 2 ++ rfc/_quarto.yml | 8 ++++++++ rfc/filters.lua | 3 +++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d34b6c53..118bbbe7c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -293,6 +293,7 @@ jobs: uses: quarto-dev/quarto-actions/render@v2 with: path: rfc + to: html - name: Report cache status run: | diff --git a/rfc/0004.md b/rfc/0004.md index 61a8bac9f..03ee0e896 100644 --- a/rfc/0004.md +++ b/rfc/0004.md @@ -1205,7 +1205,7 @@ having a cell ```tikz % https://q.uiver.app/#q=WzAsNSxbMCwwLCJcXExpc3RcXExpc3RcXGNhbHtYfSJdLFsxLDAsIlxcTGlzdFxcY2Fse1h9Il0sWzIsMCwiXFxjYWx7WH0iXSxbMCwxLCJcXExpc3RcXGNhbHtYfSJdLFsyLDEsIlxcY2Fse1h9Il0sWzAsMSwiXFxMaXN0IFAiLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMSwyLCJQIiwwLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzIsNCwiIiwwLHsibGV2ZWwiOjIsInN0eWxlIjp7ImhlYWQiOnsibmFtZSI6Im5vbmUifX19XSxbMyw0LCJQIiwyLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzAsMywiXFxtdV97XFxjYWx7WH19IiwyXSxbMSw4LCJcXHRleHR7Y29tcH0iLDEseyJsYWJlbF9wb3NpdGlvbiI6NDAsInNob3J0ZW4iOnsidGFyZ2V0IjoyMH0sInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6Im5vbmUifSwiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dXQ== -\begin{tikzcd} +\[\begin{tikzcd} {\List\List\cal{X}} & {\List\cal{X}} & {\cal{X}} \\ {\List\cal{X}} && {\cal{X}} \arrow["{\List P}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] @@ -1214,7 +1214,7 @@ having a cell \arrow[equals, from=1-3, to=2-3] \arrow[""{name=0, anchor=center, inner sep=0}, "P"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-3] \arrow["{\text{comp}}"{description, pos=0.4}, draw=none, from=1-2, to=0] -\end{tikzcd} +\end{tikzcd}\] ``` giving the composition operation in the multicategory. Recall, however, that @@ -1225,7 +1225,7 @@ normalization axiom ```tikz % https://q.uiver.app/#q=WzAsNCxbMCwwLCJcXGNhbHtYfSJdLFsxLDAsIlxcY2Fse1h9Il0sWzAsMSwiXFxMaXN0XFxjYWx7WH0iXSxbMSwxLCJcXGNhbHtYfSJdLFswLDEsIlxcSG9tX3tcXGNhbHtYfX0iLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMCwyLCJcXGV0YV97XFxjYWx7WH19IiwyXSxbMSwzLCIiLDAseyJsZXZlbCI6Miwic3R5bGUiOnsiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dLFsyLDMsIlAiLDIseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbNCw3LCJcXHRleHR7cmVzfSIsMSx7InNob3J0ZW4iOnsic291cmNlIjoyMCwidGFyZ2V0IjoyMH0sInN0eWxlIjp7ImJvZHkiOnsibmFtZSI6Im5vbmUifSwiaGVhZCI6eyJuYW1lIjoibm9uZSJ9fX1dXQ== -\begin{tikzcd} +\[\begin{tikzcd} {\cal{X}} & {\cal{X}} \\ {\List\cal{X}} & {\cal{X}} \arrow[""{name=0, anchor=center, inner sep=0}, "{\Hom_{\cal{X}}}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] @@ -1233,7 +1233,7 @@ normalization axiom \arrow[equals, from=1-2, to=2-2] \arrow[""{name=1, anchor=center, inner sep=0}, "P"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-2] \arrow["{\text{res}}"{description}, draw=none, from=0, to=1] -\end{tikzcd} +\end{tikzcd}\] ``` says that the unary multimorphisms of $P$ are in natural bijection with the @@ -1557,13 +1557,11 @@ $\cal{X}$, a proarrow $M: \List_{\bij} \cal{X} \proto \List_{\bij} \cal{X}$, and a cell ```tikz -\newcommand{\bij}{\mathrm{bij}} - % https://q.uiver.app/#q=WzAsNCxbMCwwLCJcXExpc3Rfe1xcYmlqfV4yIFxcY2Fse1h9Il0sWzEsMCwiXFxMaXN0X3tcXGJpan1eMiBcXGNhbHtYfSJdLFswLDEsIlxcTGlzdF97XFxiaWp9IFxcY2Fse1h9Il0sWzEsMSwiXFxMaXN0X3tcXGJpan0gXFxjYWx7WH0iXSxbMCwyLCJcXG11X3tcXGNhbHtYfX0iLDJdLFswLDEsIlxcTGlzdF97XFxiaWp9IE0iLDAseyJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJiYXJyZWQifX19XSxbMSwzLCJcXG11X1giXSxbMiwzLCJNIiwyLHsic3R5bGUiOnsiYm9keSI6eyJuYW1lIjoiYmFycmVkIn19fV0sWzUsNywiXFxvdGltZXMiLDEseyJzaG9ydGVuIjp7InNvdXJjZSI6MjAsInRhcmdldCI6MjB9LCJzdHlsZSI6eyJib2R5Ijp7Im5hbWUiOiJub25lIn0sImhlYWQiOnsibmFtZSI6Im5vbmUifX19XV0= \[\begin{tikzcd}[column sep=large] - {\List_{\bij}^2 \cal{X}} & {\List_{\bij}^2 \cal{X}} \\ - {\List_{\bij} \cal{X}} & {\List_{\bij} \cal{X}} - \arrow[""{name=0, anchor=center, inner sep=0}, "{\List_{\bij} M}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] + {\List_{\mathrm{bij}}^2 \cal{X}} & {\List_{\mathrm{bij}}^2 \cal{X}} \\ + {\List_{\mathrm{bij}} \cal{X}} & {\List_{\mathrm{bij}} \cal{X}} + \arrow[""{name=0, anchor=center, inner sep=0}, "{\List_{\mathrm{bij}} M}"{inner sep=.8ex}, "\shortmid"{marking}, from=1-1, to=1-2] \arrow["{\mu_{\cal{X}}}"', from=1-1, to=2-1] \arrow["{\mu_X}", from=1-2, to=2-2] \arrow[""{name=1, anchor=center, inner sep=0}, "M"'{inner sep=.8ex}, "\shortmid"{marking}, from=2-1, to=2-2] diff --git a/rfc/_macros.qmd b/rfc/_macros.qmd index ba611574c..517d710c8 100644 --- a/rfc/_macros.qmd +++ b/rfc/_macros.qmd @@ -1,4 +1,6 @@ +\renewcommand{\cal}[1]{\mathcal{#1}} \renewcommand{\vec}[1]{\underline{#1}} +\newcommand{\vecvec}[1]{\underline{\underline{#1}}} \newcommand{\Ob}{\operatorname{Ob}} diff --git a/rfc/_quarto.yml b/rfc/_quarto.yml index 68b20cdad..ecebd5f2f 100644 --- a/rfc/_quarto.yml +++ b/rfc/_quarto.yml @@ -32,6 +32,14 @@ format: number-sections: true date-format: YYYY-MM-DD csl: chicago-author-date.csl + pdf: + include-in-header: + text: | + \usepackage{mathtools} + \usepackage{tikz-cd} + \usepackage{quiver} + \usepackage{ebproof} + \ebproofnewstyle{small}{template=\footnotesize$\inserttext$} filters: - filters.lua diff --git a/rfc/filters.lua b/rfc/filters.lua index 11093eda1..6c8886b94 100644 --- a/rfc/filters.lua +++ b/rfc/filters.lua @@ -98,6 +98,9 @@ end local function handle_codeblock(el) local tikz_template = tikz_templates[el.classes[1]] if tikz_template ~= nil then + if FORMAT:match "latex" then + return pandoc.RawBlock("latex", el.text) + end local before = tikz_template[1]:gsub("@OPTIONAL_PREAMBLE", tikz_user_preamble) return pandoc.Div( memoize_svg(el.text, tikz2image(tikz_template), before .. tikz_template[2]), From b69a804b86f2fd5e522ab0aa0e09ebf87fcf3cae Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Wed, 27 May 2026 22:29:37 -0700 Subject: [PATCH 66/81] BUILD: Remove CODEOWNERS file. (#1300) --- .github/CODEOWNERS | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 37ee268d9..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,8 +0,0 @@ -# Build and CI infrastructure -/.github/workflows/ @kasbah @jmoggr -/infrastructure/ @kasbah @jmoggr -*.nix @kasbah @jmoggr - -# Components and styling -*.css @kasbah -/packages/ui-components/ @kasbah From f21a7c70f83575711ed4462d2ee8fbc3441ef80a Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Thu, 28 May 2026 10:55:08 -0700 Subject: [PATCH 67/81] BUILD: Work around incompatibility with current version of `spath3`. (#1301) --- .github/workflows/build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 118bbbe7c..d53d47ac9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -227,6 +227,11 @@ jobs: run: | tlmgr update --self tlmgr install dvisvgm standalone preview pgf tikz-cd amsmath quiver spath3 ebproof + # Work around https://github.com/loopspace/spath3/issues/37 by + # dropping the redundant `NNn` variant that newer expl3 rejects. + SPATH3="$(kpsewhich spath3.sty)" + sed -i 's|\\spath_maybe_split_curve:NNn {NNn, NNV }|\\spath_maybe_split_curve:NNn {NNV}|' "$SPATH3" + sed -i 's|\\spath_maybe_gsplit_curve:NNn {NNn, NNV}|\\spath_maybe_gsplit_curve:NNn {NNV}|' "$SPATH3" - name: Build mathematical docs if: steps.cache-math-docs.outputs.cache-hit != 'true' @@ -287,6 +292,11 @@ jobs: run: | tlmgr update --self tlmgr install dvisvgm standalone preview pgf tikz-cd amsmath quiver spath3 ebproof luatex85 + # Work around https://github.com/loopspace/spath3/issues/37 by + # dropping the redundant `NNn` variant that newer expl3 rejects. + SPATH3="$(kpsewhich spath3.sty)" + sed -i 's|\\spath_maybe_split_curve:NNn {NNn, NNV }|\\spath_maybe_split_curve:NNn {NNV}|' "$SPATH3" + sed -i 's|\\spath_maybe_gsplit_curve:NNn {NNn, NNV}|\\spath_maybe_gsplit_curve:NNn {NNV}|' "$SPATH3" - name: Render Quarto project if: steps.cache-rfc.outputs.cache-hit != 'true' From d67d370529647d525e828e08b121ae0a757b42f2 Mon Sep 17 00:00:00 2001 From: Kaspar Date: Fri, 29 May 2026 04:52:25 +0100 Subject: [PATCH 68/81] BUILD: Add a vite-plugin-monorepo-dedupe tool (#1290) --- packages/frontend/default.nix | 4 + packages/frontend/package.json | 1 + packages/frontend/pnpm-lock.yaml | 3 + packages/frontend/vite.config.ts | 34 +- packages/gaios/package.json | 1 + packages/gaios/pnpm-lock.yaml | 4 +- packages/gaios/vite.config.ts | 40 +- packages/ui-components/package.json | 1 + packages/ui-components/pnpm-lock.yaml | 3 + packages/ui-components/vite.config.ts | 8 +- pnpm-workspace.yaml | 2 +- .../vite-plugin-monorepo-dedupe/package.json | 18 + .../pnpm-lock.yaml | 654 ++++++++++++++++++ .../vite-plugin-monorepo-dedupe/src/index.ts | 178 +++++ 14 files changed, 875 insertions(+), 76 deletions(-) create mode 100644 tools/vite-plugin-monorepo-dedupe/package.json create mode 100644 tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml create mode 100644 tools/vite-plugin-monorepo-dedupe/src/index.ts diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index 21d0fafaa..d898dffec 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -40,6 +40,7 @@ let "packages/ui-components" = ../ui-components/pnpm-lock.yaml; "packages/document-methods" = ../document-methods/pnpm-lock.yaml; "packages/backend/pkg" = ../backend/pkg/pnpm-lock.yaml; + "tools/vite-plugin-monorepo-dedupe" = ../../tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml; }; processedLocks = lib.mapAttrs (_: processLock) lockfilesToProcess; @@ -75,6 +76,7 @@ let (lib.fileset.maybeMissing ../../patches) ../../packages/frontend ../../packages/ui-components + ../../tools/vite-plugin-monorepo-dedupe ../../packages/document-methods ../../packages/backend/pkg ]; @@ -203,6 +205,8 @@ let cp -r packages/backend $out/packages/ cp -r packages/frontend $out/packages/ cp -r packages/ui-components $out/packages/ + mkdir -p $out/tools + cp -r tools/vite-plugin-monorepo-dedupe $out/tools/ cp -r packages/document-methods $out/packages/ mkdir -p $out/packages/document-types cp -r packages/document-types/pkg $out/packages/document-types/ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index c22d2ff4e..94d962a6b 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -77,6 +77,7 @@ "uuid": "^13.0.0" }, "devDependencies": { + "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@mdx-js/mdx": "^2.3.0", "@types/html-escaper": "^3.0.4", "@types/mdx": "^2.0.13", diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index af36bf556..e8242be4c 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -162,6 +162,9 @@ importers: specifier: ^13.0.0 version: 13.0.0 devDependencies: + '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': + specifier: link:../../tools/vite-plugin-monorepo-dedupe + version: link:../../tools/vite-plugin-monorepo-dedupe '@mdx-js/mdx': specifier: ^2.3.0 version: 2.3.0 diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 8aafe4976..dd0570286 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,6 +1,4 @@ -import { readFileSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; import mdx from "@mdx-js/rollup"; import rehypeKatex from "rehype-katex"; import remarkMath from "remark-math"; @@ -8,13 +6,9 @@ import solid from "vite-plugin-solid"; import wasm from "vite-plugin-wasm"; import { defineConfig } from "vitest/config"; -// __dirname is not available in ES modules. The test:ci script uses --configLoader=runner -// (required for readonly (Nix) environments), which runs Vite in ESM mode. -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - export default defineConfig({ plugins: [ + monorepoDedupe(), wasm(), mdx({ // https://mdxjs.com/docs/getting-started/#solid @@ -30,9 +24,6 @@ export default defineConfig({ sourcemap: true, target: "es2022", }, - resolve: { - dedupe: getCommonDependencies(), - }, test: { // Run test files sequentially to prevent cross-test contamination via // the server's shared user state. @@ -52,24 +43,3 @@ export default defineConfig({ }, }, }); - -/** - * Get common dependencies between frontend and ui-components packages. - * Needed to link other packages that use Solid.js: - * https://github.com/solidjs/solid/issues/1472 - */ -function getCommonDependencies(): string[] { - const frontendPkg = JSON.parse(readFileSync(resolve(__dirname, "./package.json"), "utf-8")); - const uiComponentsPkg = JSON.parse( - readFileSync(resolve(__dirname, "../ui-components/package.json"), "utf-8"), - ); - - const frontendDeps = new Set(Object.keys(frontendPkg.dependencies || {})); - const uiComponentsDeps = new Set(Object.keys(uiComponentsPkg.dependencies || {})); - - // @ts-expect-error: intersection method does exist on Set in our - // vite.config target i.e. NodeJS - const commonDeps = frontendDeps.intersection(uiComponentsDeps); - - return Array.from(commonDeps); -} diff --git a/packages/gaios/package.json b/packages/gaios/package.json index 97102b6df..ea3269b05 100644 --- a/packages/gaios/package.json +++ b/packages/gaios/package.json @@ -32,6 +32,7 @@ "uuid": "^11.0.3" }, "devDependencies": { + "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@types/react": "^18.3.3", "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/packages/gaios/pnpm-lock.yaml b/packages/gaios/pnpm-lock.yaml index a23fbaa4e..95e60342f 100644 --- a/packages/gaios/pnpm-lock.yaml +++ b/packages/gaios/pnpm-lock.yaml @@ -39,6 +39,9 @@ importers: specifier: ^11.0.3 version: 11.1.0 devDependencies: + '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': + specifier: link:../../tools/vite-plugin-monorepo-dedupe + version: link:../../tools/vite-plugin-monorepo-dedupe '@types/react': specifier: ^18.3.3 version: 18.3.18 @@ -992,7 +995,6 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globals@11.12.0: diff --git a/packages/gaios/vite.config.ts b/packages/gaios/vite.config.ts index a7e7f7889..404d933ab 100644 --- a/packages/gaios/vite.config.ts +++ b/packages/gaios/vite.config.ts @@ -1,21 +1,12 @@ -import { readFileSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; import { defineConfig } from "vite"; import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; import solid from "vite-plugin-solid"; import wasm from "vite-plugin-wasm"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - export default defineConfig({ base: "./", - plugins: [wasm(), solid(), cssInjectedByJsPlugin()], - - resolve: { - dedupe: getCommonDependencies(), - }, + plugins: [monorepoDedupe(), wasm(), solid(), cssInjectedByJsPlugin()], build: { minify: false, @@ -40,30 +31,3 @@ export default defineConfig({ }, }, }); - -/** - * Get common dependencies between gaios, frontend, and ui-components packages. - * Needed to link other packages that use Solid.js: - * https://github.com/solidjs/solid/issues/1472 - */ -function getCommonDependencies(): string[] { - const gaiosPkg = JSON.parse(readFileSync(resolve(__dirname, "./package.json"), "utf-8")); - const frontendPkg = JSON.parse( - readFileSync(resolve(__dirname, "../frontend/package.json"), "utf-8"), - ); - const uiComponentsPkg = JSON.parse( - readFileSync(resolve(__dirname, "../ui-components/package.json"), "utf-8"), - ); - - const gaiosDeps = new Set(Object.keys(gaiosPkg.dependencies || {})); - const frontendDeps = new Set(Object.keys(frontendPkg.dependencies || {})); - const uiComponentsDeps = new Set(Object.keys(uiComponentsPkg.dependencies || {})); - - // @ts-expect-error: intersection method does exist on Set in our - // vite.config target i.e. NodeJS - const commonDeps = gaiosDeps - .intersection(frontendDeps) - .union(gaiosDeps.intersection(uiComponentsDeps)); - - return Array.from(commonDeps); -} diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index a20951061..bc8d301fd 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -31,6 +31,7 @@ "solid-js": "^1.9.10" }, "devDependencies": { + "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@chromatic-com/storybook": "^4.1.2", "@storybook/addon-a11y": "^10.0.2", "@storybook/addon-docs": "^10.0.2", diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 9ba62674b..3a3ac31d7 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -45,6 +45,9 @@ importers: specifier: ^1.9.10 version: 1.9.10 devDependencies: + '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': + specifier: link:../../tools/vite-plugin-monorepo-dedupe + version: link:../../tools/vite-plugin-monorepo-dedupe '@chromatic-com/storybook': specifier: ^4.1.2 version: 4.1.2(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0))) diff --git a/packages/ui-components/vite.config.ts b/packages/ui-components/vite.config.ts index 0c065616d..3aad71bd0 100644 --- a/packages/ui-components/vite.config.ts +++ b/packages/ui-components/vite.config.ts @@ -1,16 +1,16 @@ /// import path from "node:path"; import { fileURLToPath } from "node:url"; +import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; import { playwright } from "@vitest/browser-playwright"; import { defineConfig } from "vite"; -const dirname = - typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url)); +const configDir = path.dirname(fileURLToPath(import.meta.url)); // https://vite.dev/config/ export default defineConfig({ - plugins: [], + plugins: [monorepoDedupe()], define: { "process.env": {}, }, @@ -22,7 +22,7 @@ export default defineConfig({ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest storybookTest({ - configDir: path.join(dirname, ".storybook"), + configDir: path.join(configDir, ".storybook"), }), ], test: { diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0745ec4cb..f9a2a19e9 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,4 +10,4 @@ packages: - "packages/document-methods" - "packages/ui-components" - "packages/gaios" - + - "tools/vite-plugin-monorepo-dedupe" diff --git a/tools/vite-plugin-monorepo-dedupe/package.json b/tools/vite-plugin-monorepo-dedupe/package.json new file mode 100644 index 000000000..21c6a216d --- /dev/null +++ b/tools/vite-plugin-monorepo-dedupe/package.json @@ -0,0 +1,18 @@ +{ + "name": "@catcolab-dev-tools/vite-plugin-monorepo-dedupe", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "scripts": { + "format": "oxfmt ." + }, + "devDependencies": { + "vite": "^7.2.2" + } +} diff --git a/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml b/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml new file mode 100644 index 000000000..8dd92da7a --- /dev/null +++ b/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml @@ -0,0 +1,654 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + vite: + specifier: ^7.2.2 + version: 7.3.3 + +packages: + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@rollup/rollup-android-arm-eabi@4.60.3': + resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.3': + resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.3': + resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.3': + resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.3': + resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.3': + resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.3': + resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.3': + resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.3': + resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.3': + resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.3': + resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.3': + resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.3': + resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.3': + resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} + cpu: [x64] + os: [win32] + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + rollup@4.60.3: + resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + vite@7.3.3: + resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + +snapshots: + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@rollup/rollup-android-arm-eabi@4.60.3': + optional: true + + '@rollup/rollup-android-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.3': + optional: true + + '@rollup/rollup-darwin-x64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.3': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.3': + optional: true + + '@types/estree@1.0.8': {} + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fsevents@2.3.3: + optional: true + + nanoid@3.3.12: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rollup@4.60.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.3 + '@rollup/rollup-android-arm64': 4.60.3 + '@rollup/rollup-darwin-arm64': 4.60.3 + '@rollup/rollup-darwin-x64': 4.60.3 + '@rollup/rollup-freebsd-arm64': 4.60.3 + '@rollup/rollup-freebsd-x64': 4.60.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 + '@rollup/rollup-linux-arm-musleabihf': 4.60.3 + '@rollup/rollup-linux-arm64-gnu': 4.60.3 + '@rollup/rollup-linux-arm64-musl': 4.60.3 + '@rollup/rollup-linux-loong64-gnu': 4.60.3 + '@rollup/rollup-linux-loong64-musl': 4.60.3 + '@rollup/rollup-linux-ppc64-gnu': 4.60.3 + '@rollup/rollup-linux-ppc64-musl': 4.60.3 + '@rollup/rollup-linux-riscv64-gnu': 4.60.3 + '@rollup/rollup-linux-riscv64-musl': 4.60.3 + '@rollup/rollup-linux-s390x-gnu': 4.60.3 + '@rollup/rollup-linux-x64-gnu': 4.60.3 + '@rollup/rollup-linux-x64-musl': 4.60.3 + '@rollup/rollup-openbsd-x64': 4.60.3 + '@rollup/rollup-openharmony-arm64': 4.60.3 + '@rollup/rollup-win32-arm64-msvc': 4.60.3 + '@rollup/rollup-win32-ia32-msvc': 4.60.3 + '@rollup/rollup-win32-x64-gnu': 4.60.3 + '@rollup/rollup-win32-x64-msvc': 4.60.3 + fsevents: 2.3.3 + + source-map-js@1.2.1: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + vite@7.3.3: + dependencies: + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.14 + rollup: 4.60.3 + tinyglobby: 0.2.16 + optionalDependencies: + fsevents: 2.3.3 diff --git a/tools/vite-plugin-monorepo-dedupe/src/index.ts b/tools/vite-plugin-monorepo-dedupe/src/index.ts new file mode 100644 index 000000000..877f63154 --- /dev/null +++ b/tools/vite-plugin-monorepo-dedupe/src/index.ts @@ -0,0 +1,178 @@ +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import process from "node:process"; +import { fileURLToPath } from "node:url"; +import type { Plugin } from "vite"; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); +const dependencyFields = ["dependencies", "devDependencies", "peerDependencies"] as const; + +type DependencyField = (typeof dependencyFields)[number]; + +type PackageJson = { + name?: string; +} & Partial>>; + +/** + * Dedupe dependencies from linked monorepo packages, recursively. + * + * This prevents linked packages from loading separate framework/runtime + * copies (e.g. Solid.js, see https://github.com/solidjs/solid/issues/1472). + * + * - The recursive crawl through `link:`/`file:`/`workspace:` edges is used only + * to *discover* which package names matter; the final dedupe list is + * intersected with the consumer package's own direct dependencies so that + * packages the consumer doesn't import are never forced to a single copy. + * - When the same dependency appears with different version ranges across the + * crawled packages, the plugin throws to fail the build. + */ +export function monorepoDedupe(): Plugin { + return { + name: "@catcolab-dev-tools/vite-plugin-monorepo-dedupe", + configResolved(config) { + const packageDir = + config.configFile === undefined ? process.cwd() : dirname(config.configFile); + config.resolve.dedupe = getRecursiveMonorepoDependencies(packageDir); + }, + }; +} + +function getRecursiveMonorepoDependencies(packageDir: string): string[] { + const packageByName = getWorkspacePackages(); + const dedupeVersions = new Map>(); + const visitedPackages = new Set(); + + const consumerPackage = readPackageJson(resolve(packageDir, "package.json")); + const consumerDepNames = new Set(getPackageDependencies(consumerPackage).map(([name]) => name)); + + addLinkedPackageDependencies(packageDir); + + throwOnVersionConflicts(dedupeVersions); + + return Array.from(dedupeVersions.keys()) + .filter((name) => consumerDepNames.has(name)) + .sort(); + + function addLinkedPackageDependencies(currentPackageDir: string) { + const currentPackage = readPackageJson(resolve(currentPackageDir, "package.json")); + + for (const [dependencyName, dependencyVersion] of getPackageDependencies(currentPackage)) { + const linkedPackageDir = getLinkedPackageDir( + currentPackageDir, + packageByName, + dependencyName, + dependencyVersion, + ); + + if (linkedPackageDir === undefined) { + continue; + } + + if (addPackageDependencyNames(linkedPackageDir)) { + addLinkedPackageDependencies(linkedPackageDir); + } + } + } + + function addPackageDependencyNames(linkedPackageDir: string): boolean { + const realPackageDir = resolve(linkedPackageDir); + const packageJsonPath = resolve(realPackageDir, "package.json"); + if (!existsSync(packageJsonPath)) { + return false; + } + + if (visitedPackages.has(realPackageDir)) { + return false; + } + visitedPackages.add(realPackageDir); + + for (const [dependencyName, dependencyVersion] of getPackageDependencies( + readPackageJson(packageJsonPath), + )) { + let versions = dedupeVersions.get(dependencyName); + if (versions === undefined) { + versions = new Set(); + dedupeVersions.set(dependencyName, versions); + } + versions.add(dependencyVersion); + } + + return true; + } +} + +function throwOnVersionConflicts(dedupeVersions: Map>): void { + const conflicts: string[] = []; + for (const [name, versions] of dedupeVersions) { + const realVersions = Array.from(versions).filter((v) => !isLinkSpecifier(v)); + if (realVersions.length > 1) { + conflicts.push(` - ${name}: ${realVersions.join(", ")}`); + } + } + if (conflicts.length > 0) { + throw new Error( + "[@catcolab-dev-tools/vite-plugin-monorepo-dedupe] conflicting version ranges across linked workspace " + + `packages:\n${conflicts.join("\n")}\n` + + "Align the versions before deduping.", + ); + } +} + +function isLinkSpecifier(version: string): boolean { + return ( + version.startsWith("link:") || + version.startsWith("file:") || + version.startsWith("workspace:") + ); +} + +function getWorkspacePackages(): Map { + const packages = new Map(); + + for (const workspaceRoot of ["packages", "tools"]) { + const workspaceDir = resolve(repoRoot, workspaceRoot); + if (!existsSync(workspaceDir)) { + continue; + } + + for (const packageName of readdirSync(workspaceDir)) { + const packageDir = resolve(workspaceDir, packageName); + const packageJsonPath = resolve(packageDir, "package.json"); + if (!existsSync(packageJsonPath)) { + continue; + } + + const packageJson = readPackageJson(packageJsonPath); + if (packageJson.name !== undefined) { + packages.set(packageJson.name, packageDir); + } + } + } + + return packages; +} + +function getPackageDependencies(packageJson: PackageJson): [string, string][] { + return dependencyFields.flatMap((field) => Object.entries(packageJson[field] ?? {})); +} + +function getLinkedPackageDir( + packageDir: string, + packageByName: Map, + dependencyName: string, + dependencyVersion: string, +): string | undefined { + if (dependencyVersion.startsWith("link:") || dependencyVersion.startsWith("file:")) { + return resolve(packageDir, dependencyVersion.replace(/^(link|file):/, "")); + } + + if (dependencyVersion.startsWith("workspace:")) { + return packageByName.get(dependencyName); + } + + return undefined; +} + +function readPackageJson(packageJsonPath: string): PackageJson { + return JSON.parse(readFileSync(packageJsonPath, "utf-8")); +} From 0cdadc29be2d1759688b17fd53d52f7fd72b97c3 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Thu, 28 May 2026 21:02:21 -0700 Subject: [PATCH 69/81] ENH: Deferrable foreign keys for PostgresSQL backend (#1057) --- packages/catlog-wasm/src/theories.rs | 22 +- packages/catlog/src/one/graph_algorithms.rs | 65 ++- packages/catlog/src/stdlib/analyses/sql.rs | 447 +++++++++++++++--- packages/frontend/src/stdlib/analyses.tsx | 2 +- .../src/stdlib/analyses/sql.module.css | 9 + packages/frontend/src/stdlib/analyses/sql.tsx | 22 +- 6 files changed, 456 insertions(+), 111 deletions(-) create mode 100644 packages/frontend/src/stdlib/analyses/sql.module.css diff --git a/packages/catlog-wasm/src/theories.rs b/packages/catlog-wasm/src/theories.rs index 2b95175c5..f01b6c7a1 100644 --- a/packages/catlog-wasm/src/theories.rs +++ b/packages/catlog-wasm/src/theories.rs @@ -9,7 +9,7 @@ use wasm_bindgen::prelude::*; use catlog::dbl::theory::{self as theory, NonUnital, Unital}; use catlog::one::Path; use catlog::stdlib::{analyses, models, theories, theory_morphisms}; -use catlog::zero::{QualifiedLabel, name}; +use catlog::zero::name; use super::latex::LatexEquations; use super::model_morphism::{MotifOccurrence, MotifsOptions, motifs}; @@ -94,19 +94,13 @@ impl ThSchema { pub fn render_sql(&self, model: &DblModel, backend: &str) -> JsResult { analyses::sql::SQLBackend::try_from(backend) .and_then(|backend| { - analyses::sql::SQLAnalysis::new(backend).render( - model.discrete()?, - |id| { - model - .ob_generator_label(id) - .unwrap_or_else(|| QualifiedLabel::single("".into())) - }, - |id| { - model - .mor_generator_label(id) - .unwrap_or_else(|| QualifiedLabel::single("".into())) - }, - ) + analyses::sql::SQLAnalysis::new(backend) + .render( + model.discrete()?, + |id| model.ob_namespace.label_string(id), + |id| model.mor_namespace.label_string(id), + ) + .map_err(|e| format!("{}", e)) }) .into() } diff --git a/packages/catlog/src/one/graph_algorithms.rs b/packages/catlog/src/one/graph_algorithms.rs index 3ef0d58ce..576e77570 100644 --- a/packages/catlog/src/one/graph_algorithms.rs +++ b/packages/catlog/src/one/graph_algorithms.rs @@ -1,6 +1,8 @@ //! Algorithms on graphs. use derivative::Derivative; +use derive_more::Constructor; +use indexmap::IndexMap; use std::collections::{HashMap, HashSet, VecDeque}; use std::hash::Hash; @@ -301,14 +303,44 @@ where } } +/// Contains both the topologically-sorted stack of vertices and feedback vertices. +#[derive(Debug, Clone, Constructor)] +pub struct ToposortData { + /// Stores an array of topologically-sorted vertices. + pub stack: Vec, + + /// Stores the feedback vertices with their outneighbors. + pub cycles: IndexMap>, +} + +type ToposortResult = Result, V>; + +/// Implementation of topological sort which returns an error when it encounters a cycle. +pub fn toposort_strict(graph: &G) -> Result, G::V> +where + G: FinGraph, + G::V: Hash + std::fmt::Debug, +{ + toposort_impl(graph, true).map(|t| t.stack) +} + +/// Implementation of topological sort which does not return an error when it encounters cycle. +pub fn toposort_lenient(graph: &G) -> ToposortData +where + G: FinGraph, + G::V: Hash + std::fmt::Debug, +{ + toposort_impl(graph, false).expect("toposort in lenient mode should return a valid result") +} + /// Computes a topological sorting for a given graph. /// /// This toposort algorithm was adapted from the crate `petgraph`, found /// [here](https://github.com/petgraph/petgraph/blob/4d807c19304c02c9dd687c68577f75aefcb98491/src/algo/mod.rs#L204). -pub fn toposort<'a, G>(graph: &'a G) -> Result, String> +fn toposort_impl(graph: &G, is_strict: bool) -> ToposortResult where G: FinGraph, - G::V: Hash + std::fmt::Debug + 'a, + G::V: Hash + std::fmt::Debug, { let mut finished = HashSet::new(); let mut finish_stack = Vec::new(); @@ -334,17 +366,23 @@ where // simply test directly by comparing positions of vertices. let position: HashMap = finish_stack.iter().enumerate().map(|(i, v)| (v.clone(), i)).collect(); + let mut cycles = IndexMap::new(); for e in graph.edges() { let s = graph.src(&e); let t = graph.tgt(&e); // Note that we did a DFS starting at every vertex, so it's impossible // that they don't appear _somewhere_ in our map. if position[&s] >= position[&t] { - return Err(format!("Cycle detected involving node {:#?}", s)); + if is_strict { + return Err(s); + } else { + let outs = graph.out_neighbors(&s).collect(); + cycles.insert(s, outs); + } } } - Ok(finish_stack) + Ok(ToposortData::new(finish_stack, cycles)) } #[cfg(test)] @@ -393,24 +431,26 @@ mod tests { #[test] fn toposorting() { let g = SkelGraph::path(5); - assert_eq!(toposort(&g), Ok(vec![0, 1, 2, 3, 4])); + let result = toposort_strict(&g); + assert_eq!(result.unwrap(), vec![0, 1, 2, 3, 4]); let mut g = SkelGraph::path(3); g.add_vertices(1); g.add_edge(2, 3); g.add_edge(3, 0); - expect_test::expect!["Cycle detected involving node 3"] - .assert_eq(&toposort(&g).unwrap_err()); + let t = &toposort_strict(&g).unwrap_err(); + expect_test::expect!["3"].assert_eq(&format!("{t}")); let g = SkelGraph::triangle(); - assert_eq!(toposort(&g), Ok(vec![0, 1, 2])); + assert_eq!(toposort_strict(&g).unwrap(), vec![0, 1, 2]); let mut g = SkelGraph::path(4); g.add_vertices(2); g.add_edge(1, 4); g.add_edge(4, 3); g.add_edge(5, 2); - assert_eq!(toposort(&g), Ok(vec![5, 0, 1, 2, 4, 3])); + + assert_eq!(toposort_strict(&g).unwrap(), vec![5, 0, 1, 2, 4, 3]); let mut g: HashGraph<_, _> = Default::default(); g.add_vertices(vec![0, 1, 2, 3, 4, 5]); @@ -420,9 +460,10 @@ mod tests { g.add_edge("1-4", 1, 4); g.add_edge("4-3", 4, 3); g.add_edge("5-2", 5, 2); - let sort = toposort(&g).unwrap(); - let (i0, i1) = (sort.iter().position(|&x| x == 5), sort.iter().position(|&x| x == 2)); - assert!(i0.unwrap() < i1.unwrap()); + if let Ok(sort) = toposort_strict(&g) { + let (i0, i1) = (sort.iter().position(|&x| x == 5), sort.iter().position(|&x| x == 2)); + assert!(i0.unwrap() < i1.unwrap()); + } } #[test] diff --git a/packages/catlog/src/stdlib/analyses/sql.rs b/packages/catlog/src/stdlib/analyses/sql.rs index 8bbc10760..587835670 100644 --- a/packages/catlog/src/stdlib/analyses/sql.rs +++ b/packages/catlog/src/stdlib/analyses/sql.rs @@ -1,14 +1,20 @@ //! Produces a valid SQL data manipulation script from a model in the theory of schemas. use crate::{ dbl::model::*, - one::{Path, graph::FinGraph, graph_algorithms::toposort}, - zero::{QualifiedLabel, QualifiedName, label, name}, + one::{ + Path, + graph::FinGraph, + graph_algorithms::{ToposortData, toposort_lenient}, + }, + zero::{QualifiedLabel, QualifiedName, name}, }; +use derive_more::Constructor; use indexmap::IndexMap; use itertools::Itertools; +use nonempty::nonempty; use sea_query::SchemaBuilder; use sea_query::{ - ColumnDef, ForeignKey, ForeignKeyCreateStatement, Iden, MysqlQueryBuilder, + Alias, ColumnDef, ForeignKey, ForeignKeyCreateStatement, Iden, MysqlQueryBuilder, PostgresQueryBuilder, SqliteQueryBuilder, Table, TableCreateStatement, prepare::Write, }; use sqlformat::{Dialect, format}; @@ -32,35 +38,188 @@ impl Iden for &QualifiedLabel { } } +/// Enum for specifying the behavior of a column. For example, an Ordinary column is simply +/// a foreign key constraint. +#[derive(Debug, Clone, PartialEq)] +pub enum ColumnType { + /// A foreign key constraint. The target is an entity. + Ordinary { + /// The name of the morphism. + mor: QualifiedName, + /// The name of the target entity. + tgt: QualifiedName, + }, + /// A deferrable key constraint. The target is an entity. + Deferrable { + /// The name of the morphism. + mor: QualifiedName, + /// The name of the target entity. + tgt: QualifiedName, + }, + /// An attribute column. The target is an attribute type. + Attribute { + /// The name of the morphism. + mor: QualifiedName, + /// The name of the target attribute. + tgt: QualifiedName, + }, +} + +impl ColumnType { + fn build( + model: &DiscreteDblModel, + cycles: &IndexMap>, + src: &QualifiedName, + mor: QualifiedName, + ) -> Self { + let tgt = model.get_cod(&mor).unwrap(); + match model.mor_generator_type(&mor) { + t if t == Path::Seq(nonempty![name("Attr")]) => { + ColumnType::Attribute { mor, tgt: tgt.clone() } + } + _ => { + if cycles.contains_key(src) || cycles.contains_key(&tgt.clone()) { + ColumnType::Deferrable { mor, tgt: tgt.clone() } + } else { + ColumnType::Ordinary { mor, tgt: tgt.clone() } + } + } + } + } + + fn mor(&self) -> &QualifiedName { + match self { + ColumnType::Ordinary { mor, tgt: _ } + | ColumnType::Deferrable { mor, tgt: _ } + | ColumnType::Attribute { mor, tgt: _ } => mor, + } + } + + fn tgt(&self) -> &QualifiedName { + match self { + ColumnType::Ordinary { mor: _, tgt } + | ColumnType::Deferrable { mor: _, tgt } + | ColumnType::Attribute { mor: _, tgt } => tgt, + } + } + + /// The function creates foreign key constraints for PostgresSQL. Here, deferrable key + /// constraints are special. + fn render_postgres_fk( + &self, + src: &QualifiedName, + ob_label: impl Fn(&QualifiedName) -> String, + mor_label: impl Fn(&QualifiedName) -> String, + ) -> String { + let fk = |src: String, mor: &String, tgt: &String| -> String { + format!( + r#"ALTER TABLE "{src}" + ADD CONSTRAINT fk_{mor}_{src}_{tgt} + FOREIGN KEY ({mor}) REFERENCES "{tgt}" (id)"# + ) + }; + match self { + ColumnType::Ordinary { mor, tgt } => { + fk(ob_label(src), &mor_label(mor), &ob_label(tgt)) + ";" + } + ColumnType::Deferrable { mor, tgt } => { + fk(ob_label(src), &mor_label(mor), &ob_label(tgt)) + + "\n" + + r#"DEFERRABLE INITIALLY DEFERRED;"# + } + // this is unreachable, since attributes cannot be foreign keys. + ColumnType::Attribute { mor: _, tgt: _ } => unreachable!(), + } + } +} + +/// Data containing foreign key constraints and their behavior, which are interpreted as +/// backend-specific attributes. +#[derive(Clone, Debug)] +pub struct ForeignKeyConstraints { + /// Foreign key constraints for every table. + fks: IndexMap>, +} + +impl ForeignKeyConstraints { + fn new(model: &DiscreteDblModel) -> Self { + let g = model.generating_graph(); + let toposort: ToposortData = toposort_lenient(g); + let cycles = toposort.cycles; + let fks = IndexMap::from_iter(toposort.stack.into_iter().rev().filter_map(|v| { + (name("Entity") == model.ob_generator_type(&v)).then_some(( + v.clone(), + g.out_edges(&v) + .map(|e| ColumnType::build(model, &cycles, &v, e)) + .collect::>(), + )) + })); + Self { fks } + } + + fn any_deferrable(&self) -> bool { + self.fks + .values() + .flatten() + .into_iter() + .any(|s| matches!(s, ColumnType::Deferrable { mor: _, tgt: _ })) + } +} + +/// Error thrown when the SQL Analysis fails. +#[derive(Clone, Debug, PartialEq)] +pub enum SQLAnalysisError { + /// Its possible that a SQL backend cannot support cyclic foreign key constraints. + CyclicForeignKeyError { + /// The SQL backend that fails. Of the supported SQL backends, MySQL is the only one which + /// does not support cyclic foreign key constraints. + backend: SQLBackend, + /// The tables which have failing foreign key constraints. + cycles: Vec<(QualifiedName, ColumnType)>, + }, +} + +impl std::fmt::Display for SQLAnalysisError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + match self { + SQLAnalysisError::CyclicForeignKeyError { backend, cycles } => write!( + f, + "Cycle detected at tables {:#?}. {backend} cannot support cyclic foreign keys.", + cycles + ), + } + } +} + /// Struct for building a valid SQL DDL. +#[derive(Constructor)] pub struct SQLAnalysis { backend: SQLBackend, } impl SQLAnalysis { - /// Constructs a new SQLAnalysis instance. - pub fn new(backend: SQLBackend) -> Self { - Self { backend } + /// Returns formatted output. + pub fn format(&self, output: &str) -> String { + format( + output, + &sqlformat::QueryParams::None, + &sqlformat::FormatOptions { + lines_between_queries: 2, + dialect: self.backend.clone().into(), + ..Default::default() + }, + ) } - /// Consumes itself and a discrete double model to produce a SQL string. - pub fn render( + /// Builds table statements into valid SQL DML. + fn build( &self, - model: &DiscreteDblModel, - ob_label: impl Fn(&QualifiedName) -> QualifiedLabel, - mor_label: impl Fn(&QualifiedName) -> QualifiedLabel, - ) -> Result { - let g = model.generating_graph(); - let t = toposort(g).map_err(|e| format!("Topological sort failed: {}", e))?; - let morphisms: IndexMap<&QualifiedName, Vec> = - IndexMap::from_iter(t.iter().rev().filter_map(|v| { - (name("Entity") == model.ob_generator_type(v)) - .then_some((v, g.out_edges(v).collect::>())) - })); - - let tables = self.make_tables(model, morphisms, ob_label, mor_label); - - let output: String = tables + tables: Vec, + constraints: ForeignKeyConstraints, + ob_label: impl Fn(&QualifiedName) -> String, + mor_label: impl Fn(&QualifiedName) -> String, + ) -> String { + let table_def: String = tables .iter() .map(|table| match self.backend { SQLBackend::MySQL => table.to_string(MysqlQueryBuilder), @@ -70,80 +229,132 @@ impl SQLAnalysis { .join(";\n") + ";"; - // TODO SQL analysis should interface with this - let formatted_output = format( - &output, - &sqlformat::QueryParams::None, - &sqlformat::FormatOptions { - lines_between_queries: 2, - dialect: self.backend.clone().into(), - ..Default::default() - }, - ); + // for PostgresSQL only + let deferrable_fks: String = constraints + .fks + .iter() + .flat_map(|(ob, mors)| { + mors.iter() + .filter(|fkb| matches!(fkb, ColumnType::Deferrable { mor: _, tgt: _ })) + .map(|fkb| fkb.render_postgres_fk(ob, &ob_label, &mor_label)) + .collect::>() + }) + .join("\n"); - let result = match self.backend { - SQLBackend::SQLite => ["PRAGMA foreign_keys = ON", &formatted_output].join(";\n\n"), - _ => formatted_output, - }; - Ok(result) + table_def + &deferrable_fks } - fn fk( + fn validate_toposort( &self, - src_name: QualifiedLabel, - tgt_name: QualifiedLabel, - mor_name: QualifiedLabel, - ) -> ForeignKeyCreateStatement { + constraints: ForeignKeyConstraints, + ) -> Result { + // TODO: punting fixing SQLite cycles for now + if (self.backend == SQLBackend::MySQL || self.backend == SQLBackend::SQLite) + && constraints.any_deferrable() + { + let cycles = constraints + .fks + .into_iter() + .flat_map(|(k, v)| v.into_iter().map(move |e| (k.clone(), e))) + .filter(|(_, e)| matches!(e, ColumnType::Deferrable { mor: _, tgt: _ })) + .collect::>(); + Err(SQLAnalysisError::CyclicForeignKeyError { backend: self.backend.clone(), cycles }) + } else { + Ok(constraints) + } + } + + fn toposort_morphisms( + &self, + model: &DiscreteDblModel, + ) -> Result { + // if a morphism is a key in toposort.cycles, then its source and targets are deferrable. + let constraints = ForeignKeyConstraints::new(model); + self.validate_toposort(constraints) + } + + /// Consumes itself and a discrete double model to produce a SQL string. + pub fn render( + &self, + model: &DiscreteDblModel, + ob_label: impl Fn(&QualifiedName) -> String, + mor_label: impl Fn(&QualifiedName) -> String, + ) -> Result { + let constraints = self.toposort_morphisms(model); + let tables = self.make_tables(model, constraints.clone()?, &ob_label, &mor_label); + let output: String = self.build(tables, constraints.clone()?, ob_label, mor_label); + let formatted_output = self.format(&output); + // pragmas + match self.backend { + SQLBackend::SQLite => Ok(["PRAGMA foreign_keys = ON", &formatted_output].join(";\n\n")), + _ => Ok(formatted_output), + } + } + + fn fk(&self, src: &str, tgt: &str, mor: &str) -> ForeignKeyCreateStatement { ForeignKey::create() - .name(format!("FK_{}_{}_{}", mor_name, src_name, tgt_name)) - .from(src_name.clone(), mor_name) - .to(tgt_name.clone(), "id") + .name(format!("FK_{}_{}_{}", mor, src, tgt)) + .from(Alias::new(src), Alias::new(mor)) + .to(Alias::new(tgt), "id") .to_owned() } fn make_tables( &self, model: &DiscreteDblModel, - morphisms: IndexMap<&QualifiedName, Vec>, - ob_label: impl Fn(&QualifiedName) -> QualifiedLabel, - mor_label: impl Fn(&QualifiedName) -> QualifiedLabel, + constraints: ForeignKeyConstraints, + ob_label: impl Fn(&QualifiedName) -> String, + mor_label: impl Fn(&QualifiedName) -> String, ) -> Vec { - morphisms + constraints + .fks .into_iter() .map(|(ob, mors)| { let mut tbl = Table::create(); // the targets for arrows let table_column_defs = mors.iter().fold( - tbl.table(ob_label(ob)).if_not_exists().col( + tbl.table(Alias::new(ob_label(&ob))).if_not_exists().col( ColumnDef::new("id").integer().not_null().auto_increment().primary_key(), ), |acc, mor| { - let mor_name = mor_label(mor); + let mor_tgt = mor.tgt(); + let ob_name = ob_label(mor_tgt); + let mor_name = mor_label(mor.mor()); // if the Id of the name is an entity, it is assumed to be a column // which references the primary key of another table. - if model.mor_generator_type(mor) == Path::Id(name("Entity")) { - acc.col(ColumnDef::new(mor_name.clone()).integer().not_null()) + if model.mor_generator_type(mor.mor()) == Path::Id(name("Entity")) { + acc.col( + ColumnDef::new(Alias::new(mor_name.as_str())).integer().not_null(), + ) } else { - let tgt = - model.get_cod(mor).map(&ob_label).unwrap_or_else(|| label("")); - let mut col = ColumnDef::new(mor_name); + let mut col = ColumnDef::new(Alias::new(mor_name.as_str())); col.not_null(); - add_column_type(&mut col, &tgt); + add_column_type(&mut col, ob_name.as_str()); acc.col(col) } }, ); mors.iter() - .filter(|mor| model.mor_generator_type(mor) == Path::Id(name("Entity"))) + .filter(|mor| { + (model.mor_generator_type(mor.mor()) == Path::Id(name("Entity"))) + && (if self.backend == SQLBackend::PostgresSQL { + matches!(mor, ColumnType::Ordinary { mor: _, tgt: _ }) + } else { + true + }) + }) .fold( // TABLE AND COLUMN DEFS table_column_defs, |acc, mor| { - let tgt = - model.get_cod(mor).map(&ob_label).unwrap_or_else(|| label("")); - acc.foreign_key(&mut self.fk(ob_label(ob), tgt, mor_label(mor))) + // if there is a cyclic pattern, we want to add deferrable... + acc.foreign_key(&mut self.fk( + ob_label(&ob).as_str(), + ob_label(mor.tgt()).as_str(), + mor_label(mor.mor()).as_str(), + )) }, ) .to_owned() @@ -155,7 +366,7 @@ impl SQLAnalysis { /// Variants of SQL backends. Each correspond to types which implement the /// `SchemaBuilder` trait that is used to render into the correct backend. The `SchemaBuilder` and /// the types implementing that trait are owned by `sea_query`. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SQLBackend { /// The MySQL backend. MySQL, @@ -210,8 +421,8 @@ impl fmt::Display for SQLBackend { } } -fn add_column_type(col: &mut ColumnDef, name: &QualifiedLabel) { - match format!("{}", name).as_str() { +fn add_column_type(col: &mut ColumnDef, label: &str) { + match label { "Int" => col.integer(), "TinyInt" => col.tiny_integer(), "Bool" => col.boolean(), @@ -219,7 +430,7 @@ fn add_column_type(col: &mut ColumnDef, name: &QualifiedLabel) { "Time" => col.timestamp(), "Date" => col.date(), "DateTime" => col.date_time(), - _ => col.custom(name.clone()), + _ => col.custom(Alias::new(label)), }; } @@ -234,17 +445,17 @@ mod tests { #[test] fn sql_schema() { let th = Rc::new(th_schema()); - let model = tt::modelgen::Model::from_text( - &th.into(), - "[ + let source = "[ Person : Entity, Dog : Entity, walks : (Hom Entity)[Person, Dog], Hair : AttrType, has : Attr[Person, Hair], - ]", - ); - let model = model.unwrap().as_discrete().unwrap(); + ]"; + let model = tt::modelgen::Model::from_text(&th.clone().into(), source) + .ok() + .and_then(|m| m.as_discrete()) + .unwrap(); let expected = expect![[ r#"CREATE TABLE IF NOT EXISTS `Dog` (`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY); @@ -265,4 +476,96 @@ CREATE TABLE IF NOT EXISTS `Person` ( .expect("SQL should render"); expected.assert_eq(&ddl); } + + #[test] + fn sql_postgres_cycles() { + let th = Rc::new(th_schema()); + let source = "[ + Refs : Entity, + Snapshots : Entity, + head : (Hom Entity)[Refs, Snapshots], + for_ref: (Hom Entity)[Snapshots, Refs], + Timestamp : AttrType, + created : Attr[Refs, Timestamp], + last_updated: Attr[Snapshots, Timestamp], + ]"; + let model = tt::modelgen::Model::from_text(&th.into(), source) + .ok() + .and_then(|m| m.as_discrete()) + .unwrap(); + + let expected = expect![[r#"CREATE TABLE IF NOT EXISTS "Snapshots" ( + "id" serial NOT NULL PRIMARY KEY, + "for_ref" integer NOT NULL, + "last_updated" Timestamp NOT NULL +); + +CREATE TABLE IF NOT EXISTS "Refs" ( + "id" serial NOT NULL PRIMARY KEY, + "head" integer NOT NULL, + "created" Timestamp NOT NULL +); + +ALTER TABLE + "Snapshots" +ADD + CONSTRAINT fk_for_ref_Snapshots_Refs FOREIGN KEY (for_ref) REFERENCES "Refs" (id) DEFERRABLE INITIALLY DEFERRED; + +ALTER TABLE + "Refs" +ADD + CONSTRAINT fk_head_Refs_Snapshots FOREIGN KEY (head) REFERENCES "Snapshots" (id) DEFERRABLE INITIALLY DEFERRED;"#]]; + let ddl = SQLAnalysis::new(SQLBackend::PostgresSQL) + .render( + &model, + |id| format!("{id}").as_str().into(), + |id| format!("{id}").as_str().into(), + ) + .expect("SQL should render"); + expected.assert_eq(&ddl); + } + + #[test] + fn sql_mysql_cycles() { + let th = Rc::new(th_schema()); + let source = "[ + Refs : Entity, + Snapshots : Entity, + head : (Hom Entity)[Refs, Snapshots], + for_ref: (Hom Entity)[Snapshots, Refs], + Timestamp : AttrType, + created : Attr[Refs, Timestamp], + last_updated: Attr[Snapshots, Timestamp], + ]"; + let model = tt::modelgen::Model::from_text(&th.into(), source) + .ok() + .and_then(|m| m.as_discrete()) + .unwrap(); + + let ddl = SQLAnalysis::new(SQLBackend::MySQL).render( + &model, + |id| format!("{id}").as_str().into(), + |id| format!("{id}").as_str().into(), + ); + let e = ddl.unwrap_err(); + assert_eq!( + e, + SQLAnalysisError::CyclicForeignKeyError { + backend: SQLBackend::MySQL, + cycles: vec![ + ( + name("Snapshots"), + ColumnType::Deferrable { mor: name("for_ref"), tgt: name("Refs") } + ), + ( + name("Refs"), + ColumnType::Deferrable { + mor: name("head"), + tgt: name("Snapshots") + } + ) + ] + } + ); + } } diff --git a/packages/frontend/src/stdlib/analyses.tsx b/packages/frontend/src/stdlib/analyses.tsx index 38b19a6a0..2bb74794c 100644 --- a/packages/frontend/src/stdlib/analyses.tsx +++ b/packages/frontend/src/stdlib/analyses.tsx @@ -358,7 +358,7 @@ export function renderSQL( help, component: (props) => , initialContent: () => ({ - backend: SQLBackend.MySQL, + backend: SQLBackend.PostgresSQL, filename: "schema.sql", }), }; diff --git a/packages/frontend/src/stdlib/analyses/sql.module.css b/packages/frontend/src/stdlib/analyses/sql.module.css new file mode 100644 index 000000000..83698f2af --- /dev/null +++ b/packages/frontend/src/stdlib/analyses/sql.module.css @@ -0,0 +1,9 @@ +.headerContainer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 4px; + flex-wrap: nowrap; + white-space: nowrap; + flex-direction: row; +} diff --git a/packages/frontend/src/stdlib/analyses/sql.tsx b/packages/frontend/src/stdlib/analyses/sql.tsx index e01ca25b0..c8c8e1c18 100644 --- a/packages/frontend/src/stdlib/analyses/sql.tsx +++ b/packages/frontend/src/stdlib/analyses/sql.tsx @@ -8,6 +8,8 @@ import { CodeView, BlockTitle, ErrorAlert, IconButton } from "catcolab-ui-compon import type { ModelAnalysisProps } from "../../analysis"; import * as SQL from "./sql_types.ts"; +import styles from "./sql.module.css"; + const copyToClipboard = (text: string) => navigator.clipboard.writeText(text); const tooltip = () => ( @@ -31,14 +33,7 @@ const tooltip = () => ( export function SQLHeader(sql: string) { return ( -
      +
      copyToClipboard(sql)} disabled={false} @@ -112,10 +107,13 @@ export default function SQLSchemaAnalysis( )} - -

      {"The model failed to compile into a SQL script."}

      -

      {"Check for cycles in foreign key constraints."}

      -
      +
      + + +

      {"The model failed to compile into a SQL script."}

      +

      {"Check for cycles in foreign key constraints."}

      +
      +
      )} From 1e36eea579bd2a229c08bb8635aea9537ee7312d Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Fri, 29 May 2026 11:47:45 -0700 Subject: [PATCH 70/81] Revert "BUILD: Add a vite-plugin-monorepo-dedupe tool (#1290)" (#1302) This reverts commit efa6f83165c90cd38d6c6a239cc729e4ba33c8ad. --- packages/frontend/default.nix | 4 - packages/frontend/package.json | 1 - packages/frontend/pnpm-lock.yaml | 3 - packages/frontend/vite.config.ts | 34 +- packages/gaios/package.json | 1 - packages/gaios/pnpm-lock.yaml | 4 +- packages/gaios/vite.config.ts | 40 +- packages/ui-components/package.json | 1 - packages/ui-components/pnpm-lock.yaml | 3 - packages/ui-components/vite.config.ts | 8 +- pnpm-workspace.yaml | 2 +- .../vite-plugin-monorepo-dedupe/package.json | 18 - .../pnpm-lock.yaml | 654 ------------------ .../vite-plugin-monorepo-dedupe/src/index.ts | 178 ----- 14 files changed, 76 insertions(+), 875 deletions(-) delete mode 100644 tools/vite-plugin-monorepo-dedupe/package.json delete mode 100644 tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml delete mode 100644 tools/vite-plugin-monorepo-dedupe/src/index.ts diff --git a/packages/frontend/default.nix b/packages/frontend/default.nix index d898dffec..21d0fafaa 100644 --- a/packages/frontend/default.nix +++ b/packages/frontend/default.nix @@ -40,7 +40,6 @@ let "packages/ui-components" = ../ui-components/pnpm-lock.yaml; "packages/document-methods" = ../document-methods/pnpm-lock.yaml; "packages/backend/pkg" = ../backend/pkg/pnpm-lock.yaml; - "tools/vite-plugin-monorepo-dedupe" = ../../tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml; }; processedLocks = lib.mapAttrs (_: processLock) lockfilesToProcess; @@ -76,7 +75,6 @@ let (lib.fileset.maybeMissing ../../patches) ../../packages/frontend ../../packages/ui-components - ../../tools/vite-plugin-monorepo-dedupe ../../packages/document-methods ../../packages/backend/pkg ]; @@ -205,8 +203,6 @@ let cp -r packages/backend $out/packages/ cp -r packages/frontend $out/packages/ cp -r packages/ui-components $out/packages/ - mkdir -p $out/tools - cp -r tools/vite-plugin-monorepo-dedupe $out/tools/ cp -r packages/document-methods $out/packages/ mkdir -p $out/packages/document-types cp -r packages/document-types/pkg $out/packages/document-types/ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 94d962a6b..c22d2ff4e 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -77,7 +77,6 @@ "uuid": "^13.0.0" }, "devDependencies": { - "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@mdx-js/mdx": "^2.3.0", "@types/html-escaper": "^3.0.4", "@types/mdx": "^2.0.13", diff --git a/packages/frontend/pnpm-lock.yaml b/packages/frontend/pnpm-lock.yaml index e8242be4c..af36bf556 100644 --- a/packages/frontend/pnpm-lock.yaml +++ b/packages/frontend/pnpm-lock.yaml @@ -162,9 +162,6 @@ importers: specifier: ^13.0.0 version: 13.0.0 devDependencies: - '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': - specifier: link:../../tools/vite-plugin-monorepo-dedupe - version: link:../../tools/vite-plugin-monorepo-dedupe '@mdx-js/mdx': specifier: ^2.3.0 version: 2.3.0 diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index dd0570286..8aafe4976 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,4 +1,6 @@ -import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; import mdx from "@mdx-js/rollup"; import rehypeKatex from "rehype-katex"; import remarkMath from "remark-math"; @@ -6,9 +8,13 @@ import solid from "vite-plugin-solid"; import wasm from "vite-plugin-wasm"; import { defineConfig } from "vitest/config"; +// __dirname is not available in ES modules. The test:ci script uses --configLoader=runner +// (required for readonly (Nix) environments), which runs Vite in ESM mode. +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + export default defineConfig({ plugins: [ - monorepoDedupe(), wasm(), mdx({ // https://mdxjs.com/docs/getting-started/#solid @@ -24,6 +30,9 @@ export default defineConfig({ sourcemap: true, target: "es2022", }, + resolve: { + dedupe: getCommonDependencies(), + }, test: { // Run test files sequentially to prevent cross-test contamination via // the server's shared user state. @@ -43,3 +52,24 @@ export default defineConfig({ }, }, }); + +/** + * Get common dependencies between frontend and ui-components packages. + * Needed to link other packages that use Solid.js: + * https://github.com/solidjs/solid/issues/1472 + */ +function getCommonDependencies(): string[] { + const frontendPkg = JSON.parse(readFileSync(resolve(__dirname, "./package.json"), "utf-8")); + const uiComponentsPkg = JSON.parse( + readFileSync(resolve(__dirname, "../ui-components/package.json"), "utf-8"), + ); + + const frontendDeps = new Set(Object.keys(frontendPkg.dependencies || {})); + const uiComponentsDeps = new Set(Object.keys(uiComponentsPkg.dependencies || {})); + + // @ts-expect-error: intersection method does exist on Set in our + // vite.config target i.e. NodeJS + const commonDeps = frontendDeps.intersection(uiComponentsDeps); + + return Array.from(commonDeps); +} diff --git a/packages/gaios/package.json b/packages/gaios/package.json index ea3269b05..97102b6df 100644 --- a/packages/gaios/package.json +++ b/packages/gaios/package.json @@ -32,7 +32,6 @@ "uuid": "^11.0.3" }, "devDependencies": { - "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@types/react": "^18.3.3", "@types/uuid": "^10.0.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/packages/gaios/pnpm-lock.yaml b/packages/gaios/pnpm-lock.yaml index 95e60342f..a23fbaa4e 100644 --- a/packages/gaios/pnpm-lock.yaml +++ b/packages/gaios/pnpm-lock.yaml @@ -39,9 +39,6 @@ importers: specifier: ^11.0.3 version: 11.1.0 devDependencies: - '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': - specifier: link:../../tools/vite-plugin-monorepo-dedupe - version: link:../../tools/vite-plugin-monorepo-dedupe '@types/react': specifier: ^18.3.3 version: 18.3.18 @@ -995,6 +992,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globals@11.12.0: diff --git a/packages/gaios/vite.config.ts b/packages/gaios/vite.config.ts index 404d933ab..a7e7f7889 100644 --- a/packages/gaios/vite.config.ts +++ b/packages/gaios/vite.config.ts @@ -1,12 +1,21 @@ -import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; import { defineConfig } from "vite"; import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; import solid from "vite-plugin-solid"; import wasm from "vite-plugin-wasm"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + export default defineConfig({ base: "./", - plugins: [monorepoDedupe(), wasm(), solid(), cssInjectedByJsPlugin()], + plugins: [wasm(), solid(), cssInjectedByJsPlugin()], + + resolve: { + dedupe: getCommonDependencies(), + }, build: { minify: false, @@ -31,3 +40,30 @@ export default defineConfig({ }, }, }); + +/** + * Get common dependencies between gaios, frontend, and ui-components packages. + * Needed to link other packages that use Solid.js: + * https://github.com/solidjs/solid/issues/1472 + */ +function getCommonDependencies(): string[] { + const gaiosPkg = JSON.parse(readFileSync(resolve(__dirname, "./package.json"), "utf-8")); + const frontendPkg = JSON.parse( + readFileSync(resolve(__dirname, "../frontend/package.json"), "utf-8"), + ); + const uiComponentsPkg = JSON.parse( + readFileSync(resolve(__dirname, "../ui-components/package.json"), "utf-8"), + ); + + const gaiosDeps = new Set(Object.keys(gaiosPkg.dependencies || {})); + const frontendDeps = new Set(Object.keys(frontendPkg.dependencies || {})); + const uiComponentsDeps = new Set(Object.keys(uiComponentsPkg.dependencies || {})); + + // @ts-expect-error: intersection method does exist on Set in our + // vite.config target i.e. NodeJS + const commonDeps = gaiosDeps + .intersection(frontendDeps) + .union(gaiosDeps.intersection(uiComponentsDeps)); + + return Array.from(commonDeps); +} diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index bc8d301fd..a20951061 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -31,7 +31,6 @@ "solid-js": "^1.9.10" }, "devDependencies": { - "@catcolab-dev-tools/vite-plugin-monorepo-dedupe": "link:../../tools/vite-plugin-monorepo-dedupe", "@chromatic-com/storybook": "^4.1.2", "@storybook/addon-a11y": "^10.0.2", "@storybook/addon-docs": "^10.0.2", diff --git a/packages/ui-components/pnpm-lock.yaml b/packages/ui-components/pnpm-lock.yaml index 3a3ac31d7..9ba62674b 100644 --- a/packages/ui-components/pnpm-lock.yaml +++ b/packages/ui-components/pnpm-lock.yaml @@ -45,9 +45,6 @@ importers: specifier: ^1.9.10 version: 1.9.10 devDependencies: - '@catcolab-dev-tools/vite-plugin-monorepo-dedupe': - specifier: link:../../tools/vite-plugin-monorepo-dedupe - version: link:../../tools/vite-plugin-monorepo-dedupe '@chromatic-com/storybook': specifier: ^4.1.2 version: 4.1.2(storybook@10.0.2(@testing-library/dom@10.4.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(vite@7.2.2(@types/node@24.10.0))) diff --git a/packages/ui-components/vite.config.ts b/packages/ui-components/vite.config.ts index 3aad71bd0..0c065616d 100644 --- a/packages/ui-components/vite.config.ts +++ b/packages/ui-components/vite.config.ts @@ -1,16 +1,16 @@ /// import path from "node:path"; import { fileURLToPath } from "node:url"; -import { monorepoDedupe } from "@catcolab-dev-tools/vite-plugin-monorepo-dedupe"; import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; import { playwright } from "@vitest/browser-playwright"; import { defineConfig } from "vite"; -const configDir = path.dirname(fileURLToPath(import.meta.url)); +const dirname = + typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url)); // https://vite.dev/config/ export default defineConfig({ - plugins: [monorepoDedupe()], + plugins: [], define: { "process.env": {}, }, @@ -22,7 +22,7 @@ export default defineConfig({ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest storybookTest({ - configDir: path.join(configDir, ".storybook"), + configDir: path.join(dirname, ".storybook"), }), ], test: { diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f9a2a19e9..0745ec4cb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,4 +10,4 @@ packages: - "packages/document-methods" - "packages/ui-components" - "packages/gaios" - - "tools/vite-plugin-monorepo-dedupe" + diff --git a/tools/vite-plugin-monorepo-dedupe/package.json b/tools/vite-plugin-monorepo-dedupe/package.json deleted file mode 100644 index 21c6a216d..000000000 --- a/tools/vite-plugin-monorepo-dedupe/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@catcolab-dev-tools/vite-plugin-monorepo-dedupe", - "version": "0.0.0", - "private": true, - "type": "module", - "exports": { - ".": { - "types": "./src/index.ts", - "default": "./src/index.ts" - } - }, - "scripts": { - "format": "oxfmt ." - }, - "devDependencies": { - "vite": "^7.2.2" - } -} diff --git a/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml b/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml deleted file mode 100644 index 8dd92da7a..000000000 --- a/tools/vite-plugin-monorepo-dedupe/pnpm-lock.yaml +++ /dev/null @@ -1,654 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - vite: - specifier: ^7.2.2 - version: 7.3.3 - -packages: - - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@rollup/rollup-android-arm-eabi@4.60.3': - resolution: {integrity: sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.60.3': - resolution: {integrity: sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.60.3': - resolution: {integrity: sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.60.3': - resolution: {integrity: sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.60.3': - resolution: {integrity: sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.60.3': - resolution: {integrity: sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': - resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.60.3': - resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.60.3': - resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.60.3': - resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.60.3': - resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.60.3': - resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.60.3': - resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.60.3': - resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.60.3': - resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.60.3': - resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.60.3': - resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.60.3': - resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.60.3': - resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.60.3': - resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.60.3': - resolution: {integrity: sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.60.3': - resolution: {integrity: sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.60.3': - resolution: {integrity: sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.60.3': - resolution: {integrity: sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.60.3': - resolution: {integrity: sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==} - cpu: [x64] - os: [win32] - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - - postcss@8.5.14: - resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} - engines: {node: ^10 || ^12 || >=14} - - rollup@4.60.3: - resolution: {integrity: sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} - engines: {node: '>=12.0.0'} - - vite@7.3.3: - resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - -snapshots: - - '@esbuild/aix-ppc64@0.27.7': - optional: true - - '@esbuild/android-arm64@0.27.7': - optional: true - - '@esbuild/android-arm@0.27.7': - optional: true - - '@esbuild/android-x64@0.27.7': - optional: true - - '@esbuild/darwin-arm64@0.27.7': - optional: true - - '@esbuild/darwin-x64@0.27.7': - optional: true - - '@esbuild/freebsd-arm64@0.27.7': - optional: true - - '@esbuild/freebsd-x64@0.27.7': - optional: true - - '@esbuild/linux-arm64@0.27.7': - optional: true - - '@esbuild/linux-arm@0.27.7': - optional: true - - '@esbuild/linux-ia32@0.27.7': - optional: true - - '@esbuild/linux-loong64@0.27.7': - optional: true - - '@esbuild/linux-mips64el@0.27.7': - optional: true - - '@esbuild/linux-ppc64@0.27.7': - optional: true - - '@esbuild/linux-riscv64@0.27.7': - optional: true - - '@esbuild/linux-s390x@0.27.7': - optional: true - - '@esbuild/linux-x64@0.27.7': - optional: true - - '@esbuild/netbsd-arm64@0.27.7': - optional: true - - '@esbuild/netbsd-x64@0.27.7': - optional: true - - '@esbuild/openbsd-arm64@0.27.7': - optional: true - - '@esbuild/openbsd-x64@0.27.7': - optional: true - - '@esbuild/openharmony-arm64@0.27.7': - optional: true - - '@esbuild/sunos-x64@0.27.7': - optional: true - - '@esbuild/win32-arm64@0.27.7': - optional: true - - '@esbuild/win32-ia32@0.27.7': - optional: true - - '@esbuild/win32-x64@0.27.7': - optional: true - - '@rollup/rollup-android-arm-eabi@4.60.3': - optional: true - - '@rollup/rollup-android-arm64@4.60.3': - optional: true - - '@rollup/rollup-darwin-arm64@4.60.3': - optional: true - - '@rollup/rollup-darwin-x64@4.60.3': - optional: true - - '@rollup/rollup-freebsd-arm64@4.60.3': - optional: true - - '@rollup/rollup-freebsd-x64@4.60.3': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.60.3': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.60.3': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.60.3': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.60.3': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.60.3': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.60.3': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.60.3': - optional: true - - '@rollup/rollup-linux-x64-musl@4.60.3': - optional: true - - '@rollup/rollup-openbsd-x64@4.60.3': - optional: true - - '@rollup/rollup-openharmony-arm64@4.60.3': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.60.3': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.60.3': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.60.3': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.60.3': - optional: true - - '@types/estree@1.0.8': {} - - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - fsevents@2.3.3: - optional: true - - nanoid@3.3.12: {} - - picocolors@1.1.1: {} - - picomatch@4.0.4: {} - - postcss@8.5.14: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - rollup@4.60.3: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.3 - '@rollup/rollup-android-arm64': 4.60.3 - '@rollup/rollup-darwin-arm64': 4.60.3 - '@rollup/rollup-darwin-x64': 4.60.3 - '@rollup/rollup-freebsd-arm64': 4.60.3 - '@rollup/rollup-freebsd-x64': 4.60.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.3 - '@rollup/rollup-linux-arm-musleabihf': 4.60.3 - '@rollup/rollup-linux-arm64-gnu': 4.60.3 - '@rollup/rollup-linux-arm64-musl': 4.60.3 - '@rollup/rollup-linux-loong64-gnu': 4.60.3 - '@rollup/rollup-linux-loong64-musl': 4.60.3 - '@rollup/rollup-linux-ppc64-gnu': 4.60.3 - '@rollup/rollup-linux-ppc64-musl': 4.60.3 - '@rollup/rollup-linux-riscv64-gnu': 4.60.3 - '@rollup/rollup-linux-riscv64-musl': 4.60.3 - '@rollup/rollup-linux-s390x-gnu': 4.60.3 - '@rollup/rollup-linux-x64-gnu': 4.60.3 - '@rollup/rollup-linux-x64-musl': 4.60.3 - '@rollup/rollup-openbsd-x64': 4.60.3 - '@rollup/rollup-openharmony-arm64': 4.60.3 - '@rollup/rollup-win32-arm64-msvc': 4.60.3 - '@rollup/rollup-win32-ia32-msvc': 4.60.3 - '@rollup/rollup-win32-x64-gnu': 4.60.3 - '@rollup/rollup-win32-x64-msvc': 4.60.3 - fsevents: 2.3.3 - - source-map-js@1.2.1: {} - - tinyglobby@0.2.16: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - - vite@7.3.3: - dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.14 - rollup: 4.60.3 - tinyglobby: 0.2.16 - optionalDependencies: - fsevents: 2.3.3 diff --git a/tools/vite-plugin-monorepo-dedupe/src/index.ts b/tools/vite-plugin-monorepo-dedupe/src/index.ts deleted file mode 100644 index 877f63154..000000000 --- a/tools/vite-plugin-monorepo-dedupe/src/index.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { existsSync, readFileSync, readdirSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import process from "node:process"; -import { fileURLToPath } from "node:url"; -import type { Plugin } from "vite"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); -const dependencyFields = ["dependencies", "devDependencies", "peerDependencies"] as const; - -type DependencyField = (typeof dependencyFields)[number]; - -type PackageJson = { - name?: string; -} & Partial>>; - -/** - * Dedupe dependencies from linked monorepo packages, recursively. - * - * This prevents linked packages from loading separate framework/runtime - * copies (e.g. Solid.js, see https://github.com/solidjs/solid/issues/1472). - * - * - The recursive crawl through `link:`/`file:`/`workspace:` edges is used only - * to *discover* which package names matter; the final dedupe list is - * intersected with the consumer package's own direct dependencies so that - * packages the consumer doesn't import are never forced to a single copy. - * - When the same dependency appears with different version ranges across the - * crawled packages, the plugin throws to fail the build. - */ -export function monorepoDedupe(): Plugin { - return { - name: "@catcolab-dev-tools/vite-plugin-monorepo-dedupe", - configResolved(config) { - const packageDir = - config.configFile === undefined ? process.cwd() : dirname(config.configFile); - config.resolve.dedupe = getRecursiveMonorepoDependencies(packageDir); - }, - }; -} - -function getRecursiveMonorepoDependencies(packageDir: string): string[] { - const packageByName = getWorkspacePackages(); - const dedupeVersions = new Map>(); - const visitedPackages = new Set(); - - const consumerPackage = readPackageJson(resolve(packageDir, "package.json")); - const consumerDepNames = new Set(getPackageDependencies(consumerPackage).map(([name]) => name)); - - addLinkedPackageDependencies(packageDir); - - throwOnVersionConflicts(dedupeVersions); - - return Array.from(dedupeVersions.keys()) - .filter((name) => consumerDepNames.has(name)) - .sort(); - - function addLinkedPackageDependencies(currentPackageDir: string) { - const currentPackage = readPackageJson(resolve(currentPackageDir, "package.json")); - - for (const [dependencyName, dependencyVersion] of getPackageDependencies(currentPackage)) { - const linkedPackageDir = getLinkedPackageDir( - currentPackageDir, - packageByName, - dependencyName, - dependencyVersion, - ); - - if (linkedPackageDir === undefined) { - continue; - } - - if (addPackageDependencyNames(linkedPackageDir)) { - addLinkedPackageDependencies(linkedPackageDir); - } - } - } - - function addPackageDependencyNames(linkedPackageDir: string): boolean { - const realPackageDir = resolve(linkedPackageDir); - const packageJsonPath = resolve(realPackageDir, "package.json"); - if (!existsSync(packageJsonPath)) { - return false; - } - - if (visitedPackages.has(realPackageDir)) { - return false; - } - visitedPackages.add(realPackageDir); - - for (const [dependencyName, dependencyVersion] of getPackageDependencies( - readPackageJson(packageJsonPath), - )) { - let versions = dedupeVersions.get(dependencyName); - if (versions === undefined) { - versions = new Set(); - dedupeVersions.set(dependencyName, versions); - } - versions.add(dependencyVersion); - } - - return true; - } -} - -function throwOnVersionConflicts(dedupeVersions: Map>): void { - const conflicts: string[] = []; - for (const [name, versions] of dedupeVersions) { - const realVersions = Array.from(versions).filter((v) => !isLinkSpecifier(v)); - if (realVersions.length > 1) { - conflicts.push(` - ${name}: ${realVersions.join(", ")}`); - } - } - if (conflicts.length > 0) { - throw new Error( - "[@catcolab-dev-tools/vite-plugin-monorepo-dedupe] conflicting version ranges across linked workspace " + - `packages:\n${conflicts.join("\n")}\n` + - "Align the versions before deduping.", - ); - } -} - -function isLinkSpecifier(version: string): boolean { - return ( - version.startsWith("link:") || - version.startsWith("file:") || - version.startsWith("workspace:") - ); -} - -function getWorkspacePackages(): Map { - const packages = new Map(); - - for (const workspaceRoot of ["packages", "tools"]) { - const workspaceDir = resolve(repoRoot, workspaceRoot); - if (!existsSync(workspaceDir)) { - continue; - } - - for (const packageName of readdirSync(workspaceDir)) { - const packageDir = resolve(workspaceDir, packageName); - const packageJsonPath = resolve(packageDir, "package.json"); - if (!existsSync(packageJsonPath)) { - continue; - } - - const packageJson = readPackageJson(packageJsonPath); - if (packageJson.name !== undefined) { - packages.set(packageJson.name, packageDir); - } - } - } - - return packages; -} - -function getPackageDependencies(packageJson: PackageJson): [string, string][] { - return dependencyFields.flatMap((field) => Object.entries(packageJson[field] ?? {})); -} - -function getLinkedPackageDir( - packageDir: string, - packageByName: Map, - dependencyName: string, - dependencyVersion: string, -): string | undefined { - if (dependencyVersion.startsWith("link:") || dependencyVersion.startsWith("file:")) { - return resolve(packageDir, dependencyVersion.replace(/^(link|file):/, "")); - } - - if (dependencyVersion.startsWith("workspace:")) { - return packageByName.get(dependencyName); - } - - return undefined; -} - -function readPackageJson(packageJsonPath: string): PackageJson { - return JSON.parse(readFileSync(packageJsonPath, "utf-8")); -} From 34fd5a3ee58d35087b9de51b3bab804f654d02c1 Mon Sep 17 00:00:00 2001 From: Evan Patterson Date: Mon, 1 Jun 2026 11:04:00 -0700 Subject: [PATCH 71/81] DOC: Update CHANGELOG for CatColab v0.6 release. --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e134c084..e49186561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,10 @@ announcement and a blog post. Minor versions are not announced but allow features and fixes to be released with greater frequency. Minor versions often include notable new features. -## [Unreleased] +## [v0.6.0](https://github.com/ToposInstitute/CatColab/releases/tag/v0.6.0) (2026-05-27) + +Blog post: [CatColab v0.6: +Starling](https://topos.institute/blog/2026-06-01-catcolab-0-6-starling/) ### Added From 460e2d73a030748b1b1e18d2e61a9b17da8e89dd Mon Sep 17 00:00:00 2001 From: Tim Hosgood <101851908+tim-at-topos@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:07:07 +0100 Subject: [PATCH 72/81] BUILD: Disable RFC PDF builds (#1310) --- rfc/0001.md | 1 - rfc/_quarto.yml | 8 -------- rfc/nonactionable/0005.md | 20 +++++++++++--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/rfc/0001.md b/rfc/0001.md index 536413edd..8645da80d 100644 --- a/rfc/0001.md +++ b/rfc/0001.md @@ -370,7 +370,6 @@ DOTS [@libkind-myers-2025]. ### Instances -\taxon{alternative} My original vision for "set-theoretic models" in CatColab is that they would exist one level lower in the system: as [instances](double-instances-2026) over diff --git a/rfc/_quarto.yml b/rfc/_quarto.yml index ecebd5f2f..68b20cdad 100644 --- a/rfc/_quarto.yml +++ b/rfc/_quarto.yml @@ -32,14 +32,6 @@ format: number-sections: true date-format: YYYY-MM-DD csl: chicago-author-date.csl - pdf: - include-in-header: - text: | - \usepackage{mathtools} - \usepackage{tikz-cd} - \usepackage{quiver} - \usepackage{ebproof} - \ebproofnewstyle{small}{template=\footnotesize$\inserttext$} filters: - filters.lua diff --git a/rfc/nonactionable/0005.md b/rfc/nonactionable/0005.md index 4ff12d204..491e7ea83 100644 --- a/rfc/nonactionable/0005.md +++ b/rfc/nonactionable/0005.md @@ -9,8 +9,11 @@ aliases: tikz-preamble: | \DeclareMathOperator{\Mnd}{\textsf{Mnd}} \DeclareMathOperator{\Cat}{\textsf{Cat}} - \DeclareMathOperator{\Set}{\textsf{Set}} + \renewcommand{\Set}{\operatorname{\textsf{Set}}} \DeclareMathOperator{\Maybe}{Maybe} + \DeclareMathOperator{\Psh}{Psh} + \DeclareMathOperator{\eel}{\mathbb{E}l} + \DeclareMathOperator{\Mor}{Mor} \newlength{\ProArLineHeight}\setlength{\ProArLineHeight}{1.25ex} @@ -49,18 +52,17 @@ tikz-preamble: | \draw ({#1}.center) ++(360-\pullbackangle:\pullbackradius) -- +(0,-\pullbackmarkerlength); \draw ({#1}.center) ++(270+\pullbackangle:\pullbackradius) -- +(\pullbackmarkerlength,0); } - --- {{< include ../_macros.qmd >}} -\DeclareMathOperator{\Mnd}{\textsf{Mnd}} -\DeclareMathOperator{\Cat}{\textsf{Cat}} -\DeclareMathOperator{\Set}{\textsf{Set}} -\DeclareMathOperator{\Maybe}{Maybe} -\DeclareMathOperator{\Psh}{Psh} -\DeclareMathOperator{\eel}{\mathbb{E}l} -\DeclareMathOperator{\Mor}{Mor} +\newcommand{\Mnd}{\operatorname{\textsf{Mnd}}} +\newcommand{\Cat}{\operatorname{\textsf{Cat}}} +\renewcommand{\Set}{\operatorname{\textsf{Set}}} +\newcommand{\Maybe}{\operatorname{Maybe}} +\newcommand{\Psh}{\operatorname{Psh}} +\newcommand{\eel}{\operatorname{\mathbb{E}l}} +\newcommand{\Mor}{\operatorname{Mor}} ::: {.callout-warning} ### RFC status From aa8e30d9d1c2d77201f69557d51f0dd1e70be6ff Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Wed, 27 May 2026 19:50:30 -0700 Subject: [PATCH 73/81] HOUSEKEEPING: checking out files from diagrams of modal models --- .../catlog/src/dbl/discrete/model_diagram.rs | 6 +- .../catlog/src/dbl/discrete/model_morphism.rs | 51 +++-- packages/catlog/src/dbl/modal/diagram.rs | 180 ++++++++++++++++++ packages/catlog/src/dbl/modal/mod.rs | 4 + packages/catlog/src/dbl/modal/model.rs | 43 ++++- packages/catlog/src/dbl/modal/morphism.rs | 170 +++++++++++++++++ packages/catlog/src/dbl/model_morphism.rs | 52 +++++ 7 files changed, 474 insertions(+), 32 deletions(-) create mode 100644 packages/catlog/src/dbl/modal/diagram.rs create mode 100644 packages/catlog/src/dbl/modal/morphism.rs diff --git a/packages/catlog/src/dbl/discrete/model_diagram.rs b/packages/catlog/src/dbl/discrete/model_diagram.rs index 24c26b0f2..928bac164 100644 --- a/packages/catlog/src/dbl/discrete/model_diagram.rs +++ b/packages/catlog/src/dbl/discrete/model_diagram.rs @@ -6,7 +6,9 @@ use nonempty::NonEmpty; #[cfg(feature = "serde-wasm")] use tsify::declare; -use crate::dbl::{model::*, model_diagram::*, model_morphism::*}; +use crate::dbl::{ + discrete::DiscreteDblModelMapping, model::*, model_diagram::*, model_morphism::*, +}; use crate::one::{Category, FgCategory, GraphMapping}; use crate::validate; use crate::zero::{Mapping, QualifiedName}; @@ -95,7 +97,7 @@ mod tests { domain.add_mor(name("f"), name("x"), name("y"), name("Attr").into()); let mut f: DiscreteDblModelMapping = Default::default(); f.assign_mor(name("f"), Path::single(name("attr"))); - let mut diagram = DblModelDiagram(f, domain); + let mut diagram = DblModelDiagram(f, domain.clone()); let model = walking_attr(th); diagram.infer_missing_from(&model); diff --git a/packages/catlog/src/dbl/discrete/model_morphism.rs b/packages/catlog/src/dbl/discrete/model_morphism.rs index eabd0e906..273b27674 100644 --- a/packages/catlog/src/dbl/discrete/model_morphism.rs +++ b/packages/catlog/src/dbl/discrete/model_morphism.rs @@ -15,17 +15,19 @@ use crate::zero::{HashColumn, Mapping, MutMapping, QualifiedName}; /// /// Because a discrete double theory has only trivial operations, the naturality /// axioms for a model morphism are also trivial. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct DiscreteDblModelMapping(pub DiscreteDblModelMappingData); +pub type DiscreteDblModelMapping = DblModelMapping; type DiscreteDblModelMappingData = FpFunctorData< HashColumn, HashColumn, >; -impl DiscreteDblModelMapping { +impl MutDblModelMapping for DiscreteDblModelMapping { + type ObGen = QualifiedName; + type MorGen = QualifiedPath; + /// Constructs a model mapping from a pair of hash maps. - pub fn new( + fn new( ob_pairs: impl IntoIterator, mor_pairs: impl IntoIterator, ) -> Self { @@ -36,33 +38,27 @@ impl DiscreteDblModelMapping { } /// Assigns an object generator, returning the previous assignment. - pub fn assign_ob(&mut self, x: QualifiedName, y: QualifiedName) -> Option { + fn assign_ob(&mut self, x: QualifiedName, y: QualifiedName) -> Option { self.0.ob_generator_map.set(x, y) } /// Assigns a morphism generator, returning the previous assignment. - pub fn assign_mor(&mut self, e: QualifiedName, n: QualifiedPath) -> Option { + fn assign_mor(&mut self, e: QualifiedName, n: QualifiedPath) -> Option { self.0.mor_generator_map.set(e, n) } /// Unassigns an object generator, returning the previous assignment. - pub fn unassign_ob(&mut self, x: &QualifiedName) -> Option { + fn unassign_ob(&mut self, x: &QualifiedName) -> Option { self.0.ob_generator_map.unset(x) } /// Unassigns a morphism generator, returning the previous assignment. - pub fn unassign_mor(&mut self, e: &QualifiedName) -> Option { + fn unassign_mor(&mut self, e: &QualifiedName) -> Option { self.0.mor_generator_map.unset(e) } +} - /// Interprets the data as a functor into the given model. - pub fn functor_into<'a>( - &'a self, - cod: &'a DiscreteDblModel, - ) -> FpFunctor<'a, DiscreteDblModelMappingData, QualifiedFpCategory> { - self.0.functor_into(&cod.category) - } - +impl DiscreteDblModelMapping { /// Finder of morphisms between two models of a discrete double theory. pub fn morphisms<'a>( dom: &'a DiscreteDblModel, @@ -70,14 +66,15 @@ impl DiscreteDblModelMapping { ) -> DiscreteDblModelMorphismFinder<'a> { DiscreteDblModelMorphismFinder::new(dom, cod) } -} -/// A functor between models of a double theory. -/// -/// This struct borrows its data to perform validation. The domain and codomain are -/// assumed to be valid models of double theories. If that is in question, the -/// models should be validated *before* validating this object. -pub struct DblModelMorphism<'a, Map, Dom, Cod>(pub &'a Map, pub &'a Dom, pub &'a Cod); + /// Interprets the data as a functor into the given model. + pub fn functor_into<'a>( + &'a self, + cod: &'a DiscreteDblModel, + ) -> FpFunctor<'a, DiscreteDblModelMappingData, QualifiedFpCategory> { + self.0.functor_into(&cod.category) + } +} /// A morphism between models of a discrete double theory. pub type DiscreteDblModelMorphism<'a> = @@ -89,7 +86,7 @@ impl<'a> DiscreteDblModelMorphism<'a> { &self, ) -> impl Iterator> + 'a + use<'a> { - let DblModelMorphism(DiscreteDblModelMapping(mapping), dom, cod) = *self; + let DblModelMorphism(DblModelMapping(mapping), dom, cod) = *self; let category_errors: Vec<_> = mapping .functor_into(&cod.category) .iter_invalid_on(&dom.category) @@ -128,14 +125,14 @@ impl<'a> DiscreteDblModelMorphism<'a> { /// Are morphism generators sent to simple composites of morphisms in the /// codomain? fn is_simple(&self) -> bool { - let DblModelMorphism(DiscreteDblModelMapping(mapping), dom, _) = *self; + let DblModelMorphism(DblModelMapping(mapping), dom, _) = *self; dom.mor_generators() .all(|e| mapping.apply_edge(e).map(|p| p.is_simple()).unwrap_or(true)) } /// Is the model morphism injective on objects? pub fn is_injective_objects(&self) -> bool { - let DblModelMorphism(DiscreteDblModelMapping(mapping), dom, _) = *self; + let DblModelMorphism(DblModelMapping(mapping), dom, _) = *self; let mut seen_obs: HashSet<_> = HashSet::new(); for x in dom.ob_generators() { if let Some(f_x) = mapping.apply_vertex(x) { @@ -157,7 +154,7 @@ impl<'a> DiscreteDblModelMorphism<'a> { /// morphisms in the domain to simple paths in the codomain. If any of these /// assumptions are violated, the function will panic. pub fn is_free_simple_faithful(&self) -> bool { - let DblModelMorphism(DiscreteDblModelMapping(mapping), dom, cod) = *self; + let DblModelMorphism(DblModelMapping(mapping), dom, cod) = *self; assert!(dom.is_free(), "Domain model should be free"); assert!(cod.is_free(), "Codomain model should be free"); diff --git a/packages/catlog/src/dbl/modal/diagram.rs b/packages/catlog/src/dbl/modal/diagram.rs new file mode 100644 index 000000000..775899e0f --- /dev/null +++ b/packages/catlog/src/dbl/modal/diagram.rs @@ -0,0 +1,180 @@ +//! Diagrams in models of a modal double theory. + +#[cfg(feature = "serde-wasm")] +use tsify::declare; + +use crate::dbl::{ + modal::ModalDblModelMapping, + model::{InvalidDblModel, ModalDblModel, MutDblModel}, + model_diagram::*, + model_morphism::{DblModelMorphism, InvalidDblModelMorphism, MutDblModelMapping}, + theory::Unital, +}; +use crate::one::{ + category::{Category, FgCategory}, + graph::GraphMapping, +}; +use crate::validate; +use crate::zero::{QualifiedName, column::Mapping}; + +use itertools::Either; +use nonempty::NonEmpty; + +/// A diagram is a model of a modal double theoruy. +pub type ModalDblModelDiagram = DblModelDiagram>; + +/// A failure to be valid in a diagram in a model of a discrete double theory. +#[cfg_attr(feature = "serde-wasm", declare)] +pub type InvalidModalDblModelDiagram = + InvalidDblModelDiagram>; + +impl ModalDblModelDiagram { + /// Validates that the diagram is well-defined in the given model. + /// + /// Assumes that the model is valid. If it is not, this function may panic. + pub fn validate_in( + &self, + model: &ModalDblModel, + ) -> Result<(), NonEmpty> { + validate::wrap_errors(self.iter_invalid_in(model)) + } + + /// Iterates over failures of the diagram to be valid in the given model. + pub fn iter_invalid_in<'a>( + &'a self, + model: &'a ModalDblModel, + ) -> impl Iterator + 'a { + let mut dom_errs = self.1.iter_invalid().peekable(); + if dom_errs.peek().is_some() { + Either::Left(dom_errs.map(InvalidDblModelDiagram::Dom)) + } else { + let morphism = DblModelMorphism(&self.0, &self.1, model); + Either::Right(morphism.iter_invalid().map(InvalidDblModelDiagram::Map)) + } + } + + /// Infer missing data in the diagram from the model, where possible. + /// + /// Assumes that the model is valid. + pub fn infer_missing_from(&mut self, model: &ModalDblModel) { + let (mapping, domain) = self.into(); + domain.infer_missing(); + for e in domain.mor_generators() { + let Some(g) = mapping.0.edge_map().apply_to_ref(&e) else { + continue; + }; + if !model.has_mor(&g) { + continue; + } + + mapping.infer_missing(domain.clone().get_dom(&e), model.dom(&g)); + mapping.infer_missing(domain.clone().get_cod(&e), model.cod(&g)); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dbl::model_diagram::DblModelDiagram; + use crate::stdlib::dec; + use crate::stdlib::th_multicategory; + use crate::tt::modelgen::Model; + use crate::validate; + use crate::zero::name; + use std::rc::Rc; + + #[test] + fn validate_modal_model_diagram() { + let th = Rc::new(th_multicategory()); + let dec = dec(th.clone()); + // TODO does not like numbers + let heat_eq = Model::from_text( + &th.into(), + "[ + u : Object, + dot_u : Object, + k : Object, + anon : Object, + partial_t : Multihom[[u], dot_u], + laplacian : Multihom[[u], anon], + multiplication : Multihom[[k, anon], dot_u] + ]", + ); + let heat_eq = heat_eq.unwrap().as_modal().unwrap(); + + let mut f: ModalDblModelMapping = Default::default(); + f.assign_ob(name("u"), name("Form0").into()); + f.assign_ob(name("dot_u"), name("Form0").into()); + f.assign_ob(name("k"), name("Form0").into()); + f.assign_ob(name("anon"), name("Form0").into()); + f.assign_mor(name("laplacian"), name("laplacian").into()); + f.assign_mor(name("partial_t"), name("partial_t0").into()); + f.assign_mor(name("multiplication"), name("multiplication").into()); + + let diagram = DblModelDiagram(f, heat_eq); + assert!(diagram.validate_in(&dec).is_ok()); + } + + #[test] + fn validate_bad_modal_model_diagram() { + let th = Rc::new(th_multicategory()); + let dec = dec(th.clone()); + // TODO does not like numbers + let heat_eq = Model::from_text( + &th.into(), + "[ + u : Object, + dot_u : Object, + k : Object, + anon : Object, + partial_t : Multihom[[u], dot_u], + laplacian : Multihom[[u], anon], + multiplication : Multihom[[k, anon], dot_u] + ]", + ); + let heat_eq = heat_eq.unwrap().as_modal().unwrap(); + + let mut f: ModalDblModelMapping = Default::default(); + f.assign_ob(name("u"), name("Form0").into()); + f.assign_ob(name("dot_u"), name("Form0").into()); + f.assign_ob(name("k"), name("Form1").into()); // this is intentionally wrong. + f.assign_ob(name("anon"), name("Form0").into()); + f.assign_mor(name("laplacian"), name("laplacian").into()); + f.assign_mor(name("partial_t"), name("partial_t0").into()); + f.assign_mor(name("multiplication"), name("multiplication").into()); + + let diagram = DblModelDiagram(f, heat_eq); + let err = validate::wrap_errors( + vec![InvalidDblModelDiagram::Map(InvalidDblModelMorphism::Dom(name( + "multiplication", + )))] + .into_iter(), + ); + assert_eq!(diagram.validate_in(&dec), err); + } + + #[test] + fn infer_modal_model_diagram() { + let th = Rc::new(th_multicategory()); + let domain = Model::from_text( + &th.clone().into(), + "[ + u : Object, + dot_u : Object, + partial_t : Multihom[[u], dot_u] + ]", + ) + .unwrap() + .as_modal() + .unwrap(); + + let mut f: ModalDblModelMapping = Default::default(); + f.assign_mor(name("partial_t"), name("partial_t0").into()); + let mut diagram = DblModelDiagram(f, domain.clone()); + + let dec = dec(th.clone()); + diagram.infer_missing_from(&dec); + assert!(diagram.validate_in(&dec).is_ok()); + } +} diff --git a/packages/catlog/src/dbl/modal/mod.rs b/packages/catlog/src/dbl/modal/mod.rs index fe527ba76..dd20e65ce 100644 --- a/packages/catlog/src/dbl/modal/mod.rs +++ b/packages/catlog/src/dbl/modal/mod.rs @@ -1,7 +1,11 @@ //! Doctrine of modal double theories. +pub mod diagram; pub mod model; +pub mod morphism; pub mod theory; +pub use diagram::*; pub use model::*; +pub use morphism::*; pub use theory::*; diff --git a/packages/catlog/src/dbl/modal/model.rs b/packages/catlog/src/dbl/modal/model.rs index 11f3c9363..180f6ec58 100644 --- a/packages/catlog/src/dbl/modal/model.rs +++ b/packages/catlog/src/dbl/modal/model.rs @@ -10,8 +10,11 @@ use itertools::Itertools; use ref_cast::RefCast; use super::theory::*; -use crate::dbl::theory::DblTheoryKind; -use crate::dbl::{graph::VDblGraph, model::*, theory::DblTheory}; +use crate::dbl::{ + category::VDblCategory, + graph::VDblGraph, + model::*, theory::{DblTheory, DblTheoryKind}, +}; use crate::tt::util::pretty::*; use crate::validate::{self, Validate}; use crate::{one::computad::*, one::*, zero::*}; @@ -73,7 +76,7 @@ impl MorListData { } /// A model of a modal double theory. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ModalDblModel { theory: Rc>, ob_generators: HashFinSet, @@ -100,6 +103,30 @@ impl ModalDblModel { fn computad(&self) -> Computad<'_, ModalOb, ModalDblModelObs, QualifiedName> { Computad::new(ModalDblModelObs::ref_cast(self), &self.mor_generators) } + + /// Infers missing data in the model, where possible. + /// + /// Objects used in the domain or codomain of morphisms, but not contained as + /// objects of the model, are added and their types are inferred. It is not + /// always possible to do this consistently, so it is important to `validate` + /// the model even after calling this method. + pub fn infer_missing(&mut self) { + let edges: Vec<_> = self.mor_generators().collect(); + for e in edges { + if let Some(x) = self.get_dom(&e).filter(|x| !self.has_ob(x)) { + let ob_type = self.theory.src(&self.mor_generator_type(&e)); + if let Some(id) = x.clone().generator() { + self.add_ob(id.clone(), ob_type) + }; + } + if let Some(x) = self.get_cod(&e).filter(|x| !self.has_ob(x)) { + let ob_type = self.theory.tgt(&self.mor_generator_type(&e)); + if let Some(id) = x.clone().generator() { + self.add_ob(id.clone(), ob_type) + }; + } + } + } } #[derive(RefCast)] @@ -410,6 +437,13 @@ impl ModalDblModel { _ => false, } } + + // TODO + /// Iterates over failures of model to be well defined. + pub fn iter_invalid(&self) -> impl Iterator + '_ { + // type Invalid = InvalidDblModel; + vec![].into_iter() + } } impl ModalObOp { @@ -723,6 +757,9 @@ mod tests { ); model.add_mor(name("nullary"), ModalOb::List(List::Plain, vec![]), x.clone(), mor_type); assert!(model.validate().is_ok()); + + println!("{model}"); + dbg!(&model.mor_types); } #[test] diff --git a/packages/catlog/src/dbl/modal/morphism.rs b/packages/catlog/src/dbl/modal/morphism.rs new file mode 100644 index 000000000..563ae7f75 --- /dev/null +++ b/packages/catlog/src/dbl/modal/morphism.rs @@ -0,0 +1,170 @@ +//! Morphism between models of a modal double theory. + +use crate::dbl::modal::{ModalDblModel, ModalMor, ModalOb}; +use crate::dbl::model::MutDblModel; +use crate::dbl::model_morphism::{DblModelMorphism, InvalidDblModelMorphism, MutDblModelMapping}; +use crate::dbl::theory::Unital; +use crate::one::{ + FpFunctorData, + category::{Category, FgCategory}, + graph::GraphMapping, +}; +use crate::validate::{self, Validate}; +use crate::zero::{HashColumn, Mapping, MutMapping, QualifiedName}; + +use nonempty::NonEmpty; + +// TODO FpFunctorData on ModalDblModalMapping...? +type ModalDblModelMappingData = + FpFunctorData, HashColumn>; + +/// A mapping between models of a modal double theory. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ModalDblModelMapping(pub ModalDblModelMappingData); + +impl MutDblModelMapping for ModalDblModelMapping { + type ObGen = ModalOb; + type MorGen = ModalMor; + /// Constructs a new model mapping from a pair of hash maps. + fn new( + ob_pairs: impl IntoIterator, + mor_pairs: impl IntoIterator, + ) -> Self { + Self(FpFunctorData::new( + ob_pairs.into_iter().collect(), + mor_pairs.into_iter().collect(), + )) + } + + /// Assigns an object generator, returning the previous assignment. + fn assign_ob(&mut self, x: QualifiedName, y: ModalOb) -> Option { + self.0.ob_generator_map.set(x, y) + } + + /// Assigns a morphism generator, returning the previous assignment. + fn assign_mor(&mut self, e: QualifiedName, n: ModalMor) -> Option { + self.0.mor_generator_map.set(e, n) + } + + /// Unassigns an object generator, returning the previous assignment. + fn unassign_ob(&mut self, x: &QualifiedName) -> Option { + self.0.ob_generator_map.unset(x) + } + + /// Unassigns a morphism generator, returning the previous assignment. + fn unassign_mor(&mut self, e: &QualifiedName) -> Option { + self.0.mor_generator_map.unset(e) + } + + // Finder of morphisms between two models of a modal double theory. + // fn morphisms<'a>( + // dom: &'a ModalDblModel, + // cod: &'a ModalDblModel, + // ) -> QualifiedName { + // todo!() + // } +} + +impl ModalDblModelMapping { + /// This checks if an object in the domain also exists in the model. + pub fn infer_missing(&mut self, domain_ob: Option<&ModalOb>, model_ob: ModalOb) { + if let Some(ob) = domain_ob { + let names: Vec = match ob { + ModalOb::Generator(name) => vec![name.clone()], + ModalOb::App(_, name) => vec![name.clone()], + ModalOb::List(_, args) => { + args.iter().filter_map(|ob| ob.clone().generator()).collect() + } + }; + + for name in names { + if !self.0.is_vertex_assigned(&name) { + match model_ob { + ref ob @ ModalOb::Generator(_) => self.assign_ob(name, ob.clone()), + ref ob @ ModalOb::App(_, _) => self.assign_ob(name, ob.clone()), + ModalOb::List(_, ref args) => match args.as_slice() { + [only] => self.assign_ob(name, only.clone()), + _ => todo!("What happens when we receive more than one arg?"), + }, + }; + }; + } + }; + } + + /// This applies a mapping onto a given object in the domain, returning the corresponding + /// object in the codomain. + fn apply_ob(&self, ob: ModalOb) -> Result { + match ob { + ModalOb::Generator(name) => { + self.0.apply_vertex(name.clone()).ok_or("Vertex {name} not found".to_string()) + } + ModalOb::App(_, name) => { + self.0.apply_vertex(name.clone()).ok_or("Vertex {name} not found".to_string()) + } + ModalOb::List(list, args) => args + .into_iter() + .map(|name| self.apply_ob(name.clone())) + .collect::, String>>() + .map(|args| ModalOb::List(list, args)), + } + } +} + +/// A morphism between models of a modal double theory. +// TODO kinds are fixed +pub type ModalDblModelMorphism<'a> = + DblModelMorphism<'a, ModalDblModelMapping, ModalDblModel, ModalDblModel>; + +impl<'a> ModalDblModelMorphism<'a> { + /// Iterates over failures of the mapping to be a model morphism. + pub fn iter_invalid( + &self, + ) -> impl Iterator> + 'a + use<'a> + { + // DiscreteDblModelMapping is destructured at this step, but I've decided not to + // destructure further out of convenience. + let DblModelMorphism(mapping, dom, cod) = *self; + + let ob_errors = dom.ob_generators().filter_map(|v| { + if mapping.0.vertex_map().apply_to_ref(&v).is_some_and(|w| cod.has_ob(&w)) { + None + } else { + Some(InvalidDblModelMorphism::::Ob(v)) + } + }); + + let mor_errors = dom.mor_generators().filter_map(|e| { + // Check if the morphism is correct. + let f = match mapping.0.edge_map().apply_to_ref(&e) { + Some(ModalMor::Generator(f)) if cod.has_mor(&ModalMor::Generator(f.clone())) => f, + Some(ModalMor::Generator(f)) => return Some(InvalidDblModelMorphism::Mor(f)), + _ => return None, + }; + + let dom_check = dom.get_dom(&e).zip(cod.get_dom(&f)).and_then(|(left, right)| { + (mapping.apply_ob(left.clone()) != Ok(right.clone())) + .then_some(InvalidDblModelMorphism::Dom(e.clone())) + }); + + let cod_check = dom.get_cod(&e).zip(cod.get_cod(&f)).and_then(|(left, right)| { + (mapping.apply_ob(left.clone()) != Ok(right.clone())) + .then_some(InvalidDblModelMorphism::Cod(e)) + }); + + // we're short-circuiting errors here. i'd like to collect them into one error message + // in the future + dom_check.or(cod_check) + }); + + ob_errors.chain(mor_errors) + } +} + +impl Validate for ModalDblModelMorphism<'_> { + type ValidationError = InvalidDblModelMorphism; + + fn validate(&self) -> Result<(), NonEmpty> { + validate::wrap_errors(self.iter_invalid()) + } +} diff --git a/packages/catlog/src/dbl/model_morphism.rs b/packages/catlog/src/dbl/model_morphism.rs index 3102bf82f..f428cc587 100644 --- a/packages/catlog/src/dbl/model_morphism.rs +++ b/packages/catlog/src/dbl/model_morphism.rs @@ -26,7 +26,59 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "serde-wasm")] use tsify::Tsify; +use crate::zero::QualifiedName; + pub use super::discrete::model_morphism::*; +pub use super::modal::morphism::*; + +/// Mapping +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct DblModelMapping(pub MappingData); + +/// Mapping +pub trait MutDblModelMapping { + /// Object generators for the DblModelMapping + type ObGen; + /// Morphism generators for the DblModelMapping + type MorGen; + + /// Constructs a model mapping from a pair of hash maps. + fn new( + ob_pairs: impl IntoIterator, + mor_pairs: impl IntoIterator, + ) -> Self; + + /// Assigns an object generator, returning the previous assignment. + fn assign_ob(&mut self, x: QualifiedName, y: Self::ObGen) -> Option; + + /// Assigns a morphism generator, returning the previous assignment. + fn assign_mor(&mut self, e: QualifiedName, n: Self::MorGen) -> Option; + + /// Unassigns an object generator, returning the previous assignment. + fn unassign_ob(&mut self, x: &QualifiedName) -> Option; + + /// Unassigns a morphism generator, returning the previous assignment. + fn unassign_mor(&mut self, e: &QualifiedName) -> Option; + + // /// Interprets the data as a functor into the given model. + // fn functor_into<'a>( + // &'a self, + // cod: &'a Self::DblModel, + // ) -> FpFunctor<'a, Self::DblModelMappingData, QualifiedFpCategory>; + + // /// Finder of morphisms between two models of a discrete double theory. + // fn morphisms<'a>( + // dom: &'a Self::DblModel, + // cod: &'a Self::DblModel, + // ) -> Self::DblModelMorphismFinder; +} + +/// A functor between models of a double theory. +/// +/// This struct borrows its data to perform validation. The domain and codomain are +/// assumed to be valid models of double theories. If that is in question, the +/// models should be validated *before* validating this object. +pub struct DblModelMorphism<'a, Map, Dom, Cod>(pub &'a Map, pub &'a Dom, pub &'a Cod); /// An invalid assignment in a morphism between models of a double theory. #[derive(Clone, Debug, Error, PartialEq, Eq)] From a57e0519ab2183da99d15a61e21ef0761e2ac584 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Thu, 28 May 2026 11:58:08 -0700 Subject: [PATCH 74/81] WIP: adding DblModelDiagramType enum and its Modal variant --- .../catlog/src/dbl/discrete/model_diagram.rs | 17 +- packages/catlog/src/dbl/modal/diagram.rs | 210 +++++++++-------- packages/catlog/src/dbl/modal/model.rs | 28 ++- packages/catlog/src/dbl/modal/morphism.rs | 7 +- packages/catlog/src/dbl/modal/theory.rs | 8 +- packages/catlog/src/dbl/model_diagram.rs | 38 ++- packages/catlog/src/one/path.rs | 18 ++ packages/catlog/src/tt/batch.rs | 110 ++++----- packages/catlog/src/tt/modelgen.rs | 218 +++++++++++++++--- 9 files changed, 455 insertions(+), 199 deletions(-) diff --git a/packages/catlog/src/dbl/discrete/model_diagram.rs b/packages/catlog/src/dbl/discrete/model_diagram.rs index 928bac164..c0c401560 100644 --- a/packages/catlog/src/dbl/discrete/model_diagram.rs +++ b/packages/catlog/src/dbl/discrete/model_diagram.rs @@ -21,11 +21,20 @@ pub type DiscreteDblModelDiagram = DblModelDiagram>; -impl DiscreteDblModelDiagram { +impl Diagram for DiscreteDblModelDiagram { + type Model = DiscreteDblModel; + type Mapping = DiscreteDblModelMapping; + type InvalidDiagram = InvalidDiscreteDblModelDiagram; + + /// Destructure + fn destructure(&self) -> (Self::Mapping, Self::Model) { + (self.0.clone(), self.1.clone()) + } + /// Validates that the diagram is well-defined in the given model. /// /// Assumes that the model is valid. If it is not, this function may panic. - pub fn validate_in( + fn validate_in( &self, model: &DiscreteDblModel, ) -> Result<(), NonEmpty> { @@ -33,7 +42,7 @@ impl DiscreteDblModelDiagram { } /// Iterates over failures of the diagram to be valid in the given model. - pub fn iter_invalid_in<'a>( + fn iter_invalid_in<'a>( &'a self, model: &'a DiscreteDblModel, ) -> impl Iterator + 'a { @@ -49,7 +58,7 @@ impl DiscreteDblModelDiagram { /// Infer missing data in the diagram from the model, where possible. /// /// Assumes that the model is valid. - pub fn infer_missing_from(&mut self, model: &DiscreteDblModel) { + fn infer_missing_from(&mut self, model: &DiscreteDblModel) { let (mapping, domain) = self.into(); domain.infer_missing(); for e in domain.mor_generators() { diff --git a/packages/catlog/src/dbl/modal/diagram.rs b/packages/catlog/src/dbl/modal/diagram.rs index 775899e0f..3c43d0fa7 100644 --- a/packages/catlog/src/dbl/modal/diagram.rs +++ b/packages/catlog/src/dbl/modal/diagram.rs @@ -7,7 +7,7 @@ use crate::dbl::{ modal::ModalDblModelMapping, model::{InvalidDblModel, ModalDblModel, MutDblModel}, model_diagram::*, - model_morphism::{DblModelMorphism, InvalidDblModelMorphism, MutDblModelMapping}, + model_morphism::{DblModelMorphism, InvalidDblModelMorphism}, theory::Unital, }; use crate::one::{ @@ -20,7 +20,7 @@ use crate::zero::{QualifiedName, column::Mapping}; use itertools::Either; use nonempty::NonEmpty; -/// A diagram is a model of a modal double theoruy. +/// A diagram is a model of a modal double theory. pub type ModalDblModelDiagram = DblModelDiagram>; /// A failure to be valid in a diagram in a model of a discrete double theory. @@ -28,11 +28,20 @@ pub type ModalDblModelDiagram = DblModelDiagram>; -impl ModalDblModelDiagram { +impl Diagram for ModalDblModelDiagram { + type Model = ModalDblModel; + type Mapping = ModalDblModelMapping; + type InvalidDiagram = InvalidModalDblModelDiagram; + + /// Destructures the diagram + fn destructure(&self) -> (Self::Mapping, Self::Model) { + (self.0.clone(), self.1.clone()) + } + /// Validates that the diagram is well-defined in the given model. /// /// Assumes that the model is valid. If it is not, this function may panic. - pub fn validate_in( + fn validate_in( &self, model: &ModalDblModel, ) -> Result<(), NonEmpty> { @@ -40,7 +49,7 @@ impl ModalDblModelDiagram { } /// Iterates over failures of the diagram to be valid in the given model. - pub fn iter_invalid_in<'a>( + fn iter_invalid_in<'a>( &'a self, model: &'a ModalDblModel, ) -> impl Iterator + 'a { @@ -56,7 +65,7 @@ impl ModalDblModelDiagram { /// Infer missing data in the diagram from the model, where possible. /// /// Assumes that the model is valid. - pub fn infer_missing_from(&mut self, model: &ModalDblModel) { + fn infer_missing_from(&mut self, model: &ModalDblModel) { let (mapping, domain) = self.into(); domain.infer_missing(); for e in domain.mor_generators() { @@ -77,104 +86,105 @@ impl ModalDblModelDiagram { mod tests { use super::*; use crate::dbl::model_diagram::DblModelDiagram; - use crate::stdlib::dec; + use crate::dbl::model_morphism::MutDblModelMapping; + // use crate::stdlib::dec; use crate::stdlib::th_multicategory; use crate::tt::modelgen::Model; use crate::validate; use crate::zero::name; use std::rc::Rc; - #[test] - fn validate_modal_model_diagram() { - let th = Rc::new(th_multicategory()); - let dec = dec(th.clone()); - // TODO does not like numbers - let heat_eq = Model::from_text( - &th.into(), - "[ - u : Object, - dot_u : Object, - k : Object, - anon : Object, - partial_t : Multihom[[u], dot_u], - laplacian : Multihom[[u], anon], - multiplication : Multihom[[k, anon], dot_u] - ]", - ); - let heat_eq = heat_eq.unwrap().as_modal().unwrap(); - - let mut f: ModalDblModelMapping = Default::default(); - f.assign_ob(name("u"), name("Form0").into()); - f.assign_ob(name("dot_u"), name("Form0").into()); - f.assign_ob(name("k"), name("Form0").into()); - f.assign_ob(name("anon"), name("Form0").into()); - f.assign_mor(name("laplacian"), name("laplacian").into()); - f.assign_mor(name("partial_t"), name("partial_t0").into()); - f.assign_mor(name("multiplication"), name("multiplication").into()); - - let diagram = DblModelDiagram(f, heat_eq); - assert!(diagram.validate_in(&dec).is_ok()); - } - - #[test] - fn validate_bad_modal_model_diagram() { - let th = Rc::new(th_multicategory()); - let dec = dec(th.clone()); - // TODO does not like numbers - let heat_eq = Model::from_text( - &th.into(), - "[ - u : Object, - dot_u : Object, - k : Object, - anon : Object, - partial_t : Multihom[[u], dot_u], - laplacian : Multihom[[u], anon], - multiplication : Multihom[[k, anon], dot_u] - ]", - ); - let heat_eq = heat_eq.unwrap().as_modal().unwrap(); - - let mut f: ModalDblModelMapping = Default::default(); - f.assign_ob(name("u"), name("Form0").into()); - f.assign_ob(name("dot_u"), name("Form0").into()); - f.assign_ob(name("k"), name("Form1").into()); // this is intentionally wrong. - f.assign_ob(name("anon"), name("Form0").into()); - f.assign_mor(name("laplacian"), name("laplacian").into()); - f.assign_mor(name("partial_t"), name("partial_t0").into()); - f.assign_mor(name("multiplication"), name("multiplication").into()); - - let diagram = DblModelDiagram(f, heat_eq); - let err = validate::wrap_errors( - vec![InvalidDblModelDiagram::Map(InvalidDblModelMorphism::Dom(name( - "multiplication", - )))] - .into_iter(), - ); - assert_eq!(diagram.validate_in(&dec), err); - } - - #[test] - fn infer_modal_model_diagram() { - let th = Rc::new(th_multicategory()); - let domain = Model::from_text( - &th.clone().into(), - "[ - u : Object, - dot_u : Object, - partial_t : Multihom[[u], dot_u] - ]", - ) - .unwrap() - .as_modal() - .unwrap(); - - let mut f: ModalDblModelMapping = Default::default(); - f.assign_mor(name("partial_t"), name("partial_t0").into()); - let mut diagram = DblModelDiagram(f, domain.clone()); - - let dec = dec(th.clone()); - diagram.infer_missing_from(&dec); - assert!(diagram.validate_in(&dec).is_ok()); - } + // #[test] + // fn validate_modal_model_diagram() { + // let th = Rc::new(th_multicategory()); + // let dec = dec(th.clone()); + // // TODO does not like numbers + // let heat_eq = Model::from_text( + // &th.into(), + // "[ + // u : Object, + // dot_u : Object, + // k : Object, + // anon : Object, + // partial_t : Multihom[[u], dot_u], + // laplacian : Multihom[[u], anon], + // multiplication : Multihom[[k, anon], dot_u] + // ]", + // ); + // let heat_eq = heat_eq.unwrap().as_modal().unwrap(); + + // let mut f: ModalDblModelMapping = Default::default(); + // f.assign_ob(name("u"), name("Form0").into()); + // f.assign_ob(name("dot_u"), name("Form0").into()); + // f.assign_ob(name("k"), name("Form0").into()); + // f.assign_ob(name("anon"), name("Form0").into()); + // f.assign_mor(name("laplacian"), name("laplacian").into()); + // f.assign_mor(name("partial_t"), name("partial_t0").into()); + // f.assign_mor(name("multiplication"), name("multiplication").into()); + + // let diagram = DblModelDiagram(f, heat_eq); + // assert!(diagram.validate_in(&dec).is_ok()); + // } + + // #[test] + // fn validate_bad_modal_model_diagram() { + // let th = Rc::new(th_multicategory()); + // let dec = dec(th.clone()); + // // TODO does not like numbers + // let heat_eq = Model::from_text( + // &th.into(), + // "[ + // u : Object, + // dot_u : Object, + // k : Object, + // anon : Object, + // partial_t : Multihom[[u], dot_u], + // laplacian : Multihom[[u], anon], + // multiplication : Multihom[[k, anon], dot_u] + // ]", + // ); + // let heat_eq = heat_eq.unwrap().as_modal().unwrap(); + + // let mut f: ModalDblModelMapping = Default::default(); + // f.assign_ob(name("u"), name("Form0").into()); + // f.assign_ob(name("dot_u"), name("Form0").into()); + // f.assign_ob(name("k"), name("Form1").into()); // this is intentionally wrong. + // f.assign_ob(name("anon"), name("Form0").into()); + // f.assign_mor(name("laplacian"), name("laplacian").into()); + // f.assign_mor(name("partial_t"), name("partial_t0").into()); + // f.assign_mor(name("multiplication"), name("multiplication").into()); + + // let diagram = DblModelDiagram(f, heat_eq); + // let err = validate::wrap_errors( + // vec![InvalidDblModelDiagram::Map(InvalidDblModelMorphism::Dom(name( + // "multiplication", + // )))] + // .into_iter(), + // ); + // assert_eq!(diagram.validate_in(&dec), err); + // } + + // #[test] + // fn infer_modal_model_diagram() { + // let th = Rc::new(th_multicategory()); + // let domain = Model::from_text( + // &th.clone().into(), + // "[ + // u : Object, + // dot_u : Object, + // partial_t : Multihom[[u], dot_u] + // ]", + // ) + // .unwrap() + // .as_modal() + // .unwrap(); + + // let mut f: ModalDblModelMapping = Default::default(); + // f.assign_mor(name("partial_t"), name("partial_t0").into()); + // let mut diagram = DblModelDiagram(f, domain.clone()); + + // let dec = dec(th.clone()); + // diagram.infer_missing_from(&dec); + // assert!(diagram.validate_in(&dec).is_ok()); + // } } diff --git a/packages/catlog/src/dbl/modal/model.rs b/packages/catlog/src/dbl/modal/model.rs index 180f6ec58..2a3f99c18 100644 --- a/packages/catlog/src/dbl/modal/model.rs +++ b/packages/catlog/src/dbl/modal/model.rs @@ -1,9 +1,9 @@ //! Models of modal double theories. -use std::collections::HashMap; -use std::fmt::Debug; +use std::fmt::{self, Debug}; use std::rc::Rc; use std::sync::LazyLock; +use std::{collections::HashMap, fmt::Display}; use derive_more::From; use itertools::Itertools; @@ -13,7 +13,8 @@ use super::theory::*; use crate::dbl::{ category::VDblCategory, graph::VDblGraph, - model::*, theory::{DblTheory, DblTheoryKind}, + model::*, + theory::{DblTheory, DblTheoryKind}, }; use crate::tt::util::pretty::*; use crate::validate::{self, Validate}; @@ -33,6 +34,12 @@ pub enum ModalOb { List(List, Vec), } +impl Display for ModalOb { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:#?}", self) + } +} + /// Morphism is a model of a modal double theory. #[derive(Clone, Debug, PartialEq, Eq, From)] pub enum ModalMor { @@ -53,6 +60,16 @@ pub enum ModalMor { List(MorListData, Vec), } +impl ModalMor { + /// Render a [`QualifiedPath`] for snapshot output. + pub fn format_path(&self) -> String { + match &self { + ModalMor::Generator(name) => format!("{name}"), + _ => todo!(), + } + } +} + /// Extra data associated with a list of morphisms in a [list modality](List). #[derive(Clone, Debug, PartialEq, Eq)] pub enum MorListData { @@ -127,6 +144,11 @@ impl ModalDblModel { } } } + + /// Render a [`QualifiedPath`] for snapshot output. + pub fn format_path(&self, p: &ModalMorType) -> String { + format!("{:#?}", p) + } } #[derive(RefCast)] diff --git a/packages/catlog/src/dbl/modal/morphism.rs b/packages/catlog/src/dbl/modal/morphism.rs index 563ae7f75..545504443 100644 --- a/packages/catlog/src/dbl/modal/morphism.rs +++ b/packages/catlog/src/dbl/modal/morphism.rs @@ -2,7 +2,9 @@ use crate::dbl::modal::{ModalDblModel, ModalMor, ModalOb}; use crate::dbl::model::MutDblModel; -use crate::dbl::model_morphism::{DblModelMorphism, InvalidDblModelMorphism, MutDblModelMapping}; +use crate::dbl::model_morphism::{ + DblModelMapping, DblModelMorphism, InvalidDblModelMorphism, MutDblModelMapping, +}; use crate::dbl::theory::Unital; use crate::one::{ FpFunctorData, @@ -19,8 +21,7 @@ type ModalDblModelMappingData = FpFunctorData, HashColumn>; /// A mapping between models of a modal double theory. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ModalDblModelMapping(pub ModalDblModelMappingData); +pub type ModalDblModelMapping = DblModelMapping; impl MutDblModelMapping for ModalDblModelMapping { type ObGen = ModalOb; diff --git a/packages/catlog/src/dbl/modal/theory.rs b/packages/catlog/src/dbl/modal/theory.rs index 57551ca69..1161b02ce 100644 --- a/packages/catlog/src/dbl/modal/theory.rs +++ b/packages/catlog/src/dbl/modal/theory.rs @@ -18,7 +18,7 @@ //! 2-category or monoidal category. Instead, the mode theory is implicit and baked //! in at the type level. -use std::fmt; +use std::fmt::{self, Display}; use std::hash::Hash; use std::iter::repeat_n; use std::marker::PhantomData; @@ -167,6 +167,12 @@ impl ModeApp { /// These are (object or morphism) types that cannot be built out of others. pub type ModalType = ModeApp; +impl Display for ModeApp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.arg) + } +} + /// A basic operation in a modal double theory. /// /// These are (object or morphism) operations that cannot be built out of others diff --git a/packages/catlog/src/dbl/model_diagram.rs b/packages/catlog/src/dbl/model_diagram.rs index fd3d5af48..73eabc448 100644 --- a/packages/catlog/src/dbl/model_diagram.rs +++ b/packages/catlog/src/dbl/model_diagram.rs @@ -8,20 +8,54 @@ //! fibered perspective, generalizing how a diagram in a category can be used to //! represent a copresheaf over that category. -use derive_more::Into; +use derive_more::{From, Into}; +use nonempty::NonEmpty; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "serde-wasm")] use tsify::Tsify; +use crate::dbl::discrete::DiscreteDblModelMapping; +use crate::dbl::modal::ModalDblModelMapping; +use crate::dbl::model::{DiscreteDblModel, FpDblModel, ModalDblModel, MutDblModel}; +use crate::dbl::theory::Unital; +use crate::one::FgCategory; + pub use super::discrete::model_diagram::*; +pub use super::modal::diagram::*; + +/// A diagram in a model of a double theory. +/// +/// This struct owns its data, namely, the domain of the diagram (a model) and the +/// model mapping itself. +pub trait Diagram { + type Model: FgCategory + FpDblModel + MutDblModel; + type Mapping; + type InvalidDiagram; + + fn destructure(&self) -> (Self::Mapping, Self::Model); + + fn validate_in(&self, model: &Self::Model) -> Result<(), NonEmpty>; + + fn iter_invalid_in<'a>( + &'a self, + model: &'a Self::Model, + ) -> impl Iterator + 'a; + + fn infer_missing_from(&mut self, model: &Self::Model); +} + +pub enum DblModelDiagramType { + Discrete(DblModelDiagram), + ModalUnital(DblModelDiagram>), +} /// A diagram in a model of a double theory. /// /// This struct owns its data, namely, the domain of the diagram (a model) and the /// model mapping itself. -#[derive(Clone, Into)] +#[derive(Clone, Into, From)] #[into(owned, ref, ref_mut)] pub struct DblModelDiagram(pub Map, pub Dom); diff --git a/packages/catlog/src/one/path.rs b/packages/catlog/src/one/path.rs index a1e214918..98c977adf 100644 --- a/packages/catlog/src/one/path.rs +++ b/packages/catlog/src/one/path.rs @@ -50,6 +50,19 @@ pub enum Path { /// A path whose vertices and edges are qualified names. pub type QualifiedPath = Path; +impl QualifiedPath { + /// Render a [`QualifiedPath`] for snapshot output. + pub fn format_path(&self) -> String { + match &self { + Path::Id(v) => format!("Hom({v})"), + Path::Seq(es) => { + let parts: Vec = es.iter().map(|e| format!("{e}")).collect(); + parts.join(".") + } + } + } +} + /// A path in a graph with skeletal vertex and edge sets. pub type SkelPath = Path; @@ -485,6 +498,11 @@ impl TryFrom> for ShortPath { } impl ShortPath { + /// + pub fn format_path(&self) -> String { + "TODO".into() + } + /// Is the path contained in the given graph? pub fn contained_in(&self, graph: &impl Graph) -> bool { match self { diff --git a/packages/catlog/src/tt/batch.rs b/packages/catlog/src/tt/batch.rs index da490338f..3296971cd 100644 --- a/packages/catlog/src/tt/batch.rs +++ b/packages/catlog/src/tt/batch.rs @@ -6,23 +6,19 @@ use std::ops::DerefMut; use std::time::{Duration, Instant}; use std::{fs, io}; +use all_the_same::all_the_same; use fnotation::FNtnTop; use scopeguard::guard; use tattle::display::SourceInfo; use tattle::{Reporter, declare_error}; -use super::{ - modelgen::{Model, diagram_from_diag}, - text_elab::*, - theory::std_theories, - toplevel::*, -}; -use crate::dbl::discrete::{DiscreteDblModelDiagram, InvalidDiscreteDblModelDiagram}; +use super::{modelgen::diagram_from_diag, text_elab::*, theory::std_theories, toplevel::*}; +use crate::dbl::discrete::InvalidDiscreteDblModelDiagram; use crate::dbl::model::{FpDblModel, MutDblModel}; -use crate::dbl::model_diagram::DblModelDiagram; +use crate::dbl::model_diagram::{DblModelDiagramType, Diagram}; use crate::one::category::FgCategory; -use crate::one::path::Path; -use crate::zero::{Mapping, NameSegment, QualifiedName}; +use crate::tt::modelgen::Model; +use crate::zero::{Mapping, NameSegment}; declare_error!(TOP_ERROR, "top", "an error at the top-level"); @@ -74,38 +70,42 @@ impl BatchOutput { } } - fn diagram_summary(&self, diagram: &DiscreteDblModelDiagram) { + fn diagram_summary(&self, diagram: &DblModelDiagramType) { if let BatchOutput::Snapshot(out) = self { - let DblModelDiagram(mapping, domain) = diagram; - let mut out = out.borrow_mut(); - let ob_gens: Vec<_> = domain.ob_generators().collect(); - let mor_gens: Vec<_> = domain.mor_generators().collect(); - if ob_gens.is_empty() && mor_gens.is_empty() { - writeln!(out, "#/ diagram has no domain generators").unwrap(); - return; - } - writeln!(out, "#/ diagram domain:").unwrap(); - for g in &ob_gens { - let ot = domain.ob_generator_type(g); - match mapping.0.ob_generator_map.apply_to_ref(g) { - Some(target) => writeln!(out, "#/ {g} : {ot} -> {target}").unwrap(), - None => writeln!(out, "#/ {g} : {ot}").unwrap(), + all_the_same!(match diagram { + DblModelDiagramType::[Discrete, ModalUnital](diagram) => { + let (mapping, domain) = diagram.destructure(); + let mut out = out.borrow_mut(); + let ob_gens: Vec<_> = domain.ob_generators().collect(); + let mor_gens: Vec<_> = domain.mor_generators().collect(); + if ob_gens.is_empty() && mor_gens.is_empty() { + writeln!(out, "#/ diagram has no domain generators").unwrap(); + return; + } + writeln!(out, "#/ diagram domain:").unwrap(); + for g in &ob_gens { + let ot = domain.ob_generator_type(g); + match mapping.0.ob_generator_map.apply_to_ref(g) { + Some(target) => writeln!(out, "#/ {g} : {ot} -> {target}").unwrap(), + None => writeln!(out, "#/ {g} : {ot}").unwrap(), + } + } + for g in &mor_gens { + let mt = &domain.mor_generator_type(g).format_path(); + let dom_str = + domain.get_dom(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); + let cod_str = + domain.get_cod(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); + let target_str = mapping + .0 + .mor_generator_map + .apply_to_ref(g) + .map(|p| format!(" -> {}", &p.format_path())) + .unwrap_or_default(); + writeln!(out, "#/ {g} : {mt}[{dom_str}, {cod_str}]{target_str}").unwrap(); + } } - } - for g in &mor_gens { - let mt = format_path(&domain.mor_generator_type(g)); - let dom_str = - domain.get_dom(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); - let cod_str = - domain.get_cod(g).map(|o| format!("{o}")).unwrap_or_else(|| "?".into()); - let target_str = mapping - .0 - .mor_generator_map - .apply_to_ref(g) - .map(|p| format!(" -> {}", format_path(&p))) - .unwrap_or_default(); - writeln!(out, "#/ {g} : {mt}[{dom_str}, {cod_str}]{target_str}").unwrap(); - } + }) } } @@ -260,10 +260,21 @@ pub fn elaborate(src: &str, path: &str, output: &BatchOutput) -> io::Result { + if let Some(cod) = cod.as_discrete() { + output.validation_result( + diagram.iter_invalid_in(&cod), + ); + } + } + DblModelDiagramType::ModalUnital(diagram) => { + if let Some(cod) = cod.as_modal() { + output.validation_result( + diagram.iter_invalid_in(&cod), + ); + } + } } } Err(msg) => output.diagram_error(&msg), @@ -330,14 +341,3 @@ fn snapshot_examples() { } assert!(succeeded); } - -/// Render a [`QualifiedPath`] for snapshot output. -fn format_path(p: &Path) -> String { - match p { - Path::Id(v) => format!("Hom({v})"), - Path::Seq(es) => { - let parts: Vec = es.iter().map(|e| format!("{e}")).collect(); - parts.join(".") - } - } -} diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index f55cdec9f..974c53426 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -6,13 +6,16 @@ use tattle::display::SourceInfo; use super::{eval::*, prelude::*, text_elab, theory::*, toplevel::*, val::*}; use crate::dbl::{ - discrete, discrete_tabulator, modal, - model::{DblModel, DblModelPrinter, MutDblModel}, - model_diagram::DblModelDiagram, + discrete::{self, DiscreteDblModelMapping}, + discrete_tabulator, + modal::{self, ModalDblModel, ModalDblModelMapping, ModalMorType, ModalOb, ModalObType}, + model::{DblModel, DblModelPrinter, DiscreteDblModel, MutDblModel}, + model_diagram::{DblModelDiagram, DblModelDiagramType}, + model_morphism::{DblModelMapping, MutDblModelMapping}, theory::{DblTheory, DblTheoryKind, NonUnital, Unital}, }; use crate::one::{ - Category, QualifiedPath, + Category, FgCategory, QualifiedPath, path::{Path, PathEq}, }; use crate::zero::{Namespace, QualifiedName}; @@ -413,29 +416,42 @@ pub fn diagram_from_diag( toplevel: &Toplevel, th: &TheoryDef, diag: &Diag, -) -> Result<(discrete::DiscreteDblModelDiagram, Namespace), String> { - let TheoryDef::Discrete(theory) = th else { - return Err("diagram generation only supports discrete double theories".into()); - }; - let mut generator = DiagramGenerator { - eval: Evaluator::empty(toplevel), - codomain_ty: diag.model.clone(), - domain: discrete::DiscreteDblModel::new(theory.clone()), - mapping: discrete::DiscreteDblModelMapping::default(), - }; - let namespace = generator.generate(&diag.body_val)?; - let DiagramGenerator { domain, mapping, .. } = generator; - Ok((DblModelDiagram(mapping, domain), namespace)) +) -> Result<(DblModelDiagramType, Namespace), String> { + match th { + TheoryDef::Discrete(theory) => { + let mut generator = DiagramGenerator { + eval: Evaluator::empty(toplevel), + codomain_ty: diag.model.clone(), + domain: discrete::DiscreteDblModel::new(theory.clone()), + mapping: discrete::DiscreteDblModelMapping::default(), + }; + let namespace = generator.generate(&diag.body_val)?; + let DiagramGenerator { domain, mapping, .. } = generator; + Ok((DblModelDiagramType::Discrete(DblModelDiagram(mapping, domain)), namespace)) + } + TheoryDef::ModalUnital(theory) => { + let mut generator = DiagramGenerator { + eval: Evaluator::empty(toplevel), + codomain_ty: diag.model.clone(), + domain: modal::ModalDblModel::new(theory.clone()), + mapping: modal::ModalDblModelMapping::default(), + }; + let namespace = generator.generate(&diag.body_val)?; + let DiagramGenerator { domain, mapping, .. } = generator; + Ok((DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)), namespace)) + } + _ => Err("!".into()), + } } -struct DiagramGenerator<'a> { +struct DiagramGenerator<'a, M: DblModel + MutDblModel + FgCategory, Mapping: MutDblModelMapping> { eval: Evaluator<'a>, codomain_ty: TyV, - domain: discrete::DiscreteDblModel, - mapping: discrete::DiscreteDblModelMapping, + domain: M, + mapping: Mapping, } -impl DiagramGenerator<'_> { +impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { fn generate(&mut self, body_ty: &TyV) -> Result { let (self_n, eval_with_self) = self.eval.bind_self(body_ty.clone()); self.eval = eval_with_self; @@ -504,7 +520,12 @@ impl DiagramGenerator<'_> { .try_into() .map_err(|_| "morphism type is not valid for a discrete theory".to_string())?; let qname: QualifiedName = prefix.clone().into(); - self.domain.add_mor(qname.clone(), dom_name, cod_name, mt_inner); + self.domain.add_mor( + qname.clone(), + dom_name.into(), + cod_name.into(), + mt_inner.into(), + ); // Mapping target: extract the codomain morphism's name // from the synthetic lift name `~f(e)`. For other shapes // (e.g., a future user-declared morphism in a body), fall @@ -524,6 +545,95 @@ impl DiagramGenerator<'_> { } } +impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { + fn generate(&mut self, body_ty: &TyV) -> Result { + let (self_n, eval_with_self) = self.eval.bind_self(body_ty.clone()); + self.eval = eval_with_self; + let self_val = self.eval.eta_neu(&self_n, body_ty); + Ok(self + .extract(vec![], &self_val, body_ty)? + .unwrap_or_else(Namespace::new_for_uuid)) + } + + fn extract( + &mut self, + prefix: Vec, + val: &TmV, + ty: &TyV, + ) -> Result, String> { + match &**ty { + TyV_::Record(r) => { + let mut namespace = Namespace::new_for_uuid(); + for (name, (label, _)) in r.fields.iter() { + let mut child_prefix = prefix.clone(); + child_prefix.push(*name); + if let NameSegment::Uuid(uuid) = name { + namespace.set_label(*uuid, *label); + } + let field_tm = self.eval.proj(val, *name, *label); + let field_ty = self.eval.field_ty(ty, val, *name); + if let Some(inner) = self.extract(child_prefix, &field_tm, &field_ty)? { + namespace.add_inner(*name, inner); + } + } + Ok(Some(namespace)) + } + TyV_::Over(path) => { + // Resolve the path against the codomain to find the + // catlog object type for this generator. + let (cod_self_n, cod_eval) = self.eval.bind_self(self.codomain_ty.clone()); + let cod_val = cod_eval.eta_neu(&cod_self_n, &self.codomain_ty); + let resolved = cod_eval.path_ty(&self.codomain_ty, &cod_val, path)?; + let TyV_::Object(ot) = &*resolved else { + return Err( + "@over path does not refer to an object generator in the codomain".into() + ); + }; + let ot: ModalObType = ot.clone().try_into().map_err(|_| { + "@over codomain object type is not valid for a discrete theory".to_string() + })?; + let qname: QualifiedName = prefix.into(); + self.domain.add_ob(qname.clone(), ot.into()); + let target: QualifiedName = + path.iter().map(|(seg, _)| *seg).collect::>().into(); + self.mapping.assign_ob(qname, target.into()); + Ok(None) + } + TyV_::Morphism(mt, dom, cod) => { + // Augmentation produces lift morphisms whose dom and cod + // are projections from the body's self, resolving to the + // qualified names of generators (declared or specialized). + let dom_name = neutral_qualified_name(dom).ok_or_else(|| { + "morphism domain does not resolve to a generator name".to_string() + })?; + let cod_name = neutral_qualified_name(cod).ok_or_else(|| { + "morphism codomain does not resolve to a generator name".to_string() + })?; + let mt_inner: ModalMorType = mt + .clone() + .try_into() + .map_err(|_| "morphism type is not valid for a discrete theory".to_string())?; + let qname: QualifiedName = prefix.clone().into(); + self.domain.add_mor(qname.clone(), dom_name.into(), cod_name.into(), mt_inner); + // Mapping target: extract the codomain morphism's name + // from the synthetic lift name `~f(e)`. For other shapes + // (e.g., a future user-declared morphism in a body), fall + // back to the field name itself. + if let Some(last) = prefix.last() + && let Some(cod_mor) = parse_lift_morphism_name(last) + { + let target: modal::ModalMor = QualifiedName::single(cod_mor).into(); + self.mapping.assign_mor(qname, target); + } + Ok(None) + } + TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Id(_, _, _) | TyV_::Unit | TyV_::Meta(_) => { + Ok(None) + } + } + } +} + /// Read off a [`QualifiedName`] from a value that's a neutral chain of /// projections from a self-variable (regardless of which level the self /// is bound at). Returns `None` if the value isn't of that shape. @@ -553,7 +663,7 @@ mod tests { use crate::dbl::model::FpDblModel; use crate::tt::text_elab::{TT_PARSE_CONFIG, TopElabResult, TopElaborator}; use crate::tt::theory::std_theories; - use crate::zero::Mapping; + use crate::zero::{Mapping, name}; fn elaborate_to_toplevel(src: &str) -> Toplevel { let reporter = Reporter::new(); @@ -564,6 +674,7 @@ mod tests { if let Some(TopElabResult::Declaration(name, decl)) = topelab.elab(&toplevel, topntn) { + dbg!(&name); toplevel.declarations.insert(name, decl); } } @@ -596,15 +707,60 @@ diagram I : @Instance(WeightedGraph) := [ Some(TopDecl::Diag(d)) => d.clone(), _ => panic!("expected I to be a diagram declaration"), }; - let (DblModelDiagram(mapping, domain), _ns) = - diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - let e_qname: QualifiedName = vec![name_seg("e")].into(); - let entity_ot: QualifiedName = vec![name_seg("Entity")].into(); - let e_target: QualifiedName = vec![name_seg("E")].into(); + let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + match model_diag { + DblModelDiagramType::Discrete(DblModelDiagram(mapping, domain)) => { + let e_qname: QualifiedName = vec![name_seg("e")].into(); + let entity_ot: QualifiedName = vec![name_seg("Entity")].into(); + let e_target: QualifiedName = vec![name_seg("E")].into(); + assert!(domain.has_ob(&e_qname)); + assert_eq!(domain.ob_generator_type(&e_qname), entity_ot); + assert_eq!(mapping.0.ob_generator_map.apply_to_ref(&e_qname), Some(e_target)); + } + DblModelDiagramType::ModalUnital(_) => { + panic!("We should not here!") + } + } + } + + #[test] + fn diagram_over_dec() { + let src = r#" +set_theory ThMulticategory - assert!(domain.has_ob(&e_qname)); - assert_eq!(domain.ob_generator_type(&e_qname), entity_ot); - assert_eq!(mapping.0.ob_generator_map.apply_to_ref(&e_qname), Some(e_target)); +type DEC := [ + Form0 : Object, + Form1 : Object, + Form2 : Object +] + +diagram I : @Instance(DEC) := [ + s : @over .Form0, +] +"#; + let toplevel = elaborate_to_toplevel(src); + let diag = match toplevel.declarations.get(&name_seg("I")) { + Some(TopDecl::Diag(d)) => d.clone(), + _ => panic!("expected I to be a diagram declaration"), + }; + + let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + match model_diag { + DblModelDiagramType::Discrete(_) => { + panic!("We should not be here!") + } + DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)) => { + let e_qname: QualifiedName = vec![name_seg("s")].into(); + let entity_ot = modal::ModeApp::new(name("Object")); + let e_target: QualifiedName = vec![name_seg("Form0")].into(); + assert!(domain.has_ob(&e_qname.clone().into())); + assert_eq!(domain.ob_generator_type(&e_qname), entity_ot); + assert_eq!( + mapping.0.ob_generator_map.apply_to_ref(&e_qname), + Some(e_target.into()) + ); + } + } } } From 9621fcb41cf2236ae4d413c34da0a080ec8e437e Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Sun, 31 May 2026 12:02:16 -0700 Subject: [PATCH 75/81] WIP: multihom lifting working --- packages/catlog/src/dbl/modal/morphism.rs | 1 + packages/catlog/src/tt/modelgen.rs | 176 ++++++++++++++++++++-- packages/catlog/src/tt/text_elab.rs | 156 ++++++++++++++----- 3 files changed, 286 insertions(+), 47 deletions(-) diff --git a/packages/catlog/src/dbl/modal/morphism.rs b/packages/catlog/src/dbl/modal/morphism.rs index 545504443..efa4cf7de 100644 --- a/packages/catlog/src/dbl/modal/morphism.rs +++ b/packages/catlog/src/dbl/modal/morphism.rs @@ -17,6 +17,7 @@ use crate::zero::{HashColumn, Mapping, MutMapping, QualifiedName}; use nonempty::NonEmpty; // TODO FpFunctorData on ModalDblModalMapping...? +// define a new struct `ModalDblModelMapping` which carries type ModalDblModelMappingData = FpFunctorData, HashColumn>; diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 974c53426..e822236e7 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -184,9 +184,11 @@ impl Model { Model::DiscreteTab(_) => { // Discrete tabulator models currently do not support equations, so we ignore them. } - Model::ModalUnital(_) | Model::ModalNonUnital(_) => { + Model::ModalUnital(_) => { + // model.add_equation(PathEq::new(lhs.try_into().unwrap(), rhs.try_into().unwrap())); // Modal models currently do not support equations, so we ignore them. } + Model::ModalNonUnital(_) => {} } } @@ -603,18 +605,42 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { // Augmentation produces lift morphisms whose dom and cod // are projections from the body's self, resolving to the // qualified names of generators (declared or specialized). - let dom_name = neutral_qualified_name(dom).ok_or_else(|| { - "morphism domain does not resolve to a generator name".to_string() - })?; - let cod_name = neutral_qualified_name(cod).ok_or_else(|| { - "morphism codomain does not resolve to a generator name".to_string() - })?; + println!("CHECKING: {}", self.eval.quote_tm(&dom)); + let mt_inner: ModalMorType = mt .clone() .try_into() .map_err(|_| "morphism type is not valid for a discrete theory".to_string())?; + let dom_ob: ModalOb = match &**dom { + TmV_::List(elems) => { + let src_ot: ModalObType = self.domain.theory().src_type(&mt_inner); // adapt: see note + let Some(modal::Modality::List(list_type)) = src_ot.modalities.last() + else { + return Err("multihom source is not a list object type".to_string()); + }; + let obs = elems + .iter() + .map(|e| neutral_qualified_name(e).map(ModalOb::Generator)) + .collect::>>() + .ok_or_else(|| { + "multihom domain element does not resolve to a generator name" + .to_string() + })?; + ModalOb::List(*list_type, obs) + } + _ => ModalOb::Generator(neutral_qualified_name(dom).ok_or_else(|| { + "morphism domain does not resolve to a generator name".to_string() + })?), + }; + + // let dom_name = neutral_qualified_name(dom).ok_or_else(|| { + // "morphism domain does not resolve to a generator name".to_string() + // })?; + let cod_name = neutral_qualified_name(cod).ok_or_else(|| { + "morphism codomain does not resolve to a generator name".to_string() + })?; let qname: QualifiedName = prefix.clone().into(); - self.domain.add_mor(qname.clone(), dom_name.into(), cod_name.into(), mt_inner); + self.domain.add_mor(qname.clone(), dom_ob.into(), cod_name.into(), mt_inner); // Mapping target: extract the codomain morphism's name // from the synthetic lift name `~f(e)`. For other shapes // (e.g., a future user-declared morphism in a body), fall @@ -638,12 +664,29 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { /// projections from a self-variable (regardless of which level the self /// is bound at). Returns `None` if the value isn't of that shape. fn neutral_qualified_name(tm: &TmV) -> Option { + if let TmV_::List(elems) = &**tm { + for e in elems { + println!("{:?}", neutral_qualified_name(e)); + } + }; let TmV_::Neu(n, _) = &**tm else { return None; }; Some(n.to_qualified_name()) } +fn neutral_qualified_names(tm: &TmV) -> Option> { + if let TmV_::List(elems) = &**tm { + for e in elems { + println!("{:?}", neutral_qualified_name(e)); + } + }; + let TmV_::List(elems) = &**tm else { + return None; + }; + elems.into_iter().map(|n| neutral_qualified_name(n)).collect::>>() +} + /// Parse a synthetic lift-morphism field name of the form `~f(e)` and /// return the codomain morphism's name `f`. Returns `None` if the name /// isn't of that shape. @@ -676,6 +719,8 @@ mod tests { { dbg!(&name); toplevel.declarations.insert(name, decl); + } else { + dbg!(&topntn.name); } } Some(()) @@ -726,18 +771,26 @@ diagram I : @Instance(WeightedGraph) := [ #[test] fn diagram_over_dec() { + // TODO breaks when i write "dot-u" and other hyphens let src = r#" set_theory ThMulticategory type DEC := [ Form0 : Object, Form1 : Object, - Form2 : Object + partial : Multihom[[Form0], Form0], + mult: Multihom[[Form0, Form0], Form0], + wedge00: Multihom[[Form0, Form0], Form0], + wedge10: Multihom[[Form1, Form0], Form1] ] diagram I : @Instance(DEC) := [ - s : @over .Form0, + v : @over .Form0, + u : @over .Form0, + w : @over .Form1, + result : (partial(u) == v) ] + "#; let toplevel = elaborate_to_toplevel(src); let diag = match toplevel.declarations.get(&name_seg("I")) { @@ -751,7 +804,108 @@ diagram I : @Instance(DEC) := [ panic!("We should not be here!") } DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)) => { - let e_qname: QualifiedName = vec![name_seg("s")].into(); + let e_qname: QualifiedName = vec![name_seg("u")].into(); + let entity_ot = modal::ModeApp::new(name("Object")); + let e_target: QualifiedName = vec![name_seg("Form0")].into(); + assert!(domain.has_ob(&e_qname.clone().into())); + assert_eq!(domain.ob_generator_type(&e_qname), entity_ot); + assert_eq!( + mapping.0.ob_generator_map.apply_to_ref(&e_qname), + Some(e_target.into()) + ); + } + } + } + + #[test] + fn glueing_modal_diagrams() { + let src = r#" + set_theory ThMulticategory + + type DEC := [ + Form0 : Object, + Form1 : Object, + DualForm0 : Object, + lapl_d0 : Multihom[[DualForm0], DualForm0], + partial_0 : Multihom[[Form0], Form0], + partial_1 : Multihom[[Form1], Form1], + partial_d0 : Multihom[[DualForm0], DualForm0], + square_d0 : Multihom[[DualForm0], DualForm0], + add_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + sub_d01 : Multihom[[DualForm0, Form0], DualForm0], + sub_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + mult_00 : Multihom[[Form0, Form0], Form0], + mult_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + lie_1d0 : Multihom[[Form1, DualForm0], DualForm0], + wedge_00: Multihom[[Form0, Form0], Form0], + wedge_10: Multihom[[Form1, Form0], Form1] + ] + + diagram Hydrodynamics : @Instance(DEC) := [ + a : @over .Form0, + k : @over .Form0, + n : @over .DualForm0, + w : @over .DualForm0, + dX : @over .Form1, + x0 : @over .DualForm0, + x1 : @over .DualForm0, + x2 : @over .DualForm0, + x3 : @over .DualForm0, + x4 : @over .DualForm0, + x5 : @over .DualForm0, + x6 : @over .DualForm0, + _ : x0 == sub_d01([a, w]), + _ : x1 == square_d0([n]), + _ : x2 == mult_d0d0([w, x1]), + _ : x3 == sub_d01([x0, x2]), + _ : x4 == lie_1d0([dX, w]), + _ : x5 == mult_00([k, x4]), + _ : x6 == add_d0d0([x3, x5]), + _ : x6 == partial_d0([w]) + ] + + diagram Phytodynamics : @Instance(DEC) := [ + n : @over .DualForm0, + w : @over .DualForm0, + m : @over .Form0, + y0 : @over .DualForm0, + y1 : @over .DualForm0, + y2 : @over .DualForm0, + y3 : @over .DualForm0, + y4 : @over .DualForm0, + y5 : @over .DualForm0, + y6 : @over .DualForm0, + _ : y0 == square_d0([n]), + _ : y1 == mult_d0d0([w, x0]), + _ : y2 == mult_d0d0([m, n]), + _ : y3 == sub_d0d0([y1, y2]), + _ : y4 == lapl([n]), + _ : y5 == add_d0d0([y3, y4]), + _ : y6 == partial([w]), + _ : y6 == y5 + ] + + diagram Klausmeier : @Instance(DEC) := [ + hydro : Hydrodynamics, + phyto : Phytodynamics, + _ : hydro.n == phyto.n, + _ : hydro.w == phyto.w + ] + "#; + let toplevel = elaborate_to_toplevel(src); + let diag = match toplevel.declarations.get(&name_seg("Klausmeier")) { + Some(TopDecl::Diag(d)) => d.clone(), + _ => panic!("expected I to be a diagram declaration"), + }; + + let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + // dbg!((&model_diag)); + match model_diag { + DblModelDiagramType::Discrete(_) => { + panic!("We should not be here!") + } + DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)) => { + let e_qname: QualifiedName = vec![name_seg("u")].into(); let entity_ot = modal::ModeApp::new(name("Object")); let e_target: QualifiedName = vec![name_seg("Form0")].into(); assert!(domain.has_ob(&e_qname.clone().into())); diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index 01104e72c..5b20adcb9 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -1,6 +1,7 @@ //! Elaboration from plain text for DoubleTT. use fnotation::*; +use itertools::Itertools; use scopeguard::{ScopeGuard, guard}; use fnotation::{ParseConfig, parser::Prec}; @@ -124,6 +125,7 @@ impl TopElaborator { ) })?; let (ty_s, ty_v) = self.elaborator(&theory, toplevel).ty(ty_n); + println!("TYPE: {}, \n\n {}", ty_s, ty_n); Some(TopElabResult::Declaration( name, TopDecl::Type(Type::new(theory.clone(), ty_s, ty_v)), @@ -133,12 +135,14 @@ impl TopElaborator { let theory = self.get_theory(tn.loc)?; let mut elab = self.elaborator(&theory, toplevel); let (name, args_n, annotn, valn) = self.annotated_def(tn.body).or_else(|| { + dbg!(&tn.body); self.error( tn.loc, "unknown syntax for diagram declaration, expected : := ", ) })?; if args_n.is_some() { + dbg!(&tn.loc); return self.error(tn.loc, "diagrams cannot have arguments"); } let (_, ret_ty_v) = elab.ty(annotn); @@ -146,17 +150,21 @@ impl TopElaborator { // so that `@over .E` inside the body can recover both the // diagram's name and its codomain type, while ordinary // term lookups cannot see it. + dbg!(&name); let diag_label = match name { NameSegment::Text(s) => label_seg(s), NameSegment::Uuid(_) => label_seg("diag"), }; elab.intro_diagram(name, diag_label, ret_ty_v.clone()); + println!("VALN: {}", &valn); let (val_s, _val_v) = elab.ty(valn); // Augment the body with synthetic lift-target fields forced // by the discrete-opfibration condition: for each declared // `e : @over .X` and codomain morphism `f : X → Y`, add a // synthetic field `f(e) : @over .Y`. + println!("VALS: {}", &val_s); let val_s = augment_with_lifts(&val_s, &ret_ty_v); + println!("AUGMENTED: {}", &val_s); let val_v = elab.evaluator().eval_ty(&val_s); Some(TopElabResult::Declaration( name, @@ -461,9 +469,10 @@ impl<'a> Elaborator<'a> { } Var(name) => { let qname = QualifiedName::single(name_seg(*name)); - if let Some(mor_type) = theory.basic_mor_type(qname) { + if let Some(mor_type) = theory.basic_mor_type(qname.clone()) { let dom = theory.src_type(&mor_type); let cod = theory.tgt_type(&mor_type); + println!("MOR {} TYPE {} : DOM {} => COD {}", &qname, &mor_type, &dom, &cod); Some((mor_type, dom, cod)) } else { elab.error(format!("no such morphism type {name}")) @@ -524,6 +533,7 @@ impl<'a> Elaborator<'a> { /// Elaborates a type from notation, returning both syntax and value. pub fn ty(&mut self, n: &FNtn) -> (TyS, TyV) { let mut elab = self.enter(n.loc()); + println!("ELABORATING TYPE... {}", &n); match n.ast0() { Var(name) => elab.lookup_ty(name_seg(*name)), Keyword("Unit") => (TyS::unit(), TyV::unit()), @@ -650,7 +660,10 @@ impl<'a> Elaborator<'a> { let eq_ty_v = TyV::id(tm1_ty, tm1_v, tm2_v); (eq_ty_s, eq_ty_v) } - _ => elab.ty_error("unexpected notation for type"), + _ => { + println!("UNEXPECTED NOTATION FOR TYPE"); + elab.ty_error("unexpected notation for type") + } } } @@ -900,52 +913,112 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { // morphism field whose source/target reduce to a single chain of // fields. type LiftPath = Vec<(NameSegment, LabelSegment)>; - type CodMor = (NameSegment, MorType, LiftPath, LiftPath); + // TODO assuming List for now + type CodMor = (NameSegment, MorType, Vec, LiftPath); let mut cod_morphisms: Vec = Vec::new(); for (mor_name, (_, mor_ty_s)) in cod_r.fields.iter() { if let TyS_::Morphism(mt, dom_s, cod_s) = &**mor_ty_s - && let (Some(dom_path), Some(cod_path)) = (tms_to_path(dom_s), tms_to_path(cod_s)) + // XXX replace tms_to_path + && let (Some(dom_path), Some(cod_path)) = (term_to_paths(dom_s), tms_to_path(cod_s)) { cod_morphisms.push((*mor_name, mt.clone(), dom_path, cod_path)); } } let mut row = initial.clone(); - for _ in 0..MAX_DEPTH { + for _ in 0..1 { let snapshot = row.clone(); let mut added = false; - for (field_name, (field_label, field_ty)) in snapshot.iter() { - let TyS_::Over(path) = &**field_ty else { - continue; - }; - for (mor_name, mt, dom_path, cod_path) in &cod_morphisms { - if dom_path != path { - continue; - } - // Synthetic object field `f(e) : @over .Y`, the unique - // lift target forced by the discrete-opfibration condition. - let synth = ustr(&format!("{mor_name}({field_name})")); - let synth_seg = name_seg(synth); - let synth_label = label_seg(synth); - if !row.has(synth_seg) { - row.insert(synth_seg, synth_label, TyS::over(cod_path.clone())); - added = true; + + // path (of an @over field's target) -> declared fields over that path + let mut fields_by_path: HashMap> = + HashMap::new(); + for (fname, (flabel, fty)) in snapshot.iter() { + if let TyS_::Over(p) = &**fty { + fields_by_path.entry(p.clone()).or_default().push((*fname, *flabel)); + } + } + + for (mor_name, mt, dom_path, cod_path) in &cod_morphisms { + match mt { + MorType::Modal(_) => { + println!("MORTYPE: {}", &mt); + let Some(per_slot): Option>> = + dom_path.iter().map(|slot| fields_by_path.get(slot)).collect() + else { + continue; + }; + + // let Some(per_slot) = per_slot else { continue }; // a slot with no candidate object + // ( (u, u), (v, v) ) + for tuple in + per_slot.into_iter().map(|v| v.iter().copied()).multi_cartesian_product() + { + let args = + tuple.iter().map(|(n, _)| n.to_string()).collect::>().join(", "); + let synth = ustr(&format!("{mor_name}({args})")); + let synth_seg = name_seg(synth); + let synth_label = label_seg(synth); + if !row.has(synth_seg) { + row.insert(synth_seg, synth_label, TyS::over(cod_path.clone())); + added = true; + } + let lift_name = ustr(&format!("~{mor_name}({args})")); + let lift_seg = name_seg(lift_name); + let lift_label = label_seg(lift_name); + if !row.has(lift_seg) { + let self_var = + TmS::var(BwdIdx::from(0), name_seg("self"), label_seg("self")); + let dom_elems: Vec = tuple + .iter() + .map(|(n, l)| TmS::proj(self_var.clone(), *n, *l)) + .collect(); + let dom_term = TmS::list(dom_elems); + let cod_term = TmS::proj(self_var, synth_seg, synth_label); + row.insert( + lift_seg, + lift_label, + TyS::morphism(mt.clone(), dom_term, cod_term), + ); + added = true; + } + } } - // Synthetic morphism field `~f(e) : Morphism(mt, e, f(e))`, - // the lift morphism itself. The `~` prefix evokes the - // overline notation for lifts and disambiguates per source - // object so multiple declared `@over .X` fields can each - // get their own lift; modelgen strips the wrapper to - // recover the codomain morphism's name. - let lift_name = ustr(&format!("~{mor_name}({field_name})")); - let lift_seg = name_seg(lift_name); - let lift_label = label_seg(lift_name); - if !row.has(lift_seg) { - let self_var = TmS::var(BwdIdx::from(0), name_seg("self"), label_seg("self")); - let dom_term = TmS::proj(self_var.clone(), *field_name, *field_label); - let cod_term = TmS::proj(self_var, synth_seg, synth_label); - row.insert(lift_seg, lift_label, TyS::morphism(mt.clone(), dom_term, cod_term)); - added = true; + _ => { + // A discrete morphism's domain is a single object, so exactly one slot. + let [slot] = &dom_path[..] else { + continue; + }; + let Some(candidates) = fields_by_path.get(slot) else { + continue; + }; + for &(field_name, field_label) in candidates { + // Synthetic object field `f(e) : @over .Y`, the unique lift + // target forced by the discrete-opfibration condition. + let synth = ustr(&format!("{mor_name}({field_name})")); + let synth_seg = name_seg(synth); + let synth_label = label_seg(synth); + if !row.has(synth_seg) { + row.insert(synth_seg, synth_label, TyS::over(cod_path.clone())); + added = true; + } + // Synthetic morphism field `~f(e) : Morphism(mt, e, f(e))`. + let lift_name = ustr(&format!("~{mor_name}({field_name})")); + let lift_seg = name_seg(lift_name); + let lift_label = label_seg(lift_name); + if !row.has(lift_seg) { + let self_var = + TmS::var(BwdIdx::from(0), name_seg("self"), label_seg("self")); + let dom_term = TmS::proj(self_var.clone(), field_name, field_label); + let cod_term = TmS::proj(self_var, synth_seg, synth_label); + row.insert( + lift_seg, + lift_label, + TyS::morphism(mt.clone(), dom_term, cod_term), + ); + added = true; + } + } } } } @@ -958,6 +1031,16 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { TyS::record(row) } +/// Flatten a morphism domain/codomain term into the object paths it +/// references. A scalar chain yields one path; a `List` yields one per +/// element. `None` if any leaf isn't a projection chain. +fn term_to_paths(tm: &TmS) -> Option>> { + match &**tm { + TmS_::List(args) => args.iter().flat_map(|a| tms_to_path(a)).collect::>().into(), + _ => Some(vec![tms_to_path(tm)?]), + } +} + /// Read off a path of `(name, label)` segments from a term that is a chain /// of projections rooted in a variable. /// @@ -968,6 +1051,7 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { /// Returns `None` if the term has any other shape. fn tms_to_path(tm: &TmS) -> Option> { match &**tm { + // XXX It doesn't know what to do here when the incoming morphism is a multihom TmS_::Var(_, _, _) => Some(vec![]), TmS_::Proj(inner, name, label) => { let mut p = tms_to_path(inner)?; From 2cd41f58cc36f9df4d4e2b957fc5916c7dc215b2 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Wed, 3 Jun 2026 16:26:42 -0700 Subject: [PATCH 76/81] WIP: parsing equality --- Cargo.lock | 7 ++ packages/catlog/Cargo.toml | 2 +- packages/catlog/src/tt/batch.rs | 2 +- packages/catlog/src/tt/modelgen.rs | 145 ++++++++++++++++++---------- packages/catlog/src/tt/stx.rs | 8 +- packages/catlog/src/tt/text_elab.rs | 139 +++++++++++++++++++------- packages/catlog/src/tt/theory.rs | 5 +- packages/catlog/src/tt/toplevel.rs | 10 +- packages/catlog/src/tt/util/row.rs | 2 +- packages/catlog/src/tt/val.rs | 14 ++- 10 files changed, 232 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6913f607a..c5d67e1e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1010,6 +1010,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", + "unicode-xid", ] [[package]] @@ -5111,6 +5112,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unicode_categories" version = "0.1.1" diff --git a/packages/catlog/Cargo.toml b/packages/catlog/Cargo.toml index dc0f9219c..51086a3f6 100644 --- a/packages/catlog/Cargo.toml +++ b/packages/catlog/Cargo.toml @@ -16,7 +16,7 @@ stochastic = ["dep:rebop"] all-the-same = "1.1.0" bwd = "0.2.1" derivative = "2" -derive_more = { version = "2", features = ["constructor", "deref", "from", "into", "try_into"] } +derive_more = { version = "2", features = ["debug", "constructor", "deref", "from", "into", "try_into"] } duplicate = "2" egglog = { version = "2.0.0", default-features = false } ego-tree = "0.10" diff --git a/packages/catlog/src/tt/batch.rs b/packages/catlog/src/tt/batch.rs index 3296971cd..31c12444a 100644 --- a/packages/catlog/src/tt/batch.rs +++ b/packages/catlog/src/tt/batch.rs @@ -253,7 +253,7 @@ pub fn elaborate(src: &str, path: &str, output: &BatchOutput) -> io::Result { + Ok((model_diag, _, _)) => { output.diagram_summary(&model_diag); let (cod, _) = Model::from_ty( &toplevel, diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index e822236e7..20ea777c0 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -5,20 +5,23 @@ use derive_more::{From, TryInto}; use tattle::display::SourceInfo; use super::{eval::*, prelude::*, text_elab, theory::*, toplevel::*, val::*}; -use crate::dbl::{ - discrete::{self, DiscreteDblModelMapping}, - discrete_tabulator, - modal::{self, ModalDblModel, ModalDblModelMapping, ModalMorType, ModalOb, ModalObType}, - model::{DblModel, DblModelPrinter, DiscreteDblModel, MutDblModel}, - model_diagram::{DblModelDiagram, DblModelDiagramType}, - model_morphism::{DblModelMapping, MutDblModelMapping}, - theory::{DblTheory, DblTheoryKind, NonUnital, Unital}, -}; use crate::one::{ Category, FgCategory, QualifiedPath, path::{Path, PathEq}, }; use crate::zero::{Namespace, QualifiedName}; +use crate::{ + dbl::{ + discrete::{self, DiscreteDblModelMapping}, + discrete_tabulator, + modal::{self, ModalDblModel, ModalDblModelMapping, ModalMorType, ModalOb, ModalObType}, + model::{DblModel, DblModelPrinter, DiscreteDblModel, MutDblModel}, + model_diagram::{DblModelDiagram, DblModelDiagramType}, + model_morphism::MutDblModelMapping, + theory::{DblTheory, DblTheoryKind, NonUnital, Unital}, + }, + tt::stx::TmS, +}; /// A model generated by DoubleTT. /// @@ -418,7 +421,7 @@ pub fn diagram_from_diag( toplevel: &Toplevel, th: &TheoryDef, diag: &Diag, -) -> Result<(DblModelDiagramType, Namespace), String> { +) -> Result<(DblModelDiagramType, Namespace, Vec<(TmS, TmS)>), String> { match th { TheoryDef::Discrete(theory) => { let mut generator = DiagramGenerator { @@ -426,10 +429,15 @@ pub fn diagram_from_diag( codomain_ty: diag.model.clone(), domain: discrete::DiscreteDblModel::new(theory.clone()), mapping: discrete::DiscreteDblModelMapping::default(), + equations: vec![], }; let namespace = generator.generate(&diag.body_val)?; - let DiagramGenerator { domain, mapping, .. } = generator; - Ok((DblModelDiagramType::Discrete(DblModelDiagram(mapping, domain)), namespace)) + let DiagramGenerator { domain, mapping, equations, .. } = generator; + Ok(( + DblModelDiagramType::Discrete(DblModelDiagram(mapping, domain)), + namespace, + equations, + )) } TheoryDef::ModalUnital(theory) => { let mut generator = DiagramGenerator { @@ -437,10 +445,15 @@ pub fn diagram_from_diag( codomain_ty: diag.model.clone(), domain: modal::ModalDblModel::new(theory.clone()), mapping: modal::ModalDblModelMapping::default(), + equations: vec![], }; let namespace = generator.generate(&diag.body_val)?; - let DiagramGenerator { domain, mapping, .. } = generator; - Ok((DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)), namespace)) + let DiagramGenerator { domain, mapping, equations, .. } = generator; + Ok(( + DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)), + namespace, + equations, + )) } _ => Err("!".into()), } @@ -451,6 +464,7 @@ struct DiagramGenerator<'a, M: DblModel + MutDblModel + FgCategory, Mapping: Mut codomain_ty: TyV, domain: M, mapping: Mapping, + equations: Vec<(TmS, TmS)>, } impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { @@ -540,9 +554,14 @@ impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { } Ok(None) } - TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Id(_, _, _) | TyV_::Unit | TyV_::Meta(_) => { + TyV_::Id(_, lhs, rhs) => { + println!("LHS: {:#?}, RHS: {:#?}", lhs, rhs); + // XXX trying to print as syntax, not values + self.equations.push((self.eval.quote_tm(lhs), self.eval.quote_tm(rhs))); + // self.equations.push((lhs.clone(), rhs.clone())); Ok(None) } + TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Unit | TyV_::Meta(_) => Ok(None), } } } @@ -605,7 +624,7 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { // Augmentation produces lift morphisms whose dom and cod // are projections from the body's self, resolving to the // qualified names of generators (declared or specialized). - println!("CHECKING: {}", self.eval.quote_tm(&dom)); + // println!("CHECKING: {}", self.eval.quote_tm(&dom)); let mt_inner: ModalMorType = mt .clone() @@ -653,9 +672,13 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { } Ok(None) } - TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Id(_, _, _) | TyV_::Unit | TyV_::Meta(_) => { + TyV_::Id(_, lhs, rhs) => { + self.equations.push((self.eval.quote_tm(lhs), self.eval.quote_tm(rhs))); + // self.equations.push((lhs.clone(), rhs.clone())); Ok(None) } + + TyV_::Object(_) | TyV_::Sing(_, _) | TyV_::Unit | TyV_::Meta(_) => Ok(None), } } } @@ -665,9 +688,9 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { /// is bound at). Returns `None` if the value isn't of that shape. fn neutral_qualified_name(tm: &TmV) -> Option { if let TmV_::List(elems) = &**tm { - for e in elems { - println!("{:?}", neutral_qualified_name(e)); - } + // for e in elems { + // println!("{:?}", neutral_qualified_name(e)); + // } }; let TmV_::Neu(n, _) = &**tm else { return None; @@ -678,7 +701,7 @@ fn neutral_qualified_name(tm: &TmV) -> Option { fn neutral_qualified_names(tm: &TmV) -> Option> { if let TmV_::List(elems) = &**tm { for e in elems { - println!("{:?}", neutral_qualified_name(e)); + // println!("{:?}", neutral_qualified_name(e)); } }; let TmV_::List(elems) = &**tm else { @@ -717,14 +740,20 @@ mod tests { if let Some(TopElabResult::Declaration(name, decl)) = topelab.elab(&toplevel, topntn) { - dbg!(&name); + // dbg!(&name); toplevel.declarations.insert(name, decl); } else { - dbg!(&topntn.name); + // dbg!(&topntn.name); } } Some(()) }); + + if reporter.errored() { + let source_info = SourceInfo::new(None, src); + panic!("{}", source_info.extract_report_to_string(reporter.clone())); + } + assert!(!reporter.errored(), "elaboration produced errors"); toplevel } @@ -753,7 +782,8 @@ diagram I : @Instance(WeightedGraph) := [ _ => panic!("expected I to be a diagram declaration"), }; - let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + let (model_diag, _, _) = + diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); match model_diag { DblModelDiagramType::Discrete(DblModelDiagram(mapping, domain)) => { let e_qname: QualifiedName = vec![name_seg("e")].into(); @@ -798,7 +828,8 @@ diagram I : @Instance(DEC) := [ _ => panic!("expected I to be a diagram declaration"), }; - let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + let (model_diag, _, _) = + diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); match model_diag { DblModelDiagramType::Discrete(_) => { panic!("We should not be here!") @@ -836,6 +867,7 @@ diagram I : @Instance(DEC) := [ sub_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], mult_00 : Multihom[[Form0, Form0], Form0], mult_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + mult_0d0 : Multihom[[Form0, DualForm0], DualForm0], lie_1d0 : Multihom[[Form1, DualForm0], DualForm0], wedge_00: Multihom[[Form0, Form0], Form0], wedge_10: Multihom[[Form1, Form0], Form1] @@ -853,15 +885,15 @@ diagram I : @Instance(DEC) := [ x3 : @over .DualForm0, x4 : @over .DualForm0, x5 : @over .DualForm0, - x6 : @over .DualForm0, - _ : x0 == sub_d01([a, w]), - _ : x1 == square_d0([n]), - _ : x2 == mult_d0d0([w, x1]), - _ : x3 == sub_d01([x0, x2]), - _ : x4 == lie_1d0([dX, w]), - _ : x5 == mult_00([k, x4]), - _ : x6 == add_d0d0([x3, x5]), - _ : x6 == partial_d0([w]) + x6 : @over .DualForm0, + _1 : ( x0 == sub_d01([w,a]) ), + _2 : ( x1 == square_d0([n]) ), + _3 : ( x2 == mult_d0d0([w, x1]) ), + _4 : ( x3 == sub_d0d0([x0, x2]) ), + _5 : ( x4 == lie_1d0([dX, w]) ), + _6 : ( x5 == mult_0d0([k, x4]) ), + _7 : ( x6 == add_d0d0([x3, x5]) ), + _8 : ( x6 == partial_d0([w]) ) ] diagram Phytodynamics : @Instance(DEC) := [ @@ -875,22 +907,22 @@ diagram I : @Instance(DEC) := [ y4 : @over .DualForm0, y5 : @over .DualForm0, y6 : @over .DualForm0, - _ : y0 == square_d0([n]), - _ : y1 == mult_d0d0([w, x0]), - _ : y2 == mult_d0d0([m, n]), - _ : y3 == sub_d0d0([y1, y2]), - _ : y4 == lapl([n]), - _ : y5 == add_d0d0([y3, y4]), - _ : y6 == partial([w]), - _ : y6 == y5 + _1 : ( y0 == square_d0([n]) ), + _2 : ( y1 == mult_d0d0([w, y0]) ), + _3 : ( y2 == mult_0d0([m, n]) ), + _4 : ( y3 == sub_d0d0([y1, y2]) ), + _5 : ( y4 == lapl_d0([n]) ), + _6 : ( y5 == add_d0d0([y3, y4]) ), + _7 : ( y6 == partial_d0([w]) ), + _8 : ( y6 == y5 ) ] - diagram Klausmeier : @Instance(DEC) := [ - hydro : Hydrodynamics, - phyto : Phytodynamics, - _ : hydro.n == phyto.n, - _ : hydro.w == phyto.w - ] + diagram Klausmeier : @Instance(DEC) := [ + hydro : Hydrodynamics, + phyto : Phytodynamics, + _1 : ( hydro.n == phyto.n ), + _2 : ( hydro.w == phyto.w ) + ] "#; let toplevel = elaborate_to_toplevel(src); let diag = match toplevel.declarations.get(&name_seg("Klausmeier")) { @@ -898,14 +930,25 @@ diagram I : @Instance(DEC) := [ _ => panic!("expected I to be a diagram declaration"), }; - let (model_diag, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - // dbg!((&model_diag)); + let (model_diag, _, equations) = + diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + println!("------------->{:#?}", &equations.len()); + + let eval = Evaluator::empty(&toplevel); + for (i, (lhs, rhs)) in equations.iter().enumerate() { + println!("eq {}: {} == {}", i, lhs, rhs); + } + + // let (_, _, equations) = + // diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); + // println!("EQUATION COUNT: {}", equations.len()); + match model_diag { DblModelDiagramType::Discrete(_) => { panic!("We should not be here!") } DblModelDiagramType::ModalUnital(DblModelDiagram(mapping, domain)) => { - let e_qname: QualifiedName = vec![name_seg("u")].into(); + let e_qname: QualifiedName = vec![name_seg("a")].into(); let entity_ot = modal::ModeApp::new(name("Object")); let e_target: QualifiedName = vec![name_seg("Form0")].into(); assert!(domain.has_ob(&e_qname.clone().into())); diff --git a/packages/catlog/src/tt/stx.rs b/packages/catlog/src/tt/stx.rs index 5bb3dbdf8..fca884796 100644 --- a/packages/catlog/src/tt/stx.rs +++ b/packages/catlog/src/tt/stx.rs @@ -15,7 +15,7 @@ use crate::zero::LabelSegment; /// requested with `@hole`. /// /// Metavariables in notebook elaboration are namespaced to the notebook. -#[derive(Constructor, Clone, Copy, PartialEq, Eq)] +#[derive(Constructor, Debug, Clone, Copy, PartialEq, Eq)] pub struct MetaVar { ref_id: Option, id: usize, @@ -28,6 +28,7 @@ impl fmt::Display for MetaVar { } /// Inner enum for [TyS]. +#[derive(Debug)] pub enum TyS_ { /// A reference to a top-level declaration. TopVar(TopVarName), @@ -111,7 +112,7 @@ pub enum TyS_ { /// /// See [crate::tt] for an explanation of what total types are, and for an /// explanation of our approach to Rc pointers in abstract syntax trees. -#[derive(Clone, Deref)] +#[derive(Clone, Debug, Deref)] #[deref(forward)] pub struct TyS(Rc); @@ -212,6 +213,7 @@ impl fmt::Display for TyS { } /// Inner enum for [TmS]. +#[derive(Debug)] pub enum TmS_ { /// A reference to a top-level constant def. TopVar(TopVarName), @@ -250,7 +252,7 @@ pub enum TmS_ { /// /// See [crate::tt] for an explanation of what total types are, and for an /// explanation of our approach to Rc pointers in abstract syntax trees. -#[derive(Clone, Deref)] +#[derive(Clone, Debug, Deref)] #[deref(forward)] pub struct TmS(Rc); diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index 5b20adcb9..397de28a2 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -125,7 +125,7 @@ impl TopElaborator { ) })?; let (ty_s, ty_v) = self.elaborator(&theory, toplevel).ty(ty_n); - println!("TYPE: {}, \n\n {}", ty_s, ty_n); + // println!("TYPE: {}, \n\n {}", ty_s, ty_n); Some(TopElabResult::Declaration( name, TopDecl::Type(Type::new(theory.clone(), ty_s, ty_v)), @@ -135,14 +135,14 @@ impl TopElaborator { let theory = self.get_theory(tn.loc)?; let mut elab = self.elaborator(&theory, toplevel); let (name, args_n, annotn, valn) = self.annotated_def(tn.body).or_else(|| { - dbg!(&tn.body); + // dbg!(&tn.body); self.error( tn.loc, "unknown syntax for diagram declaration, expected : := ", ) })?; if args_n.is_some() { - dbg!(&tn.loc); + // dbg!(&tn.loc); return self.error(tn.loc, "diagrams cannot have arguments"); } let (_, ret_ty_v) = elab.ty(annotn); @@ -150,21 +150,21 @@ impl TopElaborator { // so that `@over .E` inside the body can recover both the // diagram's name and its codomain type, while ordinary // term lookups cannot see it. - dbg!(&name); + // dbg!(&name); let diag_label = match name { NameSegment::Text(s) => label_seg(s), NameSegment::Uuid(_) => label_seg("diag"), }; elab.intro_diagram(name, diag_label, ret_ty_v.clone()); - println!("VALN: {}", &valn); + // println!("VALN: {}", &valn); let (val_s, _val_v) = elab.ty(valn); // Augment the body with synthetic lift-target fields forced // by the discrete-opfibration condition: for each declared // `e : @over .X` and codomain morphism `f : X → Y`, add a // synthetic field `f(e) : @over .Y`. - println!("VALS: {}", &val_s); + // println!("VALS: {}", &val_s); let val_s = augment_with_lifts(&val_s, &ret_ty_v); - println!("AUGMENTED: {}", &val_s); + // println!("AUGMENTED: {}", &val_s); let val_v = elab.evaluator().eval_ty(&val_s); Some(TopElabResult::Declaration( name, @@ -472,7 +472,7 @@ impl<'a> Elaborator<'a> { if let Some(mor_type) = theory.basic_mor_type(qname.clone()) { let dom = theory.src_type(&mor_type); let cod = theory.tgt_type(&mor_type); - println!("MOR {} TYPE {} : DOM {} => COD {}", &qname, &mor_type, &dom, &cod); + // println!("MOR {} TYPE {} : DOM {} => COD {}", &qname, &mor_type, &dom, &cod); Some((mor_type, dom, cod)) } else { elab.error(format!("no such morphism type {name}")) @@ -533,7 +533,7 @@ impl<'a> Elaborator<'a> { /// Elaborates a type from notation, returning both syntax and value. pub fn ty(&mut self, n: &FNtn) -> (TyS, TyV) { let mut elab = self.enter(n.loc()); - println!("ELABORATING TYPE... {}", &n); + // println!("ELABORATING TYPE... {}", &n); match n.ast0() { Var(name) => elab.lookup_ty(name_seg(*name)), Keyword("Unit") => (TyS::unit(), TyV::unit()), @@ -641,14 +641,29 @@ impl<'a> Elaborator<'a> { (TyS::specialize(ty_s, specializations), ty_v) } App2(L(_, Keyword("==")), tm1_n, tm2_n) => { + // println!("!!!!!!!!! {} {}", tm1_n, tm2_n); let (tm1_s, tm1_v, tm1_ty) = elab.syn(tm1_n); let (tm2_s, tm2_v, tm2_ty) = elab.syn(tm2_n); - let TyV_::Morphism(_, _, _) = &*tm1_ty else { - elab.loc = Some(tm1_n.loc()); - return elab.ty_error("Equality types are only supported for morphisms"); + // println!("{:#?}", tm1_ty); + let _ = match &*tm1_ty { + TyV_::Morphism(_, _, _) | TyV_::Over(_) => { + // println!("YES!"); + } + _ => { + elab.loc = Some(tm1_n.loc()); + // println!("ERROR {} : {:#?}", &tm1_n, &tm1_ty); + return elab.ty_error("Equality types are only supported for morphisms"); + } }; + // println!("EQUALITY: {:#?} == {:#?}", &tm1_ty, &tm2_ty); if let Err(e) = elab.evaluator().convertible_ty(&tm1_ty, &tm2_ty) { let eval = elab.evaluator(); + // println!( + // "HELLO! {:#?} : {}, {}", + // &tm1_s, + // eval.quote_ty(&tm1_ty), + // eval.quote_ty(&tm2_ty) + // ); return elab.ty_error(format!( "types {} and {} are not convertible:\n{}", eval.quote_ty(&tm1_ty), @@ -661,7 +676,7 @@ impl<'a> Elaborator<'a> { (eq_ty_s, eq_ty_v) } _ => { - println!("UNEXPECTED NOTATION FOR TYPE"); + // println!("UNEXPECTED NOTATION FOR TYPE"); elab.ty_error("unexpected notation for type") } } @@ -693,10 +708,12 @@ impl<'a> Elaborator<'a> { /// Elaborates a term from notation, returning syntax, value, and synthesized type. fn syn(&mut self, n: &FNtn) -> (TmS, TmV, TyV) { let mut elab = self.enter(n.loc()); + // println!("AST: {:#?}", &n); match n.ast0() { Var(name) => elab.lookup_tm(ustr(name)), App1(tm_n, L(_, Field(f))) => { let (tm_s, tm_v, ty_v) = elab.syn(tm_n); + // println!("DEBUGGING: {:#?}, {:#?}, {:#?}", &tm_s, &tm_v, &ty_v); let TyV_::Record(r) = &*ty_v else { return elab.syn_error("can only project from record type"); }; @@ -705,6 +722,7 @@ impl<'a> Elaborator<'a> { if !r.fields.has(f) { return elab.syn_error(format!("no such field {f}")); } + // dbg!(&tm_s); ( TmS::proj(tm_s, f, label), elab.evaluator().proj(&tm_v, f, label), @@ -783,27 +801,82 @@ impl<'a> Elaborator<'a> { } App1(L(_, Var(tv)), L(_, Tuple(args_n))) => { let tv = name_seg(*tv); - let Some(TopDecl::Def(d)) = elab.toplevel.lookup(tv) else { - return elab.syn_error(format!("no such toplevel def {tv}")); - }; - let mut arg_stxs = Vec::new(); - let mut env = Env::nil(); - if args_n.len() != d.args.len() { - return elab.syn_error(format!( - "wrong number of args for {tv}, expected {}, got {}", - d.args.len(), - args_n.len() - )); - } - for (arg_n, (_, (_, arg_ty_s))) in args_n.iter().zip(d.args.iter()) { - let arg_ty_v = elab.evaluator().with_env(env.clone()).eval_ty(arg_ty_s); - let (arg_s, arg_v) = elab.chk(&arg_ty_v, arg_n); - arg_stxs.push(arg_s); - env = env.snoc(arg_v); + if let Some(TopDecl::Def(d)) = elab.toplevel.lookup(tv) { + // ---- existing toplevel-def path, unchanged ---- + let mut arg_stxs = Vec::new(); + let mut env = Env::nil(); + if args_n.len() != d.args.len() { + return elab.syn_error(format!( + "wrong number of args for {tv}, expected {}, got {}", + d.args.len(), + args_n.len() + )); + } + for (arg_n, (_, (_, arg_ty_s))) in args_n.iter().zip(d.args.iter()) { + let arg_ty_v = elab.evaluator().with_env(env.clone()).eval_ty(arg_ty_s); + let (arg_s, arg_v) = elab.chk(&arg_ty_v, arg_n); + arg_stxs.push(arg_s); + env = env.snoc(arg_v); + } + let eval = elab.evaluator().with_env(env.clone()); + (TmS::topapp(tv, arg_stxs), eval.eval_tm(&d.body), eval.eval_ty(&d.ret_ty)) + } else if let Some((_, codomain_ty)) = elab.ctx.lookup_diagram() + && let TyV_::Record(cod_r) = &*codomain_ty + && let Some((_, (_, mor_ty_s))) = cod_r.fields.iter().find(|(n, _)| **n == tv) + && let TyS_::Morphism(_mt, dom_s, cod_s) = &**mor_ty_s + && let Some(dom_paths) = term_to_paths(dom_s) + && let Some(cod_path) = tms_to_path(cod_s) + { + if args_n.len() != dom_paths.len() { + return elab.syn_error(format!( + "{tv}: expected {} args, got {}", + dom_paths.len(), + args_n.len() + )); + } + let mut arg_stxs = Vec::new(); + let mut arg_vals = Vec::new(); + for (arg_n, expected_path) in args_n.iter().zip(dom_paths.iter()) { + let (arg_s, arg_v) = elab.chk(&TyV::over(expected_path.clone()), arg_n); + arg_stxs.push(arg_s); + arg_vals.push(arg_v); + } + ( + TmS::ob_app(tv, TmS::list(arg_stxs)), + TmV::app(tv, TmV::list(arg_vals)), + TyV::over(cod_path), + ) + } else { + elab.syn_error(format!("no such toplevel def {tv}")) } - let eval = elab.evaluator().with_env(env.clone()); - (TmS::topapp(tv, arg_stxs), eval.eval_tm(&d.body), eval.eval_ty(&d.ret_ty)) } + + // App1(L(_, Var(tv)), L(_, Tuple(args_n))) => { + // let tv = name_seg(*tv); + // println!("======= {:#?}", elab.ctx.lookup_diagram().unwrap().1); + // // println!("====== NAME: {tv} {:#?}", elab.toplevel.declarations.keys()); + // let Some(TopDecl::Def(d)) = elab.toplevel.lookup(tv) else { + // return elab.syn_error(format!("no such toplevel def {tv}")); + // }; + // println!("TOPLEVEL: {:#?}", d); + // let mut arg_stxs = Vec::new(); + // let mut env = Env::nil(); + // if args_n.len() != d.args.len() { + // return elab.syn_error(format!( + // "wrong number of args for {tv}, expected {}, got {}", + // d.args.len(), + // args_n.len() + // )); + // } + // for (arg_n, (_, (_, arg_ty_s))) in args_n.iter().zip(d.args.iter()) { + // let arg_ty_v = elab.evaluator().with_env(env.clone()).eval_ty(arg_ty_s); + // let (arg_s, arg_v) = elab.chk(&arg_ty_v, arg_n); + // arg_stxs.push(arg_s); + // env = env.snoc(arg_v); + // } + // let eval = elab.evaluator().with_env(env.clone()); + // (TmS::topapp(tv, arg_stxs), eval.eval_tm(&d.body), eval.eval_ty(&d.ret_ty)) + // } Tag("tt") => (TmS::tt(), TmV::tt(), TyV::unit()), Tuple(_) => elab.syn_error("must check against a type in order to construct a record"), Prim("hole") => elab.syn_error("explicit hole"), @@ -942,7 +1015,7 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { for (mor_name, mt, dom_path, cod_path) in &cod_morphisms { match mt { MorType::Modal(_) => { - println!("MORTYPE: {}", &mt); + // println!("MORTYPE: {}", &mt); let Some(per_slot): Option>> = dom_path.iter().map(|slot| fields_by_path.get(slot)).collect() else { diff --git a/packages/catlog/src/tt/theory.rs b/packages/catlog/src/tt/theory.rs index eda40df80..c17a81431 100644 --- a/packages/catlog/src/tt/theory.rs +++ b/packages/catlog/src/tt/theory.rs @@ -9,7 +9,7 @@ use all_the_same::all_the_same; use derivative::Derivative; -use derive_more::{Constructor, From, TryInto}; +use derive_more::{Constructor, Debug, From, TryInto}; use std::{fmt, rc::Rc}; use super::prelude::*; @@ -26,13 +26,14 @@ use crate::zero::{QualifiedName, name}; /// /// Equality of these theories is nominal; two theories are the same if and only /// if they have the same name. -#[derive(Constructor, Clone, Derivative)] +#[derive(Constructor, Debug, Clone, Derivative)] #[derivative(PartialEq, Eq)] pub struct Theory { /// The name of the theory. pub name: QualifiedName, /// The definition of the theory. #[derivative(PartialEq = "ignore")] + #[debug(skip)] pub definition: TheoryDef, } diff --git a/packages/catlog/src/tt/toplevel.rs b/packages/catlog/src/tt/toplevel.rs index 03808a16f..b9fd7df41 100644 --- a/packages/catlog/src/tt/toplevel.rs +++ b/packages/catlog/src/tt/toplevel.rs @@ -9,7 +9,7 @@ use super::{prelude::*, stx::*, theory::*, val::*}; use crate::zero::QualifiedName; /// A toplevel declaration. -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum TopDecl { /// See [Type]. Type(Type), @@ -25,7 +25,7 @@ pub enum TopDecl { /// /// Also stores the evaluation of that type. Because this is an evaluation in /// the empty context, this is OK to use in any other context as well. -#[derive(Constructor, Clone)] +#[derive(Constructor, Debug, Clone)] pub struct Type { /// The theory for the type. pub theory: Theory, @@ -40,7 +40,7 @@ pub struct Type { /// Also stores the evaluation of that term, and the evaluation of the /// corresponding type of that term. Because this is an evaluation in the empty /// context, this is OK to use in any other context as well. -#[derive(Constructor, Clone)] +#[derive(Constructor, Debug, Clone)] pub struct DefConst { /// The theory that the constant is defined in. pub theory: Theory, @@ -53,7 +53,7 @@ pub struct DefConst { } /// A toplevel declaration of a term judgment. -#[derive(Constructor, Clone)] +#[derive(Constructor, Debug, Clone)] pub struct Def { /// The theory that the definition is defined in. pub theory: Theory, @@ -68,7 +68,7 @@ pub struct Def { } /// A toplevel declaration of a diagram. -#[derive(Constructor, Clone)] +#[derive(Constructor, Debug, Clone)] pub struct Diag { /// The theory that the diagram is defined in. pub theory: Theory, diff --git a/packages/catlog/src/tt/util/row.rs b/packages/catlog/src/tt/util/row.rs index baf0ada01..7d08d8ed0 100644 --- a/packages/catlog/src/tt/util/row.rs +++ b/packages/catlog/src/tt/util/row.rs @@ -15,7 +15,7 @@ use crate::{tt::prelude::*, zero::LabelSegment}; /// of a row in a database, which is a map from fields to values. /// /// Create this using the [FromIterator] implementation. -#[derive(Clone, Derivative, PartialEq, Eq, From)] +#[derive(Clone, Debug, Derivative, PartialEq, Eq, From)] #[derivative(Default(bound = ""))] pub struct Row(IndexMap); diff --git a/packages/catlog/src/tt/val.rs b/packages/catlog/src/tt/val.rs index 428289b43..35b8d63dd 100644 --- a/packages/catlog/src/tt/val.rs +++ b/packages/catlog/src/tt/val.rs @@ -3,6 +3,7 @@ //! See [crate::tt] for what this means. use bwd::Bwd; +use derive_more::Debug; use derive_more::Deref; use super::{prelude::*, stx::*, theory::*}; @@ -12,7 +13,7 @@ use crate::zero::{LabelSegment, QualifiedName}; pub type Env = Bwd; /// The content of a record type value. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RecordV { /// The closed-over environment. pub env: Env, @@ -22,6 +23,7 @@ pub struct RecordV { /// /// When we get to actually computing the type of fields, we will look here /// to see if they have been specialized. + #[debug(skip)] pub specializations: Dtry, } @@ -77,6 +79,7 @@ pub fn merge_specializations(old: &Dtry, new: &Dtry) -> Dtry { } /// Inner enum for [TyV]. +#[derive(Debug)] pub enum TyV_ { /// Type constructor for object types, also see [TyS_::Object]. Object(ObType), @@ -107,7 +110,7 @@ pub enum TyV_ { } /// Value for total types, dereferences to [TyV_]. -#[derive(Clone, Deref)] +#[derive(Clone, Debug, Deref)] #[deref(forward)] pub struct TyV(Rc); @@ -191,7 +194,7 @@ impl TyV { } /// Inner enum for [TmN]. -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Debug, Eq)] pub enum TmN_ { /// Variable. Var(FwdIdx, VarName, LabelSegment), @@ -200,7 +203,7 @@ pub enum TmN_ { } /// Neutrals for [terms](TmV), dereferences to [TmN_]. -#[derive(Clone, Deref, PartialEq, Eq)] +#[derive(Clone, Debug, Deref, PartialEq, Eq)] #[deref(forward)] pub struct TmN(Rc); @@ -229,6 +232,7 @@ impl TmN { } /// Inner enum for [TmV]. +#[derive(Debug)] pub enum TmV_ { /// Neutrals. /// @@ -253,7 +257,7 @@ pub enum TmV_ { } /// Values for terms, dereferences to [TmV_]. -#[derive(Clone, Deref)] +#[derive(Clone, Debug, Deref)] #[deref(forward)] pub struct TmV(Rc); From b18b8386eff95f770789ccc248d0c3d1c6c18088 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Thu, 4 Jun 2026 10:02:43 -0700 Subject: [PATCH 77/81] WIP: transpiler. might need to do two passes for subbing --- Cargo.lock | 13 +- packages/catlog/Cargo.toml | 1 + .../examples/tt/text/test_klausmeier.dbltt | 72 ++++++ packages/catlog/src/tt/modelgen.rs | 222 +++++++++++------- packages/catlog/src/tt/text_elab.rs | 60 +++-- packages/catlog/src/tt/toplevel.rs | 2 + 6 files changed, 263 insertions(+), 107 deletions(-) create mode 100644 packages/catlog/examples/tt/text/test_klausmeier.dbltt diff --git a/Cargo.lock b/Cargo.lock index c5d67e1e5..4836bed21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.14", "serde", ] @@ -553,6 +553,7 @@ dependencies = [ "pretty", "rebop", "ref-cast", + "regex", "scopeguard", "sea-query", "serde", @@ -3416,13 +3417,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", + "regex-automata 0.4.14", "regex-syntax 0.8.5", ] @@ -3437,9 +3438,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", diff --git a/packages/catlog/Cargo.toml b/packages/catlog/Cargo.toml index 51086a3f6..66ea42bcb 100644 --- a/packages/catlog/Cargo.toml +++ b/packages/catlog/Cargo.toml @@ -41,6 +41,7 @@ wasm-bindgen = { version = "0.2.100", optional = true } catcolab-document-types = { version = "0.1.0", path = "../document-types" } sea-query = { version = "0.32.7", optional = true } sqlformat = { version = "0.5.0", optional = true } +regex = "1.12.3" [dev-dependencies] clap = { version = "4.5.47", features = ["derive"] } diff --git a/packages/catlog/examples/tt/text/test_klausmeier.dbltt b/packages/catlog/examples/tt/text/test_klausmeier.dbltt new file mode 100644 index 000000000..fc39b0944 --- /dev/null +++ b/packages/catlog/examples/tt/text/test_klausmeier.dbltt @@ -0,0 +1,72 @@ +set_theory ThMulticategory + +type DEC := [ + Form0 : Object, + Form1 : Object, + DualForm0 : Object, + lapl_d0 : Multihom[[DualForm0], DualForm0], + partial_0 : Multihom[[Form0], Form0], + partial_1 : Multihom[[Form1], Form1], + partial_d0 : Multihom[[DualForm0], DualForm0], + square_d0 : Multihom[[DualForm0], DualForm0], + add_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + sub_d01 : Multihom[[DualForm0, Form0], DualForm0], + sub_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + mult_00 : Multihom[[Form0, Form0], Form0], + mult_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], + mult_0d0 : Multihom[[Form0, DualForm0], DualForm0], + lie_1d0 : Multihom[[Form1, DualForm0], DualForm0], + wedge_00: Multihom[[Form0, Form0], Form0], + wedge_10: Multihom[[Form1, Form0], Form1] +] + +diagram Hydrodynamics : @Instance(DEC) := [ + a : @over .Form0, + k : @over .Form0, + n : @over .DualForm0, + w : @over .DualForm0, + dX : @over .Form1, + x0 : @over .DualForm0, + x1 : @over .DualForm0, + x2 : @over .DualForm0, + x3 : @over .DualForm0, + x4 : @over .DualForm0, + x5 : @over .DualForm0, + x6 : @over .DualForm0, + _1 : ( x0 == sub_d01([w,a]) ), + _2 : ( x1 == square_d0([n]) ), + _3 : ( x2 == mult_d0d0([w, x1]) ), + _4 : ( x3 == sub_d0d0([x0, x2]) ), + _5 : ( x4 == lie_1d0([dX, w]) ), + _6 : ( x5 == mult_0d0([k, x4]) ), + _7 : ( x6 == add_d0d0([x3, x5]) ), + _8 : ( x6 == partial_d0([w]) ) +] + +diagram Phytodynamics : @Instance(DEC) := [ + n : @over .DualForm0, + w : @over .DualForm0, + m : @over .Form0, + y0 : @over .DualForm0, + y1 : @over .DualForm0, + y2 : @over .DualForm0, + y3 : @over .DualForm0, + y4 : @over .DualForm0, + y5 : @over .DualForm0, + y6 : @over .DualForm0, + _1 : ( y0 == square_d0([n]) ), + _2 : ( y1 == mult_d0d0([w, y0]) ), + _3 : ( y2 == mult_0d0([m, n]) ), + _4 : ( y3 == sub_d0d0([y1, y2]) ), + _5 : ( y4 == lapl_d0([n]) ), + _6 : ( y5 == add_d0d0([y3, y4]) ), + _7 : ( y6 == partial_d0([w]) ), + _8 : ( y6 == y5 ) +] + +diagram Klausmeier : @Instance(DEC) := [ + hydro : Hydrodynamics, + phyto : Phytodynamics, + _1 : ( hydro.n == phyto.n ), + _2 : ( hydro.w == phyto.w ) +] diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 20ea777c0..af2848701 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -555,7 +555,7 @@ impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { Ok(None) } TyV_::Id(_, lhs, rhs) => { - println!("LHS: {:#?}, RHS: {:#?}", lhs, rhs); + // println!("LHS: {:#?}, RHS: {:#?}", lhs, rhs); // XXX trying to print as syntax, not values self.equations.push((self.eval.quote_tm(lhs), self.eval.quote_tm(rhs))); // self.equations.push((lhs.clone(), rhs.clone())); @@ -725,8 +725,14 @@ fn parse_lift_morphism_name(name: &NameSegment) -> Option { #[cfg(test)] mod tests { + use regex::Regex; + use std::fs::read_to_string; + use std::path::PathBuf; + use std::sync::LazyLock; + use super::*; use crate::dbl::model::FpDblModel; + use crate::tt::stx::TmS_; use crate::tt::text_elab::{TT_PARSE_CONFIG, TopElabResult, TopElaborator}; use crate::tt::theory::std_theories; use crate::zero::{Mapping, name}; @@ -848,100 +854,142 @@ diagram I : @Instance(DEC) := [ } } + struct JuliaTranspiler {} + + impl JuliaTranspiler { + fn transpile(src: &str, decl_name: &str) -> String { + let toplevel = elaborate_to_toplevel(&src); + let diag = match toplevel.declarations.get(&name_seg(decl_name)) { + Some(TopDecl::Diag(d)) => d.clone(), + _ => panic!("expected {decl_name} to be a diagram declaration"), + }; + + let Ok((_, _, equations)) = + diagram_from_diag(&toplevel, &diag.theory.definition, &diag) + else { + panic!("Error with destructuring diagram") + }; + + let mut out: String = Default::default(); + + let mut tms: HashMap = Default::default(); + // unhappy + let re = regex::Regex::new(r"(.+)_(\w+)([0-9]+)$").unwrap(); + for ob in diag.over_decls { + let tm = ob.0.into_iter().map(|n| n.to_string()).collect::>().join("_"); + let ty = ob.1.1.into_iter().map(|n| n.to_string()).collect::>().join("_"); + // remove anonymous variables + if !re.is_match(&tm) { + tms.insert(tm, ty); + // out.push_str(&format!("\t{tm}::{typ}")); + // out.push_str("\n"); + } + } + + static ADD: LazyLock = LazyLock::new(|| Regex::new(r"add_(.+)$").unwrap()); + static SUBTRACT: LazyLock = LazyLock::new(|| Regex::new(r"sub_(.+)$").unwrap()); + static MULT: LazyLock = LazyLock::new(|| Regex::new(r"mult_(.+)$").unwrap()); + static PARTIAL: LazyLock = + LazyLock::new(|| Regex::new(r"partial_(.+)$").unwrap()); + static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"lapl_(.+)$").unwrap()); + + fn to_plain(tm: &TmS) -> String { + match &**tm { + TmS_::Var(_, name, _) => { + let s = name.to_string(); + if s == "self" { String::new() } else { s } + } + TmS_::Proj(inner, name, _) => { + let prefix = to_plain(inner); + let n = name.to_string(); + if prefix.is_empty() { + n + } else { + format!("{prefix}_{n}") + } + } + TmS_::ObApp(op, args) => { + let op = &format!("{op}"); + let op = match op { + _ if ADD.is_match(op) => "+", + _ if SUBTRACT.is_match(op) => "-", + _ if MULT.is_match(op) => "*", + _ if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), + _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), + _ => op, + }; + format!("{op}({})", splat_plain(args)) + } + _ => format!("{}", tm), + } + } + + fn splat_plain(tm: &TmS) -> String { + match &**tm { + TmS_::List(args) => { + args.iter().map(|a| to_plain(a)).collect::>().join(", ") + } + _ => to_plain(tm), + } + } + + let mut eqs: HashMap = HashMap::new(); + let mut substitute: HashMap = HashMap::new(); + for (lhs, rhs) in &equations { + let lhs = to_plain(lhs); + let rhs = to_plain(rhs); + if tms.get(&lhs).is_some() && tms.get(&rhs).is_some() { + substitute.insert(lhs.clone(), rhs.clone()); + } else { + } + eqs.insert(lhs, rhs); + } + + // if there would exists an equality between two declared ojects, e.g., + // ``` + // a::Form0 + // b::Form0 + // a == b + // ``` + // then instead treat the LHS object as an anonymous variable, e.g., + // ``` + // b::Form0 + // a == b + // ``` + for (tm, ty) in tms.iter() { + // treat the LHS term as an anonymous variable + if substitute.get(&tm.clone()).is_none() { + out.push_str(&format!("\t{}::{}\n", tm, ty)) + } + } + + for (lhs, rhs) in eqs.iter() { + out.push_str(&format!("\n\t{} == {}", lhs, rhs)); + } + + // interpolate the diagram into a template expected by the program in the target language, e.g., + // insert `out` into a Decapode macro call. + format!("@decapode begin\n{out}\nend") + } + } + #[test] fn glueing_modal_diagrams() { - let src = r#" - set_theory ThMulticategory - - type DEC := [ - Form0 : Object, - Form1 : Object, - DualForm0 : Object, - lapl_d0 : Multihom[[DualForm0], DualForm0], - partial_0 : Multihom[[Form0], Form0], - partial_1 : Multihom[[Form1], Form1], - partial_d0 : Multihom[[DualForm0], DualForm0], - square_d0 : Multihom[[DualForm0], DualForm0], - add_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], - sub_d01 : Multihom[[DualForm0, Form0], DualForm0], - sub_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], - mult_00 : Multihom[[Form0, Form0], Form0], - mult_d0d0 : Multihom[[DualForm0, DualForm0], DualForm0], - mult_0d0 : Multihom[[Form0, DualForm0], DualForm0], - lie_1d0 : Multihom[[Form1, DualForm0], DualForm0], - wedge_00: Multihom[[Form0, Form0], Form0], - wedge_10: Multihom[[Form1, Form0], Form1] - ] - - diagram Hydrodynamics : @Instance(DEC) := [ - a : @over .Form0, - k : @over .Form0, - n : @over .DualForm0, - w : @over .DualForm0, - dX : @over .Form1, - x0 : @over .DualForm0, - x1 : @over .DualForm0, - x2 : @over .DualForm0, - x3 : @over .DualForm0, - x4 : @over .DualForm0, - x5 : @over .DualForm0, - x6 : @over .DualForm0, - _1 : ( x0 == sub_d01([w,a]) ), - _2 : ( x1 == square_d0([n]) ), - _3 : ( x2 == mult_d0d0([w, x1]) ), - _4 : ( x3 == sub_d0d0([x0, x2]) ), - _5 : ( x4 == lie_1d0([dX, w]) ), - _6 : ( x5 == mult_0d0([k, x4]) ), - _7 : ( x6 == add_d0d0([x3, x5]) ), - _8 : ( x6 == partial_d0([w]) ) - ] - - diagram Phytodynamics : @Instance(DEC) := [ - n : @over .DualForm0, - w : @over .DualForm0, - m : @over .Form0, - y0 : @over .DualForm0, - y1 : @over .DualForm0, - y2 : @over .DualForm0, - y3 : @over .DualForm0, - y4 : @over .DualForm0, - y5 : @over .DualForm0, - y6 : @over .DualForm0, - _1 : ( y0 == square_d0([n]) ), - _2 : ( y1 == mult_d0d0([w, y0]) ), - _3 : ( y2 == mult_0d0([m, n]) ), - _4 : ( y3 == sub_d0d0([y1, y2]) ), - _5 : ( y4 == lapl_d0([n]) ), - _6 : ( y5 == add_d0d0([y3, y4]) ), - _7 : ( y6 == partial_d0([w]) ), - _8 : ( y6 == y5 ) - ] - - diagram Klausmeier : @Instance(DEC) := [ - hydro : Hydrodynamics, - phyto : Phytodynamics, - _1 : ( hydro.n == phyto.n ), - _2 : ( hydro.w == phyto.w ) - ] - "#; - let toplevel = elaborate_to_toplevel(src); + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("examples/tt/text/test_klausmeier.dbltt"); + let Ok(src) = read_to_string(path) else { + panic!("Cannot read file") + }; + let toplevel = elaborate_to_toplevel(&src); let diag = match toplevel.declarations.get(&name_seg("Klausmeier")) { Some(TopDecl::Diag(d)) => d.clone(), _ => panic!("expected I to be a diagram declaration"), }; - let (model_diag, _, equations) = + let (model_diag, _, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - println!("------------->{:#?}", &equations.len()); - - let eval = Evaluator::empty(&toplevel); - for (i, (lhs, rhs)) in equations.iter().enumerate() { - println!("eq {}: {} == {}", i, lhs, rhs); - } - - // let (_, _, equations) = - // diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - // println!("EQUATION COUNT: {}", equations.len()); + let out = JuliaTranspiler::transpile(&src, "Klausmeier"); + println!("Klausmeier:\n{out}"); match model_diag { DblModelDiagramType::Discrete(_) => { diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index 397de28a2..b094f39eb 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -135,14 +135,12 @@ impl TopElaborator { let theory = self.get_theory(tn.loc)?; let mut elab = self.elaborator(&theory, toplevel); let (name, args_n, annotn, valn) = self.annotated_def(tn.body).or_else(|| { - // dbg!(&tn.body); self.error( tn.loc, "unknown syntax for diagram declaration, expected : := ", ) })?; if args_n.is_some() { - // dbg!(&tn.loc); return self.error(tn.loc, "diagrams cannot have arguments"); } let (_, ret_ty_v) = elab.ty(annotn); @@ -150,25 +148,46 @@ impl TopElaborator { // so that `@over .E` inside the body can recover both the // diagram's name and its codomain type, while ordinary // term lookups cannot see it. - // dbg!(&name); let diag_label = match name { NameSegment::Text(s) => label_seg(s), NameSegment::Uuid(_) => label_seg("diag"), }; elab.intro_diagram(name, diag_label, ret_ty_v.clone()); - // println!("VALN: {}", &valn); + + let mut sub_diag_fields = Vec::new(); + if let Tuple(field_ns) = valn.ast0() { + for field_n in field_ns.iter() { + if let App2(L(_, Keyword(":")), L(_, Var(fname)), L(_, Var(tname))) = + field_n.ast0() + { + let tname = name_seg(*tname); + if let Some(TopDecl::Diag(_)) = elab.toplevel.lookup(tname) { + sub_diag_fields.push((name_seg(*fname), tname)); + } + } + } + } + let (val_s, _val_v) = elab.ty(valn); // Augment the body with synthetic lift-target fields forced // by the discrete-opfibration condition: for each declared // `e : @over .X` and codomain morphism `f : X → Y`, add a // synthetic field `f(e) : @over .Y`. - // println!("VALS: {}", &val_s); - let val_s = augment_with_lifts(&val_s, &ret_ty_v); - // println!("AUGMENTED: {}", &val_s); + let (val_s, mut over_decls) = augment_with_lifts(&val_s, &ret_ty_v); + + for (field_name, diag_name) in &sub_diag_fields { + if let Some(TopDecl::Diag(sub)) = elab.toplevel.lookup(*diag_name) { + for (sub_path, info) in &sub.over_decls { + let mut full = vec![*field_name]; + full.extend(sub_path.iter().copied()); + over_decls.push((full, info.clone())); + } + } + } let val_v = elab.evaluator().eval_ty(&val_s); Some(TopElabResult::Declaration( name, - TopDecl::Diag(Diag::new(theory.clone(), ret_ty_v, val_s, val_v)), + TopDecl::Diag(Diag::new(theory.clone(), ret_ty_v, val_s, val_v, over_decls)), )) } "def" => { @@ -973,13 +992,16 @@ impl<'a> Elaborator<'a> { /// path-walking and specialization (e.g., `we : I & [.src(e) := v1]`) /// can resolve `.src(e)` as an ordinary field. Modelgen filters them /// out by name when extracting the domain model. -fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { +fn augment_with_lifts( + body_s: &TyS, + codomain: &TyV, +) -> (TyS, Vec<(Vec, (LabelSegment, Vec))>) { const MAX_DEPTH: usize = 16; let TyS_::Record(initial) = &**body_s else { - return body_s.clone(); + return (body_s.clone(), vec![]); }; let TyV_::Record(cod_r) = &**codomain else { - return body_s.clone(); + return (body_s.clone(), vec![]); }; // Collect (mor_name, mt, dom_path, cod_path) for each codomain @@ -998,6 +1020,17 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { } } + let over_declarations = initial + .iter() + .filter_map(|(fname, (flabel, fty))| match &**fty { + TyS_::Over(path) => { + let names: Vec<_> = path.iter().map(|(n, _)| *n).collect(); + Some((vec![fname.clone()], (flabel.clone(), names))) + } + _ => None, + }) + .collect::>(); + let mut row = initial.clone(); for _ in 0..1 { let snapshot = row.clone(); @@ -1015,7 +1048,6 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { for (mor_name, mt, dom_path, cod_path) in &cod_morphisms { match mt { MorType::Modal(_) => { - // println!("MORTYPE: {}", &mt); let Some(per_slot): Option>> = dom_path.iter().map(|slot| fields_by_path.get(slot)).collect() else { @@ -1096,12 +1128,12 @@ fn augment_with_lifts(body_s: &TyS, codomain: &TyV) -> TyS { } } if !added { - return TyS::record(row); + return (TyS::record(row), vec![]); } } // Hit the depth bound — return what we have. Cyclic theories will // produce an under-augmented body; this is a known limitation. - TyS::record(row) + (TyS::record(row), over_declarations) } /// Flatten a morphism domain/codomain term into the object paths it diff --git a/packages/catlog/src/tt/toplevel.rs b/packages/catlog/src/tt/toplevel.rs index b9fd7df41..d34bfd72b 100644 --- a/packages/catlog/src/tt/toplevel.rs +++ b/packages/catlog/src/tt/toplevel.rs @@ -79,6 +79,8 @@ pub struct Diag { pub body_stx: TyS, /// Evaluated body — useful for downstream consumers that want the TyV directly. pub body_val: TyV, + /// Collection of @over declarations and their types. + pub over_decls: Vec<(Vec, (LabelSegment, Vec))>, } impl TopDecl { /// Unwraps the type for a toplevel-declaration of a type, or panics. From 47b8d6b6f8c989defda22d7965c7a5417d3a7190 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Thu, 4 Jun 2026 17:19:53 -0700 Subject: [PATCH 78/81] WIP: moving transpiler to new file --- packages/catlog/src/tt/modelgen.rs | 127 +--------------------- packages/catlog/src/tt/util/mod.rs | 2 + packages/catlog/src/tt/util/transpiler.rs | 127 ++++++++++++++++++++++ 3 files changed, 132 insertions(+), 124 deletions(-) create mode 100644 packages/catlog/src/tt/util/transpiler.rs diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index af2848701..88f2c1d0a 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -725,16 +725,14 @@ fn parse_lift_morphism_name(name: &NameSegment) -> Option { #[cfg(test)] mod tests { - use regex::Regex; use std::fs::read_to_string; use std::path::PathBuf; - use std::sync::LazyLock; use super::*; use crate::dbl::model::FpDblModel; - use crate::tt::stx::TmS_; use crate::tt::text_elab::{TT_PARSE_CONFIG, TopElabResult, TopElaborator}; use crate::tt::theory::std_theories; + use crate::tt::toplevel::Toplevel; use crate::zero::{Mapping, name}; fn elaborate_to_toplevel(src: &str) -> Toplevel { @@ -854,125 +852,6 @@ diagram I : @Instance(DEC) := [ } } - struct JuliaTranspiler {} - - impl JuliaTranspiler { - fn transpile(src: &str, decl_name: &str) -> String { - let toplevel = elaborate_to_toplevel(&src); - let diag = match toplevel.declarations.get(&name_seg(decl_name)) { - Some(TopDecl::Diag(d)) => d.clone(), - _ => panic!("expected {decl_name} to be a diagram declaration"), - }; - - let Ok((_, _, equations)) = - diagram_from_diag(&toplevel, &diag.theory.definition, &diag) - else { - panic!("Error with destructuring diagram") - }; - - let mut out: String = Default::default(); - - let mut tms: HashMap = Default::default(); - // unhappy - let re = regex::Regex::new(r"(.+)_(\w+)([0-9]+)$").unwrap(); - for ob in diag.over_decls { - let tm = ob.0.into_iter().map(|n| n.to_string()).collect::>().join("_"); - let ty = ob.1.1.into_iter().map(|n| n.to_string()).collect::>().join("_"); - // remove anonymous variables - if !re.is_match(&tm) { - tms.insert(tm, ty); - // out.push_str(&format!("\t{tm}::{typ}")); - // out.push_str("\n"); - } - } - - static ADD: LazyLock = LazyLock::new(|| Regex::new(r"add_(.+)$").unwrap()); - static SUBTRACT: LazyLock = LazyLock::new(|| Regex::new(r"sub_(.+)$").unwrap()); - static MULT: LazyLock = LazyLock::new(|| Regex::new(r"mult_(.+)$").unwrap()); - static PARTIAL: LazyLock = - LazyLock::new(|| Regex::new(r"partial_(.+)$").unwrap()); - static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"lapl_(.+)$").unwrap()); - - fn to_plain(tm: &TmS) -> String { - match &**tm { - TmS_::Var(_, name, _) => { - let s = name.to_string(); - if s == "self" { String::new() } else { s } - } - TmS_::Proj(inner, name, _) => { - let prefix = to_plain(inner); - let n = name.to_string(); - if prefix.is_empty() { - n - } else { - format!("{prefix}_{n}") - } - } - TmS_::ObApp(op, args) => { - let op = &format!("{op}"); - let op = match op { - _ if ADD.is_match(op) => "+", - _ if SUBTRACT.is_match(op) => "-", - _ if MULT.is_match(op) => "*", - _ if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), - _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), - _ => op, - }; - format!("{op}({})", splat_plain(args)) - } - _ => format!("{}", tm), - } - } - - fn splat_plain(tm: &TmS) -> String { - match &**tm { - TmS_::List(args) => { - args.iter().map(|a| to_plain(a)).collect::>().join(", ") - } - _ => to_plain(tm), - } - } - - let mut eqs: HashMap = HashMap::new(); - let mut substitute: HashMap = HashMap::new(); - for (lhs, rhs) in &equations { - let lhs = to_plain(lhs); - let rhs = to_plain(rhs); - if tms.get(&lhs).is_some() && tms.get(&rhs).is_some() { - substitute.insert(lhs.clone(), rhs.clone()); - } else { - } - eqs.insert(lhs, rhs); - } - - // if there would exists an equality between two declared ojects, e.g., - // ``` - // a::Form0 - // b::Form0 - // a == b - // ``` - // then instead treat the LHS object as an anonymous variable, e.g., - // ``` - // b::Form0 - // a == b - // ``` - for (tm, ty) in tms.iter() { - // treat the LHS term as an anonymous variable - if substitute.get(&tm.clone()).is_none() { - out.push_str(&format!("\t{}::{}\n", tm, ty)) - } - } - - for (lhs, rhs) in eqs.iter() { - out.push_str(&format!("\n\t{} == {}", lhs, rhs)); - } - - // interpolate the diagram into a template expected by the program in the target language, e.g., - // insert `out` into a Decapode macro call. - format!("@decapode begin\n{out}\nend") - } - } - #[test] fn glueing_modal_diagrams() { let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -988,8 +867,8 @@ diagram I : @Instance(DEC) := [ let (model_diag, _, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - let out = JuliaTranspiler::transpile(&src, "Klausmeier"); - println!("Klausmeier:\n{out}"); + let out = JuliaTranspiler::transpile(&src, "Klausmeier", elaborate_to_toplevel); + println!("{out}"); match model_diag { DblModelDiagramType::Discrete(_) => { diff --git a/packages/catlog/src/tt/util/mod.rs b/packages/catlog/src/tt/util/mod.rs index 4ada5c21e..6074dcd01 100644 --- a/packages/catlog/src/tt/util/mod.rs +++ b/packages/catlog/src/tt/util/mod.rs @@ -6,8 +6,10 @@ pub mod dtry; pub mod idx; pub mod pretty; pub mod row; +pub mod transpiler; pub use dtry::*; pub use idx::*; pub use pretty::*; pub use row::*; +pub use transpiler::*; diff --git a/packages/catlog/src/tt/util/transpiler.rs b/packages/catlog/src/tt/util/transpiler.rs new file mode 100644 index 000000000..6447d836f --- /dev/null +++ b/packages/catlog/src/tt/util/transpiler.rs @@ -0,0 +1,127 @@ +use regex::Regex; +use std::collections::HashMap; +use std::sync::LazyLock; + +use crate::tt::modelgen::diagram_from_diag; +use crate::tt::stx::{TmS, TmS_}; +use crate::tt::toplevel::{TopDecl, Toplevel}; +use crate::zero::name_seg; + +/// Enables transpiling a .dbltt file to a valid Decapode +pub struct JuliaTranspiler {} + +impl JuliaTranspiler { + /// Transpiles a .dbltt file, a valid toplevel declaration in it into a valid Julia expression. In this case, we're fixed to Decapodes.jl + pub fn transpile(src: &str, decl_name: &str, elab: impl Fn(&str) -> Toplevel) -> String { + let toplevel = elab(&src); + let diag = match toplevel.declarations.get(&name_seg(decl_name)) { + Some(TopDecl::Diag(d)) => d.clone(), + _ => panic!("expected {decl_name} to be a diagram declaration"), + }; + + let Ok((_, _, equations)) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag) + else { + panic!("Error with destructuring diagram") + }; + + // the transpilation result. + let mut out: String = Default::default(); + + let mut tms: HashMap = Default::default(); + // unhappy + let re = regex::Regex::new(r"(.+)_(\w+)([0-9]+)$").unwrap(); + for ob in diag.over_decls { + // I dislike indexing into the tuple's position when i could be indexing into the field of a struct, which would be more informative. + let tm = ob.0.into_iter().map(|n| n.to_string()).collect::>().join("_"); + let ty = ob.1.1.into_iter().map(|n| n.to_string()).collect::>().join("_"); + // remove anonymous variables, e.g., those whose name is specified by the match expression above + if !re.is_match(&tm) { + tms.insert(tm, ty); + } + } + + let mut eqs: HashMap = HashMap::new(); + let mut substitute: HashMap = HashMap::new(); + for (lhs, rhs) in &equations { + let lhs = to_plain(lhs); + let rhs = to_plain(rhs); + if tms.get(&lhs).is_some() && tms.get(&rhs).is_some() { + substitute.insert(lhs.clone(), rhs.clone()); + } else { + } + eqs.insert(lhs, rhs); + } + + // if there would exists an equality between two declared ojects, e.g., + // ``` + // a::Form0 + // b::Form0 + // a == b + // ``` + // then instead treat the LHS object as an anonymous variable, e.g., + // ``` + // b::Form0 + // a == b + // ``` + for (tm, ty) in tms.iter() { + // if there isn't a substitution (e.g., `a == b`), then add the object as a declaration. + if substitute.get(&tm.clone()).is_none() { + out.push_str(&format!("\t{}::{}\n", tm, ty)) + } + } + + for (lhs, rhs) in eqs.iter() { + out.push_str(&format!("\n\t{} == {}", lhs, rhs)); + } + + // interpolate the diagram into a template expected by the program in the target language, e.g., + // insert `out` into a Decapode macro call. + format!("@decapode begin\n{out}\nend") + } +} + +static ADD: LazyLock = LazyLock::new(|| Regex::new(r"add_(.+)$").unwrap()); +static SUBTRACT: LazyLock = LazyLock::new(|| Regex::new(r"sub_(.+)$").unwrap()); +static MULT: LazyLock = LazyLock::new(|| Regex::new(r"mult_(.+)$").unwrap()); +static PARTIAL: LazyLock = LazyLock::new(|| Regex::new(r"partial_(.+)$").unwrap()); +static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"lapl_(.+)$").unwrap()); + +// this should be a single string method +fn to_plain(tm: &TmS) -> String { + match &**tm { + TmS_::Var(_, name, _) => { + let s = name.to_string(); + if s == "self" { String::new() } else { s } + } + TmS_::Proj(inner, name, _) => { + let prefix = to_plain(inner); + let n = name.to_string(); + if prefix.is_empty() { + n + } else { + format!("{prefix}_{n}") + } + } + TmS_::ObApp(op, args) => { + let op = &format!("{op}"); + let op = match op { + _ if ADD.is_match(op) => "+", + _ if SUBTRACT.is_match(op) => "-", + _ if MULT.is_match(op) => "*", + _ if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), + _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), + _ => op, + }; + format!("{op}({})", splat_plain(args)) + } + _ => format!("{}", tm), + } +} + +// merge into `to_plain` function above +fn splat_plain(tm: &TmS) -> String { + match &**tm { + TmS_::List(args) => args.iter().map(|a| to_plain(a)).collect::>().join(", "), + _ => to_plain(tm), + } +} From 8e7da8284f2f4aee7c01f1b2de6c4dcd6e1814b6 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Mon, 8 Jun 2026 08:42:36 -0700 Subject: [PATCH 79/81] CLEANUP --- packages/catlog/src/tt/modelgen.rs | 6 +-- packages/catlog/src/tt/text_elab.rs | 51 ++--------------------- packages/catlog/src/tt/util/transpiler.rs | 12 +----- 3 files changed, 7 insertions(+), 62 deletions(-) diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 88f2c1d0a..8648200f6 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -464,6 +464,7 @@ struct DiagramGenerator<'a, M: DblModel + MutDblModel + FgCategory, Mapping: Mut codomain_ty: TyV, domain: M, mapping: Mapping, + // I've added this equations: Vec<(TmS, TmS)>, } @@ -555,7 +556,6 @@ impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { Ok(None) } TyV_::Id(_, lhs, rhs) => { - // println!("LHS: {:#?}, RHS: {:#?}", lhs, rhs); // XXX trying to print as syntax, not values self.equations.push((self.eval.quote_tm(lhs), self.eval.quote_tm(rhs))); // self.equations.push((lhs.clone(), rhs.clone())); @@ -624,8 +624,6 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { // Augmentation produces lift morphisms whose dom and cod // are projections from the body's self, resolving to the // qualified names of generators (declared or specialized). - // println!("CHECKING: {}", self.eval.quote_tm(&dom)); - let mt_inner: ModalMorType = mt .clone() .try_into() @@ -674,7 +672,6 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { } TyV_::Id(_, lhs, rhs) => { self.equations.push((self.eval.quote_tm(lhs), self.eval.quote_tm(rhs))); - // self.equations.push((lhs.clone(), rhs.clone())); Ok(None) } @@ -868,7 +865,6 @@ diagram I : @Instance(DEC) := [ let (model_diag, _, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); let out = JuliaTranspiler::transpile(&src, "Klausmeier", elaborate_to_toplevel); - println!("{out}"); match model_diag { DblModelDiagramType::Discrete(_) => { diff --git a/packages/catlog/src/tt/text_elab.rs b/packages/catlog/src/tt/text_elab.rs index b094f39eb..c6fddf587 100644 --- a/packages/catlog/src/tt/text_elab.rs +++ b/packages/catlog/src/tt/text_elab.rs @@ -660,29 +660,18 @@ impl<'a> Elaborator<'a> { (TyS::specialize(ty_s, specializations), ty_v) } App2(L(_, Keyword("==")), tm1_n, tm2_n) => { - // println!("!!!!!!!!! {} {}", tm1_n, tm2_n); let (tm1_s, tm1_v, tm1_ty) = elab.syn(tm1_n); let (tm2_s, tm2_v, tm2_ty) = elab.syn(tm2_n); - // println!("{:#?}", tm1_ty); let _ = match &*tm1_ty { - TyV_::Morphism(_, _, _) | TyV_::Over(_) => { - // println!("YES!"); - } + TyV_::Morphism(_, _, _) | TyV_::Over(_) => {} _ => { elab.loc = Some(tm1_n.loc()); - // println!("ERROR {} : {:#?}", &tm1_n, &tm1_ty); return elab.ty_error("Equality types are only supported for morphisms"); } }; - // println!("EQUALITY: {:#?} == {:#?}", &tm1_ty, &tm2_ty); + if let Err(e) = elab.evaluator().convertible_ty(&tm1_ty, &tm2_ty) { let eval = elab.evaluator(); - // println!( - // "HELLO! {:#?} : {}, {}", - // &tm1_s, - // eval.quote_ty(&tm1_ty), - // eval.quote_ty(&tm2_ty) - // ); return elab.ty_error(format!( "types {} and {} are not convertible:\n{}", eval.quote_ty(&tm1_ty), @@ -694,10 +683,7 @@ impl<'a> Elaborator<'a> { let eq_ty_v = TyV::id(tm1_ty, tm1_v, tm2_v); (eq_ty_s, eq_ty_v) } - _ => { - // println!("UNEXPECTED NOTATION FOR TYPE"); - elab.ty_error("unexpected notation for type") - } + _ => elab.ty_error("unexpected notation for type"), } } @@ -727,12 +713,10 @@ impl<'a> Elaborator<'a> { /// Elaborates a term from notation, returning syntax, value, and synthesized type. fn syn(&mut self, n: &FNtn) -> (TmS, TmV, TyV) { let mut elab = self.enter(n.loc()); - // println!("AST: {:#?}", &n); match n.ast0() { Var(name) => elab.lookup_tm(ustr(name)), App1(tm_n, L(_, Field(f))) => { let (tm_s, tm_v, ty_v) = elab.syn(tm_n); - // println!("DEBUGGING: {:#?}, {:#?}, {:#?}", &tm_s, &tm_v, &ty_v); let TyV_::Record(r) = &*ty_v else { return elab.syn_error("can only project from record type"); }; @@ -741,7 +725,6 @@ impl<'a> Elaborator<'a> { if !r.fields.has(f) { return elab.syn_error(format!("no such field {f}")); } - // dbg!(&tm_s); ( TmS::proj(tm_s, f, label), elab.evaluator().proj(&tm_v, f, label), @@ -839,6 +822,7 @@ impl<'a> Elaborator<'a> { } let eval = elab.evaluator().with_env(env.clone()); (TmS::topapp(tv, arg_stxs), eval.eval_tm(&d.body), eval.eval_ty(&d.ret_ty)) + // TODO document this } else if let Some((_, codomain_ty)) = elab.ctx.lookup_diagram() && let TyV_::Record(cod_r) = &*codomain_ty && let Some((_, (_, mor_ty_s))) = cod_r.fields.iter().find(|(n, _)| **n == tv) @@ -869,33 +853,6 @@ impl<'a> Elaborator<'a> { elab.syn_error(format!("no such toplevel def {tv}")) } } - - // App1(L(_, Var(tv)), L(_, Tuple(args_n))) => { - // let tv = name_seg(*tv); - // println!("======= {:#?}", elab.ctx.lookup_diagram().unwrap().1); - // // println!("====== NAME: {tv} {:#?}", elab.toplevel.declarations.keys()); - // let Some(TopDecl::Def(d)) = elab.toplevel.lookup(tv) else { - // return elab.syn_error(format!("no such toplevel def {tv}")); - // }; - // println!("TOPLEVEL: {:#?}", d); - // let mut arg_stxs = Vec::new(); - // let mut env = Env::nil(); - // if args_n.len() != d.args.len() { - // return elab.syn_error(format!( - // "wrong number of args for {tv}, expected {}, got {}", - // d.args.len(), - // args_n.len() - // )); - // } - // for (arg_n, (_, (_, arg_ty_s))) in args_n.iter().zip(d.args.iter()) { - // let arg_ty_v = elab.evaluator().with_env(env.clone()).eval_ty(arg_ty_s); - // let (arg_s, arg_v) = elab.chk(&arg_ty_v, arg_n); - // arg_stxs.push(arg_s); - // env = env.snoc(arg_v); - // } - // let eval = elab.evaluator().with_env(env.clone()); - // (TmS::topapp(tv, arg_stxs), eval.eval_tm(&d.body), eval.eval_ty(&d.ret_ty)) - // } Tag("tt") => (TmS::tt(), TmV::tt(), TyV::unit()), Tuple(_) => elab.syn_error("must check against a type in order to construct a record"), Prim("hole") => elab.syn_error("explicit hole"), diff --git a/packages/catlog/src/tt/util/transpiler.rs b/packages/catlog/src/tt/util/transpiler.rs index 6447d836f..6bebeb20a 100644 --- a/packages/catlog/src/tt/util/transpiler.rs +++ b/packages/catlog/src/tt/util/transpiler.rs @@ -86,7 +86,6 @@ static MULT: LazyLock = LazyLock::new(|| Regex::new(r"mult_(.+)$").unwrap static PARTIAL: LazyLock = LazyLock::new(|| Regex::new(r"partial_(.+)$").unwrap()); static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"lapl_(.+)$").unwrap()); -// this should be a single string method fn to_plain(tm: &TmS) -> String { match &**tm { TmS_::Var(_, name, _) => { @@ -112,16 +111,9 @@ fn to_plain(tm: &TmS) -> String { _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), _ => op, }; - format!("{op}({})", splat_plain(args)) + format!("{op}({})", to_plain(args)) } - _ => format!("{}", tm), - } -} - -// merge into `to_plain` function above -fn splat_plain(tm: &TmS) -> String { - match &**tm { TmS_::List(args) => args.iter().map(|a| to_plain(a)).collect::>().join(", "), - _ => to_plain(tm), + _ => format!("{}", tm), } } From 721a196a79374513ea059bf3a288bcdbcbfdc538 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Tue, 9 Jun 2026 16:58:34 -0700 Subject: [PATCH 80/81] WIP: petri net nb elab, decapodes transpilation --- packages/catlog-wasm/src/model.rs | 2 +- packages/catlog-wasm/src/model_diagram.rs | 4 +- .../tt/notebook/heat_eq_analysis.json | 560 ++++++++++++ .../examples/tt/notebook/heat_eq_petri.json | 228 +++++ .../tt/notebook/heat_eq_petri_model.json | 270 ++++++ .../tt/notebook/klausmeier/Klausmeier.json | 117 +++ .../tt/notebook/klausmeier/hydrodynamics.json | 529 +++++++++++ .../klausmeier/model_dec_fragment.json | 824 ++++++++++++++++++ .../tt/notebook/klausmeier/phytodynamics.json | 485 +++++++++++ .../ns_vorticity/model_dec_fragment.json | 500 +++++++++++ .../notebook/ns_vorticity/ns_vorticity.json | 483 ++++++++++ .../examples/tt/notebook/sir_petri.json | 181 +++- packages/catlog/src/tt/modelgen.rs | 81 +- packages/catlog/src/tt/notebook_elab.rs | 502 ++++++++++- packages/catlog/src/tt/util/transpiler.rs | 194 ++++- .../document-types/src/v0/diagram_judgment.rs | 34 + 16 files changed, 4910 insertions(+), 84 deletions(-) create mode 100644 packages/catlog/examples/tt/notebook/heat_eq_analysis.json create mode 100644 packages/catlog/examples/tt/notebook/heat_eq_petri.json create mode 100644 packages/catlog/examples/tt/notebook/heat_eq_petri_model.json create mode 100644 packages/catlog/examples/tt/notebook/klausmeier/Klausmeier.json create mode 100644 packages/catlog/examples/tt/notebook/klausmeier/hydrodynamics.json create mode 100644 packages/catlog/examples/tt/notebook/klausmeier/model_dec_fragment.json create mode 100644 packages/catlog/examples/tt/notebook/klausmeier/phytodynamics.json create mode 100644 packages/catlog/examples/tt/notebook/ns_vorticity/model_dec_fragment.json create mode 100644 packages/catlog/examples/tt/notebook/ns_vorticity/ns_vorticity.json diff --git a/packages/catlog-wasm/src/model.rs b/packages/catlog-wasm/src/model.rs index 7dae0666c..18b021f73 100644 --- a/packages/catlog-wasm/src/model.rs +++ b/packages/catlog-wasm/src/model.rs @@ -747,7 +747,7 @@ pub fn elaborate_model( let theory = tt::theory::Theory::new(ustr("_").into(), theory_def); let ref_id = ustr(&ref_id); let mut elab = ElaboratorNext::new(theory.clone(), &instantiated.toplevel, ref_id); - let (ty_s, ty_v) = elab.notebook(notebook.0.formal_content()); + let (ty_s, ty_v) = elab.model_notebook(notebook.0.formal_content()); let (model, namespace) = tt::modelgen::Model::from_ty(&instantiated.toplevel, &theory.definition, &ty_v); Ok(DblModel { diff --git a/packages/catlog-wasm/src/model_diagram.rs b/packages/catlog-wasm/src/model_diagram.rs index d0b0b7b97..ea730a383 100644 --- a/packages/catlog-wasm/src/model_diagram.rs +++ b/packages/catlog-wasm/src/model_diagram.rs @@ -11,7 +11,8 @@ use wasm_bindgen::prelude::*; use catcolab_document_types::current::*; use catlog::dbl::model::{DblModel as _, DiscreteDblModel, FpDblModel, MutDblModel}; use catlog::dbl::model_diagram as diagram; -use catlog::dbl::model_morphism::DiscreteDblModelMapping; +use catlog::dbl::model_diagram::Diagram; +use catlog::dbl::model_morphism::{DiscreteDblModelMapping, MutDblModelMapping}; use catlog::one::FgCategory; use catlog::zero::{MutMapping, NameLookup, NameSegment, Namespace, QualifiedLabel, QualifiedName}; @@ -290,6 +291,7 @@ pub fn elaborate_diagram( match judgment { DiagramJudgment::Object(decl) => diagram.add_ob(&decl)?, DiagramJudgment::Morphism(decl) => diagram.add_mor(&decl)?, + DiagramJudgment::Instantiation(_) => todo!(), DiagramJudgment::Equation(_) => { return Err("Elaboration of equations in diagrams is not yet supported".into()); } diff --git a/packages/catlog/examples/tt/notebook/heat_eq_analysis.json b/packages/catlog/examples/tt/notebook/heat_eq_analysis.json new file mode 100644 index 000000000..7051ffda5 --- /dev/null +++ b/packages/catlog/examples/tt/notebook/heat_eq_analysis.json @@ -0,0 +1,560 @@ +{ + "model": { + "obGenerators": [ + { + "id": "019df3f4-8443-71d6-a106-e211b1f1a359", + "label": [ + "0-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + }, + { + "id": "019df509-56a6-7656-98e6-896813fa1052", + "label": [ + "1-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + }, + { + "id": "019df509-6110-70d2-91fd-d81a3b4eab88", + "label": [ + "2-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + }, + { + "id": "019df3f4-3ec3-7069-94bd-5f5097133079", + "label": [ + "Dual 0-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + }, + { + "id": "019df509-6978-72bf-9362-0e085ed78843", + "label": [ + "Dual 1-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + }, + { + "id": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "label": [ + "Dual 2-Form" + ], + "obType": { + "tag": "Basic", + "content": "Object" + } + } + ], + "morGenerators": [ + { + "id": "019df920-3a9a-70c4-821e-a871751e58ec", + "label": [ + "multiplication" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + }, + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df91e-8033-716b-8526-12e08e9bec59", + "label": [ + "laplace" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df50c-6981-7248-8a82-b1da5d63ab63", + "label": [ + "inv-laplace" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df509-e051-77aa-9899-db60385d3b57", + "label": [ + "0-d" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-56a6-7656-98e6-896813fa1052" + } + }, + { + "id": "019df50a-6e39-7216-9b89-87168f762a5c", + "label": [ + "1-star" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df509-56a6-7656-98e6-896813fa1052" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-6978-72bf-9362-0e085ed78843" + } + }, + { + "id": "019df50b-da40-7399-9c85-094a02bde699", + "label": [ + "sharp-flat" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df509-56a6-7656-98e6-896813fa1052" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-56a6-7656-98e6-896813fa1052" + } + }, + { + "id": "019df50d-0c28-71f9-bcc4-643ca621044d", + "label": [ + "dual 0-d" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-3ec3-7069-94bd-5f5097133079" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-6978-72bf-9362-0e085ed78843" + } + }, + { + "id": "019df508-fe6a-751c-92e0-ed1ffccbfb20", + "label": [ + "0-inv-star" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-3ec3-7069-94bd-5f5097133079" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-6110-70d2-91fd-d81a3b4eab88" + } + }, + { + "id": "019df526-a755-75f8-8528-f381f4feec4d", + "label": [ + "dual 1-d" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df509-6978-72bf-9362-0e085ed78843" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + }, + { + "id": "019df526-5735-724b-b232-140c13a4a438", + "label": [ + "2-inv-star" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df509-9301-77d7-bdd5-3ffaea8a24a1", + "label": [ + "partial" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + }, + { + "id": "019df522-b423-7419-880c-7c8b6f62eb38", + "label": [ + "dp-wedge-10" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df509-6978-72bf-9362-0e085ed78843" + }, + { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df509-56a6-7656-98e6-896813fa1052" + } + }, + { + "id": "019df5ea-62d9-761e-8c9c-9b5cf73e7240", + "label": [ + "-" + ], + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0" + } + } + ] + }, + "diagram": { + "obGenerators": [ + { + "id": "019df91a-38ee-7001-89aa-0cdfb89f2b69", + "label": [ + "u" + ], + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df91a-6c2d-75d1-8089-9485a756151c", + "label": [ + "partial-u" + ], + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + }, + { + "id": "019df91b-7974-738f-ab90-1474c2aa6914", + "label": [ + "k" + ], + "obType": { + "tag": "Basic", + "content": "Object" + }, + "over": { + "tag": "Basic", + "content": "019df3f4-8443-71d6-a106-e211b1f1a359" + } + } + ], + "morGenerators": [ + { + "id": "019df91e-0f0e-778e-838c-0de048eb9aa3", + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "over": { + "tag": "Basic", + "content": "019df91e-8033-716b-8526-12e08e9bec59" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df91a-38ee-7001-89aa-0cdfb89f2b69" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df91e-5e6e-754a-99ef-47d3b5465a39" + } + }, + { + "id": "019df91e-cfe2-728f-81ef-fbf11b684885", + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "over": { + "tag": "Basic", + "content": "019df509-9301-77d7-bdd5-3ffaea8a24a1" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df91a-38ee-7001-89aa-0cdfb89f2b69" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df91a-6c2d-75d1-8089-9485a756151c" + } + }, + { + "id": "019df91b-54c0-75ee-88d8-3a946f1d4ee2", + "morType": { + "tag": "Basic", + "content": "Multihom" + }, + "over": { + "tag": "Basic", + "content": "019df920-3a9a-70c4-821e-a871751e58ec" + }, + "dom": { + "tag": "List", + "content": { + "modality": "List", + "objects": [ + { + "tag": "Basic", + "content": "019df91e-5e6e-754a-99ef-47d3b5465a39" + }, + { + "tag": "Basic", + "content": "019df91b-7974-738f-ab90-1474c2aa6914" + } + ] + } + }, + "cod": { + "tag": "Basic", + "content": "019df91a-6c2d-75d1-8089-9485a756151c" + } + } + ] + }, + "analysis": { + "duration": 50, + "plotVariables": [ + "019df91a-6c2d-75d1-8089-9485a756151c" + ], + "domain": "Plane", + "mesh": "Rectangle", + "initialConditions": { + "019df91a-38ee-7001-89aa-0cdfb89f2b69": "Gaussian", + "019df91a-6c2d-75d1-8089-9485a756151c": "Gaussian", + "019df91b-7974-738f-ab90-1474c2aa6914": "Constant" + } + } +} diff --git a/packages/catlog/examples/tt/notebook/heat_eq_petri.json b/packages/catlog/examples/tt/notebook/heat_eq_petri.json new file mode 100644 index 000000000..2d5f11a39 --- /dev/null +++ b/packages/catlog/examples/tt/notebook/heat_eq_petri.json @@ -0,0 +1,228 @@ +{ + "diagramIn": { + "_id": "019ea9ae-f867-7373-a70b-5a860e6f8c06", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "diagram-in" + }, + "name": "Heat Equation", + "notebook": { + "cellContents": { + "019ea9af-e082-7468-a8b2-83cb49b28357": { + "content": { + "id": "019ea9af-e081-750a-9908-daf11a552da4", + "name": "u", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019ea9af-e082-7468-a8b2-83cb49b28357", + "tag": "formal" + }, + "019ea9af-f8ab-72b4-aad2-d81325a4e3e0": { + "content": { + "id": "019ea9af-f8ab-72b4-aad2-d623ce6613fe", + "name": "partial-u", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019ea9af-f8ab-72b4-aad2-d81325a4e3e0", + "tag": "formal" + }, + "019ea9b0-0e80-708d-9a8c-3e4f53c7b00b": { + "content": { + "id": "019ea9b0-0e80-708d-9a8c-3979c09e762d", + "name": "k", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019ea9b0-0e80-708d-9a8c-3e4f53c7b00b", + "tag": "formal" + }, + "019ea9b0-23e4-7758-9b54-013344564998": { + "content": { + "id": "019ea9b0-23e4-7758-9b53-fed672c047e5", + "name": "anon-0", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019ea9b0-23e4-7758-9b54-013344564998", + "tag": "formal" + }, + "019ea9b0-3404-7440-8ecb-ea5c43b9af99": { + "content": { + "cod": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-f8ab-72b4-aad2-d623ce6613fe", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "dom": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-e081-750a-9908-daf11a552da4", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019ea9b0-3404-7440-8ecb-e686c716a09c", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "", + "over": { + "content": "019ea9af-2fe2-7139-b2df-881d5af7703b", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019ea9b0-3404-7440-8ecb-ea5c43b9af99", + "tag": "formal" + }, + "019ea9b0-59a4-72ac-a395-99003d1c3157": { + "content": { + "cod": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9b0-23e4-7758-9b53-fed672c047e5", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "dom": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-e081-750a-9908-daf11a552da4", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019ea9b0-59a3-738c-b9ec-3ef91df1f1d1", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "", + "over": { + "content": "019ea9b0-7e0d-752f-b77d-a7ec0956ea8c", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019ea9b0-59a4-72ac-a395-99003d1c3157", + "tag": "formal" + }, + "019ea9b0-a2db-707a-8acb-a18a182dbac0": { + "content": { + "cod": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-f8ab-72b4-aad2-d623ce6613fe", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "dom": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9b0-23e4-7758-9b53-fed672c047e5", + "tag": "Basic" + }, + { + "content": "019ea9b0-0e80-708d-9a8c-3979c09e762d", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019ea9b0-a2db-707a-8acb-9edcb61d66b3", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "", + "over": { + "content": "019ea9af-5ddd-70c2-a9a4-31ea4065da9e", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019ea9b0-a2db-707a-8acb-a18a182dbac0", + "tag": "formal" + } + }, + "cellOrder": [ + "019ea9af-e082-7468-a8b2-83cb49b28357", + "019ea9af-f8ab-72b4-aad2-d81325a4e3e0", + "019ea9b0-0e80-708d-9a8c-3e4f53c7b00b", + "019ea9b0-23e4-7758-9b54-013344564998", + "019ea9b0-3404-7440-8ecb-ea5c43b9af99", + "019ea9b0-59a4-72ac-a395-99003d1c3157", + "019ea9b0-a2db-707a-8acb-a18a182dbac0" + ] + }, + "type": "diagram", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/heat_eq_petri_model.json b/packages/catlog/examples/tt/notebook/heat_eq_petri_model.json new file mode 100644 index 000000000..4da108bdc --- /dev/null +++ b/packages/catlog/examples/tt/notebook/heat_eq_petri_model.json @@ -0,0 +1,270 @@ +{ + "name": "Testing Petri-Net", + "notebook": { + "cellContents": { + "019ea9af-269b-721a-9cf1-d2de10f159d0": { + "content": { + "id": "019ea9af-269b-721a-9cf1-ce539f888301", + "name": "Form0", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019ea9af-269b-721a-9cf1-d2de10f159d0", + "tag": "formal" + }, + "019ea9af-2fe2-7139-b2df-8d84761e149b": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019ea9af-2fe2-7139-b2df-881d5af7703b", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "partial", + "tag": "morphism" + }, + "id": "019ea9af-2fe2-7139-b2df-8d84761e149b", + "tag": "formal" + }, + "019ea9af-5ddd-70c2-a9a4-364cc20b5879": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019ea9af-5ddd-70c2-a9a4-31ea4065da9e", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "multiplication", + "tag": "morphism" + }, + "id": "019ea9af-5ddd-70c2-a9a4-364cc20b5879", + "tag": "formal" + }, + "019ea9af-88a5-76da-b6db-4af42a0cf5c8": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + }, + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019ea9af-88a5-76da-b6db-457deb58b8a6", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "wedge", + "tag": "morphism" + }, + "id": "019ea9af-88a5-76da-b6db-4af42a0cf5c8", + "tag": "formal" + }, + "019ea9b0-7e0d-752f-b77d-ab2d32f9a41d": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019ea9af-269b-721a-9cf1-ce539f888301", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019ea9b0-7e0d-752f-b77d-a7ec0956ea8c", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "lapl", + "tag": "morphism" + }, + "id": "019ea9b0-7e0d-752f-b77d-ab2d32f9a41d", + "tag": "formal" + } + }, + "cellOrder": [ + "019ea9af-269b-721a-9cf1-d2de10f159d0", + "019ea9af-2fe2-7139-b2df-8d84761e149b", + "019ea9b0-7e0d-752f-b77d-ab2d32f9a41d", + "019ea9af-5ddd-70c2-a9a4-364cc20b5879", + "019ea9af-88a5-76da-b6db-4af42a0cf5c8" + ] + }, + "theory": "petri-net", + "type": "model", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/klausmeier/Klausmeier.json b/packages/catlog/examples/tt/notebook/klausmeier/Klausmeier.json new file mode 100644 index 000000000..6263b4b46 --- /dev/null +++ b/packages/catlog/examples/tt/notebook/klausmeier/Klausmeier.json @@ -0,0 +1,117 @@ +{ + "diagramIn": { + "_id": "019df1c9-1c72-72d3-a0f7-9de7af7db5d5", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "diagram-in" + }, + "name": "Klausmeier", + "notebook": { + "cellContents": { + "019eb903-3b40-7469-8e77-7f0d150775e5": { + "content": { + "diagram": { + "_id": "019eb37e-eb26-7283-8c68-63d4cb8cd1f7", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "instantiation" + }, + "id": "019eb903-3b40-7469-8e77-7b7019bd82ac", + "name": "Hydrodynamics", + "specializations": [ + { + "id": "019eb746-8257-751b-bd10-1963c8921101", + "ob": { + "content": "019eb916-dad1-71bc-bc0a-e1f22763b31f", + "tag": "Basic" + } + }, + { + "id": "019eb746-8606-7423-a8e5-b83e191cca53", + "ob": { + "content": "019eb916-f032-75e9-b7b5-8a3a08324a02", + "tag": "Basic" + } + } + ], + "tag": "instantiation" + }, + "id": "019eb903-3b40-7469-8e77-7f0d150775e5", + "tag": "formal" + }, + "019eb916-9299-7489-836f-84a1eb4bc9eb": { + "content": { + "diagram": { + "_id": "019eb288-c310-7f33-b2c3-171279589942", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "instantiation" + }, + "id": "019eb916-9299-7489-836f-80bd1830f913", + "name": "Phytodynamics", + "specializations": [ + { + "id": "019eb28c-ceec-779a-b09d-67307cb2d5c2", + "ob": { + "content": "019eb916-dad1-71bc-bc0a-e1f22763b31f", + "tag": "Basic" + } + }, + { + "id": "019eb289-de33-7179-8cb8-4d3ec8facbea", + "ob": { + "content": "019eb916-f032-75e9-b7b5-8a3a08324a02", + "tag": "Basic" + } + } + ], + "tag": "instantiation" + }, + "id": "019eb916-9299-7489-836f-84a1eb4bc9eb", + "tag": "formal" + }, + "019eb916-dad1-71bc-bc0a-e68649ea9d60": { + "content": { + "id": "019eb916-dad1-71bc-bc0a-e1f22763b31f", + "name": "n", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb916-dad1-71bc-bc0a-e68649ea9d60", + "tag": "formal" + }, + "019eb916-f032-75e9-b7b5-8fe879ae1ca6": { + "content": { + "id": "019eb916-f032-75e9-b7b5-8a3a08324a02", + "name": "w", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb916-f032-75e9-b7b5-8fe879ae1ca6", + "tag": "formal" + } + }, + "cellOrder": [ + "019eb903-3b40-7469-8e77-7f0d150775e5", + "019eb916-9299-7489-836f-84a1eb4bc9eb", + "019eb916-dad1-71bc-bc0a-e68649ea9d60", + "019eb916-f032-75e9-b7b5-8fe879ae1ca6" + ] + }, + "type": "diagram", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/klausmeier/hydrodynamics.json b/packages/catlog/examples/tt/notebook/klausmeier/hydrodynamics.json new file mode 100644 index 000000000..1bf784861 --- /dev/null +++ b/packages/catlog/examples/tt/notebook/klausmeier/hydrodynamics.json @@ -0,0 +1,529 @@ +{ + "diagramIn": { + "_id": "019df1c9-1c72-72d3-a0f7-9de7af7db5d5", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "diagram-in" + }, + "name": "Hydrodynamics", + "notebook": { + "cellContents": { + "019eb746-7967-751a-b4d2-f2882f92b53e": { + "content": { + "id": "019eb746-7967-751a-b4d2-ed28cb3760cf", + "name": "a", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-7967-751a-b4d2-f2882f92b53e", + "tag": "formal" + }, + "019eb746-7dde-735d-aacc-2ba8123f2ba0": { + "content": { + "id": "019eb746-7dde-735d-aacc-25e644c576e3", + "name": "k", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-7dde-735d-aacc-2ba8123f2ba0", + "tag": "formal" + }, + "019eb746-8257-751b-bd10-1dd3896594d4": { + "content": { + "id": "019eb746-8257-751b-bd10-1963c8921101", + "name": "n", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-8257-751b-bd10-1dd3896594d4", + "tag": "formal" + }, + "019eb746-8606-7423-a8e5-bcf90d58b5c5": { + "content": { + "id": "019eb746-8606-7423-a8e5-b83e191cca53", + "name": "w", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-8606-7423-a8e5-bcf90d58b5c5", + "tag": "formal" + }, + "019eb746-988f-74c8-905a-c752ab2e45fa": { + "content": { + "id": "019eb746-988f-74c8-905a-c2ca890efc41", + "name": "dX", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-988f-74c8-905a-c752ab2e45fa", + "tag": "formal" + }, + "019eb746-a49e-70d7-8cc9-df553217d6c9": { + "content": { + "id": "019eb746-a49e-70d7-8cc9-dbfe12358d3a", + "name": "x0", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-a49e-70d7-8cc9-df553217d6c9", + "tag": "formal" + }, + "019eb746-a7c8-7378-9a67-c7edff22b468": { + "content": { + "id": "019eb746-a7c7-77ef-908b-7158346b3d86", + "name": "x1", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-a7c8-7378-9a67-c7edff22b468", + "tag": "formal" + }, + "019eb746-acf7-768e-8ce2-6af09f67a4a7": { + "content": { + "id": "019eb746-acf7-768e-8ce2-66673d0af4d4", + "name": "x2", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-acf7-768e-8ce2-6af09f67a4a7", + "tag": "formal" + }, + "019eb746-b086-714a-a950-765f82a23d06": { + "content": { + "id": "019eb746-b086-714a-a950-73f7f029f09f", + "name": "x3", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-b086-714a-a950-765f82a23d06", + "tag": "formal" + }, + "019eb746-b447-7170-a93b-cdfdf44f4ae8": { + "content": { + "id": "019eb746-b447-7170-a93b-c871e5023af5", + "name": "x4", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-b447-7170-a93b-cdfdf44f4ae8", + "tag": "formal" + }, + "019eb746-b76e-7434-9dc0-2dcb1bae72be": { + "content": { + "id": "019eb746-b76e-7434-9dc0-2b6d9f1966bd", + "name": "x5", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb746-b76e-7434-9dc0-2dcb1bae72be", + "tag": "formal" + }, + "019eb747-40b7-70a3-8abe-28e1966327d4": { + "content": { + "id": "019eb747-40b7-70a3-8abe-266f406f14c2", + "name": "x6", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb747-40b7-70a3-8abe-28e1966327d4", + "tag": "formal" + }, + "019eb747-7946-721a-93fb-5e97b3f6b759": { + "content": { + "cod": { + "content": "019eb746-a49e-70d7-8cc9-dbfe12358d3a", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-7967-751a-b4d2-ed28cb3760cf", + "tag": "Basic" + }, + { + "content": "019eb746-8606-7423-a8e5-b83e191cca53", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb747-7946-721a-93fb-59e8ed9037c3", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb748-0792-7608-a9af-e803e3886a10", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb747-7946-721a-93fb-5e97b3f6b759", + "tag": "formal" + }, + "019eb747-8047-7375-b530-c3756b231031": { + "content": { + "cod": { + "content": "019eb746-a7c7-77ef-908b-7158346b3d86", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-8257-751b-bd10-1963c8921101", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb747-8047-7375-b530-be1846739c83", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28e-b02f-754a-b7ec-7c9df716cac4", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb747-8047-7375-b530-c3756b231031", + "tag": "formal" + }, + "019eb747-84d7-762d-aa5b-9fb72e2f6e2d": { + "content": { + "cod": { + "content": "019eb746-acf7-768e-8ce2-66673d0af4d4", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-8606-7423-a8e5-b83e191cca53", + "tag": "Basic" + }, + { + "content": "019eb746-a7c7-77ef-908b-7158346b3d86", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb747-84d7-762d-aa5b-9b260d1203ae", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28f-792e-7414-807f-3f3ba6138ed7", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb747-84d7-762d-aa5b-9fb72e2f6e2d", + "tag": "formal" + }, + "019eb747-8fae-734e-8183-4869e2a473bc": { + "content": { + "cod": { + "content": "019eb746-b086-714a-a950-73f7f029f09f", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-a49e-70d7-8cc9-dbfe12358d3a", + "tag": "Basic" + }, + { + "content": "019eb746-acf7-768e-8ce2-66673d0af4d4", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb747-8fae-734e-8183-447793c4c261", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb290-9d67-769e-a374-775d25df2eed", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb747-8fae-734e-8183-4869e2a473bc", + "tag": "formal" + }, + "019eb747-937a-70bd-9fef-0f1f068561f3": { + "content": { + "cod": { + "content": "019eb746-b447-7170-a93b-c871e5023af5", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-988f-74c8-905a-c2ca890efc41", + "tag": "Basic" + }, + { + "content": "019eb746-8606-7423-a8e5-b83e191cca53", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb747-9379-74b1-aa0e-4102c651df86", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb74c-be64-7701-826b-54c6b28f28d9", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb747-937a-70bd-9fef-0f1f068561f3", + "tag": "formal" + }, + "019eb74d-42df-74af-bde7-5ada8dd94a9e": { + "content": { + "cod": { + "content": "019eb746-b76e-7434-9dc0-2b6d9f1966bd", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-7dde-735d-aacc-25e644c576e3", + "tag": "Basic" + }, + { + "content": "019eb746-b447-7170-a93b-c871e5023af5", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb74d-42df-74af-bde7-57b9fcef0c4d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28f-792e-7414-807f-3f3ba6138ed7", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb74d-42df-74af-bde7-5ada8dd94a9e", + "tag": "formal" + }, + "019eb74d-93ff-7560-ba46-0beedccf25e2": { + "content": { + "cod": { + "content": "019eb747-40b7-70a3-8abe-266f406f14c2", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-b086-714a-a950-73f7f029f09f", + "tag": "Basic" + }, + { + "content": "019eb746-b76e-7434-9dc0-2b6d9f1966bd", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb74d-93fe-7573-b290-0b467ca0b667", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb293-c69e-75eb-b499-a8a62a67e559", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb74d-93ff-7560-ba46-0beedccf25e2", + "tag": "formal" + }, + "019eb74d-d4e0-77ec-99f3-b36d4cc8a6e6": { + "content": { + "cod": { + "content": "019eb747-40b7-70a3-8abe-266f406f14c2", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb746-8606-7423-a8e5-b83e191cca53", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb74d-d4e0-77ec-99f3-ac9f9137c891", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb294-5186-76ff-9cc6-49124d01a884", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb74d-d4e0-77ec-99f3-b36d4cc8a6e6", + "tag": "formal" + } + }, + "cellOrder": [ + "019eb746-7967-751a-b4d2-f2882f92b53e", + "019eb746-7dde-735d-aacc-2ba8123f2ba0", + "019eb746-8257-751b-bd10-1dd3896594d4", + "019eb746-8606-7423-a8e5-bcf90d58b5c5", + "019eb746-988f-74c8-905a-c752ab2e45fa", + "019eb746-a49e-70d7-8cc9-df553217d6c9", + "019eb746-a7c8-7378-9a67-c7edff22b468", + "019eb746-acf7-768e-8ce2-6af09f67a4a7", + "019eb746-b086-714a-a950-765f82a23d06", + "019eb746-b447-7170-a93b-cdfdf44f4ae8", + "019eb746-b76e-7434-9dc0-2dcb1bae72be", + "019eb747-40b7-70a3-8abe-28e1966327d4", + "019eb747-7946-721a-93fb-5e97b3f6b759", + "019eb747-8047-7375-b530-c3756b231031", + "019eb747-84d7-762d-aa5b-9fb72e2f6e2d", + "019eb747-8fae-734e-8183-4869e2a473bc", + "019eb747-937a-70bd-9fef-0f1f068561f3", + "019eb74d-42df-74af-bde7-5ada8dd94a9e", + "019eb74d-93ff-7560-ba46-0beedccf25e2", + "019eb74d-d4e0-77ec-99f3-b36d4cc8a6e6" + ] + }, + "type": "diagram", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/klausmeier/model_dec_fragment.json b/packages/catlog/examples/tt/notebook/klausmeier/model_dec_fragment.json new file mode 100644 index 000000000..35096213d --- /dev/null +++ b/packages/catlog/examples/tt/notebook/klausmeier/model_dec_fragment.json @@ -0,0 +1,824 @@ +{ + "name": "DEC Fragment", + "notebook": { + "cellContents": { + "019df3f4-3ec3-7069-94bd-60cf0befac08": { + "content": { + "id": "019df3f4-3ec3-7069-94bd-5f5097133079", + "name": "Dual 0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-3ec3-7069-94bd-60cf0befac08", + "tag": "formal" + }, + "019df3f4-7663-7519-ba02-a2826c5d8708": { + "content": { + "id": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "name": "Dual 2-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-7663-7519-ba02-a2826c5d8708", + "tag": "formal" + }, + "019df3f4-8444-70e8-b8a3-6eff98e1c9d4": { + "content": { + "id": "019df3f4-8443-71d6-a106-e211b1f1a359", + "name": "0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-8444-70e8-b8a3-6eff98e1c9d4", + "tag": "formal" + }, + "019df508-fe6a-751c-92e0-f04f0cf69a1a": { + "content": { + "cod": { + "content": "019df509-6110-70d2-91fd-d81a3b4eab88", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df508-fe6a-751c-92e0-ed1ffccbfb20", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "0-inv-star", + "tag": "morphism" + }, + "id": "019df508-fe6a-751c-92e0-f04f0cf69a1a", + "tag": "formal" + }, + "019df509-56a6-7656-98e6-8f4da9c812c9": { + "content": { + "id": "019df509-56a6-7656-98e6-896813fa1052", + "name": "1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-56a6-7656-98e6-8f4da9c812c9", + "tag": "formal" + }, + "019df509-6110-70d2-91fd-dd2a8dade102": { + "content": { + "id": "019df509-6110-70d2-91fd-d81a3b4eab88", + "name": "2-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-6110-70d2-91fd-dd2a8dade102", + "tag": "formal" + }, + "019df509-6978-72bf-9362-13b7ad373887": { + "content": { + "id": "019df509-6978-72bf-9362-0e085ed78843", + "name": "Dual 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-6978-72bf-9362-13b7ad373887", + "tag": "formal" + }, + "019df509-9301-77d7-bdd5-43a3d908fbb8": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df509-9301-77d7-bdd5-3ffaea8a24a1", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "partial", + "tag": "morphism" + }, + "id": "019df509-9301-77d7-bdd5-43a3d908fbb8", + "tag": "formal" + }, + "019df509-e051-77aa-9899-df703a8c58cf": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df509-e051-77aa-9899-db60385d3b57", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "0-d", + "tag": "morphism" + }, + "id": "019df509-e051-77aa-9899-df703a8c58cf", + "tag": "formal" + }, + "019df50a-6e39-7216-9b89-8a070cefc041": { + "content": { + "cod": { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50a-6e39-7216-9b89-87168f762a5c", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "1-star", + "tag": "morphism" + }, + "id": "019df50a-6e39-7216-9b89-8a070cefc041", + "tag": "formal" + }, + "019df50b-da40-7399-9c85-0f8ae1241f0d": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50b-da40-7399-9c85-094a02bde699", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "sharp-flat", + "tag": "morphism" + }, + "id": "019df50b-da40-7399-9c85-0f8ae1241f0d", + "tag": "formal" + }, + "019df50c-6981-7248-8a82-b42acdb471f0": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50c-6981-7248-8a82-b1da5d63ab63", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "inv-laplace", + "tag": "morphism" + }, + "id": "019df50c-6981-7248-8a82-b42acdb471f0", + "tag": "formal" + }, + "019df50d-0c28-71f9-bcc4-69bbe9ca152e": { + "content": { + "cod": { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50d-0c28-71f9-bcc4-643ca621044d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dual 0-d", + "tag": "morphism" + }, + "id": "019df50d-0c28-71f9-bcc4-69bbe9ca152e", + "tag": "formal" + }, + "019df522-b423-7419-880c-82c4601bf9c4": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df522-b423-7419-880c-7c8b6f62eb38", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dp-wedge-10", + "tag": "morphism" + }, + "id": "019df522-b423-7419-880c-82c4601bf9c4", + "tag": "formal" + }, + "019df526-5735-724b-b232-1a1aafd58233": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df526-5735-724b-b232-140c13a4a438", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "2-inv-star", + "tag": "morphism" + }, + "id": "019df526-5735-724b-b232-1a1aafd58233", + "tag": "formal" + }, + "019df526-a755-75f8-8528-f74ced38a971": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df526-a755-75f8-8528-f381f4feec4d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dual 1-d", + "tag": "morphism" + }, + "id": "019df526-a755-75f8-8528-f74ced38a971", + "tag": "formal" + }, + "019df528-97dd-75f8-b9fd-c193b73d0b21": { + "content": "I assume this is correct...", + "id": "019df528-97dd-75f8-b9fd-c193b73d0b21", + "tag": "rich-text" + }, + "019df5ea-62d9-761e-8c9c-9fc2629f83f0": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5ea-62d9-761e-8c9c-9b5cf73e7240", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "-", + "tag": "morphism" + }, + "id": "019df5ea-62d9-761e-8c9c-9fc2629f83f0", + "tag": "formal" + }, + "019df91e-8033-716b-8526-1414019c5649": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df91e-8033-716b-8526-12e08e9bec59", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "laplace", + "tag": "morphism" + }, + "id": "019df91e-8033-716b-8526-1414019c5649", + "tag": "formal" + }, + "019df920-3a9a-70c4-821e-ac6de6a10c70": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df920-3a9a-70c4-821e-a871751e58ec", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "multiplication", + "tag": "morphism" + }, + "id": "019df920-3a9a-70c4-821e-ac6de6a10c70", + "tag": "formal" + }, + "019eb28d-f5e6-777f-aae0-cf98e6ba85d2": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28d-f5e6-777f-aae0-cb7d08bd35a1", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "multiplication-dual0", + "tag": "morphism" + }, + "id": "019eb28d-f5e6-777f-aae0-cf98e6ba85d2", + "tag": "formal" + }, + "019eb28e-b02f-754a-b7ec-802d65be072e": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28e-b02f-754a-b7ec-7c9df716cac4", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "square-dual0", + "tag": "morphism" + }, + "id": "019eb28e-b02f-754a-b7ec-802d65be072e", + "tag": "formal" + }, + "019eb28f-792e-7414-807f-4151fa624da8": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28f-792e-7414-807f-3f3ba6138ed7", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "multiplication-0dual0", + "tag": "morphism" + }, + "id": "019eb28f-792e-7414-807f-4151fa624da8", + "tag": "formal" + }, + "019eb290-1d17-720f-855d-5310d112eea2": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb290-1d17-720f-855d-4d03eb5d79d1", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "laplace-dual0", + "tag": "morphism" + }, + "id": "019eb290-1d17-720f-855d-5310d112eea2", + "tag": "formal" + }, + "019eb290-9d68-719b-8c67-c684659a091e": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb290-9d67-769e-a374-775d25df2eed", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "subtract-dual0", + "tag": "morphism" + }, + "id": "019eb290-9d68-719b-8c67-c684659a091e", + "tag": "formal" + }, + "019eb293-c69f-7049-ae1f-1f6bacfca350": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb293-c69e-75eb-b499-a8a62a67e559", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "add-dual0", + "tag": "morphism" + }, + "id": "019eb293-c69f-7049-ae1f-1f6bacfca350", + "tag": "formal" + }, + "019eb294-5186-76ff-9cc6-4f95974a1e1c": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb294-5186-76ff-9cc6-49124d01a884", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "partial-dual0", + "tag": "morphism" + }, + "id": "019eb294-5186-76ff-9cc6-4f95974a1e1c", + "tag": "formal" + }, + "019eb294-d8bd-7508-b27d-1e04ae68cf36": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb294-d8bd-7508-b27d-18ae5f93db80", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "eq-dual0", + "tag": "morphism" + }, + "id": "019eb294-d8bd-7508-b27d-1e04ae68cf36", + "tag": "formal" + }, + "019eb748-0792-7608-a9af-ec91ac591709": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb748-0792-7608-a9af-e803e3886a10", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "subtract-0dual0", + "tag": "morphism" + }, + "id": "019eb748-0792-7608-a9af-ec91ac591709", + "tag": "formal" + }, + "019eb74c-be64-7701-826b-59b3669a1047": { + "content": { + "cod": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb74c-be64-7701-826b-54c6b28f28d9", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "lie_1d0", + "tag": "morphism" + }, + "id": "019eb74c-be64-7701-826b-59b3669a1047", + "tag": "formal" + } + }, + "cellOrder": [ + "019df3f4-8444-70e8-b8a3-6eff98e1c9d4", + "019df509-56a6-7656-98e6-8f4da9c812c9", + "019df509-6110-70d2-91fd-dd2a8dade102", + "019df3f4-3ec3-7069-94bd-60cf0befac08", + "019df509-6978-72bf-9362-13b7ad373887", + "019df3f4-7663-7519-ba02-a2826c5d8708", + "019df920-3a9a-70c4-821e-ac6de6a10c70", + "019eb28d-f5e6-777f-aae0-cf98e6ba85d2", + "019eb293-c69f-7049-ae1f-1f6bacfca350", + "019eb290-9d68-719b-8c67-c684659a091e", + "019eb748-0792-7608-a9af-ec91ac591709", + "019eb28f-792e-7414-807f-4151fa624da8", + "019eb28e-b02f-754a-b7ec-802d65be072e", + "019df91e-8033-716b-8526-1414019c5649", + "019eb290-1d17-720f-855d-5310d112eea2", + "019df50c-6981-7248-8a82-b42acdb471f0", + "019df509-e051-77aa-9899-df703a8c58cf", + "019df50a-6e39-7216-9b89-8a070cefc041", + "019df50b-da40-7399-9c85-0f8ae1241f0d", + "019df50d-0c28-71f9-bcc4-69bbe9ca152e", + "019df508-fe6a-751c-92e0-f04f0cf69a1a", + "019df526-a755-75f8-8528-f74ced38a971", + "019df526-5735-724b-b232-1a1aafd58233", + "019df509-9301-77d7-bdd5-43a3d908fbb8", + "019eb294-5186-76ff-9cc6-4f95974a1e1c", + "019df528-97dd-75f8-b9fd-c193b73d0b21", + "019df522-b423-7419-880c-82c4601bf9c4", + "019df5ea-62d9-761e-8c9c-9fc2629f83f0", + "019eb294-d8bd-7508-b27d-1e04ae68cf36", + "019eb74c-be64-7701-826b-59b3669a1047" + ] + }, + "theory": "dec", + "type": "model", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/klausmeier/phytodynamics.json b/packages/catlog/examples/tt/notebook/klausmeier/phytodynamics.json new file mode 100644 index 000000000..7c2a1403d --- /dev/null +++ b/packages/catlog/examples/tt/notebook/klausmeier/phytodynamics.json @@ -0,0 +1,485 @@ +{ + "diagramIn": { + "_id": "019df1c9-1c72-72d3-a0f7-9de7af7db5d5", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "diagram-in" + }, + "name": "Phytodynamics", + "notebook": { + "cellContents": { + "019eb289-de33-7179-8cb8-53c018616fdd": { + "content": { + "id": "019eb289-de33-7179-8cb8-4d3ec8facbea", + "name": "w", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb289-de33-7179-8cb8-53c018616fdd", + "tag": "formal" + }, + "019eb28c-ceed-7781-a89e-ca6cefa6778f": { + "content": { + "id": "019eb28c-ceec-779a-b09d-67307cb2d5c2", + "name": "n", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28c-ceed-7781-a89e-ca6cefa6778f", + "tag": "formal" + }, + "019eb28c-eadb-7449-b64d-8018dbc2b6dc": { + "content": { + "id": "019eb28c-eadb-7449-b64d-7dcaf19df3f8", + "name": "m", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28c-eadb-7449-b64d-8018dbc2b6dc", + "tag": "formal" + }, + "019eb28c-ffb3-709f-a464-099dabf466b2": { + "content": { + "id": "019eb28c-ffb3-709f-a464-04c45a151e79", + "name": "y0", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28c-ffb3-709f-a464-099dabf466b2", + "tag": "formal" + }, + "019eb28d-1a94-74ab-aad3-d1bd254c7211": { + "content": { + "id": "019eb28d-1a94-74ab-aad3-cd466479aaa9", + "name": "y1", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-1a94-74ab-aad3-d1bd254c7211", + "tag": "formal" + }, + "019eb28d-2ff3-71ae-940a-304b110b6611": { + "content": { + "id": "019eb28d-2ff3-71ae-940a-2dc597614a90", + "name": "y2", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-2ff3-71ae-940a-304b110b6611", + "tag": "formal" + }, + "019eb28d-45bb-773f-ac27-13cbae772c08": { + "content": { + "id": "019eb28d-45bb-773f-ac27-0ea5bebd6cc3", + "name": "y3", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-45bb-773f-ac27-13cbae772c08", + "tag": "formal" + }, + "019eb28d-557f-702c-96e5-46bd58ceb704": { + "content": { + "id": "019eb28d-557f-702c-96e5-407455924e70", + "name": "y4", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-557f-702c-96e5-46bd58ceb704", + "tag": "formal" + }, + "019eb28d-632c-75f7-a8d1-2e96641dc914": { + "content": { + "id": "019eb28d-632b-77ae-b839-21f5750732cf", + "name": "y5", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-632c-75f7-a8d1-2e96641dc914", + "tag": "formal" + }, + "019eb28d-7404-7026-8ff7-5deea875906f": { + "content": { + "id": "019eb28d-7404-7026-8ff7-59f9d26bdc60", + "name": "y6", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019eb28d-7404-7026-8ff7-5deea875906f", + "tag": "formal" + }, + "019eb28d-93dd-7415-a65b-ff74935aa1cc": { + "content": { + "cod": { + "content": "019eb28c-ffb3-709f-a464-04c45a151e79", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28c-ceec-779a-b09d-67307cb2d5c2", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28d-93dd-7415-a65b-fb11d1604825", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28e-b02f-754a-b7ec-7c9df716cac4", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb28d-93dd-7415-a65b-ff74935aa1cc", + "tag": "formal" + }, + "019eb28f-24f6-777e-91e0-c64a037797b6": { + "content": { + "cod": { + "content": "019eb28d-1a94-74ab-aad3-cd466479aaa9", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb289-de33-7179-8cb8-4d3ec8facbea", + "tag": "Basic" + }, + { + "content": "019eb28c-ffb3-709f-a464-04c45a151e79", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28f-24f5-77a7-9f54-d7f17152a8c7", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28d-f5e6-777f-aae0-cb7d08bd35a1", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb28f-24f6-777e-91e0-c64a037797b6", + "tag": "formal" + }, + "019eb28f-c94d-74ed-83e3-cb3cb176279c": { + "content": { + "cod": { + "content": "019eb28d-2ff3-71ae-940a-2dc597614a90", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28c-eadb-7449-b64d-7dcaf19df3f8", + "tag": "Basic" + }, + { + "content": "019eb28c-ceec-779a-b09d-67307cb2d5c2", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb28f-c94d-74ed-83e3-c553870c4524", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb28f-792e-7414-807f-3f3ba6138ed7", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb28f-c94d-74ed-83e3-cb3cb176279c", + "tag": "formal" + }, + "019eb290-42ed-759b-acb2-6b4426897467": { + "content": { + "cod": { + "content": "019eb28d-45bb-773f-ac27-0ea5bebd6cc3", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28d-1a94-74ab-aad3-cd466479aaa9", + "tag": "Basic" + }, + { + "content": "019eb28d-2ff3-71ae-940a-2dc597614a90", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb290-42ed-759b-acb2-65f2ab40a42d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb290-9d67-769e-a374-775d25df2eed", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb290-42ed-759b-acb2-6b4426897467", + "tag": "formal" + }, + "019eb293-832d-748f-8ef9-cb5abd7cc60a": { + "content": { + "cod": { + "content": "019eb28d-557f-702c-96e5-407455924e70", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28c-ceec-779a-b09d-67307cb2d5c2", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb293-832d-748f-8ef9-c76852cec2d4", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb290-1d17-720f-855d-4d03eb5d79d1", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb293-832d-748f-8ef9-cb5abd7cc60a", + "tag": "formal" + }, + "019eb293-fe05-744c-8d77-570fe94d3515": { + "content": { + "cod": { + "content": "019eb28d-632b-77ae-b839-21f5750732cf", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28d-45bb-773f-ac27-0ea5bebd6cc3", + "tag": "Basic" + }, + { + "content": "019eb28d-557f-702c-96e5-407455924e70", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb293-fe05-744c-8d77-5240e4200dc9", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb293-c69e-75eb-b499-a8a62a67e559", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb293-fe05-744c-8d77-570fe94d3515", + "tag": "formal" + }, + "019eb294-887d-745c-bae2-865a152cd301": { + "content": { + "cod": { + "content": "019eb28d-7404-7026-8ff7-59f9d26bdc60", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb289-de33-7179-8cb8-4d3ec8facbea", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb294-887d-745c-bae2-817b2fa1fba6", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb294-5186-76ff-9cc6-49124d01a884", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb294-887d-745c-bae2-865a152cd301", + "tag": "formal" + }, + "019eb295-1cc8-708b-be15-52de425b3b91": { + "content": { + "cod": { + "content": "019eb28d-632b-77ae-b839-21f5750732cf", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019eb28d-7404-7026-8ff7-59f9d26bdc60", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019eb295-1cc8-708b-be15-4fd233642073", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019eb294-d8bd-7508-b27d-18ae5f93db80", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019eb295-1cc8-708b-be15-52de425b3b91", + "tag": "formal" + } + }, + "cellOrder": [ + "019eb289-de33-7179-8cb8-53c018616fdd", + "019eb28c-ceed-7781-a89e-ca6cefa6778f", + "019eb28c-eadb-7449-b64d-8018dbc2b6dc", + "019eb28c-ffb3-709f-a464-099dabf466b2", + "019eb28d-1a94-74ab-aad3-d1bd254c7211", + "019eb28d-2ff3-71ae-940a-304b110b6611", + "019eb28d-45bb-773f-ac27-13cbae772c08", + "019eb28d-557f-702c-96e5-46bd58ceb704", + "019eb28d-632c-75f7-a8d1-2e96641dc914", + "019eb28d-7404-7026-8ff7-5deea875906f", + "019eb28d-93dd-7415-a65b-ff74935aa1cc", + "019eb28f-24f6-777e-91e0-c64a037797b6", + "019eb28f-c94d-74ed-83e3-cb3cb176279c", + "019eb290-42ed-759b-acb2-6b4426897467", + "019eb293-832d-748f-8ef9-cb5abd7cc60a", + "019eb293-fe05-744c-8d77-570fe94d3515", + "019eb294-887d-745c-bae2-865a152cd301", + "019eb295-1cc8-708b-be15-52de425b3b91" + ] + }, + "type": "diagram", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/ns_vorticity/model_dec_fragment.json b/packages/catlog/examples/tt/notebook/ns_vorticity/model_dec_fragment.json new file mode 100644 index 000000000..dc5e42864 --- /dev/null +++ b/packages/catlog/examples/tt/notebook/ns_vorticity/model_dec_fragment.json @@ -0,0 +1,500 @@ +{ + "name": "DEC Fragment", + "notebook": { + "cellContents": { + "019df3f4-3ec3-7069-94bd-60cf0befac08": { + "content": { + "id": "019df3f4-3ec3-7069-94bd-5f5097133079", + "name": "Dual 0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-3ec3-7069-94bd-60cf0befac08", + "tag": "formal" + }, + "019df3f4-7663-7519-ba02-a2826c5d8708": { + "content": { + "id": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "name": "Dual 2-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-7663-7519-ba02-a2826c5d8708", + "tag": "formal" + }, + "019df3f4-8444-70e8-b8a3-6eff98e1c9d4": { + "content": { + "id": "019df3f4-8443-71d6-a106-e211b1f1a359", + "name": "0-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df3f4-8444-70e8-b8a3-6eff98e1c9d4", + "tag": "formal" + }, + "019df508-fe6a-751c-92e0-f04f0cf69a1a": { + "content": { + "cod": { + "content": "019df509-6110-70d2-91fd-d81a3b4eab88", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df508-fe6a-751c-92e0-ed1ffccbfb20", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "0-inv-star", + "tag": "morphism" + }, + "id": "019df508-fe6a-751c-92e0-f04f0cf69a1a", + "tag": "formal" + }, + "019df509-56a6-7656-98e6-8f4da9c812c9": { + "content": { + "id": "019df509-56a6-7656-98e6-896813fa1052", + "name": "1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-56a6-7656-98e6-8f4da9c812c9", + "tag": "formal" + }, + "019df509-6110-70d2-91fd-dd2a8dade102": { + "content": { + "id": "019df509-6110-70d2-91fd-d81a3b4eab88", + "name": "2-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-6110-70d2-91fd-dd2a8dade102", + "tag": "formal" + }, + "019df509-6978-72bf-9362-13b7ad373887": { + "content": { + "id": "019df509-6978-72bf-9362-0e085ed78843", + "name": "Dual 1-Form", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df509-6978-72bf-9362-13b7ad373887", + "tag": "formal" + }, + "019df509-9301-77d7-bdd5-43a3d908fbb8": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df509-9301-77d7-bdd5-3ffaea8a24a1", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "partial", + "tag": "morphism" + }, + "id": "019df509-9301-77d7-bdd5-43a3d908fbb8", + "tag": "formal" + }, + "019df509-e051-77aa-9899-df703a8c58cf": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df509-e051-77aa-9899-db60385d3b57", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "0-d", + "tag": "morphism" + }, + "id": "019df509-e051-77aa-9899-df703a8c58cf", + "tag": "formal" + }, + "019df50a-6e39-7216-9b89-8a070cefc041": { + "content": { + "cod": { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50a-6e39-7216-9b89-87168f762a5c", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "1-star", + "tag": "morphism" + }, + "id": "019df50a-6e39-7216-9b89-8a070cefc041", + "tag": "formal" + }, + "019df50b-da40-7399-9c85-0f8ae1241f0d": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50b-da40-7399-9c85-094a02bde699", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "sharp-flat", + "tag": "morphism" + }, + "id": "019df50b-da40-7399-9c85-0f8ae1241f0d", + "tag": "formal" + }, + "019df50c-6981-7248-8a82-b42acdb471f0": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50c-6981-7248-8a82-b1da5d63ab63", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "inv-laplace", + "tag": "morphism" + }, + "id": "019df50c-6981-7248-8a82-b42acdb471f0", + "tag": "formal" + }, + "019df50d-0c28-71f9-bcc4-69bbe9ca152e": { + "content": { + "cod": { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-3ec3-7069-94bd-5f5097133079", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df50d-0c28-71f9-bcc4-643ca621044d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dual 0-d", + "tag": "morphism" + }, + "id": "019df50d-0c28-71f9-bcc4-69bbe9ca152e", + "tag": "formal" + }, + "019df522-b423-7419-880c-82c4601bf9c4": { + "content": { + "cod": { + "content": "019df509-56a6-7656-98e6-896813fa1052", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df522-b423-7419-880c-7c8b6f62eb38", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dp-wedge-10", + "tag": "morphism" + }, + "id": "019df522-b423-7419-880c-82c4601bf9c4", + "tag": "formal" + }, + "019df526-5735-724b-b232-1a1aafd58233": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df526-5735-724b-b232-140c13a4a438", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "2-inv-star", + "tag": "morphism" + }, + "id": "019df526-5735-724b-b232-1a1aafd58233", + "tag": "formal" + }, + "019df526-a755-75f8-8528-f74ced38a971": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df526-a755-75f8-8528-f381f4feec4d", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "dual 1-d", + "tag": "morphism" + }, + "id": "019df526-a755-75f8-8528-f74ced38a971", + "tag": "formal" + }, + "019df528-97dd-75f8-b9fd-c193b73d0b21": { + "content": "I assume this is correct...", + "id": "019df528-97dd-75f8-b9fd-c193b73d0b21", + "tag": "rich-text" + }, + "019df5ea-62d9-761e-8c9c-9fc2629f83f0": { + "content": { + "cod": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5ea-62d9-761e-8c9c-9b5cf73e7240", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "-", + "tag": "morphism" + }, + "id": "019df5ea-62d9-761e-8c9c-9fc2629f83f0", + "tag": "formal" + }, + "019df91e-8033-716b-8526-1414019c5649": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df91e-8033-716b-8526-12e08e9bec59", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "laplace", + "tag": "morphism" + }, + "id": "019df91e-8033-716b-8526-1414019c5649", + "tag": "formal" + }, + "019df920-3a9a-70c4-821e-ac6de6a10c70": { + "content": { + "cod": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df920-3a9a-70c4-821e-a871751e58ec", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "multiplication", + "tag": "morphism" + }, + "id": "019df920-3a9a-70c4-821e-ac6de6a10c70", + "tag": "formal" + } + }, + "cellOrder": [ + "019df3f4-8444-70e8-b8a3-6eff98e1c9d4", + "019df509-56a6-7656-98e6-8f4da9c812c9", + "019df509-6110-70d2-91fd-dd2a8dade102", + "019df3f4-3ec3-7069-94bd-60cf0befac08", + "019df509-6978-72bf-9362-13b7ad373887", + "019df3f4-7663-7519-ba02-a2826c5d8708", + "019df920-3a9a-70c4-821e-ac6de6a10c70", + "019df91e-8033-716b-8526-1414019c5649", + "019df50c-6981-7248-8a82-b42acdb471f0", + "019df509-e051-77aa-9899-df703a8c58cf", + "019df50a-6e39-7216-9b89-8a070cefc041", + "019df50b-da40-7399-9c85-0f8ae1241f0d", + "019df50d-0c28-71f9-bcc4-69bbe9ca152e", + "019df508-fe6a-751c-92e0-f04f0cf69a1a", + "019df526-a755-75f8-8528-f74ced38a971", + "019df526-5735-724b-b232-1a1aafd58233", + "019df509-9301-77d7-bdd5-43a3d908fbb8", + "019df528-97dd-75f8-b9fd-c193b73d0b21", + "019df522-b423-7419-880c-82c4601bf9c4", + "019df5ea-62d9-761e-8c9c-9fc2629f83f0" + ] + }, + "theory": "dec", + "type": "model", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/ns_vorticity/ns_vorticity.json b/packages/catlog/examples/tt/notebook/ns_vorticity/ns_vorticity.json new file mode 100644 index 000000000..cd3eeec1a --- /dev/null +++ b/packages/catlog/examples/tt/notebook/ns_vorticity/ns_vorticity.json @@ -0,0 +1,483 @@ +{ + "diagramIn": { + "_id": "019df1c9-1c72-72d3-a0f7-9de7af7db5d5", + "_server": "backend-next.catcolab.org", + "_version": null, + "type": "diagram-in" + }, + "name": "NS Vorticity", + "notebook": { + "cellContents": { + "019df529-6dbd-70ad-b1bb-452cb63e81ef": { + "content": { + "id": "019df529-6dbc-74ef-b09b-e9033b1cd729", + "name": "v", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df509-6978-72bf-9362-0e085ed78843", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df529-6dbd-70ad-b1bb-452cb63e81ef", + "tag": "formal" + }, + "019df529-8da6-763a-b0b6-f0fcca704e74": { + "content": { + "id": "019df529-8da6-763a-b0b6-ef7faa28e8f2", + "name": "dv", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-7663-7519-ba02-9cadb41b87f0", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df529-8da6-763a-b0b6-f0fcca704e74", + "tag": "formal" + }, + "019df529-ab54-760c-a5d6-d5417c8ad548": { + "content": { + "id": "019df529-ab54-760c-a5d6-d16cd2905323", + "name": "psi", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "over": { + "content": "019df3f4-8443-71d6-a106-e211b1f1a359", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019df529-ab54-760c-a5d6-d5417c8ad548", + "tag": "formal" + }, + "019df53a-d21a-76b8-a969-a907758040d7": { + "content": { + "cod": { + "content": "019df5e1-f2ff-7458-9b18-7552e4ed2411", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df529-8da6-763a-b0b6-ef7faa28e8f2", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df53a-d21a-76b8-a969-a408c47360a3", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df508-fe6a-751c-92e0-ed1ffccbfb20", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df53a-d21a-76b8-a969-a907758040d7", + "tag": "formal" + }, + "019df5e1-fe53-737f-ae36-9f9724456c7f": { + "content": { + "cod": { + "content": "019df529-ab54-760c-a5d6-d16cd2905323", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e1-f2ff-7458-9b18-7552e4ed2411", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e1-fe53-737f-ae36-9a8a537486c7", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df50c-6981-7248-8a82-b1da5d63ab63", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e1-fe53-737f-ae36-9f9724456c7f", + "tag": "formal" + }, + "019df5e5-af2b-7008-9f4c-db1a24416d55": { + "content": { + "cod": { + "content": "019df5e5-c238-72a0-bfd8-07e43d5877bc", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df529-ab54-760c-a5d6-d16cd2905323", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e5-af2b-7008-9f4c-d6019e3131f4", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df509-e051-77aa-9899-db60385d3b57", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e5-af2b-7008-9f4c-db1a24416d55", + "tag": "formal" + }, + "019df5e5-dc48-73d8-80ff-b0696f8fcfc7": { + "content": { + "cod": { + "content": "019df529-6dbc-74ef-b09b-e9033b1cd729", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e5-c238-72a0-bfd8-07e43d5877bc", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e5-dc48-73d8-80ff-adce21a06334", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df50a-6e39-7216-9b89-87168f762a5c", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e5-dc48-73d8-80ff-b0696f8fcfc7", + "tag": "formal" + }, + "019df5e6-1b54-7791-b6d6-2dd1f0f34ca6": { + "content": { + "cod": { + "content": "019df5e7-2dea-777b-beaf-1d649ae5dc63", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df529-6dbc-74ef-b09b-e9033b1cd729", + "tag": "Basic" + }, + { + "content": "019df5e7-cd6a-757d-989b-821888c27cc6", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e6-1b54-7791-b6d6-2b773528d386", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df522-b423-7419-880c-7c8b6f62eb38", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e6-1b54-7791-b6d6-2dd1f0f34ca6", + "tag": "formal" + }, + "019df5e6-7de5-72dd-a1ca-ce67fdb559d5": { + "content": { + "cod": { + "content": "019df5e7-3b4f-769e-8b78-4e7aa6ed6cca", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df529-6dbc-74ef-b09b-e9033b1cd729", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e6-7de5-72dd-a1ca-c94fec27e219", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df526-a755-75f8-8528-f381f4feec4d", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e6-7de5-72dd-a1ca-ce67fdb559d5", + "tag": "formal" + }, + "019df5e7-54dd-73a3-9d4d-fe96f8f88e34": { + "content": { + "cod": { + "content": "019df5e7-cd6a-757d-989b-821888c27cc6", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e7-3b4f-769e-8b78-4e7aa6ed6cca", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e7-54dd-73a3-9d4d-fb851fab23eb", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df508-fe6a-751c-92e0-ed1ffccbfb20", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e7-54dd-73a3-9d4d-fe96f8f88e34", + "tag": "formal" + }, + "019df5e8-04c5-76a7-94f0-f3ba870ea0a4": { + "content": { + "cod": { + "content": "019df5e8-1e54-742a-a46b-e67afce0c7b9", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e7-2dea-777b-beaf-1d649ae5dc63", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e8-04c5-76a7-94f0-ecd7e73b9b5a", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df50b-da40-7399-9c85-094a02bde699", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e8-04c5-76a7-94f0-f3ba870ea0a4", + "tag": "formal" + }, + "019df5e8-3257-7148-b61b-4022cead3750": { + "content": { + "cod": { + "content": "019df5e9-1ff1-76be-876c-9ca1ff79c322", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df529-8da6-763a-b0b6-ef7faa28e8f2", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e8-3257-7148-b61b-3d04eb245d60", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df509-9301-77d7-bdd5-3ffaea8a24a1", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e8-3257-7148-b61b-4022cead3750", + "tag": "formal" + }, + "019df5e8-a1a0-7387-ba3d-4c14e3c0d09c": { + "content": { + "cod": { + "content": "019df5e8-d2b9-7747-a43b-13c7b6c6a0ff", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e8-1e54-742a-a46b-e67afce0c7b9", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e8-a1a0-7387-ba3d-4a014db3e8dc", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df50a-6e39-7216-9b89-87168f762a5c", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e8-a1a0-7387-ba3d-4c14e3c0d09c", + "tag": "formal" + }, + "019df5e9-3517-729b-8ead-9ff927e40a12": { + "content": { + "cod": { + "content": "019df5ea-23eb-714f-aceb-d3c8130fb2a3", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5e8-d2b9-7747-a43b-13c7b6c6a0ff", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5e9-3517-729b-8ead-98793f1ab691", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df526-a755-75f8-8528-f381f4feec4d", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5e9-3517-729b-8ead-9ff927e40a12", + "tag": "formal" + }, + "019df5ea-2af4-709f-8807-fcfccfdd5cdc": { + "content": { + "cod": { + "content": "019df5e9-1ff1-76be-876c-9ca1ff79c322", + "tag": "Basic" + }, + "dom": { + "content": { + "modality": "List", + "objects": [ + { + "content": "019df5ea-23eb-714f-aceb-d3c8130fb2a3", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "id": "019df5ea-2af4-709f-8807-f8981a83d190", + "morType": { + "content": "Multihom", + "tag": "Basic" + }, + "name": "", + "over": { + "content": "019df5ea-62d9-761e-8c9c-9b5cf73e7240", + "tag": "Basic" + }, + "tag": "morphism" + }, + "id": "019df5ea-2af4-709f-8807-fcfccfdd5cdc", + "tag": "formal" + } + }, + "cellOrder": [ + "019df529-6dbd-70ad-b1bb-452cb63e81ef", + "019df529-8da6-763a-b0b6-f0fcca704e74", + "019df529-ab54-760c-a5d6-d5417c8ad548", + "019df53a-d21a-76b8-a969-a907758040d7", + "019df5e1-fe53-737f-ae36-9f9724456c7f", + "019df5e5-af2b-7008-9f4c-db1a24416d55", + "019df5e5-dc48-73d8-80ff-b0696f8fcfc7", + "019df5e6-7de5-72dd-a1ca-ce67fdb559d5", + "019df5e7-54dd-73a3-9d4d-fe96f8f88e34", + "019df5e6-1b54-7791-b6d6-2dd1f0f34ca6", + "019df5e8-04c5-76a7-94f0-f3ba870ea0a4", + "019df5e8-a1a0-7387-ba3d-4c14e3c0d09c", + "019df5e8-3257-7148-b61b-4022cead3750", + "019df5e9-3517-729b-8ead-9ff927e40a12", + "019df5ea-2af4-709f-8807-fcfccfdd5cdc" + ] + }, + "type": "diagram", + "version": "2" +} diff --git a/packages/catlog/examples/tt/notebook/sir_petri.json b/packages/catlog/examples/tt/notebook/sir_petri.json index aea520347..16ef8b07d 100644 --- a/packages/catlog/examples/tt/notebook/sir_petri.json +++ b/packages/catlog/examples/tt/notebook/sir_petri.json @@ -1 +1,180 @@ -{"name":"SIR","notebook":{"cellContents":{"019c34ca-5c0e-77af-88e3-c7ae9e1936cc":{"content":{"id":"019c34ca-5c0e-77af-88e3-c2d4487cadaf","name":"S","obType":{"content":"Object","tag":"Basic"},"tag":"object"},"id":"019c34ca-5c0e-77af-88e3-c7ae9e1936cc","tag":"formal"},"019c34ca-8090-7566-9db3-49f54b5d7eae":{"content":{"id":"019c34ca-8090-7566-9db3-45d5fd4f8ffd","name":"I","obType":{"content":"Object","tag":"Basic"},"tag":"object"},"id":"019c34ca-8090-7566-9db3-49f54b5d7eae","tag":"formal"},"019c34ca-83b0-70f9-a861-663b422d3109":{"content":{"id":"019c34ca-83b0-70f9-a861-61ad91bdd5b6","name":"R","obType":{"content":"Object","tag":"Basic"},"tag":"object"},"id":"019c34ca-83b0-70f9-a861-663b422d3109","tag":"formal"},"019c34ca-92a8-702b-b09b-f711a18563b8":{"content":{"cod":{"content":{"ob":{"content":{"modality":"SymmetricList","objects":[{"content":"019c34ca-8090-7566-9db3-45d5fd4f8ffd","tag":"Basic"},{"content":"019c34ca-8090-7566-9db3-45d5fd4f8ffd","tag":"Basic"}]},"tag":"List"},"op":{"content":"tensor","tag":"Basic"}},"tag":"App"},"dom":{"content":{"ob":{"content":{"modality":"SymmetricList","objects":[{"content":"019c34ca-5c0e-77af-88e3-c2d4487cadaf","tag":"Basic"},{"content":"019c34ca-8090-7566-9db3-45d5fd4f8ffd","tag":"Basic"}]},"tag":"List"},"op":{"content":"tensor","tag":"Basic"}},"tag":"App"},"id":"019c34ca-92a8-702b-b09b-f303b34fddde","morType":{"content":{"content":"Object","tag":"Basic"},"tag":"Hom"},"name":"infect","tag":"morphism"},"id":"019c34ca-92a8-702b-b09b-f711a18563b8","tag":"formal"},"019c34ca-a808-7119-8682-626850b293e2":{"content":{"cod":{"content":{"ob":{"content":{"modality":"SymmetricList","objects":[{"content":"019c34ca-83b0-70f9-a861-61ad91bdd5b6","tag":"Basic"}]},"tag":"List"},"op":{"content":"tensor","tag":"Basic"}},"tag":"App"},"dom":{"content":{"ob":{"content":{"modality":"SymmetricList","objects":[{"content":"019c34ca-8090-7566-9db3-45d5fd4f8ffd","tag":"Basic"}]},"tag":"List"},"op":{"content":"tensor","tag":"Basic"}},"tag":"App"},"id":"019c34ca-a808-7119-8682-5d964079256e","morType":{"content":{"content":"Object","tag":"Basic"},"tag":"Hom"},"name":"recover","tag":"morphism"},"id":"019c34ca-a808-7119-8682-626850b293e2","tag":"formal"}},"cellOrder":["019c34ca-5c0e-77af-88e3-c7ae9e1936cc","019c34ca-8090-7566-9db3-49f54b5d7eae","019c34ca-83b0-70f9-a861-663b422d3109","019c34ca-92a8-702b-b09b-f711a18563b8","019c34ca-a808-7119-8682-626850b293e2"]},"theory":"petri-net","type":"model","version":"1"} \ No newline at end of file +{ + "name": "SIR", + "notebook": { + "cellContents": { + "019c34ca-5c0e-77af-88e3-c7ae9e1936cc": { + "content": { + "id": "019c34ca-5c0e-77af-88e3-c2d4487cadaf", + "name": "S", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019c34ca-5c0e-77af-88e3-c7ae9e1936cc", + "tag": "formal" + }, + "019c34ca-8090-7566-9db3-49f54b5d7eae": { + "content": { + "id": "019c34ca-8090-7566-9db3-45d5fd4f8ffd", + "name": "I", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019c34ca-8090-7566-9db3-49f54b5d7eae", + "tag": "formal" + }, + "019c34ca-83b0-70f9-a861-663b422d3109": { + "content": { + "id": "019c34ca-83b0-70f9-a861-61ad91bdd5b6", + "name": "R", + "obType": { + "content": "Object", + "tag": "Basic" + }, + "tag": "object" + }, + "id": "019c34ca-83b0-70f9-a861-663b422d3109", + "tag": "formal" + }, + "019c34ca-92a8-702b-b09b-f711a18563b8": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019c34ca-8090-7566-9db3-45d5fd4f8ffd", + "tag": "Basic" + }, + { + "content": "019c34ca-8090-7566-9db3-45d5fd4f8ffd", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019c34ca-5c0e-77af-88e3-c2d4487cadaf", + "tag": "Basic" + }, + { + "content": "019c34ca-8090-7566-9db3-45d5fd4f8ffd", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019c34ca-92a8-702b-b09b-f303b34fddde", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "infect", + "tag": "morphism" + }, + "id": "019c34ca-92a8-702b-b09b-f711a18563b8", + "tag": "formal" + }, + "019c34ca-a808-7119-8682-626850b293e2": { + "content": { + "cod": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019c34ca-83b0-70f9-a861-61ad91bdd5b6", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "dom": { + "content": { + "ob": { + "content": { + "modality": "SymmetricList", + "objects": [ + { + "content": "019c34ca-8090-7566-9db3-45d5fd4f8ffd", + "tag": "Basic" + } + ] + }, + "tag": "List" + }, + "op": { + "content": "tensor", + "tag": "Basic" + } + }, + "tag": "App" + }, + "id": "019c34ca-a808-7119-8682-5d964079256e", + "morType": { + "content": { + "content": "Object", + "tag": "Basic" + }, + "tag": "Hom" + }, + "name": "recover", + "tag": "morphism" + }, + "id": "019c34ca-a808-7119-8682-626850b293e2", + "tag": "formal" + } + }, + "cellOrder": [ + "019c34ca-5c0e-77af-88e3-c7ae9e1936cc", + "019c34ca-8090-7566-9db3-49f54b5d7eae", + "019c34ca-83b0-70f9-a861-663b422d3109", + "019c34ca-92a8-702b-b09b-f711a18563b8", + "019c34ca-a808-7119-8682-626850b293e2" + ] + }, + "theory": "petri-net", + "type": "model", + "version": "1" +} diff --git a/packages/catlog/src/tt/modelgen.rs b/packages/catlog/src/tt/modelgen.rs index 8648200f6..939465b4c 100644 --- a/packages/catlog/src/tt/modelgen.rs +++ b/packages/catlog/src/tt/modelgen.rs @@ -526,6 +526,7 @@ impl DiagramGenerator<'_, DiscreteDblModel, DiscreteDblModelMapping> { // Augmentation produces lift morphisms whose dom and cod // are projections from the body's self, resolving to the // qualified names of generators (declared or specialized). + dbg!(&mt, &dom); let dom_name = neutral_qualified_name(dom).ok_or_else(|| { "morphism domain does not resolve to a generator name".to_string() })?; @@ -633,6 +634,8 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { let src_ot: ModalObType = self.domain.theory().src_type(&mt_inner); // adapt: see note let Some(modal::Modality::List(list_type)) = src_ot.modalities.last() else { + // XXX + dbg!(&mt_inner); return Err("multihom source is not a list object type".to_string()); }; let obs = elems @@ -645,19 +648,76 @@ impl DiagramGenerator<'_, ModalDblModel, ModalDblModelMapping> { })?; ModalOb::List(*list_type, obs) } + TmV_::App(n, tm_v) => { + let ob_op = modal::ModalObOp::generator(QualifiedName::single(*n)); + let op_dom: ModalObType = self.domain.theory().ob_op_dom(&ob_op); + let Some(modal::Modality::List(list_type)) = op_dom.modalities.last() + else { + return Err("ob_op domain is not a list type".to_string()); + }; + match &**tm_v { + TmV_::List(elems) => { + let obs = elems + .iter() + .map(|e| neutral_qualified_name(e).map(ModalOb::Generator)) + .collect::>>() + .ok_or_else(|| "domain element does not resolve".to_string())?; + ModalOb::List(*list_type, obs) + } + _ => return Err("expected list inside ob_op".to_string()), + } + } _ => ModalOb::Generator(neutral_qualified_name(dom).ok_or_else(|| { "morphism domain does not resolve to a generator name".to_string() })?), }; + // + + let cod_ob: ModalOb = match &**cod { + TmV_::List(elems) => { + let src_ot: ModalObType = self.domain.theory().src_type(&mt_inner); // adapt: see note + let Some(modal::Modality::List(list_type)) = src_ot.modalities.last() + else { + // XXX + dbg!(&mt_inner); + return Err("multihom source is not a list object type".to_string()); + }; + let obs = elems + .iter() + .map(|e| neutral_qualified_name(e).map(ModalOb::Generator)) + .collect::>>() + .ok_or_else(|| { + "multihom domain element does not resolve to a generator name" + .to_string() + })?; + ModalOb::List(*list_type, obs) + } + TmV_::App(n, tm_v) => { + let ob_op = modal::ModalObOp::generator(QualifiedName::single(*n)); + let op_cod: ModalObType = self.domain.theory().ob_op_dom(&ob_op); + let Some(modal::Modality::List(list_type)) = op_cod.modalities.last() + else { + return Err("ob_op domain is not a list type".to_string()); + }; + match &**tm_v { + TmV_::List(elems) => { + let obs = elems + .iter() + .map(|e| neutral_qualified_name(e).map(ModalOb::Generator)) + .collect::>>() + .ok_or_else(|| "domain element does not resolve".to_string())?; + ModalOb::List(*list_type, obs) + } + _ => return Err("expected list inside ob_op".to_string()), + } + } + _ => ModalOb::Generator(neutral_qualified_name(cod).ok_or_else(|| { + "morphism domain does not resolve to a generator name".to_string() + })?), + }; - // let dom_name = neutral_qualified_name(dom).ok_or_else(|| { - // "morphism domain does not resolve to a generator name".to_string() - // })?; - let cod_name = neutral_qualified_name(cod).ok_or_else(|| { - "morphism codomain does not resolve to a generator name".to_string() - })?; let qname: QualifiedName = prefix.clone().into(); - self.domain.add_mor(qname.clone(), dom_ob.into(), cod_name.into(), mt_inner); + self.domain.add_mor(qname.clone(), dom_ob.into(), cod_ob.into(), mt_inner); // Mapping target: extract the codomain morphism's name // from the synthetic lift name `~f(e)`. For other shapes // (e.g., a future user-declared morphism in a body), fall @@ -864,7 +924,12 @@ diagram I : @Instance(DEC) := [ let (model_diag, _, _) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag).unwrap(); - let out = JuliaTranspiler::transpile(&src, "Klausmeier", elaborate_to_toplevel); + let diagram = TextModel { + decl: "Klausmeier".into(), + toplevel: elaborate_to_toplevel(&src), + }; + let out = diagram.transpile(); + println!("{}", &out); match model_diag { DblModelDiagramType::Discrete(_) => { diff --git a/packages/catlog/src/tt/notebook_elab.rs b/packages/catlog/src/tt/notebook_elab.rs index 7fbe38408..15057da9d 100644 --- a/packages/catlog/src/tt/notebook_elab.rs +++ b/packages/catlog/src/tt/notebook_elab.rs @@ -5,10 +5,10 @@ //! must be completely different to be well adapted to the notebook interface. //! As a first pass, we are associating cell UUIDs with errors. -use catcolab_document_types::current as nb; +use catcolab_document_types::{current as nb, v2::Mor}; use nonempty::NonEmpty; -use std::str::FromStr; -use uuid::Uuid; +use std::{fmt::Debug, str::FromStr}; +use uuid::{Uuid, uuid}; use super::{context::*, eval::*, prelude::*, stx::*, theory::*, toplevel::*, val::*}; use crate::dbl::{ @@ -112,6 +112,27 @@ impl<'a> Elaborator<'a> { (name, label, ty_s, ty_v) } + fn diag_object_cell( + &mut self, + model: &RecordV, + ob_decl: &nb::DiagramObDecl, + ) -> (NameSegment, LabelSegment, TyS, TyV) { + let name = NameSegment::Uuid(ob_decl.id); + let label = LabelSegment::Text(ustr(&ob_decl.name)); + + let over_uuid = match &ob_decl.over { + Some(nb::Ob::Basic(id)) => id, + _ => panic!("expected basic"), + }; + let over_name = NameSegment::Uuid(Uuid::parse_str(&over_uuid).unwrap()); + let Some((_, (over_label, _))) = model.fields.iter().find(|(n, _)| *n == &over_name) else { + panic!("over reference not found in codomain model"); + }; + + let path = vec![(over_name, *over_label)]; + (name, label, TyS::over(path.clone()), TyV::over(path)) + } + fn lookup_tm(&self, name: VarName) -> Option<(TmS, TmV, TyV)> { let (i, label, ty) = self.ctx.lookup(name)?; let v = self.ctx.env.get(*i).unwrap().clone(); @@ -173,7 +194,7 @@ impl<'a> Elaborator<'a> { let name = QualifiedName::deserialize_str(name).unwrap(); let (stx, val, ty) = self.resolve_name(name.as_slice())?; let TyV_::Morphism(..) = &*ty else { - return None; + return None.unwrap(); }; Some((stx, val, ty)) } @@ -287,6 +308,132 @@ impl<'a> Elaborator<'a> { (name, label, ty_s, ty_v) } + fn diag_morphism_cell_ty( + &mut self, + model: &RecordV, + mor_decl: &nb::DiagramMorDecl, + ) -> (TyS, TyV) { + let over_uuid = match &mor_decl.over { + Some(nb::Mor::Basic(id)) => id, + _ => panic!("expected basic over reference"), + }; + let over_name = NameSegment::Uuid(Uuid::parse_str(&over_uuid).unwrap()); + let Some((_, (_, mor_ty_s))) = model.fields.iter().find(|(n, _)| *n == &over_name) else { + panic!("over morphism not found in codomain"); + }; + let TyS_::Morphism(mt, cod_dom_s, cod_cod_s) = &**mor_ty_s else { + panic!("over reference is not a morphism"); + }; + + let ob_op = match &**cod_dom_s { + TmS_::ObApp(op, _) => Some(*op), + _ => None, + }; + + let mut dom_stxs = Vec::new(); + let mut dom_vals = Vec::new(); + match &mor_decl.dom { + Some(nb::Ob::List { modality, objects }) => { + for ob in objects { + let id = match ob { + Some(nb::Ob::Basic(id)) => id, + _ => panic!(), + }; + let Some((s, v, _)) = + self.lookup_tm(NameSegment::Uuid(Uuid::parse_str(&id).unwrap())) + else { + panic!() + }; + dom_stxs.push(s); + dom_vals.push(v); + } + } + + Some(nb::Ob::Basic(id)) => { + let Some((s, v, _)) = + self.lookup_tm(NameSegment::Uuid(Uuid::parse_str(&id).unwrap())) + else { + panic!() + }; + dom_stxs.push(s); + dom_vals.push(v); + } + + _ => todo!(), + } + + let (dom_s, dom_v) = if let Some(op) = ob_op { + ( + TmS::ob_app(op, TmS::list(dom_stxs.clone())), + TmV::app(op, TmV::list(dom_vals.clone())), + ) + } else { + (TmS::list(dom_stxs.clone()), TmV::list(dom_vals.clone())) + }; + + let mut cod_stxs = Vec::new(); + let mut cod_vals = Vec::new(); + match &mor_decl.cod { + Some(nb::Ob::List { modality, objects }) => { + for ob in objects { + let id = match ob { + Some(nb::Ob::Basic(id)) => id, + _ => panic!(), + }; + let Some((s, v, _)) = + self.lookup_tm(NameSegment::Uuid(Uuid::parse_str(&id).unwrap())) + else { + panic!() + }; + cod_stxs.push(s); + cod_vals.push(v); + } + } + Some(nb::Ob::Basic(id)) => { + let Some((s, v, _)) = + self.lookup_tm(NameSegment::Uuid(Uuid::parse_str(&id).unwrap())) + else { + panic!("{}", id) + }; + cod_stxs.push(s); + cod_vals.push(v); + } + _ => panic!(), + } + + let (cod_s, cod_v) = if let Some(op) = ob_op { + ( + TmS::ob_app(op, TmS::list(cod_stxs.clone())), + TmV::app(op, TmV::list(cod_vals.clone())), + ) + } else { + (TmS::list(cod_stxs.clone()), TmV::list(cod_vals.clone())) + }; + + (TyS::morphism(mt.clone(), dom_s, cod_s), TyV::morphism(mt.clone(), dom_v, cod_v)) + } + + fn diag_morphism_cell( + &mut self, + model: &RecordV, + mor_decl: &nb::DiagramMorDecl, + ) -> (NameSegment, LabelSegment, TyS, TyV) { + let name = NameSegment::Uuid(mor_decl.id); + let label = LabelSegment::Text(ustr(&mor_decl.name)); + + let over_uuid = match &mor_decl.over { + Some(nb::Mor::Basic(id)) => id, + _ => panic!("expected basic"), + }; + let over_name = NameSegment::Uuid(Uuid::parse_str(&over_uuid).unwrap()); + let Some((_, (over_label, _))) = model.fields.iter().find(|(n, _)| *n == &over_name) else { + panic!("over reference not found in codomain model"); + }; + + let (ty_s, ty_v) = self.diag_morphism_cell_ty(model, mor_decl); + (name, over_label.clone(), ty_s, ty_v) + } + fn equation_cell_ty(&mut self, eqn_decl: &nb::EqnDecl) -> (TyS, TyV) { let (lhs_m, rhs_m) = match (&eqn_decl.lhs, &eqn_decl.rhs) { (Some(lhs), Some(rhs)) => (lhs, rhs), @@ -424,33 +571,91 @@ impl<'a> Elaborator<'a> { (name, label, ty_s, ty_v) } - /// Elaborate a notebook into a type. - pub fn notebook<'b>( + // XXX + fn diag_instantiation_cell_ty(&mut self, i_decl: &nb::InstantiatedDiagram) -> (TyS, TyV) { + let name = QualifiedName::single(NameSegment::Uuid(i_decl.id)); + let link = match &i_decl.diagram { + Some(l) => l, + None => return self.ty_error(InvalidDblModel::InvalidLink(name)), + }; + let catcolab_document_types::current::LinkType::Instantiation = link.r#type else { + return self.ty_error(InvalidDblModel::InvalidLink(name)); + }; + let ref_id = ustr(&link.stable_ref.id); + let topname = NameSegment::Text(ref_id); + let Some(TopDecl::Diag(diag_def)) = self.toplevel.declarations.get(&topname) else { + return self.ty_error(InvalidDblModel::InvalidLink(name)); + }; + // if type_def.theory != self.theory { + // return self.ty_error(InvalidDblModel::InvalidLink(name)); + // } + let mut specializations = Vec::new(); + let TyV_::Record(r) = &*diag_def.body_val else { + return self.ty_error(InvalidDblModel::InvalidLink(name)); + }; + let mut r = r.clone(); + for specialization in i_decl.specializations.iter() { + if let (Some(field_id), Some(ob)) = (&specialization.id, &specialization.ob) { + let field_name = NameSegment::Uuid(Uuid::from_str(field_id).unwrap()); + let Some((ob_s, ob_v, ob_type)) = self.ob_syn(ob) else { + continue; + }; + let Some((field_label, field_ty)) = r.fields.get_with_label(field_name) else { + continue; + }; + match &**field_ty { + TyS_::Object(expected_ob_ty) => { + if &ob_type != expected_ob_ty { + continue; + } + } + _ => { + continue; + } + } + specializations.push(( + vec![(field_name, *field_label)], + TyS::sing(TyS::object(ob_type.clone()), ob_s), + )); + r = r.add_specialization( + &[(field_name, *field_label)], + TyV::sing(TyV::object(ob_type), ob_v), + ) + } + } + let ty_s = if specializations.is_empty() { + TyS::topvar(topname) + } else { + TyS::specialize(TyS::topvar(topname), specializations) + }; + (ty_s, TyV::record(r)) + } + + fn diag_instantiation_cell( &mut self, - cells: impl Iterator, + i_decl: &nb::InstantiatedDiagram, + ) -> (NameSegment, LabelSegment, TyS, TyV) { + let name = NameSegment::Uuid(i_decl.id); + let label = LabelSegment::Text(ustr(&i_decl.name)); + let (ty_s, ty_v) = self.diag_instantiation_cell_ty(i_decl); + (name, label, ty_s, ty_v) + } + + fn elaborate_cells<'b, T: 'b + Debug>( + &mut self, + cells: impl Iterator, + sort_key: impl Fn(&T) -> u8, + elab_cell: impl Fn(&mut Self, &T) -> (NameSegment, LabelSegment, TyS, TyV), ) -> (TyS, TyV) { - // Process the cells in dependency order. This is important because the - // UI allows users to reorder cells freely and that shouldn't affect the - // result of elaboration. let mut cells: Vec<_> = cells.collect(); - cells.sort_by_key(|judgment| match judgment { - nb::ModelJudgment::Object(_) => 0, - nb::ModelJudgment::Instantiation(_) => 1, - nb::ModelJudgment::Morphism(_) => 2, - nb::ModelJudgment::Equation(_) => 3, - }); + cells.sort_by_key(|j| sort_key(j)); let mut field_ty_vs = Vec::new(); let self_var = self.intro(name_seg("self"), label_seg("self"), None).unwrap_neu(); let c = self.checkpoint(); for cell in cells { - let (name, label, _, ty_v) = match &cell { - nb::ModelJudgment::Object(ob_decl) => self.object_cell(ob_decl), - nb::ModelJudgment::Morphism(mor_decl) => self.morphism_cell(mor_decl), - nb::ModelJudgment::Instantiation(i_decl) => self.instantiation_cell(i_decl), - nb::ModelJudgment::Equation(eqn_decl) => self.equation_cell(eqn_decl), - }; + let (name, label, _, ty_v) = elab_cell(self, cell); field_ty_vs.push((name, (label, ty_v.clone()))); self.ctx .scope @@ -467,6 +672,52 @@ impl<'a> Elaborator<'a> { let r_v = RecordV::new(self.ctx.env.clone(), field_tys.clone(), Dtry::empty()); (TyS::record(field_tys), TyV::record(r_v)) } + + /// Elaborate a notebook into a type. + pub fn model_notebook<'b>( + &mut self, + cells: impl Iterator, + ) -> (TyS, TyV) { + self.elaborate_cells( + cells, + |j| match j { + nb::ModelJudgment::Object(_) => 0, + nb::ModelJudgment::Instantiation(_) => 1, + nb::ModelJudgment::Morphism(_) => 2, + nb::ModelJudgment::Equation(_) => 3, + }, + |elab, cell| match cell { + nb::ModelJudgment::Object(ob) => elab.object_cell(ob), + nb::ModelJudgment::Morphism(mor) => elab.morphism_cell(mor), + nb::ModelJudgment::Instantiation(i) => elab.instantiation_cell(i), + nb::ModelJudgment::Equation(eq) => elab.equation_cell(eq), + }, + ) + } + + /// Elaborate a diagram notebook into a type. + pub fn diagram_notebook<'b>( + &mut self, + model: TyV, + cells: impl Iterator, + ) -> (TyS, TyV) { + let TyV_::Record(r) = &*model else { panic!() }; + self.elaborate_cells( + cells, + |j| match j { + nb::DiagramJudgment::Object(_) => 0, + nb::DiagramJudgment::Instantiation(_) => 1, + nb::DiagramJudgment::Morphism(_) => 1, + nb::DiagramJudgment::Equation(_) => 2, + }, + |elab, cell| match cell { + nb::DiagramJudgment::Object(ob) => elab.diag_object_cell(r, ob), + nb::DiagramJudgment::Morphism(mor) => elab.diag_morphism_cell(r, mor), + nb::DiagramJudgment::Instantiation(i) => elab.diag_instantiation_cell(i), + nb::DiagramJudgment::Equation(_) => todo!(), // elab.equation_cell(eq), + }, + ) + } } /// Promotes a modality from notebook type to modality for modal theory. @@ -499,20 +750,29 @@ pub fn demote_modality(modality: modal::Modality) -> nb::Modality { #[cfg(test)] mod test { + use catcolab_document_types::v1::DiagramDocumentContent; use expect_test::{Expect, expect}; use serde_json; + use std::any::Any; use std::{fmt::Write, fs}; use ustr::ustr; use crate::dbl::model::DblModelPrinter; - use crate::stdlib::{th_schema, th_sym_monoidal_category}; + use crate::dbl::model_diagram::DblModelDiagram; + use crate::stdlib::{th_multicategory, th_schema, th_sym_monoidal_category}; + use crate::tt::eval::Evaluator; + use crate::tt::modelgen::diagram_from_diag; + use crate::tt::stx::TyS_; + use crate::tt::toplevel::{Diag, TopDecl}; + use crate::tt::util::{Decapodes, JuliaTranspiler}; + use crate::tt::val::{TmV_, TyV_}; use crate::tt::{ modelgen::Model, notebook_elab::Elaborator, theory::{Theory, TheoryDef}, toplevel::Toplevel, }; - use crate::zero::name; + use crate::zero::{NameSegment, name}; use catcolab_document_types::current::ModelDocumentContent; fn elab_example(theory: &Theory, name: &str, expected: Expect) -> Model { @@ -520,7 +780,7 @@ mod test { let doc: ModelDocumentContent = serde_json::from_str(&src).unwrap(); let toplevel = Toplevel::new(Default::default()); let mut elab = Elaborator::new(theory.clone(), &toplevel, ustr("")); - let (_, ty_v) = elab.notebook(doc.notebook.formal_content()); + let (_, ty_v) = elab.model_notebook(doc.notebook.formal_content()); let (model, ns) = Model::from_ty(&toplevel, &theory.definition, &ty_v); let mut out = model.to_doc(&DblModelPrinter::new(), &ns).pretty().to_string(); for error in elab.errors() { @@ -603,4 +863,196 @@ mod test { let eqns: Vec<_> = model.category.equations().collect(); assert_eq!(eqns.len(), 1); } + + /// Heat equation in the guise of a petri net + #[test] + fn heat_eq() { + // LOAD THE MODEL + let th_petri = + Theory::new(name("ThPetri"), TheoryDef::modal_unital(th_sym_monoidal_category())); + let src = + fs::read_to_string(format!("examples/tt/notebook/heat_eq_petri_model.json")).unwrap(); + let doc: ModelDocumentContent = serde_json::from_str(&src).unwrap(); + let toplevel = Toplevel::new(Default::default()); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (_, model_ty_v) = elab.model_notebook(doc.notebook.formal_content()); + + // LOAD THE DIAGRAM + let src = fs::read_to_string(format!("examples/tt/notebook/heat_eq_petri.json")).unwrap(); + let doc: DiagramDocumentContent = serde_json::from_str(&src).unwrap(); + let toplevel = Toplevel::new(Default::default()); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (ty_s, ty_v) = elab.diagram_notebook(model_ty_v.clone(), doc.notebook.formal_content()); + + // The diagram is now a record type value. we must convert it to string + let TyV_::Record(r) = &*ty_v else { panic!() }; + + let TyV_::Record(r) = &*ty_v else { panic!() }; + let eval = Evaluator::empty(&toplevel); + let (self_n, eval) = eval.bind_self(ty_v.clone()); + let self_v = eval.eta_neu(&self_n, &ty_v); + for (name, (label, _)) in r.fields.iter() { + let field_ty = eval.field_ty(&ty_v, &self_v, *name); + // let field_tm = eval.proj(&self_v, *name, *label); + + match &*field_ty { + TyV_::Over(path) => { + // Object declaration: name is the generator, path is the codomain object + println!("ob: {} : @over {:?}", label, path); + } + TyV_::Morphism(mt, dom, cod) => { + // dom/cod are TmV — could be App("tensor", List([...])) or Neu(...) + match (&**dom, &**cod) { + (TmV_::App(op, dom_args), TmV_::App(_, cod_args)) => { + // ob_op morphism: e.g. tensor([u]) -> tensor([partial_u]) + println!( + "mor (op {}): {} -> {}", + op, + eval.quote_tm(dom), + eval.quote_tm(cod) + ); + } + _ => { + // plain morphism + println!("mor: {} -> {}", eval.quote_tm(dom), eval.quote_tm(cod)); + } + } + } + TyV_::Id(_, lhs, rhs) => { + println!("eq: {} == {}", eval.quote_tm(lhs), eval.quote_tm(rhs)); + } + _ => {} // Unit, Sing, Meta, Record (sub-diagrams), etc. + } + } + + // + let over_decls: Vec<_> = r + .fields + .iter() + .filter_map(|(name, (label, ty_s))| { + if let TyS_::Over(path) = &**ty_s { + let names = path.iter().map(|(n, _)| *n).collect(); + Some((vec![*name], (*label, names))) + } else { + None + } + }) + .collect(); + + // old news + let (model, ns) = Model::from_ty(&toplevel, &th_petri.definition, &ty_v); + let diag = Diag::new(th_petri.clone(), model_ty_v.clone(), ty_s, ty_v, over_decls); + + let (model_diag, _, _) = diagram_from_diag(&toplevel, &th_petri.definition, &diag).unwrap(); + + let (mapping, domain) = match model_diag { + crate::dbl::model_diagram::DblModelDiagramType::ModalUnital(DblModelDiagram( + mapping, + domain, + )) => (mapping, domain), + _ => todo!(), + }; + + // let mut out = domain.to_doc(&DblModelPrinter::new(), &ns).pretty().to_string(); + // dbg!(&out); + } + + /// Klausmeier + #[test] + fn klausmeier() { + // LOAD THE MODEL + let th_petri = Theory::new(name("ThDEC"), TheoryDef::modal_unital(th_multicategory())); + let src = + fs::read_to_string(format!("examples/tt/notebook/klausmeier/model_dec_fragment.json")) + .unwrap(); + let doc: ModelDocumentContent = serde_json::from_str(&src).unwrap(); + let mut toplevel = Toplevel::new(Default::default()); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (_, model_ty_v) = elab.model_notebook(doc.notebook.formal_content()); + + let hydro_src = + fs::read_to_string(format!("examples/tt/notebook/klausmeier/hydrodynamics.json")) + .unwrap(); + let hydro_doc: DiagramDocumentContent = serde_json::from_str(&hydro_src).unwrap(); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (hydro_s, hydro_v) = + elab.diagram_notebook(model_ty_v.clone(), hydro_doc.notebook.formal_content()); + // Store under the document ID that Klausmeier's instantiation references + let hydro_doc_id = "019eb37e-eb26-7283-8c68-63d4cb8cd1f7"; // from Klausmeier.json + toplevel.declarations.insert( + NameSegment::Text(ustr(hydro_doc_id)), + TopDecl::Diag(Diag::new( + th_petri.clone(), + model_ty_v.clone(), + hydro_s, + hydro_v, + vec![], + )), + ); + + let phyto_src = + fs::read_to_string(format!("examples/tt/notebook/klausmeier/phytodynamics.json")) + .unwrap(); + let phyto_doc: DiagramDocumentContent = serde_json::from_str(&phyto_src).unwrap(); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (phyto_s, phyto_v) = + elab.diagram_notebook(model_ty_v.clone(), phyto_doc.notebook.formal_content()); + // Store under the document ID that Klausmeier's instantiation references + let phyto_doc_id = "019eb288-c310-7f33-b2c3-171279589942"; + // let phyto_doc_id = "019eb916-9299-7489-836f-80bd1830f913"; // from Klausmeier.json + toplevel.declarations.insert( + NameSegment::Text(ustr(phyto_doc_id)), + TopDecl::Diag(Diag::new( + th_petri.clone(), + model_ty_v.clone(), + phyto_s, + phyto_v, + vec![], + )), + ); + + // LOAD THE DIAGRAM + let src = + fs::read_to_string(format!("examples/tt/notebook/klausmeier/Klausmeier.json")).unwrap(); + let doc: DiagramDocumentContent = serde_json::from_str(&src).unwrap(); + // let toplevel = Toplevel::new(Default::default()); + let mut elab = Elaborator::new(th_petri.clone(), &toplevel, ustr("")); + let (_, ty_v) = elab.diagram_notebook(model_ty_v.clone(), doc.notebook.formal_content()); + + let pode = Decapodes { pode: ty_v }; + let out = pode.transpile(); + println!("{}", &out); + + // The diagram is now a record type value. we must convert it to string + // let TyV_::Record(r) = &*ty_v else { panic!() }; + // let eval = Evaluator::empty(&toplevel); + // let (self_n, eval) = eval.bind_self(ty_v.clone()); + // let self_v = eval.eta_neu(&self_n, &ty_v); + // for (name, (label, _)) in r.fields.iter() { + // let field_ty = eval.field_ty(&ty_v, &self_v, *name); + + // match &*field_ty { + // TyV_::Over(path) => { + // println!("ob: {} : @over {:?}", label, path); + // } + // TyV_::Morphism(_, dom, cod) => match (&**dom, &**cod) { + // (TmV_::App(op, _), TmV_::App(_, _)) => { + // println!( + // "mor (op {}): {} -> {}", + // op, + // eval.quote_tm(dom), + // eval.quote_tm(cod) + // ); + // } + // _ => { + // println!("{}: {} -> {}", label, eval.quote_tm(dom), eval.quote_tm(cod)); + // } + // }, + // TyV_::Id(_, lhs, rhs) => { + // println!("eq: {} == {}", eval.quote_tm(lhs), eval.quote_tm(rhs)); + // } + // _ => {} + // } + // } + } } diff --git a/packages/catlog/src/tt/util/transpiler.rs b/packages/catlog/src/tt/util/transpiler.rs index 6bebeb20a..5ad078fa1 100644 --- a/packages/catlog/src/tt/util/transpiler.rs +++ b/packages/catlog/src/tt/util/transpiler.rs @@ -1,25 +1,161 @@ +use indexmap::{IndexMap, IndexSet}; use regex::Regex; use std::collections::HashMap; use std::sync::LazyLock; +use crate::tt::eval::Evaluator; use crate::tt::modelgen::diagram_from_diag; -use crate::tt::stx::{TmS, TmS_}; +use crate::tt::stx::{TmS, TmS_, TyS_}; use crate::tt::toplevel::{TopDecl, Toplevel}; +use crate::tt::val::{TmV, TyV, TyV_}; use crate::zero::name_seg; +pub trait JuliaTranspiler { + fn transpile(&self) -> String; +} + +pub struct Decapodes { + pub pode: TyV, +} + +impl JuliaTranspiler for Decapodes { + fn transpile(&self) -> String { + let TyV_::Record(r) = &*self.pode else { + panic!() + }; + let toplevel = Toplevel::new(Default::default()); + let eval = Evaluator::empty(&toplevel); + let (self_n, eval) = eval.bind_self(self.pode.clone()); + let self_v = eval.eta_neu(&self_n, &self.pode); + + let mut obs = IndexMap::new(); + let mut mors = IndexSet::new(); + collect_fields(&eval, &self.pode, &self_v, "", &mut obs, &mut mors); + + let mut out = String::new(); + + for (ob, ty) in obs { + // `first` is unhappy + out.push_str(&format!("\t{}::{}\n", ob, ty.first().unwrap())); + } + + for (lhs, rhs) in mors { + out.push_str(&format!("\n\t{} == {}", lhs, rhs)); + } + out + } +} + +fn collect_fields( + eval: &Evaluator, + ty: &TyV, + self_v: &TmV, + prefix: &str, + obs: &mut IndexMap>, + mors: &mut IndexSet<(String, String)>, +) { + let TyV_::Record(r) = &**ty else { return }; + for (name, (label, _)) in r.fields.iter() { + let field_ty = eval.field_ty(ty, self_v, *name); + let field_v = eval.proj(self_v, *name, *label); + let qt = eval.quote_ty(&field_ty); + + let full_label = if prefix.is_empty() { + label.to_string() + } else { + format!("{}_{}", prefix, label) + }; + + match &*qt { + TyS_::Over(path) => { + let p = path.iter().map(|(_, l)| l.to_string()).collect(); + obs.insert(full_label, p); + } + + TyS_::Morphism(_, dom, cod) => { + let dom = to_plain_text(dom); + let cod = to_plain_text(cod); + let op = &format!("{label}"); + if EQ.is_match(op) { + mors.insert((cod, dom)); + } else { + let op = match op { + op if ADD.is_match(op) => "+", + op if SUBTRACT.is_match(op) => "-", + op if MULT.is_match(op) => "*", + op if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), + op if LAPL.is_match(op) => &format!("{}", '\u{0394}'), + op => op, + }; + mors.insert((cod, format!("{op}({dom})"))); + } + } + TyS_::Record(_) => { + // Recurse into sub-diagram + collect_fields(eval, &field_ty, &field_v, &full_label, obs, mors); + } + _ => {} + } + } +} + +static ADD: LazyLock = LazyLock::new(|| Regex::new(r"add-(.+)$").unwrap()); +static SUBTRACT: LazyLock = LazyLock::new(|| Regex::new(r"subtract-(.+)$").unwrap()); +static MULT: LazyLock = LazyLock::new(|| Regex::new(r"multiplication-(.+)$").unwrap()); +static PARTIAL: LazyLock = LazyLock::new(|| Regex::new(r"partial-(.+)$").unwrap()); +static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"laplace-(.+)$").unwrap()); +static EQ: LazyLock = LazyLock::new(|| Regex::new(r"eq-(.+)$").unwrap()); + +fn to_plain_text(tm: &TmS) -> String { + match &**tm { + TmS_::Var(_, name, _) => { + let s = name.to_string(); + if s == "self" { String::new() } else { s } + } + TmS_::Proj(inner, _, label) => { + let prefix = to_plain_text(inner); + let n = label.to_string(); + if prefix.is_empty() { + n + } else { + format!("{prefix}_{n}") + } + } + TmS_::ObApp(op, args) => { + let op = &format!("{op}"); + let op = match op { + _ if ADD.is_match(op) => "+", + _ if SUBTRACT.is_match(op) => "-", + _ if MULT.is_match(op) => "*", + _ if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), + _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), + _ => op, + }; + format!("{op}({})", to_plain_text(args)) + } + TmS_::List(args) => args.iter().map(|a| to_plain_text(a)).collect::>().join(", "), + _ => format!("{}", tm), + } +} + +// ================================================================================= + /// Enables transpiling a .dbltt file to a valid Decapode -pub struct JuliaTranspiler {} +pub struct TextModel { + pub decl: String, + pub toplevel: Toplevel, +} -impl JuliaTranspiler { +impl JuliaTranspiler for TextModel { /// Transpiles a .dbltt file, a valid toplevel declaration in it into a valid Julia expression. In this case, we're fixed to Decapodes.jl - pub fn transpile(src: &str, decl_name: &str, elab: impl Fn(&str) -> Toplevel) -> String { - let toplevel = elab(&src); - let diag = match toplevel.declarations.get(&name_seg(decl_name)) { + fn transpile(&self) -> String { + let diag = match self.toplevel.declarations.get(&name_seg(self.decl.clone())) { Some(TopDecl::Diag(d)) => d.clone(), - _ => panic!("expected {decl_name} to be a diagram declaration"), + _ => panic!(), }; - let Ok((_, _, equations)) = diagram_from_diag(&toplevel, &diag.theory.definition, &diag) + let Ok((_, _, equations)) = + diagram_from_diag(&self.toplevel, &diag.theory.definition, &diag) else { panic!("Error with destructuring diagram") }; @@ -43,8 +179,8 @@ impl JuliaTranspiler { let mut eqs: HashMap = HashMap::new(); let mut substitute: HashMap = HashMap::new(); for (lhs, rhs) in &equations { - let lhs = to_plain(lhs); - let rhs = to_plain(rhs); + let lhs = to_plain_text(lhs); + let rhs = to_plain_text(rhs); if tms.get(&lhs).is_some() && tms.get(&rhs).is_some() { substitute.insert(lhs.clone(), rhs.clone()); } else { @@ -79,41 +215,3 @@ impl JuliaTranspiler { format!("@decapode begin\n{out}\nend") } } - -static ADD: LazyLock = LazyLock::new(|| Regex::new(r"add_(.+)$").unwrap()); -static SUBTRACT: LazyLock = LazyLock::new(|| Regex::new(r"sub_(.+)$").unwrap()); -static MULT: LazyLock = LazyLock::new(|| Regex::new(r"mult_(.+)$").unwrap()); -static PARTIAL: LazyLock = LazyLock::new(|| Regex::new(r"partial_(.+)$").unwrap()); -static LAPL: LazyLock = LazyLock::new(|| Regex::new(r"lapl_(.+)$").unwrap()); - -fn to_plain(tm: &TmS) -> String { - match &**tm { - TmS_::Var(_, name, _) => { - let s = name.to_string(); - if s == "self" { String::new() } else { s } - } - TmS_::Proj(inner, name, _) => { - let prefix = to_plain(inner); - let n = name.to_string(); - if prefix.is_empty() { - n - } else { - format!("{prefix}_{n}") - } - } - TmS_::ObApp(op, args) => { - let op = &format!("{op}"); - let op = match op { - _ if ADD.is_match(op) => "+", - _ if SUBTRACT.is_match(op) => "-", - _ if MULT.is_match(op) => "*", - _ if PARTIAL.is_match(op) => &format!("{}{}", '\u{2202}', '\u{209C}'), - _ if LAPL.is_match(op) => &format!("{}", '\u{0394}'), - _ => op, - }; - format!("{op}({})", to_plain(args)) - } - TmS_::List(args) => args.iter().map(|a| to_plain(a)).collect::>().join(", "), - _ => format!("{}", tm), - } -} diff --git a/packages/document-types/src/v0/diagram_judgment.rs b/packages/document-types/src/v0/diagram_judgment.rs index dc15d04f9..616008af2 100644 --- a/packages/document-types/src/v0/diagram_judgment.rs +++ b/packages/document-types/src/v0/diagram_judgment.rs @@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize}; use tsify::Tsify; use uuid::Uuid; +use crate::v1::Link; + use super::model::{Mor, Ob}; use super::theory::{MorType, ObType}; @@ -80,4 +82,36 @@ pub enum DiagramJudgment { /// Declares an equation between morphisms in the diagram. #[serde(rename = "equation")] Equation(DiagramEqnDecl), + + /// + #[serde(rename = "instantiation")] + Instantiation(InstantiatedDiagram), +} + +/// Instantiates an existing diagram into the current diagram +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi, missing_as_null)] +pub struct InstantiatedDiagram { + /// Human-readable label for the instantiation. + pub name: String, + + /// Globally unique identifier of the instantiation. + pub id: Uuid, + + /// Link to the diagram to instantiate. + pub diagram: Option, + + /// List of specializations to perform on the instantiated diagram. + pub specializations: Vec, +} + +/// A specialization of a generating object in an instantiated diagram. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi, missing_as_null)] +pub struct SpecializeDiagram { + /// ID (qualified name) of generating object to specialize. + pub id: Option, + + /// Object to insert as the specialization. + pub ob: Option, } From c972fa7bb3a7629ac226361cf2a638e9ebadacf2 Mon Sep 17 00:00:00 2001 From: Matt Cuffaro Date: Thu, 11 Jun 2026 22:52:57 -0700 Subject: [PATCH 81/81] WIP: decapodes transpiling --- packages/catlog/src/tt/notebook_elab.rs | 61 ++++++++++++++++------- packages/catlog/src/tt/util/transpiler.rs | 55 +++++++++++++++++--- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/packages/catlog/src/tt/notebook_elab.rs b/packages/catlog/src/tt/notebook_elab.rs index 15057da9d..4f8938157 100644 --- a/packages/catlog/src/tt/notebook_elab.rs +++ b/packages/catlog/src/tt/notebook_elab.rs @@ -597,32 +597,59 @@ impl<'a> Elaborator<'a> { for specialization in i_decl.specializations.iter() { if let (Some(field_id), Some(ob)) = (&specialization.id, &specialization.ob) { let field_name = NameSegment::Uuid(Uuid::from_str(field_id).unwrap()); - let Some((ob_s, ob_v, ob_type)) = self.ob_syn(ob) else { + // let Some((ob_s, ob_v, ob_type)) = self.ob_syn(ob) else { + // println!("OB_SYN: {:#?}", ob); + // continue; + // }; + // let Some((field_label, field_ty)) = r.fields.get_with_label(field_name) else { + // println!("PASSING: {}", field_name); + // continue; + // }; + // match &**field_ty { + // TyS_::Object(expected_ob_ty) => { + // if &ob_type != expected_ob_ty { + // continue; + // } + // } + // _ => { + // continue; + // } + // } + let ob_name = match ob { + nb::Ob::Basic(id) => NameSegment::Uuid(Uuid::parse_str(id).unwrap()), + _ => continue, + }; + let Some((ob_s, ob_v, ob_ty)) = self.lookup_tm(ob_name) else { continue; }; let Some((field_label, field_ty)) = r.fields.get_with_label(field_name) else { continue; }; - match &**field_ty { - TyS_::Object(expected_ob_ty) => { - if &ob_type != expected_ob_ty { - continue; - } - } - _ => { - continue; + // println!("{}::{:#?}", field_ty, ob_ty); + match (&**field_ty, &*ob_ty) { + (TyS_::Over(_), TyV_::Over(path)) => { + specializations.push(( + vec![(field_name, *field_label)], + TyS::sing(TyS::over(path.clone()), ob_s), + )); + r = r.add_specialization( + &[(field_name, *field_label)], + TyV::sing(TyV::over(path.clone()), ob_v), + ); } + _ => continue, } - specializations.push(( - vec![(field_name, *field_label)], - TyS::sing(TyS::object(ob_type.clone()), ob_s), - )); - r = r.add_specialization( - &[(field_name, *field_label)], - TyV::sing(TyV::object(ob_type), ob_v), - ) + // specializations.push(( + // vec![(field_name, *field_label)], + // TyS::sing(TyS::object(ob_type.clone()), ob_s), + // )); + // r = r.add_specialization( + // &[(field_name, *field_label)], + // TyV::sing(TyV::object(ob_type), ob_v), + // ) } } + // dbg!(&specializations); let ty_s = if specializations.is_empty() { TyS::topvar(topname) } else { diff --git a/packages/catlog/src/tt/util/transpiler.rs b/packages/catlog/src/tt/util/transpiler.rs index 5ad078fa1..97309d360 100644 --- a/packages/catlog/src/tt/util/transpiler.rs +++ b/packages/catlog/src/tt/util/transpiler.rs @@ -10,6 +10,8 @@ use crate::tt::toplevel::{TopDecl, Toplevel}; use crate::tt::val::{TmV, TyV, TyV_}; use crate::zero::name_seg; +static ANON: LazyLock = LazyLock::new(|| Regex::new(r"(.+)_(\w+)([0-9]+)$").unwrap()); + pub trait JuliaTranspiler { fn transpile(&self) -> String; } @@ -20,7 +22,7 @@ pub struct Decapodes { impl JuliaTranspiler for Decapodes { fn transpile(&self) -> String { - let TyV_::Record(r) = &*self.pode else { + let TyV_::Record(_) = &*self.pode else { panic!() }; let toplevel = Toplevel::new(Default::default()); @@ -28,15 +30,39 @@ impl JuliaTranspiler for Decapodes { let (self_n, eval) = eval.bind_self(self.pode.clone()); let self_v = eval.eta_neu(&self_n, &self.pode); + let mut subs = HashMap::new(); let mut obs = IndexMap::new(); let mut mors = IndexSet::new(); - collect_fields(&eval, &self.pode, &self_v, "", &mut obs, &mut mors); + collect_fields(&eval, &self.pode, &self_v, "", &mut obs, &mut mors, &mut subs); + + // Remove specialized obs from declarations + for bound in subs.keys() { + // XXX swap_remove is slow, i understand + obs.swap_remove(bound); + } + + // Rewrite morphism terms + let mors: IndexSet<_> = mors + .into_iter() + .map(|(lhs, rhs)| { + let mut lhs = lhs; + let mut rhs = rhs; + // TODO this is not happy. what if a non-variable term matched the replacement substring + for (from, to) in &subs { + lhs = lhs.replace(from.as_str(), to); + rhs = rhs.replace(from.as_str(), to); + } + (lhs, rhs) + }) + .collect(); let mut out = String::new(); for (ob, ty) in obs { // `first` is unhappy - out.push_str(&format!("\t{}::{}\n", ob, ty.first().unwrap())); + if !ANON.is_match(&format!("{ob}")) { + out.push_str(&format!("\t{}::{}\n", ob, ty.first().unwrap())); + } } for (lhs, rhs) in mors { @@ -53,6 +79,7 @@ fn collect_fields( prefix: &str, obs: &mut IndexMap>, mors: &mut IndexSet<(String, String)>, + subs: &mut HashMap, ) { let TyV_::Record(r) = &**ty else { return }; for (name, (label, _)) in r.fields.iter() { @@ -65,19 +92,23 @@ fn collect_fields( } else { format!("{}_{}", prefix, label) }; - match &*qt { TyS_::Over(path) => { let p = path.iter().map(|(_, l)| l.to_string()).collect(); - obs.insert(full_label, p); + if subs.get(&full_label).is_none() { + obs.insert(full_label, p); + } } - TyS_::Morphism(_, dom, cod) => { let dom = to_plain_text(dom); let cod = to_plain_text(cod); let op = &format!("{label}"); if EQ.is_match(op) { - mors.insert((cod, dom)); + if obs.contains_key(&dom) && obs.contains_key(&cod) { + subs.insert(dom, cod); + } else { + mors.insert((cod, dom)); + } } else { let op = match op { op if ADD.is_match(op) => "+", @@ -90,9 +121,17 @@ fn collect_fields( mors.insert((cod, format!("{op}({dom})"))); } } + TyS_::Sing(_, tm) => { + let target = to_plain_text(tm); + subs.insert(full_label, target); + } TyS_::Record(_) => { // Recurse into sub-diagram - collect_fields(eval, &field_ty, &field_v, &full_label, obs, mors); + collect_fields(eval, &field_ty, &field_v, &full_label, obs, mors, subs); + } + // TODO whither the specializations? + TyS_::Specialize(_, _) => { + collect_fields(eval, &field_ty, &field_v, &full_label, obs, mors, subs); } _ => {} }