diff --git a/src/components/TreeReactFlow/Flavor.ts b/src/components/TreeReactFlow/Flavor.ts index 92040006..163e1be9 100644 --- a/src/components/TreeReactFlow/Flavor.ts +++ b/src/components/TreeReactFlow/Flavor.ts @@ -15,264 +15,271 @@ export type NodeFlavor = export const commonHoverClasses = "hover:ring hover:ring-4 hover:ring-offset-4"; -export const flavorClasses = (flavor: NodeFlavor): string => { - switch (flavor) { - case "Hole": - return classNames( - "rounded-full border-red-tertiary ring-red-tertiary bg-white-primary", - "hover:ring-red-tertiary", - commonHoverClasses - ); - case "EmptyHole": - return classNames( - "rounded-full border-red-tertiary ring-red-tertiary bg-white-primary", - "hover:ring-red-tertiary", - commonHoverClasses - ); - case "Ann": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "App": - return classNames( - "rounded-md border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", - "hover:ring-blue-tertiary", - commonHoverClasses - ); - case "APP": - return classNames( - "rounded-md border-yellow-secondary ring-yellow-secondary bg-yellow-secondary", - "hover:ring-yellow-secondary", - commonHoverClasses - ); - case "Con": - return classNames( - "rounded-md border-green-primary ring-green-primary bg-white-primary", - "hover:ring-green-primary", - commonHoverClasses - ); - case "KHole": - return classNames( - "rounded-full border-grey-tertiary ring-grey-tertiary bg-white-primary", - "hover:ring-grey-tertiary", - commonHoverClasses - ); - case "KType": - return classNames( - "rounded-md border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", - "hover:ring-grey-tertiary", - commonHoverClasses - ); - case "KFun": - return classNames( - "border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", - "hover:ring-grey-tertiary", - commonHoverClasses - ); - case "Lam": - return classNames( - "rounded-md border-blue-primary ring-blue-primary bg-white-primary", - "hover:ring-blue-primary", - commonHoverClasses - ); - case "LAM": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "GlobalVar": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - case "LocalVar": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - case "Let": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - case "LetType": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - case "Letrec": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - case "Case": - return classNames( - "rounded-md border-yellow-primary ring-yellow-primary bg-yellow-primary", - "hover:ring-yellow-primary", - commonHoverClasses - ); - - // Note: not selectable. - case "CaseWith": - return "rounded-md border-yellow-primary ring-yellow-primary bg-yellow-primary"; +export const flavorClasses = (flavor: NodeFlavor): string => + classNames( + typeOrTermClasses(isTypeLevel(flavor)), + (() => { + switch (flavor) { + case "Hole": + return classNames( + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", + commonHoverClasses + ); + case "EmptyHole": + return classNames( + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", + commonHoverClasses + ); + case "Ann": + return classNames( + "border-black-primary ring-black-primary bg-black-primary", + "hover:ring-black-primary", + commonHoverClasses + ); + case "App": + return classNames( + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "hover:ring-blue-tertiary", + commonHoverClasses + ); + case "APP": + return classNames( + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "hover:ring-blue-tertiary", + commonHoverClasses + ); + case "Con": + return classNames( + "border-green-primary ring-green-primary bg-white-primary", + "hover:ring-green-primary", + commonHoverClasses + ); + case "KHole": + return classNames( + "border-grey-tertiary ring-grey-tertiary bg-white-primary", + "hover:ring-grey-tertiary", + commonHoverClasses + ); + case "KType": + return classNames( + "border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", + "hover:ring-grey-tertiary", + commonHoverClasses + ); + case "KFun": + return classNames( + "border-grey-tertiary ring-grey-tertiary bg-grey-tertiary", + "hover:ring-grey-tertiary", + commonHoverClasses + ); + case "Lam": + return classNames( + "border-blue-primary ring-blue-primary bg-white-primary", + "hover:ring-blue-primary", + commonHoverClasses + ); + case "LAM": + return classNames( + "border-blue-secondary ring-blue-secondary bg-white-primary", + "hover:ring-blue-secondary", + commonHoverClasses + ); + case "GlobalVar": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-white-primary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "LocalVar": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-white-primary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "Let": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "LetType": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "Letrec": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "Case": + return classNames( + "border-yellow-primary ring-yellow-primary bg-yellow-primary", + "hover:ring-yellow-primary", + commonHoverClasses + ); - case "PrimCon": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TEmptyHole": - return classNames( - "rounded-full border-red-primary ring-red-primary bg-white-primary", - "hover:ring-red-primary", - commonHoverClasses - ); - case "THole": - return classNames( - "rounded-full border-red-primary ring-red-primary bg-white-primary", - "hover:ring-red-primary", - commonHoverClasses - ); - case "TCon": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TFun": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TVar": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TApp": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TForall": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-white-primary", - "hover:ring-black-primary", - commonHoverClasses - ); - case "TLet": - return classNames( - "rounded-md border-black-primary ring-black-primary bg-black-primary", - "hover:ring-black-primary", - commonHoverClasses - ); + // Note: not selectable. + case "CaseWith": + return "border-yellow-primary ring-yellow-primary bg-yellow-primary"; - // Note: most parts of patterns aren't selectable. + case "PrimCon": + return classNames( + "border-green-primary ring-green-primary bg-white-primary", + "hover:ring-green-primary", + commonHoverClasses + ); + case "TEmptyHole": + return classNames( + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", + commonHoverClasses + ); + case "THole": + return classNames( + "border-red-tertiary ring-red-tertiary bg-white-primary", + "hover:ring-red-tertiary", + commonHoverClasses + ); + case "TCon": + return classNames( + "border-green-primary ring-green-primary bg-white-primary", + "hover:ring-green-primary", + commonHoverClasses + ); + case "TFun": + return classNames( + "border-blue-primary ring-blue-primary bg-blue-primary", + "hover:ring-blue-primary", + commonHoverClasses + ); + case "TVar": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-white-primary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + case "TApp": + return classNames( + "border-blue-tertiary ring-blue-tertiary bg-blue-tertiary", + "hover:ring-blue-tertiary", + commonHoverClasses + ); + case "TForall": + return classNames( + "border-blue-secondary ring-blue-secondary bg-white-primary", + "hover:ring-blue-secondary", + commonHoverClasses + ); + case "TLet": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-blue-quaternary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); - // This node's background is transparent, so that we can draw - // edges over it. Otherwise, we'd need to special-case the - // z-index of edges when drawn inside a pattern. - case "Pattern": - return "rounded-md border-yellow-primary ring-yellow-primary"; + // Note: most parts of patterns aren't selectable. + // This node's background is transparent, so that we can draw + // edges over it. Otherwise, we'd need to special-case the + // z-index of edges when drawn inside a pattern. + case "Pattern": + return "border-yellow-primary ring-yellow-primary"; - case "PatternCon": - return "rounded-md border-green-primary ring-green-primary bg-white-primary"; - case "PrimPattern": - return "border-black-primary ring-black-primary bg-white-primary"; - case "PatternWildcard": - return "border-none bg-transparent"; - case "PatternBind": - return classNames( - "rounded-md border-blue-quaternary ring-blue-quaternary bg-white-primary", - "hover:ring-blue-quaternary", - commonHoverClasses - ); - } -}; + case "PatternCon": + return "border-green-primary ring-green-primary bg-white-primary"; + case "PrimPattern": + return "border-green-primary ring-green-primary bg-white-primary"; + case "PatternWildcard": + return "border-none bg-transparent"; + case "PatternBind": + return classNames( + "border-blue-quaternary ring-blue-quaternary bg-white-primary", + "hover:ring-blue-quaternary", + commonHoverClasses + ); + } + })() + ); export const flavorContentClasses = ( flavor: NodeFlavorTextBody | NodeFlavorPrimBody | NodeFlavorNoBody -): string => { - switch (flavor) { - case "Hole": - return "font-code italic text-red-tertiary"; - case "EmptyHole": - return "font-code italic text-red-tertiary"; - case "Ann": - return "text-white-primary"; - case "App": - return "text-white-primary"; - case "APP": - return "text-white-primary"; - case "Con": - return "text-blue-primary"; - case "KHole": - return "font-code italic text-grey-tertiary"; - case "KType": - return "text-white-primary"; - case "KFun": - return "text-white-primary"; - case "Lam": - return "text-blue-primary"; - case "LAM": - return "text-blue-primary"; - case "GlobalVar": - return "text-blue-primary"; - case "LocalVar": - return "text-blue-primary"; - case "Let": - return "text-white-primary"; - case "LetType": - return "text-white-primary"; - case "Letrec": - return "text-white-primary"; - case "Case": - return "text-white-primary"; - case "CaseWith": - return "text-white-primary"; - case "PrimCon": - return "text-blue-primary"; - case "TEmptyHole": - return "font-code italic text-red-primary"; - case "THole": - return "font-code italic text-red-primary"; - case "TCon": - return "text-blue-primary"; - case "TFun": - return "text-white-primary"; - case "TVar": - return "text-blue-primary"; - case "TApp": - return "text-white-primary"; - case "TForall": - return "text-blue-primary"; - case "TLet": - return "text-white-primary"; - case "PatternCon": - return "text-blue-primary"; - case "PrimPattern": - return "text-blue-primary"; - case "PatternWildcard": - // We use `scale-150` here because the text is an emoji which doesn't - // respond to Tailwind's font size classes. The `text-grey-secondary` is a - // backup in case the emoji doesn't render on a particular client. - return "text-grey-secondary scale-150"; - case "PatternBind": - return "text-blue-primary"; - } -}; +): string => + classNames( + typeOrTermContentClasses(isTypeLevel(flavor)), + (() => { + switch (flavor) { + case "Hole": + return "font-code italic text-red-tertiary"; + case "EmptyHole": + return "font-code italic text-red-tertiary"; + case "Ann": + return "text-white-primary"; + case "App": + return "text-white-primary"; + case "APP": + return "text-white-primary"; + case "Con": + return "text-blue-primary"; + case "KHole": + return "font-code italic text-grey-tertiary"; + case "KType": + return "text-white-primary"; + case "KFun": + return "text-white-primary"; + case "Lam": + return "text-blue-primary"; + case "LAM": + return "text-blue-secondary"; + case "GlobalVar": + return "text-blue-primary"; + case "LocalVar": + return "text-blue-primary"; + case "Let": + return "text-white-primary"; + case "LetType": + return "text-white-primary"; + case "Letrec": + return "text-white-primary"; + case "Case": + return "text-white-primary"; + case "CaseWith": + return "text-white-primary"; + case "PrimCon": + return "text-blue-primary"; + case "TEmptyHole": + return "font-code italic text-red-primary"; + case "THole": + return "font-code italic text-red-primary"; + case "TCon": + return "text-blue-primary"; + case "TFun": + return "text-white-primary"; + case "TVar": + return "text-blue-primary"; + case "TApp": + return "text-white-primary"; + case "TForall": + return "text-blue-secondary"; + case "TLet": + return "text-white-primary"; + case "PatternCon": + return "text-blue-primary"; + case "PrimPattern": + return "text-blue-primary"; + case "PatternWildcard": + // We use `scale-150` here because the text is an emoji which doesn't + // respond to Tailwind's font size classes. The `text-grey-secondary` is a + // backup in case the emoji doesn't render on a particular client. + return "text-grey-secondary scale-150"; + case "PatternBind": + return "text-blue-primary"; + } + })() + ); export const flavorLabelClasses = (flavor: NodeFlavor): string => { switch (flavor) { @@ -285,13 +292,13 @@ export const flavorLabelClasses = (flavor: NodeFlavor): string => { case "App": return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "APP": - return "font-code bg-yellow-secondary border-yellow-secondary text-white-primary"; + return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "Con": return "bg-green-primary border-green-primary text-white-primary"; case "Lam": return "font-code bg-blue-primary border-blue-primary text-white-primary"; case "LAM": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-secondary border-blue-secondary text-white-primary"; case "GlobalVar": return "bg-blue-quaternary border-blue-quaternary text-white-primary"; case "KHole": @@ -313,29 +320,29 @@ export const flavorLabelClasses = (flavor: NodeFlavor): string => { case "CaseWith": // Special case: we hide this label. return "hidden font-code bg-yellow-primary border-yellow-primary text-white-primary"; case "PrimCon": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "TEmptyHole": - return "italic font-code bg-red-primary border-red-primary text-white-primary"; + return "italic font-code bg-red-tertiary border-red-tertiary text-white-primary"; case "THole": - return "italic font-code bg-red-primary border-red-primary text-white-primary"; + return "italic font-code bg-red-tertiary border-red-tertiary text-white-primary"; case "TCon": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "TFun": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-primary border-blue-primary text-white-primary"; case "TVar": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-blue-quaternary border-blue-quaternary text-white-primary"; case "TApp": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-tertiary border-blue-tertiary text-white-primary"; case "TForall": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-secondary border-blue-secondary text-white-primary"; case "TLet": - return "font-code bg-black-primary border-black-primary text-white-primary"; + return "font-code bg-blue-quaternary border-blue-quaternary text-white-primary"; case "Pattern": - return "bg-yellow-primary border-yellow-primary text-white-primary"; + return ""; case "PatternCon": return "bg-green-primary border-green-primary text-white-primary"; case "PrimPattern": - return "bg-black-primary border-black-primary text-white-primary"; + return "bg-green-primary border-green-primary text-white-primary"; case "PatternWildcard": return "hidden"; case "PatternBind": @@ -354,7 +361,7 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "App": return "stroke-blue-tertiary"; case "APP": - return "stroke-yellow-secondary"; + return "stroke-blue-tertiary"; case "Con": return "stroke-green-primary"; case "KHole": @@ -366,7 +373,7 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "Lam": return "stroke-blue-primary"; case "LAM": - return "stroke-black-primary"; + return "stroke-blue-secondary"; case "GlobalVar": return "stroke-blue-quaternary"; case "LocalVar": @@ -382,31 +389,31 @@ export const flavorEdgeClasses = (flavor: NodeFlavor): string => { case "CaseWith": return "stroke-yellow-primary"; case "PrimCon": - return "stroke-black-primary"; + return "stroke-green-primary"; case "TEmptyHole": - return "stroke-red-primary"; + return "stroke-red-tertiary"; case "THole": - return "stroke-red-primary"; + return "stroke-red-tertiary"; case "TCon": - return "stroke-black-primary"; + return "stroke-green-primary"; case "TFun": - return "stroke-black-primary"; + return "stroke-blue-primary"; case "TVar": - return "stroke-black-primary"; + return "stroke-blue-quaternary"; case "TApp": - return "stroke-black-primary"; + return "stroke-blue-tertiary"; case "TForall": - return "stroke-black-primary"; + return "stroke-blue-secondary"; case "TLet": - return "stroke-black-primary"; + return "stroke-blue-quaternary"; case "Pattern": return "stroke-yellow-primary"; case "PatternCon": return "stroke-green-primary"; case "PrimPattern": - return "stroke-black-primary"; + return "stroke-green-primary"; case "PatternWildcard": - return "stroke-black-primary"; + return "stroke-grey-secondary"; case "PatternBind": return "stroke-blue-quaternary"; } @@ -421,9 +428,9 @@ export const flavorLabel = (flavor: NodeFlavor): string => { case "Ann": return ":"; case "App": - return "$"; + return "←"; case "APP": - return "@"; + return "←"; case "Con": return "V"; case "Lam": @@ -457,13 +464,13 @@ export const flavorLabel = (flavor: NodeFlavor): string => { case "TVar": return "Var"; case "TApp": - return "@"; + return "←"; case "TForall": return "∀"; case "TLet": return "let"; case "Pattern": - return "P"; + return ""; case "PatternCon": return "V"; case "PrimPattern": @@ -522,3 +529,72 @@ export const boxFlavorBackground = (flavor: NodeFlavorBoxBody): string => { return "bg-yellow-primary"; } }; + +/** What sort of node does this flavor correspond to? + * Note that the backend could in principal tell us this independently of flavors, + * since it comes down to whether the node ultimately comes from an `Expr`, `Type` or `Kind`. + */ +export const isTypeLevel = (flavor: NodeFlavor): "term" | "type" | "kind" => { + switch (flavor) { + case "Con": + case "Lam": + case "LAM": + case "Let": + case "Letrec": + case "PatternBind": + case "PatternCon": + case "LetType": + case "GlobalVar": + case "LocalVar": + case "PrimCon": + case "Pattern": + case "Hole": + case "EmptyHole": + case "Ann": + case "App": + case "APP": + case "Case": + case "CaseWith": + case "PatternWildcard": + case "PrimPattern": + return "term"; + case "TCon": + case "TVar": + case "TForall": + case "TLet": + case "TEmptyHole": + case "THole": + case "TFun": + case "TApp": + return "type"; + case "KFun": + case "KHole": + case "KType": + return "kind"; + } +}; + +export const typeOrTermClasses = (x: "term" | "type" | "kind"): string => { + switch (x) { + case "term": + return "rounded-3xl"; + case "type": + return ""; + case "kind": + return "rotate-45"; + } +}; + +export const typeOrTermContentClasses = ( + x: "term" | "type" | "kind" +): string => { + switch (x) { + case "term": + return ""; + case "type": + return ""; + case "kind": + // This keeps the content fixed once the whole node is rotated. See `typeOrTermClasses`. + return "-rotate-45"; + } +}; diff --git a/src/components/TreeReactFlow/index.tsx b/src/components/TreeReactFlow/index.tsx index 6f89b306..1337d3ac 100644 --- a/src/components/TreeReactFlow/index.tsx +++ b/src/components/TreeReactFlow/index.tsx @@ -61,6 +61,7 @@ import { flavorLabel, flavorLabelClasses, noBodyFlavorContents, + typeOrTermClasses, } from "./Flavor"; import { ZoomBar, ZoomBarProps } from "./ZoomBar"; import { WasmLayoutType } from "@zxch3n/tidy/wasm_dist"; @@ -234,7 +235,7 @@ const nodeTypes = { {handle("target", Position.Left)}