@conduction/docusaurus-preset 2.3.0 → 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 +1 -1
- package/src/components/AppMock/AppMock.jsx +9 -13
- package/src/components/AppMock/AppMock.module.css +55 -21
- package/src/components/AppMock/variants/OpenRegisterMock.jsx +20 -15
- package/src/components/AppMock/variants/ProcestMock.jsx +6 -1
- package/src/components/IntegrationIcon/IntegrationIcon.jsx +55 -0
- package/src/components/IntegrationIcon/IntegrationIcon.module.css +34 -0
- package/src/components/IntegrationIcon/registry.js +152 -0
- package/src/components/MockScene/MockScene.jsx +67 -0
- package/src/components/MockScene/MockScene.module.css +99 -0
- package/src/components/SidebarMock/SidebarMock.jsx +83 -53
- package/src/components/SidebarMock/SidebarMock.module.css +89 -141
package/package.json
CHANGED
|
@@ -82,25 +82,21 @@ export default function AppMock({app, size = 'md', sidebar = null, caption = fal
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
const {Component, label} = variant;
|
|
85
|
-
// The `sidebar` prop
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
85
|
+
// The `sidebar` prop is forwarded to the variant Component, which
|
|
86
|
+
// renders it as a flex sibling of `.col` inside the variant's
|
|
87
|
+
// `.body`, taking the `.detail` slot. SidebarMock children get
|
|
88
|
+
// `embedded: true` so they drop their standalone .smFrame chrome
|
|
89
|
+
// and render as the bare `.detail.rich` panel that slots into
|
|
90
|
+
// .body. Variants that don't accept a sidebar prop (e.g. MyDash)
|
|
91
|
+
// ignore it; this keeps the change additive.
|
|
91
92
|
const renderedSidebar = React.isValidElement(sidebar)
|
|
92
93
|
? React.cloneElement(sidebar, { embedded: true })
|
|
93
94
|
: sidebar;
|
|
94
95
|
return (
|
|
95
96
|
<div className={styles.am}>
|
|
96
97
|
<figure className={[styles.figure, className].filter(Boolean).join(' ')}>
|
|
97
|
-
<div className={[styles.frame, styles[`size-${size}`]
|
|
98
|
-
<Component />
|
|
99
|
-
{renderedSidebar && (
|
|
100
|
-
<div className={styles.sidebarOverlay} aria-label="Sidebar overlay">
|
|
101
|
-
{renderedSidebar}
|
|
102
|
-
</div>
|
|
103
|
-
)}
|
|
98
|
+
<div className={[styles.frame, styles[`size-${size}`]].filter(Boolean).join(' ')}>
|
|
99
|
+
<Component sidebar={renderedSidebar} />
|
|
104
100
|
</div>
|
|
105
101
|
{caption && <figcaption className={styles.caption}>{label}</figcaption>}
|
|
106
102
|
</figure>
|
|
@@ -47,27 +47,6 @@
|
|
|
47
47
|
.am .size-md { max-width: 720px; }
|
|
48
48
|
.am .size-lg { max-width: 960px; }
|
|
49
49
|
|
|
50
|
-
/* Sidebar overlay: when AppMock receives a `sidebar` prop, the
|
|
51
|
-
passed JSX renders pinned to the right edge of the frame, full
|
|
52
|
-
height, modeling a Nextcloud detail panel sliding over the app.
|
|
53
|
-
The .withSidebar modifier on .frame is a hook for variants that
|
|
54
|
-
want to dim the underlying canvas; today it doesn't add any
|
|
55
|
-
styles itself. */
|
|
56
|
-
.am .frame .sidebarOverlay {
|
|
57
|
-
position: absolute;
|
|
58
|
-
top: 0;
|
|
59
|
-
right: 0;
|
|
60
|
-
bottom: 0;
|
|
61
|
-
width: 32%;
|
|
62
|
-
min-width: 200px;
|
|
63
|
-
background: white;
|
|
64
|
-
border-left: 1px solid var(--c-cobalt-200);
|
|
65
|
-
z-index: 2;
|
|
66
|
-
display: flex;
|
|
67
|
-
flex-direction: column;
|
|
68
|
-
overflow: hidden;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
50
|
.am .caption {
|
|
72
51
|
font-family: var(--conduction-typography-font-family-code);
|
|
73
52
|
font-size: 11px;
|
|
@@ -163,6 +142,61 @@
|
|
|
163
142
|
flex-shrink: 0;
|
|
164
143
|
}
|
|
165
144
|
|
|
145
|
+
/* Sidebar (enriched .detail) matches the real Nextcloud sidepanel:
|
|
146
|
+
header (icon + title + description + close), tabs, then content.
|
|
147
|
+
Used by SidebarMock embedded mode (slots into <body>'s flex layout
|
|
148
|
+
as a sibling of .col, taking the .detail slot) and by the kit
|
|
149
|
+
page templates section. */
|
|
150
|
+
.am .detail.rich { padding: 0; gap: 0; }
|
|
151
|
+
.am .detail.rich .sb-head {
|
|
152
|
+
display: flex; align-items: center; gap: 6px;
|
|
153
|
+
padding: 10px;
|
|
154
|
+
border-bottom: 1px solid var(--c-cobalt-100);
|
|
155
|
+
}
|
|
156
|
+
.am .detail.rich .sb-head .ico {
|
|
157
|
+
width: 16px; height: 16px;
|
|
158
|
+
border-radius: 50%;
|
|
159
|
+
background: var(--c-cobalt-50);
|
|
160
|
+
border: 1px solid var(--c-cobalt-200);
|
|
161
|
+
flex-shrink: 0;
|
|
162
|
+
}
|
|
163
|
+
.am .detail.rich .sb-head .meta {
|
|
164
|
+
flex: 1; display: flex; flex-direction: column; gap: 3px;
|
|
165
|
+
min-width: 0;
|
|
166
|
+
}
|
|
167
|
+
.am .detail.rich .sb-head .title { height: 6px; background: var(--c-cobalt-900); border-radius: 1px; width: 50%; }
|
|
168
|
+
.am .detail.rich .sb-head .desc { height: 3px; background: var(--c-cobalt-300); border-radius: 1px; width: 85%; }
|
|
169
|
+
.am .detail.rich .sb-head .close { width: 8px; height: 8px; background: var(--c-cobalt-300); flex-shrink: 0; }
|
|
170
|
+
|
|
171
|
+
.am .detail.rich .sb-tabs {
|
|
172
|
+
display: flex;
|
|
173
|
+
padding: 0 10px;
|
|
174
|
+
border-bottom: 1px solid var(--c-cobalt-100);
|
|
175
|
+
}
|
|
176
|
+
.am .detail.rich .sb-tab {
|
|
177
|
+
flex: 1; padding: 6px 0;
|
|
178
|
+
display: flex; flex-direction: column; align-items: center; gap: 3px;
|
|
179
|
+
border-bottom: 2px solid transparent;
|
|
180
|
+
}
|
|
181
|
+
.am .detail.rich .sb-tab.active { border-bottom-color: var(--c-blue-cobalt); }
|
|
182
|
+
.am .detail.rich .sb-tab .ico { width: 7px; height: 7px; border-radius: 50%; background: var(--c-cobalt-300); }
|
|
183
|
+
.am .detail.rich .sb-tab.active .ico { background: var(--c-cobalt-700); }
|
|
184
|
+
.am .detail.rich .sb-tab .l { height: 3px; background: var(--c-cobalt-300); width: 24px; border-radius: 1px; }
|
|
185
|
+
.am .detail.rich .sb-tab.active .l { background: var(--c-cobalt-700); }
|
|
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
|
+
|
|
193
|
+
.am .detail.rich .sb-body { padding: 10px; display: flex; flex-direction: column; gap: 7px; overflow: hidden; }
|
|
194
|
+
.am .detail.rich .sb-label { height: 4px; background: var(--c-cobalt-700); width: 40%; border-radius: 1px; }
|
|
195
|
+
.am .detail.rich .sb-input { height: 14px; background: white; border: 1px solid var(--c-cobalt-200); border-radius: 3px; }
|
|
196
|
+
.am .detail.rich .sb-filter { display: flex; flex-direction: column; gap: 4px; }
|
|
197
|
+
.am .detail.rich .sb-filter .lbl { height: 3px; background: var(--c-cobalt-400); width: 30%; border-radius: 1px; }
|
|
198
|
+
.am .detail.rich .sb-filter .field { height: 12px; background: white; border: 1px solid var(--c-cobalt-200); border-radius: 2px; }
|
|
199
|
+
|
|
166
200
|
/* Generic content panel — white card with rounded corners. */
|
|
167
201
|
.am .panel {
|
|
168
202
|
background: white;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import React from 'react';
|
|
11
11
|
import styles from '../AppMock.module.css';
|
|
12
12
|
|
|
13
|
-
export default function OpenRegisterMock() {
|
|
13
|
+
export default function OpenRegisterMock({ sidebar = null }) {
|
|
14
14
|
return (
|
|
15
15
|
<>
|
|
16
16
|
<div className={styles.topbar}>
|
|
@@ -80,20 +80,25 @@ export default function OpenRegisterMock() {
|
|
|
80
80
|
</div>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
83
|
-
{/* Right detail rail
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<div
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
{/* Right detail rail. When AppMock passes a `sidebar` prop
|
|
84
|
+
(typically a <SidebarMock kind="..." embedded />), it
|
|
85
|
+
renders as the rich detail rail instead of the placeholder
|
|
86
|
+
rows. */}
|
|
87
|
+
{sidebar || (
|
|
88
|
+
<div className={styles.detail}>
|
|
89
|
+
<div className={styles.row + ' ' + styles.head}></div>
|
|
90
|
+
<div className={styles.row}></div>
|
|
91
|
+
<div className={styles.row + ' ' + styles.short}></div>
|
|
92
|
+
<div style={{height: 8}}></div>
|
|
93
|
+
<div className={styles.row + ' ' + styles.head}></div>
|
|
94
|
+
<div className={styles.row + ' ' + styles.dark}></div>
|
|
95
|
+
<div className={styles.row}></div>
|
|
96
|
+
<div className={styles.row}></div>
|
|
97
|
+
<div className={styles.row + ' ' + styles.short}></div>
|
|
98
|
+
<div className={styles.row + ' ' + styles.accent}></div>
|
|
99
|
+
<div className={styles.row + ' ' + styles.short}></div>
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
97
102
|
</div>
|
|
98
103
|
</>
|
|
99
104
|
);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import React from 'react';
|
|
12
12
|
import styles from '../AppMock.module.css';
|
|
13
13
|
|
|
14
|
-
export default function ProcestMock() {
|
|
14
|
+
export default function ProcestMock({ sidebar = null }) {
|
|
15
15
|
return (
|
|
16
16
|
<>
|
|
17
17
|
<div className={styles.topbar}>
|
|
@@ -81,6 +81,11 @@ export default function ProcestMock() {
|
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
83
83
|
</div>
|
|
84
|
+
{/* Optional sidebar (typically a <SidebarMock kind="..." />)
|
|
85
|
+
renders here as a flex sibling of .col, taking the .detail
|
|
86
|
+
slot. Procest's case detail view doesn't ship a default
|
|
87
|
+
detail rail, so without the prop nothing extra renders. */}
|
|
88
|
+
{sidebar}
|
|
84
89
|
</div>
|
|
85
90
|
</>
|
|
86
91
|
);
|
|
@@ -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; }
|
|
@@ -8,24 +8,32 @@
|
|
|
8
8
|
* so on). A SidebarMock variant represents one such sidebar with
|
|
9
9
|
* one tab active.
|
|
10
10
|
*
|
|
11
|
+
* Markup matches the existing .detail.rich pattern that already
|
|
12
|
+
* lived in the AppMock kit anatomy: a .sb-head row (icon + meta of
|
|
13
|
+
* title + description + close), a .sb-tabs strip (one per registered
|
|
14
|
+
* tab, active marker on the current one), and a .sb-body. All
|
|
15
|
+
* styles live in AppMock.module.css under .am .detail.rich; the
|
|
16
|
+
* body atoms (smKv / smPerson / smStage / smLog / smPii) live in
|
|
17
|
+
* SidebarMock.module.css under .am .sb-body so they only apply
|
|
18
|
+
* inside a sidebar.
|
|
19
|
+
*
|
|
11
20
|
* Two render modes:
|
|
12
21
|
*
|
|
13
|
-
* STANDALONE (default): wraps the panel in
|
|
14
|
-
*
|
|
15
|
-
* copy that talks about a single
|
|
22
|
+
* STANDALONE (default): wraps the .detail.rich panel in
|
|
23
|
+
* <div class="am sm"> + .smFrame so it reads as a card on the
|
|
24
|
+
* kit page. Use in marketing copy that talks about a single
|
|
25
|
+
* sidebar surface in isolation.
|
|
16
26
|
*
|
|
17
27
|
* <SidebarMock kind="procest-xwiki" />
|
|
18
28
|
*
|
|
19
|
-
* EMBEDDED: drops the
|
|
20
|
-
*
|
|
21
|
-
*
|
|
29
|
+
* EMBEDDED: drops the .smFrame wrapper, leaving just
|
|
30
|
+
* <div class="detail rich">…</div>. Slots into the .body flex
|
|
31
|
+
* layout of an AppMock variant as a sibling of .col, taking the
|
|
32
|
+
* detail-rail position. Set automatically by AppMock when the
|
|
33
|
+
* `sidebar` prop is a SidebarMock JSX element.
|
|
22
34
|
*
|
|
23
35
|
* <AppMock app="procest" sidebar={<SidebarMock kind="procest-xwiki" />} />
|
|
24
36
|
*
|
|
25
|
-
* Header bar (title + close) and tab strip are chassis. Body content
|
|
26
|
-
* is per-variant: the JSX file under variants/ provides just the body
|
|
27
|
-
* children, the chassis wraps it.
|
|
28
|
-
*
|
|
29
37
|
* Status colour map matches WidgetMock and AppMock atoms:
|
|
30
38
|
* mint = stable / done / signed
|
|
31
39
|
* orange = active / pending / due soon
|
|
@@ -34,15 +42,17 @@
|
|
|
34
42
|
*
|
|
35
43
|
* Props:
|
|
36
44
|
* - kind: one of VARIANTS keys (required)
|
|
37
|
-
* - embedded: boolean (default false) — drop the standalone
|
|
38
|
-
*
|
|
39
|
-
*
|
|
45
|
+
* - embedded: boolean (default false) — drop the standalone
|
|
46
|
+
* .smFrame chrome so the
|
|
47
|
+
* panel slots into
|
|
48
|
+
* AppMock's .body layout
|
|
40
49
|
* - className: string
|
|
41
50
|
*/
|
|
42
51
|
|
|
43
52
|
import React from 'react';
|
|
44
53
|
import styles from './SidebarMock.module.css';
|
|
45
54
|
import amStyles from '../AppMock/AppMock.module.css';
|
|
55
|
+
import IntegrationIcon from '../IntegrationIcon/IntegrationIcon.jsx';
|
|
46
56
|
|
|
47
57
|
import ProcestXWiki from './variants/ProcestXWiki.jsx';
|
|
48
58
|
import ProcestTimeline from './variants/ProcestTimeline.jsx';
|
|
@@ -56,95 +66,104 @@ import NextcloudActivity from './variants/NextcloudActivity.jsx';
|
|
|
56
66
|
|
|
57
67
|
/**
|
|
58
68
|
* Each VARIANTS entry carries:
|
|
59
|
-
* Component: the JSX that fills the body
|
|
69
|
+
* Component: the JSX that fills the .sb-body
|
|
60
70
|
* label: human-readable name (used in caption / kit page)
|
|
61
|
-
* tabs: ordered list of
|
|
62
|
-
*
|
|
71
|
+
* tabs: ordered list of tabs with one .active. The id is
|
|
72
|
+
* decorative (rendered as a placeholder bar in .sb-tab .l)
|
|
63
73
|
* but the active flag drives the highlight.
|
|
64
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
|
+
*/
|
|
65
84
|
const VARIANTS = {
|
|
66
85
|
'procest-xwiki': {
|
|
67
86
|
Component: ProcestXWiki,
|
|
68
87
|
label: 'Procest · Case sidebar, xWiki tab',
|
|
69
88
|
tabs: [
|
|
70
|
-
{ id: 'activity', active: false },
|
|
71
|
-
{ id: 'xwiki', active: true },
|
|
72
|
-
{ id: 'timeline', active: false },
|
|
73
|
-
{ 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' },
|
|
74
93
|
],
|
|
75
94
|
},
|
|
76
95
|
'procest-timeline': {
|
|
77
96
|
Component: ProcestTimeline,
|
|
78
97
|
label: 'Procest · Case sidebar, Timeline tab',
|
|
79
98
|
tabs: [
|
|
80
|
-
{ id: 'activity', active: false },
|
|
81
|
-
{ id: 'xwiki', active: false },
|
|
82
|
-
{ id: 'timeline', active: true },
|
|
83
|
-
{ 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' },
|
|
84
103
|
],
|
|
85
104
|
},
|
|
86
105
|
'docudesk-signatures': {
|
|
87
106
|
Component: DocuDeskSignatures,
|
|
88
107
|
label: 'DocuDesk · Document sidebar, Signatures tab',
|
|
89
108
|
tabs: [
|
|
90
|
-
{ id: 'activity',
|
|
91
|
-
{ id: 'signatures',
|
|
92
|
-
{ id: 'pii',
|
|
93
|
-
{ id: 'versions',
|
|
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' },
|
|
94
113
|
],
|
|
95
114
|
},
|
|
96
115
|
'docudesk-pii-map': {
|
|
97
116
|
Component: DocuDeskPiiMap,
|
|
98
117
|
label: 'DocuDesk · Document sidebar, PII map tab',
|
|
99
118
|
tabs: [
|
|
100
|
-
{ id: 'activity',
|
|
101
|
-
{ id: 'signatures',
|
|
102
|
-
{ id: 'pii',
|
|
103
|
-
{ id: 'versions',
|
|
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' },
|
|
104
123
|
],
|
|
105
124
|
},
|
|
106
125
|
'openregister-metadata': {
|
|
107
126
|
Component: OpenRegisterMetadata,
|
|
108
127
|
label: 'OpenRegister · Object sidebar, Metadata tab',
|
|
109
128
|
tabs: [
|
|
110
|
-
{ id: 'activity', active: false },
|
|
111
|
-
{ id: 'metadata', active: true },
|
|
112
|
-
{ 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' },
|
|
113
132
|
],
|
|
114
133
|
},
|
|
115
134
|
'opencatalogi-publication-history': {
|
|
116
135
|
Component: OpenCatalogiPublicationHistory,
|
|
117
136
|
label: 'OpenCatalogi · Publication sidebar, History tab',
|
|
118
137
|
tabs: [
|
|
119
|
-
{ id: 'activity', active: false },
|
|
120
|
-
{ id: 'history', active: true },
|
|
121
|
-
{ 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' },
|
|
122
141
|
],
|
|
123
142
|
},
|
|
124
143
|
'openconnector-run-detail': {
|
|
125
144
|
Component: OpenConnectorRunDetail,
|
|
126
145
|
label: 'OpenConnector · Run sidebar, Logs tab',
|
|
127
146
|
tabs: [
|
|
128
|
-
{ id: 'activity', active: false },
|
|
129
|
-
{ id: 'logs', active: true },
|
|
130
|
-
{ 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' },
|
|
131
150
|
],
|
|
132
151
|
},
|
|
133
152
|
'decidesk-decision': {
|
|
134
153
|
Component: DeciDeskDecision,
|
|
135
154
|
label: 'DeciDesk · Decision sidebar, Detail tab',
|
|
136
155
|
tabs: [
|
|
137
|
-
{ id: 'activity', active: false },
|
|
138
|
-
{ id: 'detail', active: true },
|
|
139
|
-
{ 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' },
|
|
140
159
|
],
|
|
141
160
|
},
|
|
142
161
|
'nextcloud-activity': {
|
|
143
162
|
Component: NextcloudActivity,
|
|
144
163
|
label: 'Nextcloud · Stock activity feed',
|
|
145
164
|
tabs: [
|
|
146
|
-
{ id: 'activity', active: true },
|
|
147
|
-
{ id: 'comments', active: false },
|
|
165
|
+
{ id: 'activity', active: true, icon: 'activity' },
|
|
166
|
+
{ id: 'comments', active: false, icon: 'talk' },
|
|
148
167
|
],
|
|
149
168
|
},
|
|
150
169
|
};
|
|
@@ -162,19 +181,30 @@ export default function SidebarMock({ kind, embedded = false, className }) {
|
|
|
162
181
|
}
|
|
163
182
|
const { Component, tabs } = variant;
|
|
164
183
|
const panel = (
|
|
165
|
-
<div className={
|
|
166
|
-
<div className={
|
|
167
|
-
<div className={
|
|
168
|
-
<div className={
|
|
184
|
+
<div className={[amStyles.detail, amStyles.rich].join(' ')}>
|
|
185
|
+
<div className={amStyles['sb-head']}>
|
|
186
|
+
<div className={amStyles.ico}></div>
|
|
187
|
+
<div className={amStyles.meta}>
|
|
188
|
+
<div className={amStyles.title}></div>
|
|
189
|
+
<div className={amStyles.desc}></div>
|
|
190
|
+
</div>
|
|
191
|
+
<div className={amStyles.close}></div>
|
|
169
192
|
</div>
|
|
170
193
|
{tabs && tabs.length > 0 && (
|
|
171
|
-
<div className={
|
|
194
|
+
<div className={amStyles['sb-tabs']}>
|
|
172
195
|
{tabs.map((t, i) => (
|
|
173
|
-
<div key={t.id || i} className={[
|
|
196
|
+
<div key={t.id || i} className={[amStyles['sb-tab'], t.active && amStyles.active].filter(Boolean).join(' ')}>
|
|
197
|
+
{t.icon ? (
|
|
198
|
+
<IntegrationIcon name={t.icon} size="xs" />
|
|
199
|
+
) : (
|
|
200
|
+
<div className={amStyles.ico}></div>
|
|
201
|
+
)}
|
|
202
|
+
<div className={amStyles.l}></div>
|
|
203
|
+
</div>
|
|
174
204
|
))}
|
|
175
205
|
</div>
|
|
176
206
|
)}
|
|
177
|
-
<div className={
|
|
207
|
+
<div className={amStyles['sb-body']}>
|
|
178
208
|
<Component />
|
|
179
209
|
</div>
|
|
180
210
|
</div>
|
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* <SidebarMock /> styles.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The sidebar chassis (.detail.rich, .sb-head, .sb-tabs, .sb-body and
|
|
5
|
+
* its built-in atoms .sb-input / .sb-filter / .sb-label) lives in
|
|
6
|
+
* AppMock/AppMock.module.css. SidebarMock reuses that module so the
|
|
7
|
+
* panel renders identically whether it is embedded inside an AppMock
|
|
8
|
+
* (taking the .detail slot in .body's flex layout) or rendered
|
|
9
|
+
* standalone for kit-page specimens.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
11
|
+
* What lives here:
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
+
* .smFrame — the standalone wrapper card (white box, shadow,
|
|
14
|
+
* radius, fixed size). Used only when rendering the
|
|
15
|
+
* sidebar standalone in marketing copy or kit pages;
|
|
16
|
+
* embedded mode skips it so the panel slots into
|
|
17
|
+
* .body's flex layout untouched.
|
|
13
18
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* .am .sb-body atoms — extra body-level atoms beyond what
|
|
20
|
+
* AppMock.module.css already provides under .sb-body
|
|
21
|
+
* (.sb-input, .sb-filter, .sb-label). The atoms here
|
|
22
|
+
* cover the rest of the marketing surfaces: key/value
|
|
23
|
+
* pairs (smKv), person rows with status pip (smPerson),
|
|
24
|
+
* stage timelines (smStage), log lines (smLog), and
|
|
25
|
+
* PII-redaction pill maps (smPii).
|
|
21
26
|
*/
|
|
22
27
|
|
|
23
28
|
.am.sm { display: contents; }
|
|
24
29
|
|
|
25
|
-
/*
|
|
26
|
-
|
|
30
|
+
/* Standalone wrapper card. Inside, the .detail.rich panel renders
|
|
31
|
+
identically to its embedded twin, just clipped to the smFrame box. */
|
|
27
32
|
.am .smFrame {
|
|
28
33
|
width: 300px;
|
|
29
34
|
height: 400px;
|
|
@@ -36,110 +41,46 @@
|
|
|
36
41
|
flex-direction: column;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
/*
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
min-height: 0;
|
|
46
|
-
background: white;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* Header: title bar plus close button. */
|
|
50
|
-
.am .smHead {
|
|
51
|
-
padding: 10px 12px;
|
|
52
|
-
display: flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
gap: 8px;
|
|
55
|
-
border-bottom: 1px solid var(--c-cobalt-100);
|
|
56
|
-
flex-shrink: 0;
|
|
57
|
-
}
|
|
58
|
-
.am .smHead .smTitle {
|
|
59
|
-
flex: 1;
|
|
60
|
-
height: 5px;
|
|
61
|
-
background: var(--c-cobalt-700);
|
|
62
|
-
border-radius: 1px;
|
|
63
|
-
}
|
|
64
|
-
.am .smHead .smClose {
|
|
65
|
-
width: 10px;
|
|
66
|
-
height: 10px;
|
|
67
|
-
background: var(--c-cobalt-300);
|
|
68
|
-
border-radius: 1px;
|
|
69
|
-
flex-shrink: 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* Tab strip: each tab is a width-fixed pill. Active tab is cobalt with
|
|
73
|
-
a 2px underline that crosses the .smTabs bottom border. */
|
|
74
|
-
.am .smTabs {
|
|
75
|
-
display: flex;
|
|
76
|
-
padding: 0 12px;
|
|
77
|
-
gap: 8px;
|
|
78
|
-
border-bottom: 1px solid var(--c-cobalt-100);
|
|
79
|
-
flex-shrink: 0;
|
|
80
|
-
}
|
|
81
|
-
.am .smTab {
|
|
82
|
-
height: 4px;
|
|
83
|
-
width: 28px;
|
|
84
|
-
background: var(--c-cobalt-200);
|
|
85
|
-
border-radius: 1px;
|
|
86
|
-
margin: 10px 0 8px;
|
|
87
|
-
position: relative;
|
|
88
|
-
}
|
|
89
|
-
.am .smTab.smTabActive {
|
|
90
|
-
background: var(--c-blue-cobalt);
|
|
91
|
-
}
|
|
92
|
-
.am .smTab.smTabActive::after {
|
|
93
|
-
content: "";
|
|
94
|
-
position: absolute;
|
|
95
|
-
bottom: -8px;
|
|
96
|
-
left: -2px;
|
|
97
|
-
right: -2px;
|
|
98
|
-
height: 2px;
|
|
99
|
-
background: var(--c-blue-cobalt);
|
|
100
|
-
border-radius: 1px;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/* Body: scrolling region with stacked sections. */
|
|
104
|
-
.am .smBody {
|
|
44
|
+
/* Inside .smFrame the .detail.rich child needs to fill the box; the
|
|
45
|
+
default .detail width: 26% rule is meant for an embedded layout so
|
|
46
|
+
we override here. */
|
|
47
|
+
.am .smFrame > .detail.rich {
|
|
48
|
+
width: 100%;
|
|
49
|
+
min-width: 0;
|
|
105
50
|
flex: 1;
|
|
106
|
-
|
|
107
|
-
display: flex;
|
|
108
|
-
flex-direction: column;
|
|
109
|
-
gap: 8px;
|
|
110
|
-
min-height: 0;
|
|
111
|
-
overflow: hidden;
|
|
51
|
+
border-left: none;
|
|
112
52
|
}
|
|
113
53
|
|
|
114
|
-
/* ---- Body atoms ---- */
|
|
54
|
+
/* ---- Body atoms beyond the AppMock-provided sb-input / sb-filter ---- */
|
|
115
55
|
|
|
116
|
-
/* Generic row.
|
|
117
|
-
.am .
|
|
56
|
+
/* Generic row block. Sized by class modifier; defaults to thin neutral. */
|
|
57
|
+
.am .sb-body .row {
|
|
118
58
|
height: 3px;
|
|
119
59
|
background: var(--c-cobalt-200);
|
|
120
60
|
border-radius: 1px;
|
|
121
61
|
flex-shrink: 0;
|
|
62
|
+
width: 100%;
|
|
122
63
|
}
|
|
123
|
-
.am .
|
|
64
|
+
.am .sb-body .row.head {
|
|
124
65
|
height: 5px;
|
|
125
66
|
width: 55%;
|
|
126
67
|
background: var(--c-cobalt-700);
|
|
127
68
|
margin-bottom: 4px;
|
|
128
69
|
}
|
|
129
|
-
.am .
|
|
130
|
-
.am .
|
|
131
|
-
.am .
|
|
132
|
-
.am .
|
|
70
|
+
.am .sb-body .row.short { width: 35%; }
|
|
71
|
+
.am .sb-body .row.med { width: 70%; }
|
|
72
|
+
.am .sb-body .row.dark { background: var(--c-cobalt-700); }
|
|
73
|
+
.am .sb-body .row.accent { background: var(--c-orange-knvb); }
|
|
133
74
|
|
|
134
|
-
/* Section break: horizontal rule
|
|
135
|
-
.am .
|
|
75
|
+
/* Section break: thin horizontal rule between sub-sections. */
|
|
76
|
+
.am .sb-body .smBreak {
|
|
136
77
|
height: 1px;
|
|
137
78
|
background: var(--c-cobalt-100);
|
|
138
79
|
margin: 4px 0;
|
|
139
80
|
}
|
|
140
81
|
|
|
141
|
-
/* Subhead:
|
|
142
|
-
.am .
|
|
82
|
+
/* Subhead: short bold-weight bar that introduces a new section. */
|
|
83
|
+
.am .sb-body .smSub {
|
|
143
84
|
height: 4px;
|
|
144
85
|
width: 40%;
|
|
145
86
|
background: var(--c-cobalt-700);
|
|
@@ -147,106 +88,113 @@
|
|
|
147
88
|
margin-top: 4px;
|
|
148
89
|
}
|
|
149
90
|
|
|
150
|
-
/* Key-value list (used for metadata
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
.am .smBody .smKv {
|
|
91
|
+
/* Key-value list (used for metadata tabs). Label cobalt-400 short bar
|
|
92
|
+
plus longer cobalt-700 value bar on the same row. */
|
|
93
|
+
.am .sb-body .smKv {
|
|
154
94
|
display: flex;
|
|
155
95
|
align-items: center;
|
|
156
96
|
gap: 8px;
|
|
157
97
|
}
|
|
158
|
-
.am .
|
|
98
|
+
.am .sb-body .smKv .k {
|
|
159
99
|
height: 3px;
|
|
160
100
|
width: 30%;
|
|
161
101
|
background: var(--c-cobalt-400);
|
|
162
102
|
border-radius: 1px;
|
|
163
103
|
flex-shrink: 0;
|
|
164
104
|
}
|
|
165
|
-
.am .
|
|
105
|
+
.am .sb-body .smKv .v {
|
|
166
106
|
flex: 1;
|
|
167
107
|
height: 3px;
|
|
168
108
|
background: var(--c-cobalt-700);
|
|
169
109
|
border-radius: 1px;
|
|
170
110
|
}
|
|
171
111
|
|
|
172
|
-
/* Person row (
|
|
173
|
-
|
|
174
|
-
.
|
|
112
|
+
/* Person row (signatures, comments, history). Avatar + two text rows
|
|
113
|
+
+ optional status pip. .b/.c/.d/.e modifiers shift the avatar to
|
|
114
|
+
the matching family colour. .pending / .blocked modifiers shift
|
|
115
|
+
the pip to orange / red. */
|
|
116
|
+
.am .sb-body .smPerson {
|
|
175
117
|
display: flex;
|
|
176
118
|
align-items: center;
|
|
177
119
|
gap: 6px;
|
|
178
120
|
}
|
|
179
|
-
.am .
|
|
121
|
+
.am .sb-body .smPerson .av {
|
|
180
122
|
width: 14px;
|
|
181
123
|
height: 14px;
|
|
182
124
|
border-radius: 50%;
|
|
183
125
|
background: var(--c-mint-300);
|
|
184
126
|
flex-shrink: 0;
|
|
185
127
|
}
|
|
186
|
-
.am .
|
|
187
|
-
.am .
|
|
188
|
-
.am .
|
|
189
|
-
.am .
|
|
190
|
-
.am .
|
|
128
|
+
.am .sb-body .smPerson.b .av { background: var(--c-lavender-300); }
|
|
129
|
+
.am .sb-body .smPerson.c .av { background: var(--c-orange-knvb); }
|
|
130
|
+
.am .sb-body .smPerson.d .av { background: var(--c-forest-300); }
|
|
131
|
+
.am .sb-body .smPerson.e .av { background: var(--c-terracotta-300); }
|
|
132
|
+
.am .sb-body .smPerson .lines {
|
|
191
133
|
flex: 1;
|
|
192
134
|
display: flex;
|
|
193
135
|
flex-direction: column;
|
|
194
136
|
gap: 2px;
|
|
195
137
|
min-width: 0;
|
|
196
138
|
}
|
|
197
|
-
.am .
|
|
198
|
-
.am .
|
|
199
|
-
.am .
|
|
139
|
+
.am .sb-body .smPerson .l1 { height: 3px; width: 70%; background: var(--c-cobalt-700); border-radius: 1px; }
|
|
140
|
+
.am .sb-body .smPerson .l2 { height: 2px; width: 50%; background: var(--c-cobalt-300); border-radius: 1px; }
|
|
141
|
+
.am .sb-body .smPerson .pip {
|
|
200
142
|
width: 8px;
|
|
201
143
|
height: 8px;
|
|
202
144
|
border-radius: 50%;
|
|
203
145
|
background: var(--c-mint-500);
|
|
204
146
|
flex-shrink: 0;
|
|
205
147
|
}
|
|
206
|
-
.am .
|
|
207
|
-
.am .
|
|
148
|
+
.am .sb-body .smPerson.pending .pip { background: var(--c-orange-knvb); }
|
|
149
|
+
.am .sb-body .smPerson.blocked .pip { background: var(--c-red-vermillion); }
|
|
208
150
|
|
|
209
|
-
/* Stage timeline
|
|
210
|
-
.
|
|
151
|
+
/* Stage timeline (case sidebar, Timeline tab). Vertical list with a
|
|
152
|
+
stage hex on the left and two text rows. Modifiers .now / .late /
|
|
153
|
+
.todo shift the hex colour. */
|
|
154
|
+
.am .sb-body .smStage {
|
|
211
155
|
display: flex;
|
|
212
156
|
align-items: center;
|
|
213
157
|
gap: 8px;
|
|
214
158
|
}
|
|
215
|
-
.am .
|
|
159
|
+
.am .sb-body .smStage .h {
|
|
216
160
|
width: 10px;
|
|
217
161
|
height: 11px;
|
|
218
162
|
clip-path: var(--hex-pointy-top);
|
|
219
163
|
background: var(--c-mint-500);
|
|
220
164
|
flex-shrink: 0;
|
|
221
165
|
}
|
|
222
|
-
.am .
|
|
223
|
-
.am .
|
|
224
|
-
.am .
|
|
225
|
-
.am .
|
|
226
|
-
.am .
|
|
227
|
-
.am .
|
|
228
|
-
|
|
229
|
-
/* Log line
|
|
230
|
-
.
|
|
166
|
+
.am .sb-body .smStage.now .h { background: var(--c-orange-knvb); }
|
|
167
|
+
.am .sb-body .smStage.todo .h { background: var(--c-cobalt-200); }
|
|
168
|
+
.am .sb-body .smStage.late .h { background: var(--c-red-vermillion); }
|
|
169
|
+
.am .sb-body .smStage .lines { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
170
|
+
.am .sb-body .smStage .l1 { height: 3px; width: 60%; background: var(--c-cobalt-700); border-radius: 1px; }
|
|
171
|
+
.am .sb-body .smStage .l2 { height: 2px; width: 40%; background: var(--c-cobalt-300); border-radius: 1px; }
|
|
172
|
+
|
|
173
|
+
/* Log line (run-detail sidebar). Short timestamp bar + message bar.
|
|
174
|
+
.warn / .error tint the timestamp to orange / red so the eye can
|
|
175
|
+
scan severity at a glance. */
|
|
176
|
+
.am .sb-body .smLog {
|
|
231
177
|
display: flex;
|
|
232
178
|
align-items: center;
|
|
233
179
|
gap: 6px;
|
|
234
180
|
}
|
|
235
|
-
.am .
|
|
236
|
-
.am .
|
|
237
|
-
.am .
|
|
238
|
-
.am .
|
|
181
|
+
.am .sb-body .smLog .ts { width: 22px; height: 3px; background: var(--c-cobalt-400); border-radius: 1px; flex-shrink: 0; }
|
|
182
|
+
.am .sb-body .smLog .msg { flex: 1; height: 3px; background: var(--c-cobalt-700); border-radius: 1px; }
|
|
183
|
+
.am .sb-body .smLog.warn .ts { background: var(--c-orange-knvb); }
|
|
184
|
+
.am .sb-body .smLog.error .ts { background: var(--c-red-vermillion); }
|
|
239
185
|
|
|
240
|
-
/* PII
|
|
241
|
-
|
|
186
|
+
/* PII redaction map (DocuDesk PII tab). Inline pills marking spans
|
|
187
|
+
in a paragraph; .redacted = applied (red), .suggested = proposed
|
|
188
|
+
(orange), default = neutral text. */
|
|
189
|
+
.am .sb-body .smPiiBlock {
|
|
242
190
|
display: flex;
|
|
243
191
|
flex-wrap: wrap;
|
|
244
192
|
gap: 4px;
|
|
245
193
|
}
|
|
246
|
-
.am .
|
|
194
|
+
.am .sb-body .smPii {
|
|
247
195
|
height: 6px;
|
|
248
196
|
background: var(--c-cobalt-200);
|
|
249
197
|
border-radius: 1px;
|
|
250
198
|
}
|
|
251
|
-
.am .
|
|
252
|
-
.am .
|
|
199
|
+
.am .sb-body .smPii.redacted { background: var(--c-red-vermillion); }
|
|
200
|
+
.am .sb-body .smPii.suggested { background: var(--c-orange-knvb); }
|