@conduction/docusaurus-preset 3.22.0 → 3.24.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/AppCrossLinks/AppCrossLinks.jsx +5 -2
- package/src/components/AppCrossLinks/AppCrossLinks.module.css +9 -0
- package/src/components/AppGlyph/AppGlyph.jsx +53 -0
- package/src/components/DetailHero/DetailHero.jsx +23 -4
- package/src/components/index.js +1 -0
- package/src/data/app-downloads.js +9 -1
- package/src/data/app-glyphs.json +94 -0
- package/src/data/apps-registry.js +20 -20
- package/src/plugins/extractFeatures.js +95 -0
- package/src/plugins/features-page.js +19 -9
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* learning about a Conduction app:
|
|
6
6
|
*
|
|
7
7
|
* 1. /apps/<slug> product page
|
|
8
|
-
* 2. https
|
|
8
|
+
* 2. https://<slug>.conduction.nl documentation
|
|
9
9
|
* 3. /academy?app=<slug> academy filtered
|
|
10
10
|
*
|
|
11
11
|
* URLs come from the shared apps-registry so all three surfaces stay
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
import React from 'react';
|
|
49
49
|
import {APPS_REGISTRY} from '../../data/apps-registry';
|
|
50
|
+
import AppGlyph from '../AppGlyph/AppGlyph.jsx';
|
|
50
51
|
import styles from './AppCrossLinks.module.css';
|
|
51
52
|
|
|
52
53
|
const ROWS = [
|
|
@@ -89,7 +90,9 @@ function AppCard({app, surface, variant}) {
|
|
|
89
90
|
return (
|
|
90
91
|
<div className={styles.card} aria-labelledby={tagId}>
|
|
91
92
|
<div className={styles.head}>
|
|
92
|
-
<span className={styles.hex} aria-hidden="true"
|
|
93
|
+
<span className={styles.hex} aria-hidden="true">
|
|
94
|
+
<AppGlyph app={app} className={styles.hexGlyph} />
|
|
95
|
+
</span>
|
|
93
96
|
<div>
|
|
94
97
|
<p className={styles.eyebrow}>About this app</p>
|
|
95
98
|
<h4 className={styles.title} id={tagId}>{reg.name}</h4>
|
|
@@ -62,6 +62,15 @@
|
|
|
62
62
|
clip-path: var(--hex-pointy-top);
|
|
63
63
|
background: var(--c-blue-cobalt);
|
|
64
64
|
flex-shrink: 0;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
color: #fff;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.hexGlyph {
|
|
72
|
+
width: 16px;
|
|
73
|
+
height: 16px;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
.eyebrow {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import GLYPHS from '../../data/app-glyphs.json';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AppGlyph — the single source of truth for Conduction app logos.
|
|
6
|
+
*
|
|
7
|
+
* Renders the canonical app glyph keyed by slug, extracted verbatim
|
|
8
|
+
* from the identity brand kit (identity.conduction.nl/apps, the
|
|
9
|
+
* `#g-<slug>` symbols in preview/apps.html). Every surface that shows
|
|
10
|
+
* an app mark (the /apps detail heroes, the ConNext platform diagram,
|
|
11
|
+
* the apps catalogue grid) consumes this component so the logo is the
|
|
12
|
+
* same everywhere instead of a hand-drawn placeholder per page.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import {AppGlyph} from '@conduction/docusaurus-preset/components';
|
|
16
|
+
* <AppGlyph app="opencatalogi" />
|
|
17
|
+
*
|
|
18
|
+
* Unknown slugs render nothing (returns null) so a consumer never
|
|
19
|
+
* breaks over a missing glyph; add the slug to the brand kit and
|
|
20
|
+
* regenerate src/data/app-glyphs.json to fill it in.
|
|
21
|
+
*
|
|
22
|
+
* The glyph inherits color via `fill: currentColor`, so wrap it in a
|
|
23
|
+
* element with the desired `color` (the DetailHero hex, a catalogue
|
|
24
|
+
* tile) to tint it.
|
|
25
|
+
*/
|
|
26
|
+
export const APP_GLYPH_SLUGS = Object.keys(GLYPHS);
|
|
27
|
+
|
|
28
|
+
export function hasAppGlyph(app) {
|
|
29
|
+
return Boolean(app && GLYPHS[app]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function AppGlyph({app, className, title, ...rest}) {
|
|
33
|
+
const glyph = app && GLYPHS[app];
|
|
34
|
+
if (!glyph) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return (
|
|
38
|
+
<svg
|
|
39
|
+
viewBox={glyph.viewBox}
|
|
40
|
+
className={className}
|
|
41
|
+
fill="currentColor"
|
|
42
|
+
role={title ? 'img' : undefined}
|
|
43
|
+
aria-hidden={title ? undefined : 'true'}
|
|
44
|
+
aria-label={title}
|
|
45
|
+
focusable="false"
|
|
46
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
47
|
+
// Inner markup is verbatim brand-kit SVG (paths, circles, rects)
|
|
48
|
+
// — render it raw rather than transcribing each path into JSX.
|
|
49
|
+
dangerouslySetInnerHTML={{__html: glyph.inner}}
|
|
50
|
+
{...rest}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -52,6 +52,7 @@ import Button from '../primitives/Button';
|
|
|
52
52
|
import {deriveStability} from '../../theme/brand.jsx';
|
|
53
53
|
import {downloadsForApp, formatDownloads} from '../../data/app-downloads';
|
|
54
54
|
import {APPS_REGISTRY, applicationCategoryFor} from '../../data/apps-registry';
|
|
55
|
+
import AppGlyph, {hasAppGlyph} from '../AppGlyph/AppGlyph.jsx';
|
|
55
56
|
import styles from './DetailHero.module.css';
|
|
56
57
|
|
|
57
58
|
/**
|
|
@@ -87,6 +88,20 @@ export default function DetailHero({
|
|
|
87
88
|
}) {
|
|
88
89
|
const dlCount = downloads != null ? downloads : (appId ? downloadsForApp(appId) : 0);
|
|
89
90
|
const hasIllustration = Boolean(illustration);
|
|
91
|
+
/* Default the title mark to the canonical app glyph (the same logo
|
|
92
|
+
served on identity.conduction.nl/apps) when the caller doesn't pass
|
|
93
|
+
an explicit `icon`. Keeps every /apps hero on the real brand logo
|
|
94
|
+
instead of a hand-drawn placeholder. */
|
|
95
|
+
const resolvedIcon = icon !== undefined
|
|
96
|
+
? icon
|
|
97
|
+
: (appId && hasAppGlyph(appId) ? <AppGlyph app={appId} /> : null);
|
|
98
|
+
/* Icon-hex colour follows the surface: an orange hex on the cobalt
|
|
99
|
+
hero (so the mark reads against the blue), a cobalt hex on the
|
|
100
|
+
default cream surface. Callers can still pass `iconColor` to
|
|
101
|
+
override. ("Orange hex on a blue background.") */
|
|
102
|
+
const resolvedIconColor = iconColor !== undefined
|
|
103
|
+
? iconColor
|
|
104
|
+
: (background === 'cobalt' ? 'var(--c-orange-knvb)' : 'var(--c-blue-cobalt)');
|
|
90
105
|
/* `background="cobalt"` flips the hero to a full-bleed cobalt panel
|
|
91
106
|
with white type — the product-page identity used on
|
|
92
107
|
{slug}.conduction.nl landings. Default (undefined) keeps the
|
|
@@ -250,13 +265,13 @@ export default function DetailHero({
|
|
|
250
265
|
|
|
251
266
|
{title && (
|
|
252
267
|
<h1 className={styles.title}>
|
|
253
|
-
{
|
|
268
|
+
{resolvedIcon && (
|
|
254
269
|
<span
|
|
255
270
|
className={styles.titleIcon}
|
|
256
|
-
style={
|
|
271
|
+
style={{background: resolvedIconColor}}
|
|
257
272
|
aria-hidden="true"
|
|
258
273
|
>
|
|
259
|
-
{
|
|
274
|
+
{resolvedIcon}
|
|
260
275
|
</span>
|
|
261
276
|
)}
|
|
262
277
|
<span className={styles.titleText}>{title}</span>
|
|
@@ -270,7 +285,11 @@ export default function DetailHero({
|
|
|
270
285
|
{primaryCta && (
|
|
271
286
|
<Button
|
|
272
287
|
variant="primary"
|
|
273
|
-
|
|
288
|
+
/* On the cobalt hero the primary CTA goes orange (KNVB)
|
|
289
|
+
so it pops against the blue, matching the orange icon
|
|
290
|
+
hex. Cream surface keeps the default tone. Callers can
|
|
291
|
+
still pass an explicit `tone` to override. */
|
|
292
|
+
tone={primaryCta.tone ?? (background === 'cobalt' ? 'orange' : undefined)}
|
|
274
293
|
href={primaryCta.href}
|
|
275
294
|
icon={primaryCta.icon}
|
|
276
295
|
>
|
package/src/components/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export {default as CtaBanner} from './CtaBanner/CtaBanner.jsx';
|
|
|
33
33
|
export {default as DownloadPanel} from './DownloadPanel/DownloadPanel.jsx';
|
|
34
34
|
export {default as PlatformOverview} from './PlatformOverview/PlatformOverview.jsx';
|
|
35
35
|
export {default as AppsPreview, AppCard} from './AppsPreview/AppsPreview.jsx';
|
|
36
|
+
export {default as AppGlyph, APP_GLYPH_SLUGS, hasAppGlyph} from './AppGlyph/AppGlyph.jsx';
|
|
36
37
|
|
|
37
38
|
/* Card-family components (Batch 2). Each pairs with a *Grid sibling
|
|
38
39
|
that handles the surrounding layout, so callers can drop a row
|
|
@@ -39,6 +39,8 @@ try {
|
|
|
39
39
|
export default data;
|
|
40
40
|
|
|
41
41
|
export const totalDownloads = data.totals.downloads;
|
|
42
|
+
export const totalDownloadsGithub = data.totals.downloads_github ?? data.totals.downloads;
|
|
43
|
+
export const totalDownloadsCodeberg = data.totals.downloads_codeberg ?? 0;
|
|
42
44
|
export const appsTotal = data.totals.apps_total;
|
|
43
45
|
export const appsInStore = data.totals.apps_in_store;
|
|
44
46
|
export const generatedAt = data.generated_at;
|
|
@@ -51,7 +53,13 @@ export function appStats(appId) {
|
|
|
51
53
|
|
|
52
54
|
export function downloadsForApp(appId) {
|
|
53
55
|
const app = byId.get(appId);
|
|
54
|
-
|
|
56
|
+
if (!app) return 0;
|
|
57
|
+
// New shape: combined GitHub (legacy) + Codeberg (live) total.
|
|
58
|
+
if (typeof app.downloads_total === 'number') return app.downloads_total;
|
|
59
|
+
// Back-compat with the GitHub-only JSON shape.
|
|
60
|
+
const gh = app.github ? app.github.downloads : 0;
|
|
61
|
+
const cb = app.codeberg ? app.codeberg.downloads : 0;
|
|
62
|
+
return gh + cb;
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
export function formatDownloads(n, locale = 'en') {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"app-versions": {
|
|
3
|
+
"inner": "<path fill=\"currentColor\" d=\"M5.5,9A1.5,1.5 0 0,0 7,7.5A1.5,1.5 0 0,0 5.5,6A1.5,1.5 0 0,0 4,7.5A1.5,1.5 0 0,0 5.5,9M17.41,11.58C17.77,11.94 18,12.44 18,13C18,13.55 17.78,14.05 17.41,14.41L12.41,19.41C12.05,19.77 11.55,20 11,20C10.45,20 9.95,19.78 9.58,19.41L2.59,12.42C2.22,12.05 2,11.55 2,11V6C2,4.89 2.89,4 4,4H9C9.55,4 10.05,4.22 10.41,4.58L17.41,11.58M13.54,5.71L14.54,4.71L21.41,11.58C21.78,11.94 22,12.45 22,13C22,13.55 21.78,14.05 21.42,14.41L16.04,19.79L15.04,18.79L20.75,13L13.54,5.71Z\"/>",
|
|
4
|
+
"viewBox": "0 0 24 24"
|
|
5
|
+
},
|
|
6
|
+
"decidesk": {
|
|
7
|
+
"inner": "<path fill=\"currentColor\" d=\"M2.3,20.28L11.9,10.68L10.5,9.26L9.78,9.97C9.39,10.36 8.76,10.36 8.37,9.97L7.66,9.26C7.27,8.87 7.27,8.24 7.66,7.85L13.32,2.19C13.71,1.8 14.34,1.8 14.73,2.19L15.44,2.9C15.83,3.29 15.83,3.92 15.44,4.31L14.73,5L16.15,6.43C16.54,6.04 17.17,6.04 17.56,6.43C17.95,6.82 17.95,7.46 17.56,7.85L18.97,9.26L19.68,8.55C20.07,8.16 20.71,8.16 21.1,8.55L21.8,9.26C22.19,9.65 22.19,10.29 21.8,10.68L16.15,16.33C15.76,16.72 15.12,16.72 14.73,16.33L14.03,15.63C13.63,15.24 13.63,14.6 14.03,14.21L14.73,13.5L13.32,12.09L3.71,21.7C3.32,22.09 2.69,22.09 2.3,21.7C1.91,21.31 1.91,20.67 2.3,20.28M20,19A2,2 0 0,1 22,21V22H12V21A2,2 0 0,1 14,19H20Z\"/>",
|
|
8
|
+
"viewBox": "0 0 24 24"
|
|
9
|
+
},
|
|
10
|
+
"deskdesk": {
|
|
11
|
+
"inner": "<path fill=\"currentColor\" d=\"M3 6H21C21.55 6 22 6.45 22 7C22 7.55 21.55 8 21 8V19H19V17H15V19H13V8H5V19H3V8C2.45 8 2 7.55 2 7C2 6.45 2.45 6 3 6M16 10.5V11H18V10.5C18 10.22 17.78 10 17.5 10H16.5C16.22 10 16 10.22 16 10.5M16 14.5V15H18V14.5C18 14.22 17.78 14 17.5 14H16.5C16.22 14 16 14.22 16 14.5Z\"/>",
|
|
12
|
+
"viewBox": "0 0 24 24"
|
|
13
|
+
},
|
|
14
|
+
"docudesk": {
|
|
15
|
+
"inner": "<path fill=\"currentColor\" d=\"M6,2c-1.1,0-2,.9-2,2v16c0,1.1.9,2,2,2h12c1.1,0,2-.9,2-2v-12l-6-6H6M6,4h7v5h5v11H6V4M8,12v2h8v-2h-8M8,16v2h5v-2h-5Z\"/>",
|
|
16
|
+
"viewBox": "0 0 24 24"
|
|
17
|
+
},
|
|
18
|
+
"doriath": {
|
|
19
|
+
"inner": "<rect x=\"5\" y=\"11\" width=\"14\" height=\"10\" rx=\"2\" fill=\"currentColor\"/><path d=\"M8,11V7a4,4,0,0,1,8,0v4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>",
|
|
20
|
+
"viewBox": "0 0 24 24"
|
|
21
|
+
},
|
|
22
|
+
"financeq": {
|
|
23
|
+
"inner": "<path fill=\"currentColor\" d=\"M6,16.5L3,19.44V11H6M11,14.66L9.43,13.32L8,14.64V7H11M16,13L13,16V3H16M18.81,12.81L17,11H22V16L20.21,14.21L13,21.36L9.53,18.34L5.75,22H3L9.47,15.66L13,18.64\"/>",
|
|
24
|
+
"viewBox": "0 0 24 24"
|
|
25
|
+
},
|
|
26
|
+
"hrmq": {
|
|
27
|
+
"inner": "<path fill=\"currentColor\" d=\"M12 3C14.21 3 16 4.79 16 7S14.21 11 12 11 8 9.21 8 7 9.79 3 12 3M16 13.54C16 14.6 15.72 17.07 13.81 19.83L13 15L13.94 13.12C13.32 13.05 12.67 13 12 13S10.68 13.05 10.06 13.12L11 15L10.19 19.83C8.28 17.07 8 14.6 8 13.54C5.61 14.24 4 15.5 4 17V21H20V17C20 15.5 18.4 14.24 16 13.54Z\"/>",
|
|
28
|
+
"viewBox": "0 0 24 24"
|
|
29
|
+
},
|
|
30
|
+
"larpingapp": {
|
|
31
|
+
"inner": "<path fill=\"currentColor\" d=\"M2,13h2v2h2v-2h2v2h2v-2h2v2h2v-5l3-3V1h2l4,2-4,2v2l3,3v12h-11v-3c0-1.1-.9-2-2-2s-2,.9-2,2v3H2v-9M18,10c-.5,0-1,.5-1,1.2v1.8h2v-1.8c0-.7-.5-1.2-1-1.2Z\"/>",
|
|
32
|
+
"viewBox": "0 0 24 24"
|
|
33
|
+
},
|
|
34
|
+
"launchpad": {
|
|
35
|
+
"inner": "<path fill=\"currentColor\" d=\"M3 3h8v8H3V3zm0 10h8v8H3v-8zm10-10h8v8h-8V3zm0 10h8v8h-8v-8z\"/>",
|
|
36
|
+
"viewBox": "0 0 24 24"
|
|
37
|
+
},
|
|
38
|
+
"nldesign": {
|
|
39
|
+
"inner": "<path fill=\"currentColor\" d=\"M2 2h9v9H2V2zm11 0h9v9h-9V2zM2 13h9v9H2v-9zm13 2a4 4 0 1 1 0 8 4 4 0 0 1 0-8z\"/>",
|
|
40
|
+
"viewBox": "0 0 24 24"
|
|
41
|
+
},
|
|
42
|
+
"openanonymiser": {
|
|
43
|
+
"inner": "<path fill=\"currentColor\" d=\"M17.06 13C15.2 13 13.64 14.33 13.24 16.1C12.29 15.69 11.42 15.8 10.76 16.09C10.35 14.31 8.79 13 6.94 13C4.77 13 3 14.79 3 17C3 19.21 4.77 21 6.94 21C9 21 10.68 19.38 10.84 17.32C11.18 17.08 12.07 16.63 13.16 17.34C13.34 19.39 15 21 17.06 21C19.23 21 21 19.21 21 17C21 14.79 19.23 13 17.06 13M6.94 19.86C5.38 19.86 4.13 18.58 4.13 17S5.39 14.14 6.94 14.14C8.5 14.14 9.75 15.42 9.75 17S8.5 19.86 6.94 19.86M17.06 19.86C15.5 19.86 14.25 18.58 14.25 17S15.5 14.14 17.06 14.14C18.62 14.14 19.88 15.42 19.88 17S18.61 19.86 17.06 19.86M22 10.5H2V12H22V10.5M15.53 2.63C15.31 2.14 14.75 1.88 14.22 2.05L12 2.79L9.77 2.05L9.72 2.04C9.19 1.89 8.63 2.17 8.43 2.68L6 9H18L15.56 2.68L15.53 2.63Z\"/>",
|
|
44
|
+
"viewBox": "0 0 24 24"
|
|
45
|
+
},
|
|
46
|
+
"openbuild": {
|
|
47
|
+
"inner": "<path fill=\"currentColor\" d=\"M21 16.5c0 .38-.21.71-.53.88l-7.9 4.44c-.16.12-.36.18-.57.18-.21 0-.41-.06-.57-.18l-7.9-4.44A.991.991 0 0 1 3 16.5v-9c0-.38.21-.71.53-.88l7.9-4.44c.16-.12.36-.18.57-.18.21 0 .41.06.57.18l7.9 4.44c.32.17.53.5.53.88v9M12 4.15 6.04 7.5 12 10.85 17.96 7.5 12 4.15M5 15.91l6 3.38v-6.71L5 9.21v6.7m14 0v-6.7l-6 3.37v6.71l6-3.38Z\"/>",
|
|
48
|
+
"viewBox": "0 0 24 24"
|
|
49
|
+
},
|
|
50
|
+
"opencatalogi": {
|
|
51
|
+
"inner": "<path fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M 287 28.117 C 302.675 43.536, 316.625 56.612, 318 57.176 C 322.480 59.011, 334.602 60.734, 340.618 60.389 C 343.853 60.204, 349.783 59.330, 353.795 58.448 L 361.091 56.843 388.458 29.172 C 403.510 13.952, 416.165 1.163, 416.580 0.750 C 416.994 0.338, 381.596 0.019, 337.917 0.041 L 258.500 0.083 287 28.117 M 438.433 100.434 L 439.165 200.868 434.832 204.033 C 429.209 208.143, 426.500 209.159, 420.935 209.250 C 411.567 209.404, 406.552 205.245, 358.038 157.100 C 349.493 148.620, 337.947 139.513, 331.773 136.385 C 322.369 131.619, 306.018 130.184, 293.644 133.038 C 274.943 137.351, 266.469 153.625, 269.994 178.458 C 271.713 190.570, 275.840 198.421, 286.946 210.710 C 291.813 216.094, 298.428 222.942, 301.647 225.926 C 304.866 228.910, 342.229 265.479, 384.676 307.189 L 461.851 383.026 469.676 384.986 C 479.203 387.373, 492.466 387.159, 501.778 384.470 C 508.012 382.669, 508.221 382.487, 531.278 358.712 L 554.500 334.768 555.056 426.134 L 555.613 517.500 552.229 520.673 C 544.095 528.300, 534.423 529.118, 525.302 522.951 C 522.495 521.053, 507.776 507.399, 492.592 492.609 C 462.435 463.233, 456.507 458.142, 447.500 453.884 C 440.342 450.500, 428.148 448.571, 419 449.375 C 394.520 451.527, 384.798 464.066, 386.236 491.633 C 387.207 510.252, 391.969 517.708, 421.241 546.440 C 431.833 556.838, 463.900 588.314, 492.500 616.387 C 521.100 644.460, 552.150 674.964, 561.500 684.174 C 578.220 700.643, 578.607 700.946, 585 702.618 C 593.440 704.826, 606.541 704.845, 616.104 702.663 L 623.708 700.929 643.106 681.214 C 738.670 584.094, 793.637 527.886, 798.335 522.481 C 803.287 516.784, 808.927 507.027, 810.752 501 C 813.039 493.450, 812.990 475.469, 810.663 468 C 806.894 455.900, 798.518 449.036, 784.934 446.914 C 769.405 444.489, 753.788 447.680, 742.855 455.512 C 739.910 457.621, 723.325 473.393, 706 490.559 C 688.675 507.726, 673.085 522.485, 671.355 523.357 C 663.393 527.372, 653.707 525.947, 646.564 519.709 L 642.889 516.500 642.282 381.267 L 641.675 246.033 662.105 225.267 C 692.137 194.740, 695.500 188.923, 695.500 167.500 C 695.500 152.292, 694.323 147.285, 689.134 140.419 C 682.049 131.045, 667.187 126.581, 650.490 128.812 C 646.118 129.397, 642.413 129.747, 642.257 129.590 C 642.101 129.434, 642.091 114.441, 642.236 96.272 L 642.500 63.237 661.500 81.754 C 671.950 91.938, 688.391 108.075, 698.036 117.613 L 715.572 134.956 723.536 136.957 C 733.425 139.441, 747.617 139.209, 756.383 136.422 C 762.068 134.614, 763.265 133.528, 791.983 104.135 L 821.701 73.720 822.250 186.610 L 822.798 299.500 820.704 301.772 C 819.553 303.021, 816.207 305.219, 813.269 306.657 C 806.678 309.881, 800.188 309.464, 793.148 305.363 C 790.591 303.874, 776.125 290.465, 761 275.566 C 724.888 239.992, 717.016 234.150, 702.326 232.027 C 682.232 229.121, 667.594 233.104, 659.524 243.672 C 654.567 250.165, 653.552 254.562, 653.562 269.500 C 653.569 280.549, 653.901 283.330, 655.775 288.029 C 661.070 301.314, 662.819 303.212, 734.541 373.500 C 821.062 458.290, 834.508 471.505, 840 477.138 C 844.895 482.159, 846.262 482.967, 852.432 484.482 C 860.986 486.583, 873.633 486.550, 883.294 484.401 L 890.500 482.799 907.552 465.649 C 941.383 431.626, 1056.673 313.941, 1063.951 306 C 1076.066 292.783, 1080.844 279.456, 1079.715 262.029 C 1078.526 243.680, 1070.695 232.783, 1056.254 229.385 C 1048.364 227.529, 1033.931 227.742, 1025.919 229.833 C 1013.866 232.980, 1009.702 236.323, 974.500 271.122 C 956.350 289.064, 939.925 304.560, 938 305.558 C 932.505 308.408, 924.599 307.997, 918.233 304.531 C 915.336 302.954, 912.411 300.995, 911.733 300.179 C 910.437 298.619, 909.370 202.325, 909.117 64.250 L 909 0 731.491 0 L 553.981 0 554.241 103.022 L 554.500 206.044 551 207.017 C 545.287 208.604, 540.700 208.153, 535.331 205.478 C 525.680 200.668, 526.672 212.153, 526.178 99.458 L 525.742 0 481.721 0 L 437.700 0 438.433 100.434 M 330 522.072 C 318.329 524.116, 310.453 530.116, 307.244 539.408 C 303.959 548.921, 304.077 565.694, 307.502 576.006 C 310.352 584.587, 318.251 594.082, 347.912 624.580 C 369.891 647.179, 377.673 655.833, 379.720 659.948 C 384.405 669.371, 383.107 677.437, 375.247 687.742 L 372.613 691.196 186.306 690.455 L 0 689.714 0 730.857 L 0 772 11.250 772.101 C 17.438 772.156, 100.955 772.494, 196.845 772.851 L 371.190 773.500 373.682 776.500 C 377.988 781.683, 380.980 789.073, 380.990 794.554 C 381.009 804.262, 378.535 807.443, 344.955 840.896 C 312.855 872.874, 307.209 879.655, 303.703 890.434 C 301.050 898.588, 300.864 915.113, 303.332 923.291 C 307.591 937.404, 320.343 944.019, 340.784 942.719 C 355.106 941.808, 361.461 939.082, 374 928.469 C 377.025 925.908, 409.425 894.467, 446 858.599 C 482.575 822.731, 521.139 784.939, 531.699 774.617 L 550.897 755.849 552.573 748.318 C 554.513 739.594, 554.127 721.905, 551.864 715.851 C 550.886 713.235, 529.720 691.021, 477.068 637.351 C 384.551 543.047, 375.864 534.444, 367.957 529.299 C 357.877 522.742, 342.687 519.849, 330 522.072 M 858.043 522.072 C 851.131 523.302, 844.015 526.587, 837 531.787 C 833.975 534.030, 792.047 574.482, 743.827 621.682 C 644.533 718.875, 653.161 708.849, 652.334 728 C 651.984 736.111, 652.293 740.019, 653.690 745.174 L 655.500 751.848 741.377 839.324 C 821.205 920.639, 835.015 934, 843.836 938.456 C 854.881 944.034, 876.652 944.279, 887.111 938.943 C 892.406 936.242, 897.806 929.439, 899.745 923.027 C 901.997 915.577, 901.959 898.520, 899.673 890.824 C 896.626 880.564, 889.429 871.658, 858.455 839.815 C 837.870 818.652, 828.218 808.007, 826.484 804.554 C 821.970 795.565, 823.615 785.064, 830.822 776.855 L 833.907 773.341 1018.954 773.807 L 1204 774.273 1204 733.300 L 1204 692.327 1019.875 691.706 C 859.690 691.165, 835.539 690.893, 834.125 689.611 C 833.231 688.801, 830.926 685.538, 829.002 682.361 C 825.951 677.323, 825.504 675.749, 825.502 670.042 C 825.501 665.706, 826.136 662.151, 827.387 659.500 C 828.604 656.920, 839.809 645.025, 858.942 626 C 889.655 595.461, 895.427 589.038, 899.657 580.690 C 906.028 568.118, 906.633 547.042, 900.943 535.889 C 898.321 530.750, 891.506 525.233, 885.544 523.426 C 879.173 521.494, 865.168 520.805, 858.043 522.072 M 592.500 771.649 C 581.852 773.866, 586.843 769.360, 494.150 860.441 C 445.942 907.810, 404.132 949.702, 401.238 953.533 C 392.867 964.616, 391.565 968.902, 391.530 985.500 C 391.496 1001.300, 392.713 1005.559, 399.077 1011.923 C 409.149 1021.995, 432.938 1023.707, 449.539 1015.556 C 458.681 1011.067, 465.999 1004.701, 497.768 973.604 C 526.558 945.423, 529.846 943.028, 539.783 943.010 C 545.354 942.999, 551.767 945.642, 557.402 950.271 L 560.304 952.654 560.081 1078.327 L 559.859 1204 600.816 1204 L 641.773 1204 642.146 1079.556 L 642.519 955.111 645.293 952.216 C 653.118 944.049, 666.191 941.982, 675.500 947.441 C 678.581 949.248, 690.801 960.841, 707.337 977.644 C 743.768 1014.664, 750.331 1019.820, 764.469 1022.525 C 774.209 1024.389, 781.784 1024.375, 789.830 1022.478 C 805.283 1018.835, 811.961 1008.685, 811.988 988.799 C 812.009 973.140, 808.758 964.138, 798.879 952.500 C 788.991 940.852, 625.758 775.143, 623.147 774.103 C 616.414 771.420, 599.936 770.100, 592.500 771.649\"/>",
|
|
52
|
+
"viewBox": "0 0 1204 1204"
|
|
53
|
+
},
|
|
54
|
+
"openconnector": {
|
|
55
|
+
"inner": "<path fill=\"currentColor\" d=\"M8.4,18.2c.4.5.6,1.1.6,1.8,0,1.7-1.3,3-3,3s-3-1.3-3-3,1.3-3,3-3,.8.1,1.2.3l1.4-1.8c-.9-1-1.3-2.4-1.1-3.7l-2-.7c-.5.8-1.4,1.4-2.5,1.4-1.7,0-3-1.3-3-3s1.3-3,3-3,3,1.3,3,3v.2l2,.7c.6-1.2,1.8-2.1,3.2-2.3v-2.2c-1.2-.3-2.2-1.5-2.2-2.9s1.3-3,3-3,3,1.3,3,3-1,2.6-2.2,2.9v2.2c1.4.2,2.6,1.1,3.2,2.3l2-.7v-.2c0-1.7,1.3-3,3-3s3,1.3,3,3-1.3,3-3,3-2-.6-2.5-1.4l-2,.7c.2,1.3-.2,2.7-1.1,3.7l1.4,1.8c.4-.2.8-.3,1.2-.3,1.7,0,3,1.3,3,3s-1.3,3-3,3-3-1.3-3-3,.2-1.3.6-1.8l-1.4-1.8c-1.4.8-3,.8-4.4,0l-1.4,1.8Z\"/>",
|
|
56
|
+
"viewBox": "0 0 24 24"
|
|
57
|
+
},
|
|
58
|
+
"openregister": {
|
|
59
|
+
"inner": "<path fill=\"currentColor\" d=\"M19,12v1.5c2.2,0,4,1.8,4,4s-.2,1.6-.7,2.2l-1.1-1.1c.2-.3.3-.7.3-1.1,0-1.4-1.1-2.5-2.5-2.5v1.5l-2.2-2.2,2.2-2.2M19,23v-1.5c-2.2,0-4-1.8-4-4s.2-1.6.7-2.2l1.1,1.1c-.2.3-.3.7-.3,1.1,0,1.4,1.1,2.5,2.5,2.5v-1.5l2.2,2.2-2.2,2.2M12,3c4.4,0,8,1.8,8,4s-3.6,4-8,4-8-1.8-8-4S7.6,3,12,3M4,9c0,2.2,3.6,4,8,4s2.2-.1,3.1-.3c-1,.9-1.6,2-2,3.3h-1.2c-4.4,0-8-1.8-8-4v-3M20,9v2h-1.1c.7-.6,1.1-1.3,1.1-2M4,14c0,2.2,3.6,4,8,4h1c0,1,.4,2,.9,2.9h-1.9c-4.4.1-8-1.7-8-3.9v-3Z\"/>",
|
|
60
|
+
"viewBox": "0 0 24 24"
|
|
61
|
+
},
|
|
62
|
+
"pipelinq": {
|
|
63
|
+
"inner": "<circle cx=\"9\" cy=\"7.5\" r=\"3.5\"/><path d=\"M2,20V17.5C2,15.01,6.03,13,9,13S16,15.01,16,17.5V20Z\"/><rect x=\"18\" y=\"5\" width=\"4\" height=\"2\" rx=\"0.5\"/><rect x=\"18\" y=\"10\" width=\"4\" height=\"2\" rx=\"0.5\"/><rect x=\"18\" y=\"15\" width=\"3\" height=\"2\" rx=\"0.5\"/>",
|
|
64
|
+
"viewBox": "0 0 24 24"
|
|
65
|
+
},
|
|
66
|
+
"planix": {
|
|
67
|
+
"inner": "<path fill=\"currentColor\" d=\"M16,5V18H21V5M4,18H9V5H4M10,18H15V5H10V18Z\"/>",
|
|
68
|
+
"viewBox": "0 0 24 24"
|
|
69
|
+
},
|
|
70
|
+
"procest": {
|
|
71
|
+
"inner": "<path d=\"M5,3H19A2,2,0,0,1,21,5V19A2,2,0,0,1,19,21H5A2,2,0,0,1,3,19V5A2,2,0,0,1,5,3M5,5V9H19V5H5M5,11V19H9V11H5M11,11V19H19V11H11\"/><circle cx=\"7\" cy=\"14\" r=\"1.5\"/><circle cx=\"7\" cy=\"18\" r=\"1.5\"/><rect x=\"13\" y=\"13\" width=\"4.5\" height=\"1.5\" rx=\"0.5\"/><rect x=\"13\" y=\"17\" width=\"3\" height=\"1.5\" rx=\"0.5\"/>",
|
|
72
|
+
"viewBox": "0 0 24 24"
|
|
73
|
+
},
|
|
74
|
+
"purchaseq": {
|
|
75
|
+
"inner": "<path fill=\"currentColor\" d=\"M17,18C15.89,18 15,18.89 15,20A2,2 0 0,0 17,22A2,2 0 0,0 19,20C19,18.89 18.1,18 17,18M1,2V4H3L6.6,11.59L5.24,14.04C5.09,14.32 5,14.65 5,15A2,2 0 0,0 7,17H19V15H7.42A0.25,0.25 0 0,1 7.17,14.75C7.17,14.7 7.18,14.66 7.2,14.63L8.1,13H15.55C16.3,13 16.96,12.58 17.3,11.97L20.88,5.5C20.95,5.34 21,5.17 21,5A1,1 0 0,0 20,4H5.21L4.27,2M7,18C5.89,18 5,18.89 5,20A2,2 0 0,0 7,22A2,2 0 0,0 9,20C9,18.89 8.1,18 7,18Z\"/>",
|
|
76
|
+
"viewBox": "0 0 24 24"
|
|
77
|
+
},
|
|
78
|
+
"scholiq": {
|
|
79
|
+
"inner": "<path fill=\"currentColor\" d=\"M12 3L1 9l11 6 9-4.91V17h2V9L12 3zM5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82z\"/>",
|
|
80
|
+
"viewBox": "0 0 24 24"
|
|
81
|
+
},
|
|
82
|
+
"shillinq": {
|
|
83
|
+
"inner": "<path fill=\"currentColor\" d=\"M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM64 80c0-8.8 7.2-16 16-16h64c8.8 0 16 7.2 16 16s-7.2 16-16 16H80c-8.8 0-16-7.2-16-16zm0 64c0-8.8 7.2-16 16-16h64c8.8 0 16 7.2 16 16s-7.2 16-16 16H80c-8.8 0-16-7.2-16-16zm128 72c8.8 0 16 7.2 16 16v17.3c8.5 1.2 16.7 3.1 24.1 5.1c8.5 2.3 13.6 11 11.3 19.6s-11 13.6-19.6 11.3c-11.1-3-22-5.2-32.1-5.3c-8.4-.1-17.4 1.8-23.6 5.5c-5.7 3.4-8.1 7.3-8.1 12.8c0 3.7 1.3 6.5 7.3 10.1c6.9 4.1 16.6 7.1 29.2 10.9l.5 .1 0 0 0 0c11.3 3.4 25.3 7.6 36.3 14.6c12.1 7.6 22.4 19.7 22.7 38.2c.3 19.3-9.6 33.3-22.9 41.6c-7.7 4.8-16.4 7.6-25.1 9.1V440c0 8.8-7.2 16-16 16s-16-7.2-16-16V422.2c-11.2-2.1-21.7-5.7-30.9-8.9l0 0 0 0c-2.1-.7-4.2-1.4-6.2-2.1c-8.4-2.8-12.9-11.9-10.1-20.2s11.9-12.9 20.2-10.1c2.5 .8 4.8 1.6 7.1 2.4l0 0 0 0 0 0c13.6 4.6 24.6 8.4 36.3 8.7c9.1 .3 17.9-1.7 23.7-5.3c5.1-3.2 7.9-7.3 7.8-14c-.1-4.6-1.8-7.8-7.7-11.6c-6.8-4.3-16.5-7.4-29-11.2l-1.6-.5 0 0c-11-3.3-24.3-7.3-34.8-13.7c-12-7.2-22.6-18.9-22.7-37.3c-.1-19.4 10.8-32.8 23.8-40.5c7.5-4.4 15.8-7.2 24.1-8.7V232c0-8.8 7.2-16 16-16z\"/>",
|
|
84
|
+
"viewBox": "0 0 384 512"
|
|
85
|
+
},
|
|
86
|
+
"softwarecatalog": {
|
|
87
|
+
"inner": "<path fill=\"currentColor\" d=\"M5.6,3.4l1.4,1.4-3.2,3.2,3.2,3.2-1.4,1.4L1,8,5.6,3.4M11.4,3.4l4.6,4.6-4.6,4.6-1.4-1.4,3.2-3.2-3.2-3.2,1.4-1.4M22,6v12c0,1.1-.9,2-2,2H4c-1.1,0-2-.9-2-2v-4h2v4h16V6h-3v-2h3c1.1,0,2,.9,2,2Z\"/>",
|
|
88
|
+
"viewBox": "0 0 24 24"
|
|
89
|
+
},
|
|
90
|
+
"zaakafhandelapp": {
|
|
91
|
+
"inner": "<path fill=\"currentColor\" d=\"M9.5 18V9H8V18M12.75 18V9H11.25V18M16 18V9H14.5V18M17.03 6C18.11 6 19 6.88 19 8V19C19 20.13 18.11 21 17.03 21C17.03 21.58 16.56 22 16 22C15.5 22 15 21.58 15 21H9C9 21.58 8.5 22 8 22C7.44 22 6.97 21.58 6.97 21C5.89 21 5 20.13 5 19V8C5 6.88 5.89 6 6.97 6H9V3C9 2.42 9.46 2 10 2H14C14.54 2 15 2.42 15 3V6M10.5 3.5V6H13.5V3.5M17.03 19V8H6.97V19\"/>",
|
|
92
|
+
"viewBox": "0 0 24 24"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* on while learning about an app:
|
|
7
7
|
*
|
|
8
8
|
* 1. /apps/<slug> product detail page
|
|
9
|
-
* 2. https
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* 2. https://<slug>.conduction.nl documentation site (each app's
|
|
10
|
+
* docs are served from its own per-app subdomain, built from the
|
|
11
|
+
* app's own repo)
|
|
12
12
|
* 3. /academy?app=<slug> academy posts filtered by app
|
|
13
13
|
*
|
|
14
14
|
* The registry is consumed by:
|
|
@@ -20,29 +20,29 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Adding a new app: add an entry here. The url shape is conventional:
|
|
22
22
|
* - productHref: /apps/<slug>
|
|
23
|
-
* - docsHref: https
|
|
23
|
+
* - docsHref: https://<slug>.conduction.nl
|
|
24
24
|
* - academyHref: /academy?app=<slug>
|
|
25
25
|
* Override any of the three when an app deviates from the convention
|
|
26
26
|
* (none today; the convention holds).
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
export const APPS_REGISTRY = {
|
|
30
|
-
opencatalogi: {slug: 'opencatalogi', name: 'OpenCatalogi', category: 'Data', productHref: '/apps/opencatalogi', docsHref: 'https://
|
|
31
|
-
openregister: {slug: 'openregister', name: 'OpenRegister', category: 'Data', productHref: '/apps/openregister', docsHref: 'https://
|
|
32
|
-
openconnector: {slug: 'openconnector', name: 'OpenConnector', category: 'Connectors', productHref: '/apps/openconnector', docsHref: 'https://
|
|
33
|
-
docudesk: {slug: 'docudesk', name: 'DocuDesk', category: 'Documents', productHref: '/apps/docudesk', docsHref: 'https://
|
|
34
|
-
launchpad: {slug: 'launchpad', name: 'LaunchPad', category: 'Dashboards', productHref: '/apps/launchpad', docsHref: 'https://launchpad.conduction.nl',
|
|
35
|
-
zaakafhandelapp: {slug: 'zaakafhandelapp', name: 'ZaakAfhandelApp', category: 'Processes', productHref: '/apps/zaakafhandelapp', docsHref: 'https://
|
|
36
|
-
pipelinq: {slug: 'pipelinq', name: 'PipelinQ', category: 'Processes', productHref: '/apps/pipelinq', docsHref: 'https://
|
|
37
|
-
procest: {slug: 'procest', name: 'Procest', category: 'Processes', productHref: '/apps/procest', docsHref: 'https://
|
|
38
|
-
decidesk: {slug: 'decidesk', name: 'DeciDesk', category: 'Processes', productHref: '/apps/decidesk', docsHref: 'https://
|
|
39
|
-
softwarecatalog: {slug: 'softwarecatalog', name: 'SoftwareCatalog', category: 'Data', productHref: '/apps/softwarecatalog', docsHref: 'https://
|
|
40
|
-
larpingapp: {slug: 'larpingapp', name: 'LarpingApp', category: 'Processes', productHref: '/apps/larpingapp', docsHref: 'https://
|
|
41
|
-
nldesign: {slug: 'nldesign', name: 'NLDesign', category: 'Documents', productHref: '/apps/nldesign', docsHref: 'https://
|
|
42
|
-
shillinq: {slug: 'shillinq', name: 'Shillinq', category: 'Processes', productHref: '/apps/shillinq', docsHref: 'https://
|
|
43
|
-
openbuild: {slug: 'openbuild', name: 'OpenBuild', category: 'Processes', productHref: '/apps/openbuild', docsHref: 'https://
|
|
44
|
-
doriath: {slug: 'doriath', name: 'Doriath', category: 'Connectors', productHref: '/apps/doriath', docsHref: 'https://
|
|
45
|
-
'app-versions': {slug: 'app-versions', name: 'App Versions', category: 'Data', productHref: '/apps/app-versions', docsHref: 'https://
|
|
30
|
+
opencatalogi: {slug: 'opencatalogi', name: 'OpenCatalogi', category: 'Data', productHref: '/apps/opencatalogi', docsHref: 'https://opencatalogi.conduction.nl', academyHref: '/academy?app=opencatalogi'},
|
|
31
|
+
openregister: {slug: 'openregister', name: 'OpenRegister', category: 'Data', productHref: '/apps/openregister', docsHref: 'https://openregister.conduction.nl', academyHref: '/academy?app=openregister'},
|
|
32
|
+
openconnector: {slug: 'openconnector', name: 'OpenConnector', category: 'Connectors', productHref: '/apps/openconnector', docsHref: 'https://openconnector.conduction.nl', academyHref: '/academy?app=openconnector'},
|
|
33
|
+
docudesk: {slug: 'docudesk', name: 'DocuDesk', category: 'Documents', productHref: '/apps/docudesk', docsHref: 'https://docudesk.conduction.nl', academyHref: '/academy?app=docudesk'},
|
|
34
|
+
launchpad: {slug: 'launchpad', name: 'LaunchPad', category: 'Dashboards', productHref: '/apps/launchpad', docsHref: 'https://launchpad.conduction.nl', academyHref: '/academy?app=launchpad'},
|
|
35
|
+
zaakafhandelapp: {slug: 'zaakafhandelapp', name: 'ZaakAfhandelApp', category: 'Processes', productHref: '/apps/zaakafhandelapp', docsHref: 'https://zaakafhandelapp.conduction.nl', academyHref: '/academy?app=zaakafhandelapp'},
|
|
36
|
+
pipelinq: {slug: 'pipelinq', name: 'PipelinQ', category: 'Processes', productHref: '/apps/pipelinq', docsHref: 'https://pipelinq.conduction.nl', academyHref: '/academy?app=pipelinq'},
|
|
37
|
+
procest: {slug: 'procest', name: 'Procest', category: 'Processes', productHref: '/apps/procest', docsHref: 'https://procest.conduction.nl', academyHref: '/academy?app=procest'},
|
|
38
|
+
decidesk: {slug: 'decidesk', name: 'DeciDesk', category: 'Processes', productHref: '/apps/decidesk', docsHref: 'https://decidesk.conduction.nl', academyHref: '/academy?app=decidesk'},
|
|
39
|
+
softwarecatalog: {slug: 'softwarecatalog', name: 'SoftwareCatalog', category: 'Data', productHref: '/apps/softwarecatalog', docsHref: 'https://softwarecatalog.conduction.nl', academyHref: '/academy?app=softwarecatalog'},
|
|
40
|
+
larpingapp: {slug: 'larpingapp', name: 'LarpingApp', category: 'Processes', productHref: '/apps/larpingapp', docsHref: 'https://larpingapp.conduction.nl', academyHref: '/academy?app=larpingapp'},
|
|
41
|
+
nldesign: {slug: 'nldesign', name: 'NLDesign', category: 'Documents', productHref: '/apps/nldesign', docsHref: 'https://nldesign.conduction.nl', academyHref: '/academy?app=nldesign'},
|
|
42
|
+
shillinq: {slug: 'shillinq', name: 'Shillinq', category: 'Processes', productHref: '/apps/shillinq', docsHref: 'https://shillinq.conduction.nl', academyHref: '/academy?app=shillinq'},
|
|
43
|
+
openbuild: {slug: 'openbuild', name: 'OpenBuild', category: 'Processes', productHref: '/apps/openbuild', docsHref: 'https://openbuild.conduction.nl', academyHref: '/academy?app=openbuild'},
|
|
44
|
+
doriath: {slug: 'doriath', name: 'Doriath', category: 'Connectors', productHref: '/apps/doriath', docsHref: 'https://doriath.conduction.nl', academyHref: '/academy?app=doriath'},
|
|
45
|
+
'app-versions': {slug: 'app-versions', name: 'App Versions', category: 'Data', productHref: '/apps/app-versions', docsHref: 'https://app-versions.conduction.nl', academyHref: '/academy?app=app-versions'},
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @conduction/docusaurus-preset/plugins/extractFeatures
|
|
3
|
+
*
|
|
4
|
+
* Build-time port of the org-wide `scripts/extract-features.py`
|
|
5
|
+
* (https://codeberg.org/Conduction/.github). Derives the published feature
|
|
6
|
+
* list directly from an app's `openspec/specs/<slug>/spec.md` files so the
|
|
7
|
+
* docs `/features` page is ALWAYS generated from the current specs — the
|
|
8
|
+
* committed `docs/features.json` is no longer a hand-maintained source of
|
|
9
|
+
* truth that can drift.
|
|
10
|
+
*
|
|
11
|
+
* Only specs whose YAML frontmatter declares `status: done` are emitted
|
|
12
|
+
* (the canonical "shipped" status). Each entry: { slug, title, summary,
|
|
13
|
+
* docsUrl } — identical shape and ordering (sorted by slug) to the Python
|
|
14
|
+
* extractor, so docs output matches CI output byte-for-byte.
|
|
15
|
+
*
|
|
16
|
+
* Returns null when the directory is absent or holds no done specs, so the
|
|
17
|
+
* caller can fall back to a committed file on deploys that ship without
|
|
18
|
+
* `openspec/`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const FRONTMATTER_RE = /^---\s*\n([\s\S]*?\n)---\s*\n([\s\S]*)$/;
|
|
25
|
+
|
|
26
|
+
function parseSpec(specPath, slug) {
|
|
27
|
+
let text;
|
|
28
|
+
try {
|
|
29
|
+
text = fs.readFileSync(specPath, 'utf8');
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fm = FRONTMATTER_RE.exec(text);
|
|
35
|
+
if (fm === null) return null;
|
|
36
|
+
const front = fm[1];
|
|
37
|
+
const body = fm[2];
|
|
38
|
+
|
|
39
|
+
const statusMatch = front.match(/^status:\s*(.+?)\s*$/m);
|
|
40
|
+
const status = statusMatch ? statusMatch[1].trim().replace(/^["']|["']$/g, '').toLowerCase() : '';
|
|
41
|
+
if (status !== 'done') return null;
|
|
42
|
+
|
|
43
|
+
const titleMatch = body.match(/^#\s+(.+?)\s*$/m);
|
|
44
|
+
const rawTitle = titleMatch ? titleMatch[1].trim() : slug;
|
|
45
|
+
const title = rawTitle.replace(/\s+specification\s*$/i, '').trim();
|
|
46
|
+
|
|
47
|
+
let summary = '';
|
|
48
|
+
const purposeHeading = body.match(/^##\s+Purpose\s*$/m);
|
|
49
|
+
if (purposeHeading) {
|
|
50
|
+
const rest = body.slice(purposeHeading.index + purposeHeading[0].length);
|
|
51
|
+
const nextHeadingIdx = rest.search(/\n##\s/);
|
|
52
|
+
const section = (nextHeadingIdx === -1 ? rest : rest.slice(0, nextHeadingIdx)).trim();
|
|
53
|
+
const firstPara = section.split(/\n\s*\n/)[0] || '';
|
|
54
|
+
summary = firstPara.trim().split(/\s+/).join(' ');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
slug,
|
|
59
|
+
title,
|
|
60
|
+
summary,
|
|
61
|
+
docsUrl: `openspec/specs/${slug}/spec.md`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} specsDir Absolute path to `<app>/openspec/specs`.
|
|
67
|
+
* @returns {Array<{slug,title,summary,docsUrl}>|null}
|
|
68
|
+
*/
|
|
69
|
+
function extractFeatures(specsDir) {
|
|
70
|
+
let slugs;
|
|
71
|
+
try {
|
|
72
|
+
if (!fs.statSync(specsDir).isDirectory()) return null;
|
|
73
|
+
slugs = fs.readdirSync(specsDir).sort();
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const entries = [];
|
|
79
|
+
for (const slug of slugs) {
|
|
80
|
+
const specPath = path.join(specsDir, slug, 'spec.md');
|
|
81
|
+
let entry = null;
|
|
82
|
+
try {
|
|
83
|
+
if (fs.statSync(specPath).isFile()) {
|
|
84
|
+
entry = parseSpec(specPath, slug);
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {
|
|
87
|
+
entry = null;
|
|
88
|
+
}
|
|
89
|
+
if (entry !== null) entries.push(entry);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return entries.length > 0 ? entries : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { extractFeatures };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @conduction/docusaurus-preset/plugins/features-page
|
|
3
3
|
*
|
|
4
|
-
* Adds a `/features` route to every Conduction docs site
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Adds a `/features` route to every Conduction docs site. The feature list
|
|
5
|
+
* is regenerated FROM `openspec/specs/` at docs-build time (so the page is
|
|
6
|
+
* always current with the specs and never depends on a hand-maintained
|
|
7
|
+
* artefact); it falls back to a committed `docs/features.json` only on
|
|
8
|
+
* deploys that ship without `openspec/`.
|
|
8
9
|
*
|
|
9
10
|
* The route renders the brand `<FeaturesPage />` component, which in turn
|
|
10
11
|
* maps each entry to a `<FeatureItem>` inside `<FeatureGrid>`. Every entry
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
|
|
31
32
|
const fs = require('fs');
|
|
32
33
|
const path = require('path');
|
|
34
|
+
const {extractFeatures} = require('./extractFeatures');
|
|
33
35
|
|
|
34
36
|
function loadFeatures(absPath) {
|
|
35
37
|
try {
|
|
@@ -51,12 +53,20 @@ function featuresPagePlugin(context, options = {}) {
|
|
|
51
53
|
? featuresFile
|
|
52
54
|
: path.resolve(context.siteDir, featuresFile);
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
/* Generate-at-consume: derive the feature list from the app's
|
|
57
|
+
openspec/specs at build time so the page is always current. siteDir is
|
|
58
|
+
the docs/ dir holding docusaurus.config.js, so the specs live one level
|
|
59
|
+
up at ../openspec/specs. The committed featuresFile is a fallback for
|
|
60
|
+
deploys that ship without openspec/. */
|
|
61
|
+
const specsDir = options.specsDir
|
|
62
|
+
? path.resolve(context.siteDir, options.specsDir)
|
|
63
|
+
: path.resolve(context.siteDir, '..', 'openspec', 'specs');
|
|
64
|
+
|
|
65
|
+
const features = extractFeatures(specsDir) || loadFeatures(absPath);
|
|
55
66
|
if (features === null) {
|
|
56
|
-
/*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
a publishable status. Stay silent rather than build-fail. */
|
|
67
|
+
/* Neither openspec/specs nor a committed features.json is available
|
|
68
|
+
(e.g. the app has no done specs yet). Stay silent rather than
|
|
69
|
+
build-fail. */
|
|
60
70
|
return {name: 'conduction-features-page'};
|
|
61
71
|
}
|
|
62
72
|
|