diff --git a/cadjoint/plugins/registry.py b/cadjoint/plugins/registry.py
index c1f1345d..d872ec2a 100644
--- a/cadjoint/plugins/registry.py
+++ b/cadjoint/plugins/registry.py
@@ -32,6 +32,12 @@
url = "http://thermal.cadjoint.svc.cluster.local:8000"
schema_hash = "sha256:2a1f..."
+**Seeing the public tier.** ``CADJOINT_PUBLIC_TIER=1`` leaves the private
+tier's kinds (:data:`cadjoint.tier.KINDS`) out of the process-wide registry
+wherever they come from — what a public install sees, on a machine that has
+``diff-brep`` installed. Child processes inherit it, so a playground started
+with it answers every request as the public tier does.
+
Callers ask by name (:func:`get_plugin`) for one specific component, or by
kind (:func:`plugin_for_kind`) for whatever currently fills a slot — which
is what :mod:`cadjoint.fem.tesseracts.chain` and
@@ -57,6 +63,8 @@
ENTRY_POINT_GROUP = "cadjoint.plugins"
#: Environment variable naming a config file (or a directory holding one).
CONFIG_ENV = "CADJOINT_PLUGINS"
+#: Environment variable that hides the private tier's kinds (``1`` / ``true``).
+PUBLIC_TIER_ENV = "CADJOINT_PUBLIC_TIER"
#: The file name looked for in a config directory.
CONFIG_NAME = "plugins.toml"
@@ -464,9 +472,18 @@ def registry() -> PluginRegistry:
with _REGISTRY_LOCK:
if _REGISTRY is None:
_REGISTRY = build_registry()
+ if public_tier_forced():
+ from cadjoint.plugins.contracts import KIND_CONTRACTS
+
+ _REGISTRY = _REGISTRY.without(KIND_CONTRACTS)
return _REGISTRY
+def public_tier_forced() -> bool:
+ """Whether ``$CADJOINT_PUBLIC_TIER`` asks for the public tier only."""
+ return os.environ.get(PUBLIC_TIER_ENV, "").strip().lower() in ("1", "true", "yes", "on")
+
+
def set_registry(new: PluginRegistry | None) -> PluginRegistry | None:
"""Install ``new`` as the process-wide registry, returning the old one.
diff --git a/cadjoint/tier.py b/cadjoint/tier.py
index 99dccdca..fd87ae9e 100644
--- a/cadjoint/tier.py
+++ b/cadjoint/tier.py
@@ -68,31 +68,51 @@
#: What the Meshes window shows for a Gmsh mesh without the tier.
GEOMETRY_FROZEN_NOTE = "GEOMETRY FROZEN · DIFF-BREP NOT INSTALLED"
-#: The one sentence per kind, written for a public user.
+#: The one sentence per kind, written for a public user: what the public
+#: tier does instead, and that the exact path is the private tier's. The
+#: viewer prints these verbatim beside the feature they limit.
_MESSAGES: dict[str, str] = {
PluginKind.NODE_MAP: (
- "node positions of a Gmsh mesh do not follow the design without diff-brep: "
- "the mesh is frozen geometry. Use mesher='tetgen' for a differentiable mesh, "
- "or install diff-brep."
+ "A Gmsh mesh is frozen geometry in the public tier: its nodes do not follow "
+ "the design, because the node map is part of the private tier (diff-brep). "
+ "Use mesher='tetgen' for a mesh that follows the design, or install diff-brep."
),
PluginKind.FEATURE_EDGES: (
- "feature edges are read off the lattice without diff-brep; the exact edge "
- "curves of the derived B-rep are the private tier's."
+ "Feature edges are read off the dual-contouring lattice in the public tier, "
+ "so they are approximate; exact edge curves are part of the private tier "
+ "(diff-brep)."
),
PluginKind.BREP: (
- "the derived B-rep is the private tier's; without diff-brep there is no B-rep "
- "object, only the dual-contour mesh."
+ "Exact B-rep extraction is part of the private tier (diff-brep); the public "
+ "tier has the dual-contour mesh only."
),
PluginKind.STEP_EXPORT: (
- "STEP export is faceted without diff-brep; analytic planes and cylinders from "
- "the derived B-rep are the private tier's."
+ "STEP export is faceted in the public tier: triangles from dual contouring. "
+ "Exact planes, cylinders and edge curves are part of the private tier "
+ "(diff-brep)."
),
PluginKind.DRAG: (
- "dragging a B-rep handle solves the design through the derived B-rep, which "
- "is the private tier's; install diff-brep."
+ "Dragging a B-rep handle solves the design through the exact B-rep, which is "
+ "part of the private tier (diff-brep)."
),
}
+#: Per kind, the tag the viewer shows beside the feature: ``(with the private
+#: tier, without it)``. What you get, then where it comes from.
+_LABELS: dict[str, tuple[str, str]] = {
+ PluginKind.NODE_MAP: ("Follows design · private tier", "Frozen geometry · public tier"),
+ PluginKind.FEATURE_EDGES: ("Exact curves · private tier", "Approximate · lattice"),
+ PluginKind.BREP: ("Exact · private tier", "Dual contouring · public tier"),
+ PluginKind.STEP_EXPORT: ("Exact surfaces · private tier", "Faceted · public tier"),
+ PluginKind.DRAG: ("Exact · private tier", "Unavailable · public tier"),
+}
+
+
+def label(kind: str, available: bool) -> str:
+ """The short tag for ``kind``: what the feature is in this tier."""
+ exact, public = _LABELS.get(str(kind), ("Private tier", "Unavailable · public tier"))
+ return exact if available else public
+
class TierUnavailable(RuntimeError):
"""A private-tier kind was needed and is not filled (or not compatible).
@@ -155,7 +175,14 @@ def as_dict(self) -> dict[str, Any]:
"installed": self.installed,
"provider": PROVIDER,
"contract_version": CONTRACT_VERSION,
- "kinds": {kind: asdict(entry) for kind, entry in self.kinds.items()},
+ "kinds": {
+ kind: {
+ **asdict(entry),
+ "label": label(kind, entry.available),
+ "message": None if entry.available else message(kind, entry.reason),
+ }
+ for kind, entry in self.kinds.items()
+ },
}
def flags(self) -> dict[str, bool]:
diff --git a/cadjoint/viewer/schema/api.py b/cadjoint/viewer/schema/api.py
index ba569e14..d700cddc 100644
--- a/cadjoint/viewer/schema/api.py
+++ b/cadjoint/viewer/schema/api.py
@@ -129,6 +129,10 @@ class KindStatus(Strict):
contract_version: int | None
compatible: bool
reason: str | None
+ label: str
+ """The tag the viewer shows beside the feature, e.g. ``"Faceted · public tier"``."""
+ message: str | None = None
+ """Without the kind: what the public tier does instead (:func:`cadjoint.tier.message`)."""
class CapabilitiesResponse(Strict):
diff --git a/cadjoint/viewer/schema/payloads.d.ts b/cadjoint/viewer/schema/payloads.d.ts
index f8ccce37..85f6190c 100644
--- a/cadjoint/viewer/schema/payloads.d.ts
+++ b/cadjoint/viewer/schema/payloads.d.ts
@@ -640,6 +640,8 @@ export interface KindStatus {
contract_version: number | null;
compatible: boolean;
reason: string | null;
+ label: string;
+ message?: string | null;
}
/** ``GET /api/capabilities``: which private plugin kinds this server can reach. */
diff --git a/cadjoint/viewer/static/assets/design-B0kjMxCa.js b/cadjoint/viewer/static/assets/design-B0kjMxCa.js
new file mode 100644
index 00000000..d15ee267
--- /dev/null
+++ b/cadjoint/viewer/static/assets/design-B0kjMxCa.js
@@ -0,0 +1 @@
+import{C as G,d as me,i as n,c as t,S as H,a as y,s as h,b as j,e as mn,f as Vt,F as C,g as N,t as l,h as Se,j as J,k as F,l as Gt,o as ze,r as vn,L as gn,m as pn,n as ve,p as Q,u as qt,q as bn,v as $n,w as ie,x as Ut,y as Yt,z as Ce,B as fn,P as yn,A as Be,D as he,E as Xt,M as He,G as Ke,H as Ve,I as _n,J as Jt,K as wn,N as xn,O as kn,R as Sn,Q as Cn,T as An,U as En,V as Tn,W as On,X as Mn,Y as In,Z as Ln,_ as Nn,$ as Dn,a0 as Zt,a1 as Rn,a2 as jn,a3 as Pn,a4 as Fe,a5 as Te,a6 as Fn,a7 as Wn,a8 as zn,a9 as Qt,aa as Bn,ab as Hn,ac as nt,ad as lt,ae as Kn}from"./panels-B0c2leWX.js";var Vn=l("