@conduction/docusaurus-preset 2.3.1 → 2.4.1

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.1",
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,87 @@
1
+ /**
2
+ * <MockScene />
3
+ *
4
+ * Free-form positioning surface for assembling overlap shots out of
5
+ * WidgetMocks and SidebarMocks. The scene itself is a transparent
6
+ * stage; the caller decides the canvas size and where each item
7
+ * lands. Multiple sidebars and widgets can share a scene, can be
8
+ * different sizes, and can overlap as needed.
9
+ *
10
+ * Sibling to AppMock + WidgetMock + SidebarMock. AppMock paints a
11
+ * single product surface, WidgetMock one tile, SidebarMock one detail
12
+ * panel; MockScene paints the overlap. No background, no rotation,
13
+ * no centred-anchor pattern; each item carries its own shadow and
14
+ * lifts off whatever card the scene sits inside.
15
+ *
16
+ * Usage:
17
+ *
18
+ * <MockScene
19
+ * width={960}
20
+ * height={420}
21
+ * items={[
22
+ * { type: 'widget', kind: 'nextcloud-mail', x: 0, y: 0, size: 'sm' },
23
+ * { type: 'sidebar', kind: 'openregister-metadata', x: 220, y: 30, size: 'md' },
24
+ * { type: 'widget', kind: 'openregister-activity', x: 540, y: 80, size: 'md' },
25
+ * { type: 'sidebar', kind: 'procest-xwiki', x: 700, y: 0, size: 'sm' },
26
+ * ]}
27
+ * />
28
+ *
29
+ * Each item:
30
+ * - type: 'widget' | 'sidebar'
31
+ * - kind: variant slug (see WidgetMock or SidebarMock VARIANTS)
32
+ * - x, y: pixel offset from the scene's top-left corner
33
+ * - size: 'sm' | 'md' | 'lg' (defaults to 'md' for both types)
34
+ * - z: z-index override (defaults to array order, later items
35
+ * render on top)
36
+ *
37
+ * Caller can also pass a `style` field on an item for inline overrides
38
+ * beyond what `size` provides (custom widths, transforms, etc.). The
39
+ * inline style is merged onto the item wrapper, so anything
40
+ * position-related on the wrapper (left, top, z-index) takes
41
+ * precedence over the caller's style.
42
+ *
43
+ * Props:
44
+ * - items: Array of item descriptors (see above)
45
+ * - width: scene width in px (default 960)
46
+ * - height: scene height in px (default 420)
47
+ * - className: string
48
+ */
49
+
50
+ import React from 'react';
51
+ import styles from './MockScene.module.css';
52
+ import amStyles from '../AppMock/AppMock.module.css';
53
+ import WidgetMock from '../WidgetMock/WidgetMock.jsx';
54
+ import SidebarMock from '../SidebarMock/SidebarMock.jsx';
55
+
56
+ function renderItem(item) {
57
+ const size = item.size || 'md';
58
+ if (item.type === 'widget') {
59
+ return <WidgetMock kind={item.kind} size={size} />;
60
+ }
61
+ if (item.type === 'sidebar') {
62
+ return <SidebarMock kind={item.kind} size={size} />;
63
+ }
64
+ return item.node || null;
65
+ }
66
+
67
+ export default function MockScene({ items = [], width = 960, height = 420, className }) {
68
+ return (
69
+ <div className={[amStyles.am, styles.scene, className].filter(Boolean).join(' ')}>
70
+ <div className={styles.sceneFrame} style={{ width, height }}>
71
+ {items.map((item, i) => {
72
+ const wrapperStyle = {
73
+ ...(item.style || {}),
74
+ left: item.x || 0,
75
+ top: item.y || 0,
76
+ zIndex: item.z != null ? item.z : i,
77
+ };
78
+ return (
79
+ <div key={i} className={styles.sceneItem} style={wrapperStyle}>
80
+ {renderItem(item)}
81
+ </div>
82
+ );
83
+ })}
84
+ </div>
85
+ </div>
86
+ );
87
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * <MockScene /> styles.
3
+ *
4
+ * Free-form positioning surface. Each item in the items[] prop renders
5
+ * inside a `.sceneItem` wrapper that's absolutely positioned via
6
+ * inline style (`left`, `top`, `z-index`). The scene itself is a
7
+ * transparent stage; layout is up to the caller.
8
+ *
9
+ * No background, no padding, no rotation. The mocks themselves carry
10
+ * their own borders and shadows so they read as panels lifted off
11
+ * the surrounding card.
12
+ */
13
+
14
+ .am.scene { display: contents; }
15
+
16
+ .am .sceneFrame {
17
+ position: relative;
18
+ /* Width and height come from inline style on the rendered element
19
+ so the caller decides the canvas size per scene. */
20
+ }
21
+
22
+ .am .sceneItem {
23
+ position: absolute;
24
+ /* `left`, `top`, and optionally `z-index` are set inline by the
25
+ component from the item's x/y/z values. */
26
+ }
@@ -42,6 +42,13 @@
42
42
  *
43
43
  * Props:
44
44
  * - kind: one of VARIANTS keys (required)
45
+ * - size: 'sm' | 'md' (default) | 'lg' — standalone frame size
46
+ * (240x320 / 300x400 /
47
+ * 360x480). Ignored in
48
+ * embedded mode (the
49
+ * panel sizes itself
50
+ * to AppMock's .body
51
+ * layout)
45
52
  * - embedded: boolean (default false) — drop the standalone
46
53
  * .smFrame chrome so the
47
54
  * panel slots into
@@ -52,6 +59,7 @@
52
59
  import React from 'react';
53
60
  import styles from './SidebarMock.module.css';
54
61
  import amStyles from '../AppMock/AppMock.module.css';
62
+ import IntegrationIcon from '../IntegrationIcon/IntegrationIcon.jsx';
55
63
 
56
64
  import ProcestXWiki from './variants/ProcestXWiki.jsx';
57
65
  import ProcestTimeline from './variants/ProcestTimeline.jsx';
@@ -71,99 +79,108 @@ import NextcloudActivity from './variants/NextcloudActivity.jsx';
71
79
  * decorative (rendered as a placeholder bar in .sb-tab .l)
72
80
  * but the active flag drives the highlight.
73
81
  */
82
+ /**
83
+ * Each tab carries an `icon` field: the kebab-case key into the
84
+ * IntegrationIcon registry (see IntegrationIcon/registry.js). The
85
+ * SidebarMock renders that icon in the tab's .ico slot, tinted via
86
+ * currentColor so active tabs read full-cobalt and inactive tabs
87
+ * read muted-cobalt. The icon makes the tab self-documenting:
88
+ * Procest's xWiki tab carries the xWiki icon, DocuDesk's Signatures
89
+ * tab carries an activity-style icon for "stuff happens here", etc.
90
+ */
74
91
  const VARIANTS = {
75
92
  'procest-xwiki': {
76
93
  Component: ProcestXWiki,
77
94
  label: 'Procest · Case sidebar, xWiki tab',
78
95
  tabs: [
79
- { id: 'activity', active: false },
80
- { id: 'xwiki', active: true },
81
- { id: 'timeline', active: false },
82
- { id: 'documents', active: false },
96
+ { id: 'activity', active: false, icon: 'activity' },
97
+ { id: 'xwiki', active: true, icon: 'xwiki' },
98
+ { id: 'timeline', active: false, icon: 'calendar' },
99
+ { id: 'documents', active: false, icon: 'files' },
83
100
  ],
84
101
  },
85
102
  'procest-timeline': {
86
103
  Component: ProcestTimeline,
87
104
  label: 'Procest · Case sidebar, Timeline tab',
88
105
  tabs: [
89
- { id: 'activity', active: false },
90
- { id: 'xwiki', active: false },
91
- { id: 'timeline', active: true },
92
- { id: 'documents', active: false },
106
+ { id: 'activity', active: false, icon: 'activity' },
107
+ { id: 'xwiki', active: false, icon: 'xwiki' },
108
+ { id: 'timeline', active: true, icon: 'calendar' },
109
+ { id: 'documents', active: false, icon: 'files' },
93
110
  ],
94
111
  },
95
112
  'docudesk-signatures': {
96
113
  Component: DocuDeskSignatures,
97
114
  label: 'DocuDesk · Document sidebar, Signatures tab',
98
115
  tabs: [
99
- { id: 'activity', active: false },
100
- { id: 'signatures', active: true },
101
- { id: 'pii', active: false },
102
- { id: 'versions', active: false },
116
+ { id: 'activity', active: false, icon: 'activity' },
117
+ { id: 'signatures', active: true, icon: 'mail' },
118
+ { id: 'pii', active: false, icon: 'keycloak' },
119
+ { id: 'versions', active: false, icon: 'files' },
103
120
  ],
104
121
  },
105
122
  'docudesk-pii-map': {
106
123
  Component: DocuDeskPiiMap,
107
124
  label: 'DocuDesk · Document sidebar, PII map tab',
108
125
  tabs: [
109
- { id: 'activity', active: false },
110
- { id: 'signatures', active: false },
111
- { id: 'pii', active: true },
112
- { id: 'versions', active: false },
126
+ { id: 'activity', active: false, icon: 'activity' },
127
+ { id: 'signatures', active: false, icon: 'mail' },
128
+ { id: 'pii', active: true, icon: 'keycloak' },
129
+ { id: 'versions', active: false, icon: 'files' },
113
130
  ],
114
131
  },
115
132
  'openregister-metadata': {
116
133
  Component: OpenRegisterMetadata,
117
134
  label: 'OpenRegister · Object sidebar, Metadata tab',
118
135
  tabs: [
119
- { id: 'activity', active: false },
120
- { id: 'metadata', active: true },
121
- { id: 'audit', active: false },
136
+ { id: 'activity', active: false, icon: 'activity' },
137
+ { id: 'metadata', active: true, icon: 'files' },
138
+ { id: 'audit', active: false, icon: 'keycloak' },
122
139
  ],
123
140
  },
124
141
  'opencatalogi-publication-history': {
125
142
  Component: OpenCatalogiPublicationHistory,
126
143
  label: 'OpenCatalogi · Publication sidebar, History tab',
127
144
  tabs: [
128
- { id: 'activity', active: false },
129
- { id: 'history', active: true },
130
- { id: 'sources', active: false },
145
+ { id: 'activity', active: false, icon: 'activity' },
146
+ { id: 'history', active: true, icon: 'calendar' },
147
+ { id: 'sources', active: false, icon: 'rss' },
131
148
  ],
132
149
  },
133
150
  'openconnector-run-detail': {
134
151
  Component: OpenConnectorRunDetail,
135
152
  label: 'OpenConnector · Run sidebar, Logs tab',
136
153
  tabs: [
137
- { id: 'activity', active: false },
138
- { id: 'logs', active: true },
139
- { id: 'mapping', active: false },
154
+ { id: 'activity', active: false, icon: 'activity' },
155
+ { id: 'logs', active: true, icon: 'n8n' },
156
+ { id: 'mapping', active: false, icon: 'windmill' },
140
157
  ],
141
158
  },
142
159
  'decidesk-decision': {
143
160
  Component: DeciDeskDecision,
144
161
  label: 'DeciDesk · Decision sidebar, Detail tab',
145
162
  tabs: [
146
- { id: 'activity', active: false },
147
- { id: 'detail', active: true },
148
- { id: 'actions', active: false },
163
+ { id: 'activity', active: false, icon: 'activity' },
164
+ { id: 'detail', active: true, icon: 'files' },
165
+ { id: 'actions', active: false, icon: 'decks' },
149
166
  ],
150
167
  },
151
168
  'nextcloud-activity': {
152
169
  Component: NextcloudActivity,
153
170
  label: 'Nextcloud · Stock activity feed',
154
171
  tabs: [
155
- { id: 'activity', active: true },
156
- { id: 'comments', active: false },
172
+ { id: 'activity', active: true, icon: 'activity' },
173
+ { id: 'comments', active: false, icon: 'talk' },
157
174
  ],
158
175
  },
159
176
  };
160
177
 
161
- export default function SidebarMock({ kind, embedded = false, className }) {
178
+ export default function SidebarMock({ kind, size = 'md', embedded = false, className }) {
162
179
  const variant = VARIANTS[kind];
163
180
  if (!variant) {
164
181
  return (
165
182
  <div className={[amStyles.am, styles.sm].filter(Boolean).join(' ')}>
166
- <div className={styles.smFrame}>
183
+ <div className={[styles.smFrame, styles[`sb-size-${size}`]].filter(Boolean).join(' ')}>
167
184
  <div style={{ padding: 12, color: 'var(--c-cobalt-400)', fontSize: 11 }}>Unknown sidebar: {kind}</div>
168
185
  </div>
169
186
  </div>
@@ -184,7 +201,11 @@ export default function SidebarMock({ kind, embedded = false, className }) {
184
201
  <div className={amStyles['sb-tabs']}>
185
202
  {tabs.map((t, i) => (
186
203
  <div key={t.id || i} className={[amStyles['sb-tab'], t.active && amStyles.active].filter(Boolean).join(' ')}>
187
- <div className={amStyles.ico}></div>
204
+ {t.icon ? (
205
+ <IntegrationIcon name={t.icon} size="xs" />
206
+ ) : (
207
+ <div className={amStyles.ico}></div>
208
+ )}
188
209
  <div className={amStyles.l}></div>
189
210
  </div>
190
211
  ))}
@@ -200,7 +221,7 @@ export default function SidebarMock({ kind, embedded = false, className }) {
200
221
 
201
222
  return (
202
223
  <div className={[amStyles.am, styles.sm, className].filter(Boolean).join(' ')}>
203
- <div className={styles.smFrame}>
224
+ <div className={[styles.smFrame, styles[`sb-size-${size}`]].filter(Boolean).join(' ')}>
204
225
  {panel}
205
226
  </div>
206
227
  </div>
@@ -28,10 +28,9 @@
28
28
  .am.sm { display: contents; }
29
29
 
30
30
  /* Standalone wrapper card. Inside, the .detail.rich panel renders
31
- identically to its embedded twin, just clipped to the smFrame box. */
31
+ identically to its embedded twin, just clipped to the smFrame box.
32
+ Width/height come from the .sb-size-* modifier on the same element. */
32
33
  .am .smFrame {
33
- width: 300px;
34
- height: 400px;
35
34
  background: white;
36
35
  border-radius: var(--radius-md);
37
36
  border: 1px solid var(--c-cobalt-100);
@@ -40,6 +39,9 @@
40
39
  display: flex;
41
40
  flex-direction: column;
42
41
  }
42
+ .am .smFrame.sb-size-sm { width: 240px; height: 320px; }
43
+ .am .smFrame.sb-size-md { width: 300px; height: 400px; }
44
+ .am .smFrame.sb-size-lg { width: 360px; height: 480px; }
43
45
 
44
46
  /* Inside .smFrame the .detail.rich child needs to fill the box; the
45
47
  default .detail width: 26% rule is meant for an embedded layout so