@nimblebrain/synapse 0.8.0 → 0.10.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 CHANGED
@@ -43,10 +43,43 @@ npm install @nimblebrain/synapse
43
43
  |-------------|-------------|
44
44
  | `@nimblebrain/synapse` | Vanilla JS core — `connect()`, `createSynapse()`, `createStore()` |
45
45
  | `@nimblebrain/synapse/react` | React hooks and providers (`AppProvider`, `SynapseProvider`) |
46
+ | `@nimblebrain/synapse/ui` | Component library — tokens, primitives, components, layouts |
47
+ | `@nimblebrain/synapse/ui/fonts` | Side-effect import that loads the brand fonts into the iframe |
46
48
  | `@nimblebrain/synapse/vite` | Vite plugin for dev mode |
47
49
  | `@nimblebrain/synapse/codegen` | CLI + programmatic code generation |
48
50
  | `@nimblebrain/synapse/iife` | Pre-built IIFE bundle for `<script>` tags (`window.Synapse`) |
49
51
 
52
+ ## UI Components (`@nimblebrain/synapse/ui`)
53
+
54
+ A React component library for embedded Synapse apps: a token contract, layout
55
+ primitives (`Stack`, `Inline`, …), components (`Card`, `Badge`, `Drawer`,
56
+ `Table`, `ListRow`, …), and responsive layout scaffolds (`AppFrame`,
57
+ `SidebarLayout`, `ListDetailLayout`). The `gallery/` app is a living reference —
58
+ every token and component in light/dark across several themes.
59
+
60
+ ### Design principles
61
+
62
+ These are the durable decisions behind the library; they rarely change.
63
+
64
+ - **The library holds no brand.** Tokens are `var(--token, neutral-fallback)`
65
+ references, not hex values. The host injects the real palette/fonts at runtime
66
+ (the MCP ext-apps `hostContext.styles.variables`), so the same app adopts
67
+ whatever host it runs in. Standalone, it renders in neutral fallbacks.
68
+ - **Theme via CSS, not React.** Components style with token-driven inline-style
69
+ objects whose values are those `var()` refs, so theming — including light/dark —
70
+ resolves in CSS with no re-render. `ensureStyle` injects keyframes and
71
+ pseudo-state rules once; brand values never get baked in.
72
+ - **Scaffold only genuinely-complex layouts.** `AppFrame`, `SidebarLayout`, and
73
+ `ListDetailLayout` exist because they encapsulate real responsive/stateful
74
+ complexity. Boards, grids, and simple lists are primitives + recipes, not
75
+ components — the library codifies the shapes apps actually take, not a general
76
+ layout engine.
77
+ - **Responsive to the pane, not the device.** Layouts observe their own width
78
+ (`ResizeObserver` via `useBreakpoint`), because an app's iframe may be
79
+ fullscreen, split, or a narrow rail regardless of screen size.
80
+ - **Lean on the platform.** `Drawer` is built on the native `<dialog>` element
81
+ (focus-trap, Escape, scroll behavior for free) rather than re-implementing them.
82
+
50
83
  ## Quick Start
51
84
 
52
85
  ### Vanilla JS
@@ -1,10 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var chunk42N5BB5O_cjs = require('../chunk-42N5BB5O.cjs');
4
3
  var chunkHLT5UBJF_cjs = require('../chunk-HLT5UBJF.cjs');
4
+ var chunk42N5BB5O_cjs = require('../chunk-42N5BB5O.cjs');
5
5
 
6
6
 
7
7
 
8
+ Object.defineProperty(exports, "generateTypes", {
9
+ enumerable: true,
10
+ get: function () { return chunkHLT5UBJF_cjs.generateTypes; }
11
+ });
8
12
  Object.defineProperty(exports, "readFromManifest", {
9
13
  enumerable: true,
10
14
  get: function () { return chunk42N5BB5O_cjs.readFromManifest; }
@@ -17,9 +21,5 @@ Object.defineProperty(exports, "readFromServer", {
17
21
  enumerable: true,
18
22
  get: function () { return chunk42N5BB5O_cjs.readFromServer; }
19
23
  });
20
- Object.defineProperty(exports, "generateTypes", {
21
- enumerable: true,
22
- get: function () { return chunkHLT5UBJF_cjs.generateTypes; }
23
- });
24
24
  //# sourceMappingURL=index.cjs.map
25
25
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- export { readFromManifest, readFromSchemaDir, readFromServer } from '../chunk-YWX3D24J.js';
2
1
  export { generateTypes } from '../chunk-JKHGWDZI.js';
2
+ export { readFromManifest, readFromSchemaDir, readFromServer } from '../chunk-YWX3D24J.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ // src/ui/fonts.ts
4
+ var FONTSHARE_HREF = "https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&f[]=erode@400,500&display=swap";
5
+ var LINK_ID = "nb-synapse-fonts";
6
+ function injectFonts() {
7
+ if (typeof document === "undefined") return;
8
+ if (document.getElementById(LINK_ID)) return;
9
+ const link = document.createElement("link");
10
+ link.id = LINK_ID;
11
+ link.rel = "stylesheet";
12
+ link.href = FONTSHARE_HREF;
13
+ document.head.appendChild(link);
14
+ }
15
+ injectFonts();
16
+
17
+ exports.injectFonts = injectFonts;
18
+ //# sourceMappingURL=fonts.cjs.map
19
+ //# sourceMappingURL=fonts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/fonts.ts"],"names":[],"mappings":";;;AAuBA,IAAM,cAAA,GACJ,yFAAA;AACF,IAAM,OAAA,GAAU,kBAAA;AAOT,SAAS,WAAA,GAAoB;AAClC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,OAAO,CAAA,EAAG;AACtC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,EAAA,GAAK,OAAA;AACV,EAAA,IAAA,CAAK,GAAA,GAAM,YAAA;AACX,EAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,IAAI,CAAA;AAChC;AAEA,WAAA,EAAY","file":"fonts.cjs","sourcesContent":["/**\n * Loads the NimbleBrain brand fonts into the current document. Import for the\n * side effect:\n *\n * ```ts\n * import \"@nimblebrain/synapse/ui/fonts\";\n * ```\n *\n * The token contract names these fonts (`--font-sans: Satoshi`,\n * `--nb-font-heading: Erode`), but a font *name* only renders if the font\n * *files* are loaded in the iframe document — each app iframe is its own\n * document and does not inherit the host's `@font-face`s. This module injects\n * the Fontshare stylesheet so the named fonts resolve instead of silently\n * falling back.\n *\n * JetBrains Mono (`--font-mono`) is not injected here — apps that render code\n * should add `@fontsource-variable/jetbrains-mono` to their own bundle.\n *\n * CSP note: the host iframe must allow `https://api.fontshare.com` in its\n * `style-src`/`font-src` for this to take effect. If blocked, fonts fall back\n * to the neutral system stack in the token contract.\n */\n\nconst FONTSHARE_HREF =\n \"https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&f[]=erode@400,500&display=swap\";\nconst LINK_ID = \"nb-synapse-fonts\";\n\n/**\n * Inject the Fontshare brand-font stylesheet once. Idempotent and SSR-safe\n * (no-ops when `document` is unavailable). Called automatically on import;\n * exported for explicit invocation.\n */\nexport function injectFonts(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(LINK_ID)) return;\n const link = document.createElement(\"link\");\n link.id = LINK_ID;\n link.rel = \"stylesheet\";\n link.href = FONTSHARE_HREF;\n document.head.appendChild(link);\n}\n\ninjectFonts();\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Loads the NimbleBrain brand fonts into the current document. Import for the
3
+ * side effect:
4
+ *
5
+ * ```ts
6
+ * import "@nimblebrain/synapse/ui/fonts";
7
+ * ```
8
+ *
9
+ * The token contract names these fonts (`--font-sans: Satoshi`,
10
+ * `--nb-font-heading: Erode`), but a font *name* only renders if the font
11
+ * *files* are loaded in the iframe document — each app iframe is its own
12
+ * document and does not inherit the host's `@font-face`s. This module injects
13
+ * the Fontshare stylesheet so the named fonts resolve instead of silently
14
+ * falling back.
15
+ *
16
+ * JetBrains Mono (`--font-mono`) is not injected here — apps that render code
17
+ * should add `@fontsource-variable/jetbrains-mono` to their own bundle.
18
+ *
19
+ * CSP note: the host iframe must allow `https://api.fontshare.com` in its
20
+ * `style-src`/`font-src` for this to take effect. If blocked, fonts fall back
21
+ * to the neutral system stack in the token contract.
22
+ */
23
+ /**
24
+ * Inject the Fontshare brand-font stylesheet once. Idempotent and SSR-safe
25
+ * (no-ops when `document` is unavailable). Called automatically on import;
26
+ * exported for explicit invocation.
27
+ */
28
+ declare function injectFonts(): void;
29
+
30
+ export { injectFonts };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Loads the NimbleBrain brand fonts into the current document. Import for the
3
+ * side effect:
4
+ *
5
+ * ```ts
6
+ * import "@nimblebrain/synapse/ui/fonts";
7
+ * ```
8
+ *
9
+ * The token contract names these fonts (`--font-sans: Satoshi`,
10
+ * `--nb-font-heading: Erode`), but a font *name* only renders if the font
11
+ * *files* are loaded in the iframe document — each app iframe is its own
12
+ * document and does not inherit the host's `@font-face`s. This module injects
13
+ * the Fontshare stylesheet so the named fonts resolve instead of silently
14
+ * falling back.
15
+ *
16
+ * JetBrains Mono (`--font-mono`) is not injected here — apps that render code
17
+ * should add `@fontsource-variable/jetbrains-mono` to their own bundle.
18
+ *
19
+ * CSP note: the host iframe must allow `https://api.fontshare.com` in its
20
+ * `style-src`/`font-src` for this to take effect. If blocked, fonts fall back
21
+ * to the neutral system stack in the token contract.
22
+ */
23
+ /**
24
+ * Inject the Fontshare brand-font stylesheet once. Idempotent and SSR-safe
25
+ * (no-ops when `document` is unavailable). Called automatically on import;
26
+ * exported for explicit invocation.
27
+ */
28
+ declare function injectFonts(): void;
29
+
30
+ export { injectFonts };
@@ -0,0 +1,17 @@
1
+ // src/ui/fonts.ts
2
+ var FONTSHARE_HREF = "https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&f[]=erode@400,500&display=swap";
3
+ var LINK_ID = "nb-synapse-fonts";
4
+ function injectFonts() {
5
+ if (typeof document === "undefined") return;
6
+ if (document.getElementById(LINK_ID)) return;
7
+ const link = document.createElement("link");
8
+ link.id = LINK_ID;
9
+ link.rel = "stylesheet";
10
+ link.href = FONTSHARE_HREF;
11
+ document.head.appendChild(link);
12
+ }
13
+ injectFonts();
14
+
15
+ export { injectFonts };
16
+ //# sourceMappingURL=fonts.js.map
17
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/fonts.ts"],"names":[],"mappings":";AAuBA,IAAM,cAAA,GACJ,yFAAA;AACF,IAAM,OAAA,GAAU,kBAAA;AAOT,SAAS,WAAA,GAAoB;AAClC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACrC,EAAA,IAAI,QAAA,CAAS,cAAA,CAAe,OAAO,CAAA,EAAG;AACtC,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,EAAA,GAAK,OAAA;AACV,EAAA,IAAA,CAAK,GAAA,GAAM,YAAA;AACX,EAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,IAAI,CAAA;AAChC;AAEA,WAAA,EAAY","file":"fonts.js","sourcesContent":["/**\n * Loads the NimbleBrain brand fonts into the current document. Import for the\n * side effect:\n *\n * ```ts\n * import \"@nimblebrain/synapse/ui/fonts\";\n * ```\n *\n * The token contract names these fonts (`--font-sans: Satoshi`,\n * `--nb-font-heading: Erode`), but a font *name* only renders if the font\n * *files* are loaded in the iframe document — each app iframe is its own\n * document and does not inherit the host's `@font-face`s. This module injects\n * the Fontshare stylesheet so the named fonts resolve instead of silently\n * falling back.\n *\n * JetBrains Mono (`--font-mono`) is not injected here — apps that render code\n * should add `@fontsource-variable/jetbrains-mono` to their own bundle.\n *\n * CSP note: the host iframe must allow `https://api.fontshare.com` in its\n * `style-src`/`font-src` for this to take effect. If blocked, fonts fall back\n * to the neutral system stack in the token contract.\n */\n\nconst FONTSHARE_HREF =\n \"https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700&f[]=erode@400,500&display=swap\";\nconst LINK_ID = \"nb-synapse-fonts\";\n\n/**\n * Inject the Fontshare brand-font stylesheet once. Idempotent and SSR-safe\n * (no-ops when `document` is unavailable). Called automatically on import;\n * exported for explicit invocation.\n */\nexport function injectFonts(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(LINK_ID)) return;\n const link = document.createElement(\"link\");\n link.id = LINK_ID;\n link.rel = \"stylesheet\";\n link.href = FONTSHARE_HREF;\n document.head.appendChild(link);\n}\n\ninjectFonts();\n"]}