@nimblebrain/synapse 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -25
- package/dist/check-QMTOCTRS.js +474 -0
- package/dist/check-QMTOCTRS.js.map +1 -0
- package/dist/check-VK4SOAUU.cjs +480 -0
- package/dist/check-VK4SOAUU.cjs.map +1 -0
- package/dist/{chunk-RU64F5RS.cjs → chunk-BNF352SA.cjs} +57 -146
- package/dist/chunk-BNF352SA.cjs.map +1 -0
- package/dist/{chunk-DRLVEHCD.js → chunk-DJS2FKBN.js} +21 -79
- package/dist/chunk-DJS2FKBN.js.map +1 -0
- package/dist/chunk-H3INUKA3.js +32 -0
- package/dist/chunk-H3INUKA3.js.map +1 -0
- package/dist/chunk-HQLSVUOR.cjs +667 -0
- package/dist/chunk-HQLSVUOR.cjs.map +1 -0
- package/dist/{chunk-NP7ROSH2.cjs → chunk-ISV6HFQE.cjs} +14 -7
- package/dist/chunk-ISV6HFQE.cjs.map +1 -0
- package/dist/chunk-TR2YNWZ5.js +658 -0
- package/dist/chunk-TR2YNWZ5.js.map +1 -0
- package/dist/{chunk-OYLZ5OCK.js → chunk-UKP4W6DI.js} +57 -146
- package/dist/chunk-UKP4W6DI.js.map +1 -0
- package/dist/{chunk-SZWHFTXV.cjs → chunk-YSL7KJNU.cjs} +21 -81
- package/dist/chunk-YSL7KJNU.cjs.map +1 -0
- package/dist/codegen/cli.cjs +41 -4
- package/dist/codegen/cli.cjs.map +1 -1
- package/dist/codegen/cli.js +41 -4
- package/dist/codegen/cli.js.map +1 -1
- package/dist/codegen/index.cjs +0 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/codegen/index.js +0 -1
- package/dist/connect.iife.global.js +34 -34
- package/dist/{detect-DZMJsyvZ.d.cts → detect-DVGaL2bH.d.cts} +39 -33
- package/dist/{detect-DBwc8a21.d.ts → detect-DVGaL2bH.d.ts} +39 -33
- package/dist/host/index.cjs +17 -18
- package/dist/host/index.d.cts +11 -27
- package/dist/host/index.d.ts +11 -27
- package/dist/host/index.js +2 -3
- package/dist/index.cjs +30 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -26
- package/dist/index.d.ts +72 -26
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +9 -10
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +36 -8
- package/dist/react/index.d.ts +36 -8
- package/dist/react/index.js +3 -4
- package/dist/react/index.js.map +1 -1
- package/dist/schema-reader-776246MJ.js +3 -0
- package/dist/{schema-reader-GM4A4L44.js.map → schema-reader-776246MJ.js.map} +1 -1
- package/dist/{schema-reader-MWBRQ2TQ.cjs → schema-reader-B6LTGBMJ.cjs} +2 -3
- package/dist/{schema-reader-MWBRQ2TQ.cjs.map → schema-reader-B6LTGBMJ.cjs.map} +1 -1
- package/dist/{server-LR6T6C44.cjs → server-BR5MNFOB.cjs} +13 -14
- package/dist/server-BR5MNFOB.cjs.map +1 -0
- package/dist/{server-UHPEE55X.js → server-OOEEWAEG.js} +13 -14
- package/dist/server-OOEEWAEG.js.map +1 -0
- package/dist/synapse-runtime.iife.global.js +34 -34
- package/dist/synapse-ui.iife.global.js +4 -4
- package/dist/type-generator-MZ4X4DE5.js +3 -0
- package/dist/{type-generator-ZYSVFK3X.js.map → type-generator-MZ4X4DE5.js.map} +1 -1
- package/dist/{type-generator-YTPFWLVG.cjs → type-generator-UA3L4OIA.cjs} +2 -3
- package/dist/{type-generator-YTPFWLVG.cjs.map → type-generator-UA3L4OIA.cjs.map} +1 -1
- package/dist/{types-CG08aO82.d.cts → types-BxPfGHKO.d.cts} +35 -43
- package/dist/{types-CG08aO82.d.ts → types-BxPfGHKO.d.ts} +35 -43
- package/dist/ui/base.cjs +0 -1
- package/dist/ui/base.cjs.map +1 -1
- package/dist/ui/base.js +0 -1
- package/dist/ui/base.js.map +1 -1
- package/dist/ui/index.cjs +0 -1
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +0 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/vite/index.cjs +10 -10
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.js +6 -6
- package/dist/vite/index.js.map +1 -1
- package/dist/{writer-HDRCQSX2.cjs → writer-NDK4U6C5.cjs} +3 -3
- package/dist/{writer-HDRCQSX2.cjs.map → writer-NDK4U6C5.cjs.map} +1 -1
- package/dist/{writer-QNDPMEYD.js → writer-ZNBXYUYC.js} +3 -3
- package/dist/{writer-QNDPMEYD.js.map → writer-ZNBXYUYC.js.map} +1 -1
- package/package.json +6 -3
- package/dist/chunk-54W6IAHM.js +0 -24
- package/dist/chunk-54W6IAHM.js.map +0 -1
- package/dist/chunk-DRLVEHCD.js.map +0 -1
- package/dist/chunk-NP7ROSH2.cjs.map +0 -1
- package/dist/chunk-OYLZ5OCK.js.map +0 -1
- package/dist/chunk-P5DBJFSQ.js +0 -8459
- package/dist/chunk-P5DBJFSQ.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -9
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-Q7SFCCGT.cjs +0 -11
- package/dist/chunk-Q7SFCCGT.cjs.map +0 -1
- package/dist/chunk-QXDCA7QW.cjs +0 -8466
- package/dist/chunk-QXDCA7QW.cjs.map +0 -1
- package/dist/chunk-RU64F5RS.cjs.map +0 -1
- package/dist/chunk-SZWHFTXV.cjs.map +0 -1
- package/dist/schema-reader-GM4A4L44.js +0 -4
- package/dist/server-LR6T6C44.cjs.map +0 -1
- package/dist/server-UHPEE55X.js.map +0 -1
- package/dist/type-generator-ZYSVFK3X.js +0 -4
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
// src/detection.ts
|
|
2
|
+
var DEFAULT_THEME = {
|
|
3
|
+
mode: "light",
|
|
4
|
+
tokens: {}
|
|
5
|
+
};
|
|
6
|
+
function extractTheme(ctx) {
|
|
7
|
+
if (!ctx) return { ...DEFAULT_THEME };
|
|
8
|
+
const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
|
|
9
|
+
const variables = ctx.styles?.variables;
|
|
10
|
+
const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
|
|
11
|
+
return { mode, tokens };
|
|
12
|
+
}
|
|
13
|
+
function extractHostFontCss(ctx) {
|
|
14
|
+
const fonts = ctx?.styles?.css?.fonts;
|
|
15
|
+
return typeof fonts === "string" && fonts !== "" ? fonts : void 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
1
18
|
// src/theme-defaults.ts
|
|
2
|
-
var FONT_DISPLAY_VALUES = /* @__PURE__ */ new Set([
|
|
3
|
-
"auto",
|
|
4
|
-
"block",
|
|
5
|
-
"swap",
|
|
6
|
-
"fallback",
|
|
7
|
-
"optional"
|
|
8
|
-
]);
|
|
9
19
|
var LIGHT = {
|
|
10
20
|
// ── Surfaces ──
|
|
11
21
|
"--color-background-primary": "#ffffff",
|
|
@@ -101,78 +111,10 @@ function applyThemeVariables(mode, hostVars) {
|
|
|
101
111
|
for (const [k, v] of Object.entries(incoming)) root.setProperty(k, v);
|
|
102
112
|
appliedInlineKeys = new Set(Object.keys(incoming));
|
|
103
113
|
}
|
|
104
|
-
|
|
105
|
-
var appliedFontKey = "";
|
|
106
|
-
function fontFacesKey(faces) {
|
|
107
|
-
if (!Array.isArray(faces)) return "";
|
|
108
|
-
return JSON.stringify(faces.map((d) => [d.family, d.src, d.weight, d.style, d.display]));
|
|
109
|
-
}
|
|
110
|
-
function normalizeFontFaces(raw) {
|
|
111
|
-
if (!Array.isArray(raw)) return void 0;
|
|
112
|
-
const usable = raw.filter(isFontFaceDescriptor);
|
|
113
|
-
if (raw.length > 0 && usable.length === 0) return void 0;
|
|
114
|
-
return usable;
|
|
115
|
-
}
|
|
116
|
-
function applyThemeFontFaces(faces) {
|
|
117
|
-
const next = normalizeFontFaces(faces);
|
|
118
|
-
if (next === void 0) return;
|
|
119
|
-
if (typeof document === "undefined") return;
|
|
120
|
-
if (!document.fonts || typeof FontFace === "undefined") return;
|
|
121
|
-
const key = fontFacesKey(next);
|
|
122
|
-
if (key === appliedFontKey) return;
|
|
123
|
-
appliedFontKey = key;
|
|
124
|
-
for (const face of managedFaces) {
|
|
125
|
-
try {
|
|
126
|
-
document.fonts.delete(face);
|
|
127
|
-
} catch {
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
managedFaces.clear();
|
|
131
|
-
for (const d of next) {
|
|
132
|
-
try {
|
|
133
|
-
const face = new FontFace(d.family, d.src, {
|
|
134
|
-
weight: d.weight,
|
|
135
|
-
style: d.style,
|
|
136
|
-
// Wire data is untyped: ignore an unrecognised `display` rather than let
|
|
137
|
-
// the constructor reject the descriptor and cost the app this face.
|
|
138
|
-
display: d.display && FONT_DISPLAY_VALUES.has(d.display) ? d.display : "swap"
|
|
139
|
-
});
|
|
140
|
-
document.fonts.add(face);
|
|
141
|
-
managedFaces.add(face);
|
|
142
|
-
} catch {
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
function isFontFaceDescriptor(value) {
|
|
147
|
-
if (!value || typeof value !== "object") return false;
|
|
148
|
-
const d = value;
|
|
149
|
-
return typeof d.family === "string" && typeof d.src === "string";
|
|
150
|
-
}
|
|
151
|
-
function applyTheme(mode, hostVars, fontFaces) {
|
|
114
|
+
function applyTheme(mode, hostVars) {
|
|
152
115
|
applyThemeVariables(mode, hostVars);
|
|
153
|
-
applyThemeFontFaces(fontFaces);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// src/detection.ts
|
|
157
|
-
var FONT_FACES_CONTEXT_KEY = "synapse/fontFaces";
|
|
158
|
-
var DEFAULT_THEME = {
|
|
159
|
-
mode: "light",
|
|
160
|
-
tokens: {}
|
|
161
|
-
};
|
|
162
|
-
function extractTheme(ctx) {
|
|
163
|
-
if (!ctx) return { ...DEFAULT_THEME };
|
|
164
|
-
const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
|
|
165
|
-
const variables = ctx.styles?.variables;
|
|
166
|
-
const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
|
|
167
|
-
return { mode, tokens };
|
|
168
|
-
}
|
|
169
|
-
function extractFontFaces(ctx) {
|
|
170
|
-
return normalizeFontFaces(ctx?.[FONT_FACES_CONTEXT_KEY]);
|
|
171
|
-
}
|
|
172
|
-
function foldFontFaces(prev, ctx) {
|
|
173
|
-
return extractFontFaces(ctx) ?? prev;
|
|
174
116
|
}
|
|
175
117
|
|
|
176
|
-
export {
|
|
177
|
-
//# sourceMappingURL=chunk-
|
|
178
|
-
//# sourceMappingURL=chunk-
|
|
118
|
+
export { applyTheme, extractHostFontCss, extractTheme };
|
|
119
|
+
//# sourceMappingURL=chunk-DJS2FKBN.js.map
|
|
120
|
+
//# sourceMappingURL=chunk-DJS2FKBN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/detection.ts","../src/theme-defaults.ts"],"names":[],"mappings":"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","file":"chunk-DJS2FKBN.js","sourcesContent":["import type { McpUiHostContext } from \"@modelcontextprotocol/ext-apps\";\nimport type { Theme } from \"./types\";\n\nconst DEFAULT_THEME: Theme = {\n mode: \"light\",\n tokens: {},\n};\n\nexport function extractTheme(ctx: Partial<McpUiHostContext> | undefined): Theme {\n if (!ctx) return { ...DEFAULT_THEME };\n\n // Spec: theme is a string (\"light\" | \"dark\")\n const mode = ctx.theme === \"light\" || ctx.theme === \"dark\" ? ctx.theme : DEFAULT_THEME.mode;\n\n // Spec: tokens live under styles.variables\n const variables = ctx.styles?.variables;\n const tokens =\n variables && typeof variables === \"object\" && !Array.isArray(variables)\n ? (variables as Record<string, string>)\n : {};\n\n return { mode, tokens };\n}\n\n/**\n * The host's `@font-face` CSS, from the spec's `styles.css.fonts`, or\n * `undefined` when this context carries none.\n */\nexport function extractHostFontCss(ctx: Partial<McpUiHostContext> | undefined): string | undefined {\n const fonts = ctx?.styles?.css?.fonts;\n return typeof fonts === \"string\" && fonts !== \"\" ? fonts : undefined;\n}\n","/**\n * Theme-aware default backing for the `@nimblebrain/synapse/ui` token contract.\n *\n * `tokens` (see `ui/tokens.ts`) are `var(--token, fallback)` references. A CSS\n * `var()` fallback is a *static literal* — it cannot branch on light vs. dark.\n * So any var the host does NOT inject resolves to its single hardcoded (light)\n * fallback in BOTH themes. Pair such a token with a theme-aware one and you get\n * white-on-white in dark mode: it looks correct in light, passes `tsc`/build,\n * and only breaks when the theme is toggled.\n *\n * This module closes that gap with a default layer the SDK controls and that\n * CAN branch on theme. {@link applyThemeVariables} puts the neutral defaults for\n * the active `mode` in a cascade layer and the host's variables inline — so:\n * - the host's (brand) values always win for the keys it provides, through\n * either the protocol or a stylesheet it injects, and\n * - any var nobody declares still resolves to a theme-correct neutral value.\n *\n * It's the `var()` fallback, but able to branch on theme. The values stay\n * neutral grays, a generic blue, and generic semantic hues for danger, success,\n * warning and processing — never a *brand* value. Brand arrives only by host\n * injection, mirroring `ui/tokens.ts`. `__tests__/ui/tokens.test.ts` holds the\n * line: every hex here must be in the sanctioned set. This keeps\n * the library host-agnostic while guaranteeing every token resolves correctly\n * in both themes even against an incomplete host, a standalone `connect()`\n * widget, or a third-party host.\n *\n * Font *faces* are the one part of theming CSS variables cannot carry — a token\n * names a family, it cannot load one. The spec carries them separately, as\n * `@font-face` CSS in `hostContext.styles.css.fonts`, and each client applies\n * that string itself. The CSS is injected as the host sent it: it is trusted,\n * because the host already controls the frame it renders into.\n *\n * Only theme-sensitive (color) vars are listed here. Theme-invariant vars\n * (radii, type scale, shadows, font stacks, weights, border widths) look the\n * same in both themes, so their static `var()` fallback is already correct —\n * they are intentionally absent. The `tokens` regression test enforces this\n * with a TOTAL partition: every referenced var must be either declared\n * theme-invariant (an explicit allowlist) or defined in both maps below, so a\n * newly added theme-sensitive token can't slip through unbacked.\n */\n\n/**\n * Light-theme neutral defaults. Values match the (light) fallbacks baked into\n * `tokens` — applying them changes nothing observable in light mode; they exist\n * so the light path flows through the same code as dark and so a standalone\n * render with an explicit `light` theme is backed identically to its fallbacks.\n */\nconst LIGHT: Record<string, string> = {\n // ── Surfaces ──\n \"--color-background-primary\": \"#ffffff\",\n \"--color-background-secondary\": \"#fafafa\",\n \"--color-background-tertiary\": \"#f3f4f6\",\n // ── Text ──\n \"--color-text-primary\": \"#111827\",\n \"--color-text-secondary\": \"#6b7280\",\n \"--color-text-tertiary\": \"#9ca3af\",\n \"--color-text-accent\": \"#2563eb\",\n \"--nb-color-accent-foreground\": \"#ffffff\",\n // ── Border / ring ──\n \"--color-border-primary\": \"#e5e7eb\",\n \"--color-border-secondary\": \"#d1d5db\",\n \"--color-ring-primary\": \"#2563eb\",\n // ── Status / brand semantics ──\n \"--nb-color-danger\": \"#dc2626\",\n \"--nb-color-danger-foreground\": \"#ffffff\",\n \"--nb-color-success\": \"#059669\",\n \"--nb-color-warning\": \"#f59e0b\",\n \"--nb-color-processing\": \"#7c3aed\",\n \"--nb-color-processing-light\": \"#f3eeff\",\n \"--nb-color-info-light\": \"#eef4ff\",\n};\n\n/**\n * Dark-theme neutral defaults. A monotonic neutral ladder — the three surface\n * tiers are the darkest, borders sit a step lighter so they stay visible\n * against every surface, and text inverts to light. \"Subtle\"/\"strong\" keep\n * their light-mode semantics: `bgSubtle` reads as a lifted hover/inset tint and\n * `borderStrong` is more prominent than `border`.\n */\nconst DARK: Record<string, string> = {\n // ── Surfaces (base → lifted) ──\n \"--color-background-primary\": \"#18181b\",\n \"--color-background-secondary\": \"#27272a\",\n \"--color-background-tertiary\": \"#2f2f34\",\n // ── Text ──\n \"--color-text-primary\": \"#fafafa\",\n \"--color-text-secondary\": \"#a1a1aa\",\n \"--color-text-tertiary\": \"#71717a\",\n \"--color-text-accent\": \"#818cf8\",\n \"--nb-color-accent-foreground\": \"#ffffff\",\n // ── Border / ring (lighter than surfaces so they remain visible) ──\n \"--color-border-primary\": \"#3f3f46\",\n \"--color-border-secondary\": \"#52525b\",\n \"--color-ring-primary\": \"#818cf8\",\n // ── Status / brand semantics (brightened for contrast on dark) ──\n \"--nb-color-danger\": \"#f87171\",\n // Dark text on the brightened red: white on #f87171 is under 3:1.\n \"--nb-color-danger-foreground\": \"#18181b\",\n \"--nb-color-success\": \"#34d399\",\n \"--nb-color-warning\": \"#fbbf24\",\n \"--nb-color-processing\": \"#a78bfa\",\n \"--nb-color-processing-light\": \"#2a2440\",\n \"--nb-color-info-light\": \"#1e2a44\",\n};\n\n/**\n * The neutral default theme, keyed by mode. Exported for the regression test\n * that asserts every color var referenced by `tokens` is backed in both modes.\n */\nexport const DEFAULT_THEME_VARS: Record<\"light\" | \"dark\", Record<string, string>> = {\n light: LIGHT,\n dark: DARK,\n};\n\n/** Cascade layer holding the neutral defaults. Named so an app can order it. */\nexport const DEFAULTS_LAYER_NAME = \"synapse-defaults\";\n\n/** `id` of the single `<style>` element this module owns. */\nexport const DEFAULTS_STYLE_ID = \"synapse-theme-defaults\";\n\n/** Custom properties this module last wrote inline, so a later apply clears only\n * what it owns and never an inline property the app set itself. */\nlet appliedInlineKeys = new Set<string>();\n\n/** Test seam — forget what was written inline so a fresh apply is observable. */\nexport function resetAppliedInlineKeys(): void {\n appliedInlineKeys = new Set();\n}\n\n/**\n * Whether this document can host the defaults stylesheet.\n *\n * Not the same question as `typeof document !== \"undefined\"`. An embedder or a\n * test harness may install a *partial* `document` — `documentElement.style` plus\n * listeners and nothing else — which is everything this module needed while the\n * defaults were inline properties. Moving them into a stylesheet added three new\n * requirements (`getElementById`, `createElement`, `head.prepend`), and reaching\n * for them on such a document throws.\n *\n * A throw here is not contained: it unwinds through `applyTheme` into the\n * handshake, so an app in that environment never finishes connecting. The\n * default layer is a rendering nicety and the connection is not, so a document\n * that cannot carry a stylesheet gets no layer rather than no session.\n */\nfunction canInstallStylesheet(): boolean {\n return (\n typeof document !== \"undefined\" &&\n typeof document.getElementById === \"function\" &&\n typeof document.createElement === \"function\" &&\n typeof document.head?.prepend === \"function\"\n );\n}\n\n/**\n * Install the neutral defaults for `mode` as a cascade layer.\n *\n * A layer, not inline properties on `documentElement`, and that distinction is\n * the whole point: **a default has to lose.** An inline style outranks every\n * author stylesheet, so writing defaults there made them beat any rule a host or\n * app had written for the same var — the opposite of a fallback.\n *\n * A host cannot always deliver a var through the protocol.\n * `hostContext.styles.variables` is a closed enum, so a host whose design system\n * is larger than that enum has to put the remainder in a stylesheet it injects\n * into the app document. Those declarations are ordinary unlayered author rules.\n * Unlayered beats layered, so they now win — while a var nobody declares still\n * resolves to a neutral default, which is what this map is for.\n *\n * An app's own `:root` rule wins for the same reason, so overriding a default\n * needs no `!important` and no knowledge of this module.\n *\n * One element, replaced in place, so a mode flip swaps the whole map atomically\n * and repeat calls are cheap. SSR-safe.\n */\nfunction applyDefaultThemeLayer(mode: \"light\" | \"dark\"): void {\n if (!canInstallStylesheet()) return;\n\n // Safe to concatenate ONLY because every key and value here is module-local\n // (`DEFAULT_THEME_VARS`). A host-supplied value containing `}` would escape\n // the rule, so never fold host or app input into this template; host\n // variables go through `setProperty`, which cannot escape.\n const declarations = Object.entries(DEFAULT_THEME_VARS[mode])\n .map(([k, v]) => ` ${k}: ${v};`)\n .join(\"\\n\");\n const css = `@layer ${DEFAULTS_LAYER_NAME} {\\n :root {\\n${declarations}\\n }\\n}`;\n\n const existing = document.getElementById(DEFAULTS_STYLE_ID);\n if (existing) {\n // Avoid a needless style invalidation when the mode hasn't changed.\n if (existing.textContent !== css) existing.textContent = css;\n return;\n }\n const el = document.createElement(\"style\");\n el.id = DEFAULTS_STYLE_ID;\n el.textContent = css;\n // Prepended so this is the first layer the document declares, which puts it\n // first in layer order — an app that declares its own layers sorts after, and\n // therefore wins. Unlayered app rules win regardless of DOM order.\n document.head.prepend(el);\n}\n\n/**\n * Apply theme CSS custom properties to the app document.\n *\n * The host's variables go inline on `documentElement`, where they outrank\n * everything; the neutral defaults for `mode` go into a cascade layer, where\n * they lose to any rule that actually declares the var. So a host value beats\n * this module's default through *either* channel — the protocol or a stylesheet\n * it injects — and a var nobody declares still resolves to a theme-correct\n * default. Only the protocol channel also outranks the app: a host's injected\n * stylesheet and the app's own `:root` are both unlayered rules of equal\n * specificity, so document order decides between them, not this module.\n *\n * A key this module wrote on a previous call and the incoming set does NOT carry\n * is *removed* inline rather than left behind, because a host may legitimately\n * narrow its key set: stop sending a var and it must stop applying, or it stays\n * pinned inline forever where the layer, the host's own stylesheet and the app's\n * own rules all cannot reach it. Removing lets the cascade resolve it against\n * those instead, which is the property this module exists to establish, and is\n * strictly less destructive than overwriting the key with our own default.\n *\n * Tracked as the set of keys last written, not as `DEFAULT_THEME_VARS`'s keys.\n * Those are two different sets: a host can send any spec-enum var, and ~25\n * theme-sensitive ones (`--color-text-danger`, `--color-background-inverse`, …)\n * have no neutral default, so keying the removal off the default map would pin\n * exactly those at the previous mode's value. Tracking what we wrote also means\n * this module never clears an inline property it did not set — an app writing its\n * own `documentElement.style` is left alone.\n *\n * Today an empty var set also arrives for a reason that is a bug rather than a\n * narrowing — `core.ts` replaces the host context wholesale, so a partial\n * `host-context-changed` that omits `styles` reads as \"no tokens\" (#46;\n * `connect.ts` already carries them forward). Fixing #46 is the better outcome —\n * it keeps the host's brand across a mode flip instead of collapsing to our\n * neutral — and does not make this loop unnecessary.\n *\n * SSR-safe (no-ops when `document` is unavailable). Idempotent — re-applying on\n * every theme change is correct and cheap.\n *\n * Prefer {@link applyTheme}: it applies variables *and* font faces together, so\n * a caller cannot wire up half a theme.\n */\nexport function applyThemeVariables(\n mode: \"light\" | \"dark\",\n hostVars: Record<string, string> | undefined | null,\n): void {\n if (typeof document === \"undefined\") return;\n applyDefaultThemeLayer(mode);\n\n // ONE definition of \"the host provided this key\", read by both loops below.\n // Wire data is untyped, so a key can arrive with a non-string value; that is\n // not a provided value, so the clear loop must reach it. Two predicates —\n // `k in hostVars` to clear, `typeof v === \"string\"` to write — leave such a key\n // in the gap: neither removed nor written, so the previous theme's value stays\n // pinned inline where the layer, the host's stylesheet and the app's `:root`\n // all cannot reach it. That is the un-self-healing pin this module exists to\n // eliminate, re-entered by a different door.\n const incoming: Record<string, string> = {};\n if (hostVars && typeof hostVars === \"object\") {\n for (const [k, v] of Object.entries(hostVars)) {\n if (typeof k === \"string\" && typeof v === \"string\") incoming[k] = v;\n }\n }\n\n const root = document.documentElement.style;\n // `removeProperty` is feature-checked and `setProperty` deliberately is not.\n // The setter is the one capability this module has always required, so a\n // document lacking it was never supported and gets no new promise here; the\n // clear pass arrived with the defaults layer, so a `style` carrying only the\n // setter is a shape that used to work. Dropping the clear pass for it is far\n // better than throwing out of the loop and leaving the host's variables\n // unwritten entirely.\n if (typeof root.removeProperty === \"function\") {\n for (const k of appliedInlineKeys) {\n if (!(k in incoming)) root.removeProperty(k);\n }\n }\n for (const [k, v] of Object.entries(incoming)) root.setProperty(k, v);\n appliedInlineKeys = new Set(Object.keys(incoming));\n}\n\n/**\n * Apply a resolved theme's variables to the app document.\n *\n * The single path by which theme variables reach the DOM: the handshake and\n * `host-context-changed` both funnel through here.\n */\nexport function applyTheme(\n mode: \"light\" | \"dark\",\n hostVars: Record<string, string> | undefined | null,\n): void {\n applyThemeVariables(mode, hostVars);\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RESOURCE_TEARDOWN_METHOD, TOOL_CANCELLED_METHOD, TOOL_INPUT_PARTIAL_METHOD, TOOL_INPUT_METHOD, TOOL_RESULT_METHOD } from '@modelcontextprotocol/ext-apps';
|
|
2
|
+
|
|
3
|
+
// src/event-map.ts
|
|
4
|
+
var TOOLS_CALL_METHOD = "tools/call";
|
|
5
|
+
var RESOURCE_LIST_CHANGED_METHOD = "notifications/resources/list_changed";
|
|
6
|
+
var READ_RESOURCE_METHOD = "resources/read";
|
|
7
|
+
var LIST_RESOURCES_METHOD = "resources/list";
|
|
8
|
+
var ACTION_METHOD = "ai.nimblebrain/action";
|
|
9
|
+
var REQUEST_FILE_METHOD = "ai.nimblebrain/request-file";
|
|
10
|
+
var KEYDOWN_METHOD = "ai.nimblebrain/keydown";
|
|
11
|
+
var NIMBLEBRAIN_EXTENSIONS = {
|
|
12
|
+
/** App → host notification: run a host action (navigate, open a panel). */
|
|
13
|
+
action: { method: ACTION_METHOD, capability: ACTION_METHOD },
|
|
14
|
+
/** App → host request: the host's file picker, answered `{ files }`. */
|
|
15
|
+
requestFile: { method: REQUEST_FILE_METHOD, capability: REQUEST_FILE_METHOD },
|
|
16
|
+
/** App → host notification: a keyboard shortcut pressed inside the frame. */
|
|
17
|
+
keydown: { method: KEYDOWN_METHOD, capability: KEYDOWN_METHOD }
|
|
18
|
+
};
|
|
19
|
+
var EVENT_MAP = {
|
|
20
|
+
"tool-result": TOOL_RESULT_METHOD,
|
|
21
|
+
"tool-input": TOOL_INPUT_METHOD,
|
|
22
|
+
"tool-input-partial": TOOL_INPUT_PARTIAL_METHOD,
|
|
23
|
+
"tool-cancelled": TOOL_CANCELLED_METHOD,
|
|
24
|
+
teardown: RESOURCE_TEARDOWN_METHOD
|
|
25
|
+
};
|
|
26
|
+
function resolveEventMethod(name) {
|
|
27
|
+
return EVENT_MAP[name] ?? name;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { KEYDOWN_METHOD, LIST_RESOURCES_METHOD, NIMBLEBRAIN_EXTENSIONS, READ_RESOURCE_METHOD, RESOURCE_LIST_CHANGED_METHOD, TOOLS_CALL_METHOD, resolveEventMethod };
|
|
31
|
+
//# sourceMappingURL=chunk-H3INUKA3.js.map
|
|
32
|
+
//# sourceMappingURL=chunk-H3INUKA3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/event-map.ts"],"names":[],"mappings":";;;AAoBO,IAAM,iBAAA,GAA+C;AAYrD,IAAM,4BAAA,GACX;AACK,IAAM,oBAAA,GAAsD;AAC5D,IAAM,qBAAA,GAAwD;AAU9D,IAAM,aAAA,GAAgB,uBAAA;AACtB,IAAM,mBAAA,GAAsB,6BAAA;AAC5B,IAAM,cAAA,GAAiB;AAgBvB,IAAM,sBAAA,GAAyB;AAAA;AAAA,EAEpC,MAAA,EAAQ,EAAE,MAAA,EAAQ,aAAA,EAAe,YAAY,aAAA,EAAc;AAAA;AAAA,EAE3D,WAAA,EAAa,EAAE,MAAA,EAAQ,mBAAA,EAAqB,YAAY,mBAAA,EAAoB;AAAA;AAAA,EAE5E,OAAA,EAAS,EAAE,MAAA,EAAQ,cAAA,EAAgB,YAAY,cAAA;AACjD;AAgBA,IAAM,SAAA,GAAoC;AAAA,EACxC,aAAA,EAAe,kBAAA;AAAA,EACf,YAAA,EAAc,iBAAA;AAAA,EACd,oBAAA,EAAsB,yBAAA;AAAA,EACtB,gBAAA,EAAkB,qBAAA;AAAA,EAClB,QAAA,EAAU;AACZ,CAAA;AAMO,SAAS,mBAAmB,IAAA,EAAsB;AACvD,EAAA,OAAO,SAAA,CAAU,IAAI,CAAA,IAAK,IAAA;AAC5B","file":"chunk-H3INUKA3.js","sourcesContent":["import {\n HOST_CONTEXT_CHANGED_METHOD,\n RESOURCE_TEARDOWN_METHOD,\n TOOL_CANCELLED_METHOD,\n TOOL_INPUT_METHOD,\n TOOL_INPUT_PARTIAL_METHOD,\n TOOL_RESULT_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type {\n CallToolRequest,\n ListResourcesRequest,\n ReadResourceRequest,\n ResourceListChangedNotification,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * A server's own `tools/call`. Core MCP, so the same reasoning as the resource\n * methods below: no constant to import, and typing the literal with the SDK's\n * own `method` field still breaks the build if it is renamed.\n */\nexport const TOOLS_CALL_METHOD: CallToolRequest[\"method\"] = \"tools/call\";\n\n/**\n * A server's own resource methods. Core MCP rather than ext-apps, so there are\n * no constants to import; typing each literal with the SDK's own `method` field\n * still breaks the build if one is renamed.\n *\n * `serverResources` in a host's `hostCapabilities` is the promise to carry all\n * three: the object itself says the host proxies resource reads, and its\n * `listChanged` says the host forwards the notification to that server's views.\n * A host announcing it owes an answer to every method here.\n */\nexport const RESOURCE_LIST_CHANGED_METHOD: ResourceListChangedNotification[\"method\"] =\n \"notifications/resources/list_changed\";\nexport const READ_RESOURCE_METHOD: ReadResourceRequest[\"method\"] = \"resources/read\";\nexport const LIST_RESOURCES_METHOD: ListResourcesRequest[\"method\"] = \"resources/list\";\n\n/**\n * NimbleBrain host extension methods. No spec equivalent, so no constant to\n * import — but naming them once here means no source file spells the wire\n * string twice. The `ai.nimblebrain/` prefix names the owner: these are the\n * NimbleBrain host's extensions, which this package implements a client for.\n *\n * Each is used only when the host declares it: see {@link NIMBLEBRAIN_EXTENSIONS}.\n */\nexport const ACTION_METHOD = \"ai.nimblebrain/action\";\nexport const REQUEST_FILE_METHOD = \"ai.nimblebrain/request-file\";\nexport const KEYDOWN_METHOD = \"ai.nimblebrain/keydown\";\n\n/**\n * The NimbleBrain host extensions. This is the complete list; anything else an\n * app sends is spec.\n *\n * Each extension has one name, used both as its method and as the identifier a\n * host declares in `hostCapabilities.experimental` to offer it. The ext-apps\n * host capability type has no field for extensions, and a spec client parses\n * the `ui/initialize` result against that type, so `experimental` is the one\n * slot whose contents reach the app. The MCP tasks capability travels the same\n * way, for the same reason.\n *\n * The gate is the declaration, not the host's name. A host that implements an\n * extension says so; a host that does not is never sent it.\n */\nexport const NIMBLEBRAIN_EXTENSIONS = {\n /** App → host notification: run a host action (navigate, open a panel). */\n action: { method: ACTION_METHOD, capability: ACTION_METHOD },\n /** App → host request: the host's file picker, answered `{ files }`. */\n requestFile: { method: REQUEST_FILE_METHOD, capability: REQUEST_FILE_METHOD },\n /** App → host notification: a keyboard shortcut pressed inside the frame. */\n keydown: { method: KEYDOWN_METHOD, capability: KEYDOWN_METHOD },\n} as const;\n\nexport type NimbleBrainExtension = keyof typeof NIMBLEBRAIN_EXTENSIONS;\n\n/**\n * Maps short event names used in App.on() to full MCP method names.\n * Uses canonical constants from @modelcontextprotocol/ext-apps to stay\n * in sync with the spec — if the spec changes a method name, this breaks\n * at compile time, not silently at runtime.\n *\n * `theme-changed` and `host-context-changed` are deliberately absent:\n * `connect()` routes both itself, because each is a typed *view* over the same\n * notification rather than its raw params. Listing them here would be a second\n * table nothing consults — the kind of copy that goes wrong quietly because\n * nothing reads it to notice.\n */\nconst EVENT_MAP: Record<string, string> = {\n \"tool-result\": TOOL_RESULT_METHOD,\n \"tool-input\": TOOL_INPUT_METHOD,\n \"tool-input-partial\": TOOL_INPUT_PARTIAL_METHOD,\n \"tool-cancelled\": TOOL_CANCELLED_METHOD,\n teardown: RESOURCE_TEARDOWN_METHOD,\n};\n\n/**\n * Resolve a short event name to a full MCP method name.\n * Returns the mapped method if found, otherwise passes through as-is.\n */\nexport function resolveEventMethod(name: string): string {\n return EVENT_MAP[name] ?? name;\n}\n\n/** The wire method behind the `theme-changed` / `host-context-changed` events. */\nexport { HOST_CONTEXT_CHANGED_METHOD };\n"]}
|