@conduction/docusaurus-preset 2.8.1 → 2.9.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.
@@ -90,6 +90,8 @@ slot-based children pass through. All exported from `Diagrams/Diagrams.jsx`.
90
90
  - **DiagramPipeline** wraps `<cn-pipeline>` (renamed from Pipeline to avoid the components/Pipeline name collision)
91
91
  - **SideBox** wraps `<cn-side-box>`
92
92
  - **HoneycombBg** wraps `<cn-honeycomb-bg>`
93
+ - **Pair** wraps `<cn-pair>` (leftLabel, leftCaption, leftColor, rightLabel, rightCaption, rightColor, bridgeLabel, arrow). Two systems linked by an orange arrow; the integration-page headline diagram. Owns the one-orange-per-scene budget. Extracted 2026-05-13 from the `.pair-banner` pattern in `preview/product-pages/integrations.html`.
94
+ - **ArchFlow** wraps `<cn-arch-flow>` (arrow: right | down | none). One row of a request-flow / architecture diagram. Child elements style themselves by attribute (`accent` solid cobalt, `hex` orange pointy-top, `muted` half-opacity). Stack multiple rows for a multi-layer system view. Extracted 2026-05-13 from the `.arch-diagram` pattern in `preview/product-pages/technical-docs.html`.
93
95
 
94
96
  ## Theme swizzles
95
97
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/docusaurus-preset",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js"
6
6
  },
@@ -21,7 +21,9 @@
21
21
  "./diagrams/cn-domain-tree": "./src/diagrams/cn-domain-tree.js",
22
22
  "./diagrams/cn-pipeline": "./src/diagrams/cn-pipeline.js",
23
23
  "./diagrams/cn-side-box": "./src/diagrams/cn-side-box.js",
24
- "./diagrams/cn-honeycomb-bg": "./src/diagrams/cn-honeycomb-bg.js"
24
+ "./diagrams/cn-honeycomb-bg": "./src/diagrams/cn-honeycomb-bg.js",
25
+ "./diagrams/cn-pair": "./src/diagrams/cn-pair.js",
26
+ "./diagrams/cn-arch-flow": "./src/diagrams/cn-arch-flow.js"
25
27
  },
26
28
  "files": [
27
29
  "src/",
@@ -146,3 +146,48 @@ export function HoneycombBg(props) {
146
146
  useDiagramRuntime();
147
147
  return <cn-honeycomb-bg {...props} />;
148
148
  }
149
+
150
+ /* ============================================================
151
+ <Pair /> wraps <cn-pair>
152
+ Two systems linked by an orange arrow. Attribute names are
153
+ camelCase in React, converted to dashed on the element.
154
+ ============================================================ */
155
+ export function Pair({
156
+ leftLabel, leftCaption, leftColor,
157
+ rightLabel, rightCaption, rightColor,
158
+ bridgeLabel, arrow,
159
+ children, ...rest
160
+ }) {
161
+ useDiagramRuntime();
162
+ return (
163
+ <cn-pair
164
+ {...attrs({
165
+ 'left-label': leftLabel,
166
+ 'left-caption': leftCaption,
167
+ 'left-color': leftColor,
168
+ 'right-label': rightLabel,
169
+ 'right-caption': rightCaption,
170
+ 'right-color': rightColor,
171
+ 'bridge-label': bridgeLabel,
172
+ arrow,
173
+ })}
174
+ {...rest}
175
+ >
176
+ {children}
177
+ </cn-pair>
178
+ );
179
+ }
180
+
181
+ /* ============================================================
182
+ <ArchFlow /> wraps <cn-arch-flow>
183
+ One row of an architecture diagram. Children with `accent`,
184
+ `hex`, or `muted` attributes are styled by the web component.
185
+ ============================================================ */
186
+ export function ArchFlow({arrow, children, ...rest}) {
187
+ useDiagramRuntime();
188
+ return (
189
+ <cn-arch-flow {...attrs({arrow})} {...rest}>
190
+ {children}
191
+ </cn-arch-flow>
192
+ );
193
+ }
@@ -58,11 +58,12 @@ export {default as CookieCli} from './CookieCli/CookieCli.jsx';
58
58
  export {default as GameModal} from './GameModal/GameModal.jsx';
59
59
 
60
60
  /* Diagram-set web-component React wrappers (cn-hex, cn-hex-prism,
61
- cn-platform, cn-domain-tree, cn-pipeline, cn-side-box, cn-honeycomb-bg).
62
- Type-checked, autocompletable React surface for the framework-agnostic
63
- diagram set in @conduction/diagrams. The bare web components still
64
- work in plain HTML; the wrappers are the React-friendly version. */
65
- export {Hex, HexPrism, Platform, DomainTree, DiagramPipeline, SideBox, HoneycombBg} from './Diagrams/Diagrams.jsx';
61
+ cn-platform, cn-domain-tree, cn-pipeline, cn-side-box, cn-honeycomb-bg,
62
+ cn-pair, cn-arch-flow). Type-checked, autocompletable React surface
63
+ for the framework-agnostic diagram set in @conduction/diagrams. The
64
+ bare web components still work in plain HTML; the wrappers are the
65
+ React-friendly version. */
66
+ export {Hex, HexPrism, Platform, DomainTree, DiagramPipeline, SideBox, HoneycombBg, Pair, ArchFlow} from './Diagrams/Diagrams.jsx';
66
67
  export {default as ComposeBlock} from './ComposeBlock/ComposeBlock.jsx';
67
68
  export {default as AppsGrid} from './AppsGrid/AppsGrid.jsx';
68
69
  export {default as AppMock} from './AppMock/AppMock.jsx';
@@ -0,0 +1,168 @@
1
+ /**
2
+ * <cn-arch-flow> — a single row of an architecture diagram.
3
+ *
4
+ * The "Request flow" pattern from preview/product-pages/technical-docs.html.
5
+ * One row of boxes connected by arrows, where one box is solid cobalt
6
+ * (the "owned by us" node) and at most one is an orange pointy-top hex
7
+ * (the validator or attention node). Stack multiple <cn-arch-flow>s for
8
+ * a multi-layer system diagram.
9
+ *
10
+ * <cn-arch-flow arrow="right">
11
+ * <span>Client</span>
12
+ * <span accent>API layer</span>
13
+ * <span hex>Validator</span>
14
+ * </cn-arch-flow>
15
+ * <cn-arch-flow arrow="down">
16
+ * <span>Schema registry</span>
17
+ * <span accent>Storage adapter</span>
18
+ * <span>Audit log</span>
19
+ * </cn-arch-flow>
20
+ * <cn-arch-flow arrow="none">
21
+ * <span>Nextcloud DB</span>
22
+ * <span>MySQL</span>
23
+ * <span>PostgreSQL</span>
24
+ * <span>MongoDB</span>
25
+ * <span muted>…</span>
26
+ * </cn-arch-flow>
27
+ *
28
+ * Arrows are auto-inserted between consecutive children (skip with
29
+ * arrow="none"). Each child is styled by its attributes — no class
30
+ * names needed.
31
+ *
32
+ * Slots
33
+ * default — row members, in order. Auto-numbered to interleave
34
+ * arrows between them in the shadow DOM.
35
+ *
36
+ * Attributes
37
+ * arrow — right | down | none (default: right)
38
+ *
39
+ * Child attributes (set on the slotted elements, not on cn-arch-flow)
40
+ * accent — solid cobalt fill, white text. The "system" node.
41
+ * hex — orange pointy-top hex. Use once per scene; this is the
42
+ * one-orange-per-scene knob for the diagram.
43
+ * muted — opacity 0.5; for "and more" / out-of-scope placeholders.
44
+ */
45
+
46
+ class CnArchFlow extends HTMLElement {
47
+ static get observedAttributes() { return ['arrow']; }
48
+
49
+ constructor() {
50
+ super();
51
+ this.attachShadow({ mode: 'open' });
52
+ }
53
+
54
+ connectedCallback() {
55
+ this.render();
56
+ this._observer = new MutationObserver(() => this.render());
57
+ this._observer.observe(this, { childList: true, attributes: true, subtree: true });
58
+ }
59
+
60
+ disconnectedCallback() {
61
+ if (this._observer) this._observer.disconnect();
62
+ }
63
+
64
+ attributeChangedCallback() { if (this.shadowRoot) this.render(); }
65
+
66
+ render() {
67
+ const arrow = this.getAttribute('arrow') || 'right';
68
+ const nodes = [...this.children];
69
+ nodes.forEach((el, i) => { el.setAttribute('slot', `node-${i}`); });
70
+
71
+ const arrowGlyph = arrow === 'down' ? '↓' : '→';
72
+ const showArrows = arrow !== 'none';
73
+
74
+ let rowMarkup = '';
75
+ for (let i = 0; i < nodes.length; i++) {
76
+ rowMarkup += `<slot name="node-${i}"></slot>`;
77
+ if (showArrows && i < nodes.length - 1) {
78
+ rowMarkup += `<span class="arrow" aria-hidden="true">${arrowGlyph}</span>`;
79
+ }
80
+ }
81
+
82
+ this.shadowRoot.innerHTML = `
83
+ <style>
84
+ :host {
85
+ display: block;
86
+ font-family: var(--conduction-typography-font-family-code, ui-monospace, monospace);
87
+ }
88
+
89
+ .panel {
90
+ background: var(--c-cobalt-50);
91
+ border: 1px solid var(--c-cobalt-100);
92
+ border-radius: var(--radius-md);
93
+ padding: var(--space-6);
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: center;
97
+ gap: var(--space-3);
98
+ flex-wrap: wrap;
99
+ font-size: 11px;
100
+ text-align: center;
101
+ }
102
+
103
+ .arrow {
104
+ color: var(--c-cobalt-400);
105
+ font-size: 16px;
106
+ flex-shrink: 0;
107
+ }
108
+
109
+ /* Default node: white rounded box with cobalt-200 border. */
110
+ ::slotted(*) {
111
+ flex: 1 1 0;
112
+ min-width: 80px;
113
+ padding: var(--space-3) var(--space-2);
114
+ background: white;
115
+ border: 1px solid var(--c-cobalt-200);
116
+ border-radius: var(--radius-sm);
117
+ color: var(--c-cobalt-800);
118
+ font-family: var(--conduction-typography-font-family-code, ui-monospace, monospace);
119
+ font-size: 11px;
120
+ text-align: center;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ box-sizing: border-box;
125
+ min-height: 38px;
126
+ }
127
+
128
+ /* accent: the "system" node — solid cobalt brand fill. */
129
+ ::slotted([accent]) {
130
+ background: var(--c-blue-cobalt);
131
+ color: white;
132
+ border-color: var(--c-blue-cobalt);
133
+ }
134
+
135
+ /* hex: the orange pointy-top hex. The one-orange-per-scene knob.
136
+ * Replaces the rectangle entirely with a clip-pathed shape so the
137
+ * brand rule (hexes never rotate, always pointy-top) holds. */
138
+ ::slotted([hex]) {
139
+ flex: 0 0 auto;
140
+ width: 64px;
141
+ min-width: 0;
142
+ height: 74px;
143
+ padding: 0;
144
+ background: var(--c-orange-knvb);
145
+ color: white;
146
+ border: 0;
147
+ border-radius: 0;
148
+ clip-path: var(--hex-pointy-top);
149
+ font-weight: 600;
150
+ font-size: 12px;
151
+ }
152
+
153
+ /* muted: out-of-scope / "and more" placeholder. */
154
+ ::slotted([muted]) {
155
+ opacity: 0.5;
156
+ }
157
+ </style>
158
+
159
+ <div class="panel">${rowMarkup}</div>
160
+ `;
161
+ }
162
+ }
163
+
164
+ if (!customElements.get('cn-arch-flow')) {
165
+ customElements.define('cn-arch-flow', CnArchFlow);
166
+ }
167
+
168
+ export { CnArchFlow };
@@ -0,0 +1,192 @@
1
+ /**
2
+ * <cn-pair> — two systems linked by a bridge, with one orange arrow.
3
+ *
4
+ * The "system A talks to system B" diagram. Used at the top of every
5
+ * integration / connector page where the relationship between two
6
+ * systems is the headline. Extracted from the .pair-banner pattern
7
+ * in preview/product-pages/integrations.html.
8
+ *
9
+ * <cn-pair
10
+ * left-label="Open Register"
11
+ * left-caption="nextcloud.example"
12
+ * right-label="xWiki"
13
+ * right-caption="xwiki.example"
14
+ * right-color="cobalt-700"
15
+ * bridge-label="OpenConnector integration">
16
+ * <svg slot="left-icon" viewBox="0 0 24 24">...</svg>
17
+ * <svg slot="right-icon" viewBox="0 0 24 24">...</svg>
18
+ * </cn-pair>
19
+ *
20
+ * The bridge arrow is always KNVB orange — this component owns the
21
+ * "one orange per scene" budget for the page. Don't place a second
22
+ * <cn-pair> or a <cn-hex color="orange"> on the same screen.
23
+ *
24
+ * Slots
25
+ * left-icon — SVG for the left hex (24×24 viewBox, currentColor stroke)
26
+ * right-icon — SVG for the right hex
27
+ *
28
+ * Attributes
29
+ * left-label, right-label — strong text under each hex
30
+ * left-caption, right-caption — optional Plex Mono code-style caption
31
+ * left-color, right-color — cobalt | cobalt-700 | nextcloud | commonground
32
+ * default: cobalt (left), cobalt-700 (right)
33
+ * bridge-label — text under the orange arrow
34
+ * arrow — glyph to render (default: ↔)
35
+ */
36
+
37
+ const COLOR_MAP = {
38
+ 'cobalt': 'var(--c-blue-cobalt)',
39
+ 'cobalt-700': 'var(--c-cobalt-700)',
40
+ 'cobalt-900': 'var(--c-cobalt-900)',
41
+ 'nextcloud': 'var(--c-nextcloud-blue)',
42
+ 'commonground': 'var(--c-commonground-yellow)',
43
+ 'mint': 'var(--c-mint-500)',
44
+ 'lavender': 'var(--c-lavender-500)',
45
+ };
46
+
47
+ class CnPair extends HTMLElement {
48
+ static get observedAttributes() {
49
+ return [
50
+ 'left-label', 'left-caption', 'left-color',
51
+ 'right-label', 'right-caption', 'right-color',
52
+ 'bridge-label', 'arrow',
53
+ ];
54
+ }
55
+
56
+ constructor() {
57
+ super();
58
+ this.attachShadow({ mode: 'open' });
59
+ }
60
+
61
+ connectedCallback() { this.render(); }
62
+ attributeChangedCallback() { if (this.shadowRoot) this.render(); }
63
+
64
+ render() {
65
+ const leftLabel = this.getAttribute('left-label') || '';
66
+ const leftCaption = this.getAttribute('left-caption') || '';
67
+ const leftColorKey = this.getAttribute('left-color') || 'cobalt';
68
+ const rightLabel = this.getAttribute('right-label') || '';
69
+ const rightCaption = this.getAttribute('right-caption') || '';
70
+ const rightColorKey= this.getAttribute('right-color') || 'cobalt-700';
71
+ const bridgeLabel = this.getAttribute('bridge-label') || '';
72
+ const arrow = this.getAttribute('arrow') || '↔';
73
+
74
+ const leftBg = COLOR_MAP[leftColorKey] || COLOR_MAP.cobalt;
75
+ const rightBg = COLOR_MAP[rightColorKey] || COLOR_MAP['cobalt-700'];
76
+
77
+ /* Common Ground yellow needs cobalt-900 ink for WCAG AA. Every
78
+ other supported fill takes white. */
79
+ const leftInk = leftColorKey === 'commonground' ? 'var(--c-cobalt-900)' : 'white';
80
+ const rightInk = rightColorKey === 'commonground' ? 'var(--c-cobalt-900)' : 'white';
81
+
82
+ this.shadowRoot.innerHTML = `
83
+ <style>
84
+ :host {
85
+ display: block;
86
+ font-family: var(--conduction-typography-font-family-body, system-ui, sans-serif);
87
+ }
88
+
89
+ .panel {
90
+ display: grid;
91
+ grid-template-columns: 1fr auto 1fr;
92
+ align-items: center;
93
+ gap: var(--space-5);
94
+ padding: var(--space-6);
95
+ background: var(--c-cobalt-50);
96
+ border-radius: var(--radius-md);
97
+ }
98
+
99
+ .side {
100
+ display: flex;
101
+ flex-direction: column;
102
+ align-items: center;
103
+ gap: var(--space-2);
104
+ text-align: center;
105
+ }
106
+
107
+ .hex {
108
+ width: 64px;
109
+ height: 74px;
110
+ clip-path: var(--hex-pointy-top);
111
+ display: flex;
112
+ align-items: center;
113
+ justify-content: center;
114
+ }
115
+ .hex.left { background: ${leftBg}; color: ${leftInk}; }
116
+ .hex.right { background: ${rightBg}; color: ${rightInk}; }
117
+
118
+ ::slotted(svg) {
119
+ width: 30px;
120
+ height: 30px;
121
+ }
122
+
123
+ .label {
124
+ font-size: 16px;
125
+ font-weight: 600;
126
+ color: var(--c-cobalt-900);
127
+ }
128
+ .caption {
129
+ font-family: var(--conduction-typography-font-family-code, ui-monospace, monospace);
130
+ font-size: 11px;
131
+ color: var(--c-cobalt-400);
132
+ letter-spacing: 0.06em;
133
+ }
134
+ .caption:empty { display: none; }
135
+
136
+ .bridge {
137
+ font-family: var(--conduction-typography-font-family-code, ui-monospace, monospace);
138
+ font-size: 12px;
139
+ color: var(--c-cobalt-700);
140
+ letter-spacing: 0.05em;
141
+ text-align: center;
142
+ max-width: 160px;
143
+ }
144
+ .bridge .arrow {
145
+ display: block;
146
+ font-size: 28px;
147
+ color: var(--c-orange-knvb);
148
+ line-height: 1;
149
+ margin-bottom: 4px;
150
+ }
151
+ .bridge .arrow[aria-hidden="true"] + .bridge-label:empty { display: none; }
152
+
153
+ @media (max-width: 700px) {
154
+ .panel {
155
+ grid-template-columns: 1fr;
156
+ gap: var(--space-4);
157
+ }
158
+ .bridge .arrow { transform: rotate(90deg); }
159
+ }
160
+ </style>
161
+
162
+ <div class="panel" role="figure" aria-label="${this._escape(leftLabel)} ↔ ${this._escape(rightLabel)}">
163
+ <div class="side">
164
+ <span class="hex left"><slot name="left-icon"></slot></span>
165
+ <span class="label">${this._escape(leftLabel)}</span>
166
+ <span class="caption">${this._escape(leftCaption)}</span>
167
+ </div>
168
+ <div class="bridge">
169
+ <span class="arrow" aria-hidden="true">${this._escape(arrow)}</span>
170
+ <span class="bridge-label">${this._escape(bridgeLabel)}</span>
171
+ </div>
172
+ <div class="side">
173
+ <span class="hex right"><slot name="right-icon"></slot></span>
174
+ <span class="label">${this._escape(rightLabel)}</span>
175
+ <span class="caption">${this._escape(rightCaption)}</span>
176
+ </div>
177
+ </div>
178
+ `;
179
+ }
180
+
181
+ _escape(s) {
182
+ return String(s).replace(/[<>&"']/g, c => ({
183
+ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#39;'
184
+ }[c]));
185
+ }
186
+ }
187
+
188
+ if (!customElements.get('cn-pair')) {
189
+ customElements.define('cn-pair', CnPair);
190
+ }
191
+
192
+ export { CnPair };
@@ -17,6 +17,8 @@
17
17
  * <cn-pipeline> — horizontal flow of stages connected by arrows
18
18
  * <cn-side-box> — rectangle-feed-prism pattern for non-app surfaces
19
19
  * <cn-honeycomb-bg> — honeycomb backdrop wrapper for hero scenes
20
+ * <cn-pair> — two systems bridged by an orange arrow
21
+ * <cn-arch-flow> — single row of an architecture / request-flow diagram
20
22
  */
21
23
 
22
24
  export * from './cn-hex.js';
@@ -26,3 +28,5 @@ export * from './cn-platform.js';
26
28
  export * from './cn-pipeline.js';
27
29
  export * from './cn-side-box.js';
28
30
  export * from './cn-honeycomb-bg.js';
31
+ export * from './cn-pair.js';
32
+ export * from './cn-arch-flow.js';