@conduction/docusaurus-preset 2.3.1 → 2.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/docusaurus-preset",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "scripts": {
5
5
  "prepack": "node scripts/prepack-bundle-css.js"
6
6
  },
@@ -184,6 +184,12 @@
184
184
  .am .detail.rich .sb-tab .l { height: 3px; background: var(--c-cobalt-300); width: 24px; border-radius: 1px; }
185
185
  .am .detail.rich .sb-tab.active .l { background: var(--c-cobalt-700); }
186
186
 
187
+ /* When a tab carries an <IntegrationIcon> instead of the default
188
+ .ico dot, tint it via currentColor so inactive tabs stay muted
189
+ (cobalt-300) and active tabs read in full cobalt-700. */
190
+ .am .detail.rich .sb-tab .ii { color: var(--c-cobalt-300); }
191
+ .am .detail.rich .sb-tab.active .ii { color: var(--c-cobalt-700); }
192
+
187
193
  .am .detail.rich .sb-body { padding: 10px; display: flex; flex-direction: column; gap: 7px; overflow: hidden; }
188
194
  .am .detail.rich .sb-label { height: 4px; background: var(--c-cobalt-700); width: 40%; border-radius: 1px; }
189
195
  .am .detail.rich .sb-input { height: 14px; background: white; border: 1px solid var(--c-cobalt-200); border-radius: 3px; }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * <IntegrationIcon />
3
+ *
4
+ * Render a token-painted glyph for one of the integrations in the
5
+ * Conduction registry: Nextcloud-bundled apps (Mail, Calendar, Files,
6
+ * Talk, Decks, RSS, Activity), workflow tools (xWiki, n8n, Windmill,
7
+ * OpenProject, Keycloak), LLM providers (Claude, Mistral, Ollama,
8
+ * OpenAI, Gemini), and adjacent Conduction extensions (OpenTalk,
9
+ * Matrix, Mattermost, OpenExchange).
10
+ *
11
+ * The SVG content lives in registry.js as inline strings, mirroring
12
+ * the .svg files under brand/assets/integrations/. Both copies use
13
+ * `fill="currentColor"` (or stroke) so the icon tints to whatever
14
+ * `color` the parent CSS sets. That means a SidebarMock tab can
15
+ * render a coloured Mail icon when active and a muted Mail icon when
16
+ * inactive without swapping the SVG.
17
+ *
18
+ * Usage:
19
+ *
20
+ * <IntegrationIcon name="claude" /> // 16×16 default
21
+ * <IntegrationIcon name="xwiki" size="md" /> // 24×24
22
+ * <IntegrationIcon name="n8n" size="lg" /> // 40×40
23
+ *
24
+ * Props:
25
+ * - name: one of INTEGRATIONS keys (required)
26
+ * - size: 'xs' | 'sm' (default) | 'md' | 'lg' | 'xl'
27
+ * - title: aria-label override; defaults to registry label
28
+ * - className: string
29
+ */
30
+
31
+ import React from 'react';
32
+ import styles from './IntegrationIcon.module.css';
33
+ import amStyles from '../AppMock/AppMock.module.css';
34
+ import { INTEGRATIONS } from './registry.js';
35
+
36
+ export default function IntegrationIcon({ name, size = 'sm', title, className }) {
37
+ const entry = INTEGRATIONS[name];
38
+ if (!entry) {
39
+ return (
40
+ <span
41
+ className={[amStyles.am, styles.ii, styles[size], className].filter(Boolean).join(' ')}
42
+ title={`Unknown integration: ${name}`}
43
+ aria-label={`Unknown integration: ${name}`}
44
+ />
45
+ );
46
+ }
47
+ return (
48
+ <span
49
+ className={[amStyles.am, styles.ii, styles[size], className].filter(Boolean).join(' ')}
50
+ role="img"
51
+ aria-label={title || entry.label}
52
+ dangerouslySetInnerHTML={{ __html: entry.svg }}
53
+ />
54
+ );
55
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * <IntegrationIcon /> styles.
3
+ *
4
+ * The icon itself is a 24×24 viewBox SVG with `fill="currentColor"`,
5
+ * so it tints to whatever colour the parent CSS sets. The wrapper
6
+ * fixes the rendered box size and inherits the colour from context.
7
+ */
8
+
9
+ /* IntegrationIcon's root element carries `.am` itself so the rules
10
+ below use a compound selector (no descendant space). The .am scope
11
+ still namespaces the rule so plain `.ii` on a host page that does
12
+ not adopt the design system is unaffected. Sizes are compound for
13
+ the same reason. */
14
+ .am.ii {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ flex-shrink: 0;
19
+ line-height: 0;
20
+ color: var(--c-cobalt-700);
21
+ }
22
+ .am.ii svg {
23
+ width: 100%;
24
+ height: 100%;
25
+ display: block;
26
+ }
27
+
28
+ /* Sizes match the surfaces that consume the icon: tabs (xs), widget
29
+ headers (sm), gallery cards (md), hero scene (lg). */
30
+ .am.ii.xs { width: 12px; height: 12px; }
31
+ .am.ii.sm { width: 16px; height: 16px; }
32
+ .am.ii.md { width: 24px; height: 24px; }
33
+ .am.ii.lg { width: 40px; height: 40px; }
34
+ .am.ii.xl { width: 64px; height: 64px; }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Integration icon registry.
3
+ *
4
+ * Each entry mirrors the SVG content of a file under
5
+ * brand/assets/integrations/{category}/{name}.svg. Runtime consumers
6
+ * (the React <IntegrationIcon /> component, build-kit kit pages,
7
+ * future SidebarMock tab labels) read inline strings from here so the
8
+ * icon can render with `currentColor` fill and tint to whatever
9
+ * colour the parent CSS sets. The .svg files in brand/ are the
10
+ * designer-readable copy; this file is the consumer copy. They
11
+ * stay manually in sync (each is one file, edits land in both).
12
+ *
13
+ * Categories:
14
+ * nextcloud-bundled — surfaces the Nextcloud workspace already
15
+ * ships (Mail, Calendar, Files, Talk, Decks, RSS, Activity).
16
+ * workflow — third-party workflow / wiki / SSO tools that
17
+ * Conduction integrates with (xWiki, n8n, Windmill, OpenProject,
18
+ * Keycloak).
19
+ * llm — large-language-model providers we consume via
20
+ * OpenConnector / DocuDesk (Claude, Mistral, Ollama, OpenAI,
21
+ * Gemini).
22
+ * conduction-ext — external apps in the Conduction Nextcloud
23
+ * ecosystem (OpenTalk, Matrix, Mattermost, OpenExchange).
24
+ *
25
+ * The icons themselves are simplified, monochromatic, representational
26
+ * marks in the Conduction visual style. They are NOT replacements for
27
+ * official brand marks; consumers that need the canonical logo of a
28
+ * third-party tool should use that tool's own asset.
29
+ */
30
+
31
+ export const INTEGRATIONS = {
32
+
33
+ // ---- Nextcloud-bundled ----
34
+ 'mail': {
35
+ category: 'nextcloud-bundled',
36
+ label: 'Mail',
37
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="1.5"/><path d="M3.5 7.5l8.5 6 8.5-6"/></svg>',
38
+ },
39
+ 'calendar': {
40
+ category: 'nextcloud-bundled',
41
+ label: 'Calendar',
42
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="16" rx="1.5"/><path d="M3 10h18M8 3v4M16 3v4"/><circle cx="12" cy="15" r="1.4" fill="currentColor" stroke="none"/></svg>',
43
+ },
44
+ 'files': {
45
+ category: 'nextcloud-bundled',
46
+ label: 'Files',
47
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2.5h8a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>',
48
+ },
49
+ 'talk': {
50
+ category: 'nextcloud-bundled',
51
+ label: 'Talk',
52
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 16V7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H8l-4 3z"/></svg>',
53
+ },
54
+ 'decks': {
55
+ category: 'nextcloud-bundled',
56
+ label: 'Decks',
57
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="3" y="4" width="5" height="16" rx="1"/><rect x="9.5" y="4" width="5" height="11" rx="1" opacity="0.7"/><rect x="16" y="4" width="5" height="7" rx="1" opacity="0.4"/></svg>',
58
+ },
59
+ 'rss': {
60
+ category: 'nextcloud-bundled',
61
+ label: 'RSS',
62
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 5a14 14 0 0 1 14 14"/><path d="M5 11a8 8 0 0 1 8 8"/><circle cx="6" cy="18" r="2" fill="currentColor"/></svg>',
63
+ },
64
+ 'activity': {
65
+ category: 'nextcloud-bundled',
66
+ label: 'Activity',
67
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12h4l2.5-7 5 14L17 12h4"/></svg>',
68
+ },
69
+
70
+ // ---- Workflow / auth ----
71
+ 'xwiki': {
72
+ category: 'workflow',
73
+ label: 'xWiki',
74
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M3 4h3.4L9 9.5 11.6 4H15l-4.4 8.5L15 21h-3.4L9 15.5 6.4 21H3l4.4-8.5z"/><rect x="16.5" y="4" width="1.6" height="17" opacity="0.55"/><rect x="19.4" y="4" width="1.6" height="17" opacity="0.35"/></svg>',
75
+ },
76
+ 'n8n': {
77
+ category: 'workflow',
78
+ label: 'n8n',
79
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="6" cy="12" r="2.5"/><circle cx="18" cy="6" r="2.5"/><circle cx="18" cy="18" r="2.5"/><path d="M9 12h6M9 12l9-6M9 12l9 6" stroke="currentColor" stroke-width="2" fill="none"/></svg>',
80
+ },
81
+ 'windmill': {
82
+ category: 'workflow',
83
+ label: 'Windmill',
84
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="2"/><path d="M12 12L17 5L19 8L14 12zM12 12L19 17L16 19L12 14zM12 12L5 7L8 5L12 10z"/><path d="M11 14h2v8h-2z"/></svg>',
85
+ },
86
+ 'openproject': {
87
+ category: 'workflow',
88
+ label: 'OpenProject',
89
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19.35.37h-1.86a4.628 4.628 0 0 0-4.652 4.624v5.609H4.652A4.628 4.628 0 0 0 0 15.23v3.721c0 2.569 2.083 4.679 4.652 4.679h1.86c2.57 0 4.652-2.11 4.652-4.679v-3.72h-2.791v3.88c0 1.026-.835 1.886-1.861 1.886h-1.86c-1.027 0-1.861-.864-1.861-1.886V15.23a1.839 1.839 0 0 1 1.86-1.833h14.697c2.57 0 4.652-2.11 4.652-4.679V4.997A4.628 4.628 0 0 0 19.35.37z"/></svg>',
90
+ },
91
+ 'keycloak': {
92
+ category: 'workflow',
93
+ label: 'Keycloak',
94
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="9" cy="12" r="4"/><circle cx="9" cy="12" r="1.4" fill="#fff"/><path d="M13 12h8M17 12v3M21 12v3" stroke="currentColor" stroke-width="2" fill="none"/></svg>',
95
+ },
96
+
97
+ // ---- LLMs ----
98
+ 'claude': {
99
+ category: 'llm',
100
+ label: 'Claude',
101
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="3.5"/><g stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"><path d="M12 2v4"/><path d="M12 18v4"/><path d="M2 12h4"/><path d="M18 12h4"/><path d="M5.6 5.6l2.8 2.8"/><path d="M15.6 15.6l2.8 2.8"/><path d="M18.4 5.6l-2.8 2.8"/><path d="M8.4 15.6l-2.8 2.8"/></g></svg>',
102
+ },
103
+ 'mistral': {
104
+ category: 'llm',
105
+ label: 'Mistral',
106
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="2" y="3" width="20" height="3" rx="0.5"/><rect x="2" y="8" width="14" height="3" rx="0.5" opacity="0.75"/><rect x="2" y="13" width="20" height="3" rx="0.5" opacity="0.6"/><rect x="2" y="18" width="14" height="3" rx="0.5" opacity="0.4"/></svg>',
107
+ },
108
+ 'ollama': {
109
+ category: 'llm',
110
+ label: 'Ollama',
111
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><ellipse cx="9" cy="14" rx="5" ry="6"/><ellipse cx="14.5" cy="6" rx="2" ry="3"/><path d="M14 9l1.5 2.4 2.5-1z"/><circle cx="6.5" cy="13" r="0.9" fill="#fff"/></svg>',
112
+ },
113
+ 'openai': {
114
+ category: 'llm',
115
+ label: 'OpenAI',
116
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" aria-hidden="true"><polygon points="12,3 21,8 21,16 12,21 3,16 3,8"/><polygon points="12,8 17,11 17,14 12,17 7,14 7,11" fill="currentColor" stroke="none"/></svg>',
117
+ },
118
+ 'gemini': {
119
+ category: 'llm',
120
+ label: 'Gemini',
121
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2L8 8h8z"/><path d="M8 8L4 14l8 8 8-8-4-6z" opacity="0.7"/><circle cx="18.5" cy="4.5" r="1" opacity="0.6"/><circle cx="20" cy="7" r="0.6" opacity="0.4"/></svg>',
122
+ },
123
+
124
+ // ---- Conduction Nextcloud-extension apps ----
125
+ 'opentalk': {
126
+ category: 'conduction-ext',
127
+ label: 'OpenTalk',
128
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="12.5" r="7"/><circle cx="18.5" cy="6.5" r="2.5" fill="currentColor" stroke="none"/></svg>',
129
+ },
130
+ 'matrix': {
131
+ category: 'conduction-ext',
132
+ label: 'Matrix',
133
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true"><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>',
134
+ },
135
+ 'mattermost': {
136
+ category: 'conduction-ext',
137
+ label: 'Mattermost',
138
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true"><path d="M12.081 0C7.048-.034 2.339 3.125.637 8.153c-2.125 6.276 1.24 13.086 7.516 15.21 6.276 2.125 13.086-1.24 15.21-7.516 1.727-5.1-.172-10.552-4.311-13.557l.126 2.547c2.065 2.282 2.88 5.512 1.852 8.549-1.534 4.532-6.594 6.915-11.3 5.321-4.708-1.593-7.28-6.559-5.745-11.092 1.031-3.046 3.655-5.121 6.694-5.67l1.642-1.94A4.87 4.87 0 0 0 12.08 0z"/></svg>',
139
+ },
140
+ 'openexchange': {
141
+ category: 'conduction-ext',
142
+ label: 'OpenExchange',
143
+ svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" aria-hidden="true"><circle cx="8.5" cy="12" r="5"/><path d="M14.5 7l6 10M14.5 17l6-10"/></svg>',
144
+ },
145
+ };
146
+
147
+ export const INTEGRATION_CATEGORIES = {
148
+ 'nextcloud-bundled': { label: 'Nextcloud bundled', desc: 'Surfaces every Nextcloud workspace already ships.' },
149
+ 'workflow': { label: 'Workflow & auth', desc: 'Third-party tools we wire into Conduction apps via OpenConnector.' },
150
+ 'llm': { label: 'LLM providers', desc: 'Models we call from OpenConnector and DocuDesk.' },
151
+ 'conduction-ext': { label: 'Conduction extensions', desc: 'Adjacent apps in the Conduction Nextcloud ecosystem.' },
152
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * <MockScene />
3
+ *
4
+ * Marketing-surface composition. A SidebarMock anchors the centre,
5
+ * three to five WidgetMocks float around it on a tinted cobalt-50
6
+ * surface with a soft hex-network watermark. Reach for this when a
7
+ * card or hero needs to show "data surfaced across the whole
8
+ * workspace": one sidebar + several widgets, each carrying a real
9
+ * integration icon, all lit on the same surface.
10
+ *
11
+ * Sibling to AppMock + WidgetMock + SidebarMock in the mock family.
12
+ * AppMock paints a single product surface with chassis chrome.
13
+ * WidgetMock paints a single dashboard tile. SidebarMock paints a
14
+ * single detail panel. MockScene paints the overlap.
15
+ *
16
+ * Usage:
17
+ *
18
+ * <MockScene
19
+ * sidebar={<SidebarMock kind="openregister-metadata" />}
20
+ * widgets={[
21
+ * { node: <WidgetMock kind="nextcloud-mail" />, pos: 'top-left' },
22
+ * { node: <WidgetMock kind="nextcloud-files" />, pos: 'top-right' },
23
+ * { node: <WidgetMock kind="openregister-activity" />, pos: 'bottom-right' },
24
+ * ]}
25
+ * />
26
+ *
27
+ * Each widget entry is { node, pos } where `pos` selects a corner of
28
+ * the stage:
29
+ * 'top-left' | 'top-right' |
30
+ * 'middle-left' | 'middle-right' |
31
+ * 'bottom-left' | 'bottom-right'
32
+ *
33
+ * Default rotations and offsets give each widget a slight tilt so
34
+ * the composition reads as playfully arranged rather than gridded.
35
+ *
36
+ * Props:
37
+ * - sidebar: ReactNode (typically <SidebarMock />)
38
+ * - widgets: Array<{node: ReactNode, pos: string}>
39
+ * - compact: boolean (default false) — tighter scene for use
40
+ * inside a card
41
+ * - className: string
42
+ */
43
+
44
+ import React from 'react';
45
+ import styles from './MockScene.module.css';
46
+ import amStyles from '../AppMock/AppMock.module.css';
47
+
48
+ export default function MockScene({ sidebar, widgets = [], compact = false, className }) {
49
+ return (
50
+ <div className={[amStyles.am, styles.scene, className].filter(Boolean).join(' ')}>
51
+ <div className={[styles.sceneFrame, compact && styles.compact].filter(Boolean).join(' ')}>
52
+ <div className={styles.sceneStage}>
53
+ {widgets.map((w, i) => (
54
+ <div key={i} className={styles.sceneFloat} data-pos={w.pos || 'top-left'}>
55
+ {w.node}
56
+ </div>
57
+ ))}
58
+ {sidebar && (
59
+ <div className={styles.sceneCenter}>
60
+ {sidebar}
61
+ </div>
62
+ )}
63
+ </div>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * <MockScene /> styles.
3
+ *
4
+ * Marketing-surface composition. A single sidebar sits at the centre
5
+ * (slightly larger than its standalone size), with three to five
6
+ * widgets floating around it at staggered offsets and rotations.
7
+ * Background: a tinted cobalt-50 surface with a soft hex-network
8
+ * watermark, so the scene reads as "the workspace" without committing
9
+ * to a specific app frame.
10
+ *
11
+ * Use as the illustration on a card that talks about cross-cutting
12
+ * integration ("typed data, surfaced everywhere"; "every register,
13
+ * every workspace surface"). The scene is layout-only; the props pass
14
+ * through fully-formed SidebarMock + WidgetMock JSX.
15
+ */
16
+
17
+ .am.scene { display: contents; }
18
+
19
+ .am .sceneFrame {
20
+ position: relative;
21
+ background: var(--c-cobalt-50);
22
+ border-radius: var(--radius-lg);
23
+ border: 1px solid var(--c-cobalt-100);
24
+ padding: var(--space-8);
25
+ min-height: 360px;
26
+ overflow: hidden;
27
+ isolation: isolate;
28
+ }
29
+
30
+ /* Soft hex-network watermark using radial gradients on top of cobalt-50.
31
+ Cheap visual texture; no need to bring HexNetwork in for this. */
32
+ .am .sceneFrame::before {
33
+ content: "";
34
+ position: absolute;
35
+ inset: 0;
36
+ background-image:
37
+ radial-gradient(circle at 20% 25%, var(--c-cobalt-100) 0, transparent 35%),
38
+ radial-gradient(circle at 80% 70%, var(--c-cobalt-100) 0, transparent 35%),
39
+ radial-gradient(circle at 50% 90%, var(--c-cobalt-100) 0, transparent 30%);
40
+ opacity: 0.5;
41
+ pointer-events: none;
42
+ z-index: 0;
43
+ }
44
+
45
+ /* Stage holds the sidebar at the centre and widgets at offsets.
46
+ position: relative so child position: absolute resolves here. */
47
+ .am .sceneStage {
48
+ position: relative;
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ min-height: 360px;
53
+ z-index: 1;
54
+ }
55
+
56
+ /* Centre sidebar: anchored, slightly enlarged, drop-shadow for lift. */
57
+ .am .sceneCenter {
58
+ position: relative;
59
+ z-index: 4;
60
+ filter: drop-shadow(0 16px 32px rgba(15, 35, 75, 0.2));
61
+ }
62
+ .am .sceneCenter > .am > .smFrame,
63
+ .am .sceneCenter > .smFrame {
64
+ width: 320px;
65
+ height: 440px;
66
+ }
67
+
68
+ /* Floating widget wrappers. Each one positions absolutely relative
69
+ to .sceneStage; the data-pos attribute maps to a corner. */
70
+ .am .sceneFloat {
71
+ position: absolute;
72
+ z-index: 2;
73
+ filter: drop-shadow(0 12px 22px rgba(15, 35, 75, 0.15));
74
+ transition: transform 200ms;
75
+ }
76
+
77
+ .am .sceneFloat[data-pos="top-left"] { top: 0; left: 0; transform: rotate(-3deg); }
78
+ .am .sceneFloat[data-pos="top-right"] { top: 4%; right: 0; transform: rotate(2deg); }
79
+ .am .sceneFloat[data-pos="bottom-left"] { bottom: 0; left: 6%; transform: rotate(2deg); }
80
+ .am .sceneFloat[data-pos="bottom-right"] { bottom: 0; right: 0; transform: rotate(-2deg); }
81
+ .am .sceneFloat[data-pos="middle-left"] { top: 40%; left: 0; transform: translateY(-50%) rotate(-2deg); }
82
+ .am .sceneFloat[data-pos="middle-right"] { top: 40%; right: 0; transform: translateY(-50%) rotate(2deg); }
83
+
84
+ /* Widget frames inside floats: scale down a hair and drop their own
85
+ shadow so the float-level shadow does the heavy lifting. */
86
+ .am .sceneFloat .wmFrame {
87
+ box-shadow: none;
88
+ width: 240px;
89
+ }
90
+
91
+ /* Tighter scenes for cards: pass `compact` to the component. */
92
+ .am .sceneFrame.compact {
93
+ padding: var(--space-6);
94
+ min-height: 300px;
95
+ }
96
+ .am .sceneFrame.compact .sceneStage { min-height: 300px; }
97
+ .am .sceneFrame.compact .sceneCenter > .am > .smFrame,
98
+ .am .sceneFrame.compact .sceneCenter > .smFrame { width: 260px; height: 360px; }
99
+ .am .sceneFrame.compact .sceneFloat .wmFrame { width: 200px; }
@@ -52,6 +52,7 @@
52
52
  import React from 'react';
53
53
  import styles from './SidebarMock.module.css';
54
54
  import amStyles from '../AppMock/AppMock.module.css';
55
+ import IntegrationIcon from '../IntegrationIcon/IntegrationIcon.jsx';
55
56
 
56
57
  import ProcestXWiki from './variants/ProcestXWiki.jsx';
57
58
  import ProcestTimeline from './variants/ProcestTimeline.jsx';
@@ -71,89 +72,98 @@ import NextcloudActivity from './variants/NextcloudActivity.jsx';
71
72
  * decorative (rendered as a placeholder bar in .sb-tab .l)
72
73
  * but the active flag drives the highlight.
73
74
  */
75
+ /**
76
+ * Each tab carries an `icon` field: the kebab-case key into the
77
+ * IntegrationIcon registry (see IntegrationIcon/registry.js). The
78
+ * SidebarMock renders that icon in the tab's .ico slot, tinted via
79
+ * currentColor so active tabs read full-cobalt and inactive tabs
80
+ * read muted-cobalt. The icon makes the tab self-documenting:
81
+ * Procest's xWiki tab carries the xWiki icon, DocuDesk's Signatures
82
+ * tab carries an activity-style icon for "stuff happens here", etc.
83
+ */
74
84
  const VARIANTS = {
75
85
  'procest-xwiki': {
76
86
  Component: ProcestXWiki,
77
87
  label: 'Procest · Case sidebar, xWiki tab',
78
88
  tabs: [
79
- { id: 'activity', active: false },
80
- { id: 'xwiki', active: true },
81
- { id: 'timeline', active: false },
82
- { id: 'documents', active: false },
89
+ { id: 'activity', active: false, icon: 'activity' },
90
+ { id: 'xwiki', active: true, icon: 'xwiki' },
91
+ { id: 'timeline', active: false, icon: 'calendar' },
92
+ { id: 'documents', active: false, icon: 'files' },
83
93
  ],
84
94
  },
85
95
  'procest-timeline': {
86
96
  Component: ProcestTimeline,
87
97
  label: 'Procest · Case sidebar, Timeline tab',
88
98
  tabs: [
89
- { id: 'activity', active: false },
90
- { id: 'xwiki', active: false },
91
- { id: 'timeline', active: true },
92
- { id: 'documents', active: false },
99
+ { id: 'activity', active: false, icon: 'activity' },
100
+ { id: 'xwiki', active: false, icon: 'xwiki' },
101
+ { id: 'timeline', active: true, icon: 'calendar' },
102
+ { id: 'documents', active: false, icon: 'files' },
93
103
  ],
94
104
  },
95
105
  'docudesk-signatures': {
96
106
  Component: DocuDeskSignatures,
97
107
  label: 'DocuDesk · Document sidebar, Signatures tab',
98
108
  tabs: [
99
- { id: 'activity', active: false },
100
- { id: 'signatures', active: true },
101
- { id: 'pii', active: false },
102
- { id: 'versions', active: false },
109
+ { id: 'activity', active: false, icon: 'activity' },
110
+ { id: 'signatures', active: true, icon: 'mail' },
111
+ { id: 'pii', active: false, icon: 'keycloak' },
112
+ { id: 'versions', active: false, icon: 'files' },
103
113
  ],
104
114
  },
105
115
  'docudesk-pii-map': {
106
116
  Component: DocuDeskPiiMap,
107
117
  label: 'DocuDesk · Document sidebar, PII map tab',
108
118
  tabs: [
109
- { id: 'activity', active: false },
110
- { id: 'signatures', active: false },
111
- { id: 'pii', active: true },
112
- { id: 'versions', active: false },
119
+ { id: 'activity', active: false, icon: 'activity' },
120
+ { id: 'signatures', active: false, icon: 'mail' },
121
+ { id: 'pii', active: true, icon: 'keycloak' },
122
+ { id: 'versions', active: false, icon: 'files' },
113
123
  ],
114
124
  },
115
125
  'openregister-metadata': {
116
126
  Component: OpenRegisterMetadata,
117
127
  label: 'OpenRegister · Object sidebar, Metadata tab',
118
128
  tabs: [
119
- { id: 'activity', active: false },
120
- { id: 'metadata', active: true },
121
- { id: 'audit', active: false },
129
+ { id: 'activity', active: false, icon: 'activity' },
130
+ { id: 'metadata', active: true, icon: 'files' },
131
+ { id: 'audit', active: false, icon: 'keycloak' },
122
132
  ],
123
133
  },
124
134
  'opencatalogi-publication-history': {
125
135
  Component: OpenCatalogiPublicationHistory,
126
136
  label: 'OpenCatalogi · Publication sidebar, History tab',
127
137
  tabs: [
128
- { id: 'activity', active: false },
129
- { id: 'history', active: true },
130
- { id: 'sources', active: false },
138
+ { id: 'activity', active: false, icon: 'activity' },
139
+ { id: 'history', active: true, icon: 'calendar' },
140
+ { id: 'sources', active: false, icon: 'rss' },
131
141
  ],
132
142
  },
133
143
  'openconnector-run-detail': {
134
144
  Component: OpenConnectorRunDetail,
135
145
  label: 'OpenConnector · Run sidebar, Logs tab',
136
146
  tabs: [
137
- { id: 'activity', active: false },
138
- { id: 'logs', active: true },
139
- { id: 'mapping', active: false },
147
+ { id: 'activity', active: false, icon: 'activity' },
148
+ { id: 'logs', active: true, icon: 'n8n' },
149
+ { id: 'mapping', active: false, icon: 'windmill' },
140
150
  ],
141
151
  },
142
152
  'decidesk-decision': {
143
153
  Component: DeciDeskDecision,
144
154
  label: 'DeciDesk · Decision sidebar, Detail tab',
145
155
  tabs: [
146
- { id: 'activity', active: false },
147
- { id: 'detail', active: true },
148
- { id: 'actions', active: false },
156
+ { id: 'activity', active: false, icon: 'activity' },
157
+ { id: 'detail', active: true, icon: 'files' },
158
+ { id: 'actions', active: false, icon: 'decks' },
149
159
  ],
150
160
  },
151
161
  'nextcloud-activity': {
152
162
  Component: NextcloudActivity,
153
163
  label: 'Nextcloud · Stock activity feed',
154
164
  tabs: [
155
- { id: 'activity', active: true },
156
- { id: 'comments', active: false },
165
+ { id: 'activity', active: true, icon: 'activity' },
166
+ { id: 'comments', active: false, icon: 'talk' },
157
167
  ],
158
168
  },
159
169
  };
@@ -184,7 +194,11 @@ export default function SidebarMock({ kind, embedded = false, className }) {
184
194
  <div className={amStyles['sb-tabs']}>
185
195
  {tabs.map((t, i) => (
186
196
  <div key={t.id || i} className={[amStyles['sb-tab'], t.active && amStyles.active].filter(Boolean).join(' ')}>
187
- <div className={amStyles.ico}></div>
197
+ {t.icon ? (
198
+ <IntegrationIcon name={t.icon} size="xs" />
199
+ ) : (
200
+ <div className={amStyles.ico}></div>
201
+ )}
188
202
  <div className={amStyles.l}></div>
189
203
  </div>
190
204
  ))}