@eventcatalog/core 3.30.0 → 3.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-6UG4JMUV.js → chunk-7IGMIOQF.js} +1 -1
- package/dist/{chunk-Z26P4PCB.js → chunk-HVOLSUC2.js} +1 -1
- package/dist/{chunk-RRBDF4MM.js → chunk-LWVHWR77.js} +1 -1
- package/dist/{chunk-MVZKHUX2.js → chunk-QIJOBQZ7.js} +1 -1
- package/dist/{chunk-ATRBVTJ6.js → chunk-UY5QDWK7.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +10 -6
- package/eventcatalog/public/logo.png +0 -0
- package/eventcatalog/src/components/CopyAsMarkdown.tsx +29 -24
- package/eventcatalog/src/components/MDX/Design/Design.astro +1 -1
- package/eventcatalog/src/components/MDX/Tiles/Tile.astro +11 -8
- package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +218 -0
- package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +265 -0
- package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +371 -0
- package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +183 -0
- package/eventcatalog/src/components/Settings/LogoUpload.tsx +137 -0
- package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +91 -0
- package/eventcatalog/src/components/Settings/ReadOnlyBanner.tsx +18 -0
- package/eventcatalog/src/components/Settings/Row.tsx +59 -0
- package/eventcatalog/src/components/Settings/SettingsShared.tsx +176 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +17 -18
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +45 -16
- package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +2 -2
- package/eventcatalog/src/content.config.ts +1 -1
- package/eventcatalog/src/enterprise/auth/middleware/middleware-auth.ts +11 -7
- package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +4 -4
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +70 -57
- package/eventcatalog/src/enterprise/feature.ts +2 -1
- package/eventcatalog/src/layouts/SettingsLayout.astro +116 -0
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +62 -23
- package/eventcatalog/src/pages/_index.astro +250 -255
- package/eventcatalog/src/pages/api/settings/ai.ts +57 -0
- package/eventcatalog/src/pages/api/settings/general.ts +71 -0
- package/eventcatalog/src/pages/api/settings/logo.ts +113 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +26 -32
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +40 -31
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +2 -26
- package/eventcatalog/src/pages/docs/llm/llms.txt.ts +5 -1
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -1
- package/eventcatalog/src/pages/settings/assistant.astro +37 -0
- package/eventcatalog/src/pages/settings/billing.astro +17 -0
- package/eventcatalog/src/pages/settings/general.astro +32 -0
- package/eventcatalog/src/pages/settings/index.astro +21 -0
- package/eventcatalog/src/pages/settings/llm-access.astro +34 -0
- package/eventcatalog/src/pages/settings/mcp.astro +14 -0
- package/eventcatalog/src/styles/theme.css +38 -29
- package/eventcatalog/src/styles/themes/forest.css +17 -9
- package/eventcatalog/src/styles/themes/ocean.css +10 -2
- package/eventcatalog/src/styles/themes/sapphire.css +10 -2
- package/eventcatalog/src/styles/themes/sunset.css +25 -17
- package/eventcatalog/src/utils/eventcatalog-config/config-schema.ts +49 -0
- package/eventcatalog/src/utils/eventcatalog-config/config-writer.ts +149 -0
- package/eventcatalog/src/utils/url-builder.ts +4 -2
- package/package.json +7 -5
- package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +0 -81
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { isDevMode } from '@utils/feature';
|
|
3
|
+
import { aiSettingsSchema } from '@utils/eventcatalog-config/config-schema';
|
|
4
|
+
import {
|
|
5
|
+
applyConfigUpdate,
|
|
6
|
+
readConfigSource,
|
|
7
|
+
writeConfigUpdate,
|
|
8
|
+
type ConfigUpdate,
|
|
9
|
+
} from '@utils/eventcatalog-config/config-writer';
|
|
10
|
+
|
|
11
|
+
export const prerender = !isDevMode();
|
|
12
|
+
|
|
13
|
+
const json = (status: number, body: unknown) =>
|
|
14
|
+
new Response(JSON.stringify(body), {
|
|
15
|
+
status,
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
20
|
+
if (!isDevMode()) {
|
|
21
|
+
return json(403, { error: 'Settings can only be edited when running in dev mode (EVENTCATALOG_DEV_MODE=true).' });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let body: unknown;
|
|
25
|
+
try {
|
|
26
|
+
body = await request.json();
|
|
27
|
+
} catch {
|
|
28
|
+
return json(400, { error: 'Invalid JSON body' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const parsed = aiSettingsSchema.safeParse(body);
|
|
32
|
+
if (!parsed.success) {
|
|
33
|
+
return json(400, { error: 'Validation failed', issues: parsed.error.issues });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let source: string;
|
|
37
|
+
try {
|
|
38
|
+
source = readConfigSource();
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return json(500, { error: `Could not read eventcatalog.config.js: ${(err as Error).message}` });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = parsed.data;
|
|
44
|
+
const update: ConfigUpdate = {
|
|
45
|
+
llmsTxt: { enabled: data.llmsTxtEnabled },
|
|
46
|
+
chat: { enabled: data.chatEnabled },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
applyConfigUpdate(source, update);
|
|
51
|
+
writeConfigUpdate(update);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return json(500, { error: `Could not update eventcatalog.config.js: ${(err as Error).message}` });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return json(200, { ok: true, settings: data });
|
|
57
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { isDevMode } from '@utils/feature';
|
|
3
|
+
import { generalSettingsSchema } from '@utils/eventcatalog-config/config-schema';
|
|
4
|
+
import {
|
|
5
|
+
applyConfigUpdate,
|
|
6
|
+
readConfigSource,
|
|
7
|
+
writeConfigUpdate,
|
|
8
|
+
type ConfigUpdate,
|
|
9
|
+
} from '@utils/eventcatalog-config/config-writer';
|
|
10
|
+
|
|
11
|
+
export const prerender = !isDevMode();
|
|
12
|
+
|
|
13
|
+
const json = (status: number, body: unknown) =>
|
|
14
|
+
new Response(JSON.stringify(body), {
|
|
15
|
+
status,
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
20
|
+
if (!isDevMode()) {
|
|
21
|
+
return json(403, { error: 'Settings can only be edited when running in dev mode (EVENTCATALOG_DEV_MODE=true).' });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let body: unknown;
|
|
25
|
+
try {
|
|
26
|
+
body = await request.json();
|
|
27
|
+
} catch {
|
|
28
|
+
return json(400, { error: 'Invalid JSON body' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const parsed = generalSettingsSchema.safeParse(body);
|
|
32
|
+
if (!parsed.success) {
|
|
33
|
+
return json(400, { error: 'Validation failed', issues: parsed.error.issues });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Verify the config file exists and is parseable before we attempt the write.
|
|
37
|
+
let source: string;
|
|
38
|
+
try {
|
|
39
|
+
source = readConfigSource();
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return json(500, { error: `Could not read eventcatalog.config.js: ${(err as Error).message}` });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const data = parsed.data;
|
|
45
|
+
const update: ConfigUpdate = {
|
|
46
|
+
title: data.title,
|
|
47
|
+
tagline: data.tagline ?? null,
|
|
48
|
+
organizationName: data.organizationName ?? null,
|
|
49
|
+
homepageLink: data.homepageLink ?? null,
|
|
50
|
+
editUrl: data.editUrl ?? null,
|
|
51
|
+
repositoryUrl: data.repositoryUrl ?? null,
|
|
52
|
+
theme: data.theme,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (data.logo) {
|
|
56
|
+
update.logo = {
|
|
57
|
+
alt: data.logo.alt ?? null,
|
|
58
|
+
text: data.logo.text ?? null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Dry-run the write first to surface parse errors before touching disk.
|
|
64
|
+
applyConfigUpdate(source, update);
|
|
65
|
+
writeConfigUpdate(update);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return json(500, { error: `Could not update eventcatalog.config.js: ${(err as Error).message}` });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return json(200, { ok: true, settings: data });
|
|
71
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { isDevMode } from '@utils/feature';
|
|
5
|
+
import { writeConfigUpdate } from '@utils/eventcatalog-config/config-writer';
|
|
6
|
+
|
|
7
|
+
export const prerender = !isDevMode();
|
|
8
|
+
|
|
9
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
10
|
+
const ALLOWED: Record<string, string> = {
|
|
11
|
+
'image/png': 'png',
|
|
12
|
+
'image/jpeg': 'jpg',
|
|
13
|
+
'image/svg+xml': 'svg',
|
|
14
|
+
'image/webp': 'webp',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const json = (status: number, body: unknown) =>
|
|
18
|
+
new Response(JSON.stringify(body), {
|
|
19
|
+
status,
|
|
20
|
+
headers: { 'Content-Type': 'application/json' },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const projectRoot = () => process.env.PROJECT_DIR ?? process.cwd();
|
|
24
|
+
const publicDir = () => path.join(projectRoot(), 'public');
|
|
25
|
+
const LOGO_BASENAME = 'eventcatalog-logo';
|
|
26
|
+
|
|
27
|
+
const removeExistingLogos = () => {
|
|
28
|
+
const dir = publicDir();
|
|
29
|
+
if (!fs.existsSync(dir)) return;
|
|
30
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
31
|
+
if (entry.startsWith(`${LOGO_BASENAME}.`)) {
|
|
32
|
+
fs.unlinkSync(path.join(dir, entry));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Best-effort SVG sanitization: strips <script> blocks and on* event handlers.
|
|
39
|
+
* Not a full sanitizer; we accept this as a v1 limitation for SVG uploads.
|
|
40
|
+
*/
|
|
41
|
+
const sanitizeSvg = (svg: string): string =>
|
|
42
|
+
svg
|
|
43
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
44
|
+
.replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, '')
|
|
45
|
+
.replace(/\son[a-z]+\s*=\s*'[^']*'/gi, '')
|
|
46
|
+
.replace(/javascript:/gi, '');
|
|
47
|
+
|
|
48
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
49
|
+
if (!isDevMode()) {
|
|
50
|
+
return json(403, { error: 'Logo can only be changed when running in dev mode (EVENTCATALOG_DEV_MODE=true).' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let form: FormData;
|
|
54
|
+
try {
|
|
55
|
+
form = await request.formData();
|
|
56
|
+
} catch {
|
|
57
|
+
return json(400, { error: 'Expected multipart/form-data body' });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const file = form.get('logo');
|
|
61
|
+
if (!(file instanceof File)) {
|
|
62
|
+
return json(400, { error: 'Missing `logo` file field' });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const ext = ALLOWED[file.type];
|
|
66
|
+
if (!ext) {
|
|
67
|
+
return json(400, { error: `Unsupported logo type: ${file.type}. Allowed: PNG, JPG, SVG, WebP.` });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (file.size > MAX_BYTES) {
|
|
71
|
+
return json(413, { error: `Logo exceeds 2MB size limit (got ${file.size} bytes).` });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
75
|
+
const dir = publicDir();
|
|
76
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
|
|
78
|
+
removeExistingLogos();
|
|
79
|
+
|
|
80
|
+
const targetName = `${LOGO_BASENAME}.${ext}`;
|
|
81
|
+
const targetPath = path.join(dir, targetName);
|
|
82
|
+
|
|
83
|
+
if (ext === 'svg') {
|
|
84
|
+
const sanitized = sanitizeSvg(buffer.toString('utf8'));
|
|
85
|
+
fs.writeFileSync(targetPath, sanitized, 'utf8');
|
|
86
|
+
} else {
|
|
87
|
+
fs.writeFileSync(targetPath, buffer);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const src = `/${targetName}`;
|
|
91
|
+
try {
|
|
92
|
+
writeConfigUpdate({ logo: { src } });
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return json(500, { error: `Logo saved but config update failed: ${(err as Error).message}` });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return json(200, { ok: true, src });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const DELETE: APIRoute = async () => {
|
|
101
|
+
if (!isDevMode()) {
|
|
102
|
+
return json(403, { error: 'Logo can only be changed when running in dev mode.' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
removeExistingLogos();
|
|
106
|
+
try {
|
|
107
|
+
writeConfigUpdate({ logo: { src: null } });
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return json(500, { error: `Logo files removed but config update failed: ${(err as Error).message}` });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return json(200, { ok: true });
|
|
113
|
+
};
|
|
@@ -51,12 +51,10 @@ const pagefindAttributes =
|
|
|
51
51
|
<div class="flex docs-layout w-full">
|
|
52
52
|
<div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
|
|
53
53
|
<div class="border-b border-[rgb(var(--ec-page-border))] md:pb-2">
|
|
54
|
-
<div>
|
|
55
|
-
<div class="flex justify-between items-center">
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
</div>
|
|
59
|
-
<div class="hidden lg:block">
|
|
54
|
+
<div class="flex flex-col gap-4">
|
|
55
|
+
<div class="flex justify-between items-center gap-4">
|
|
56
|
+
<h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">{title}</h2>
|
|
57
|
+
<div class="hidden lg:block shrink-0">
|
|
60
58
|
<CopyAsMarkdown
|
|
61
59
|
client:only="react"
|
|
62
60
|
schemas={[]}
|
|
@@ -69,32 +67,28 @@ const pagefindAttributes =
|
|
|
69
67
|
/>
|
|
70
68
|
</div>
|
|
71
69
|
</div>
|
|
70
|
+
{props.data.summary && <p class="text-base text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</p>}
|
|
71
|
+
{
|
|
72
|
+
badges.length > 0 && (
|
|
73
|
+
<div class="flex flex-wrap gap-3 py-4">
|
|
74
|
+
{badges.map((badge: any) => (
|
|
75
|
+
<span
|
|
76
|
+
class={`
|
|
77
|
+
inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium
|
|
78
|
+
bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))]
|
|
79
|
+
text-[rgb(var(--ec-page-text))]
|
|
80
|
+
`}
|
|
81
|
+
>
|
|
82
|
+
{badge.iconComponent && (
|
|
83
|
+
<badge.iconComponent className="w-4 h-4 flex-shrink-0 text-[rgb(var(--ec-icon-color))]" />
|
|
84
|
+
)}
|
|
85
|
+
<span>{badge.content}</span>
|
|
86
|
+
</span>
|
|
87
|
+
))}
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
72
91
|
</div>
|
|
73
|
-
{
|
|
74
|
-
props.data.summary && (
|
|
75
|
-
<p class="text-base pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</p>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
{
|
|
79
|
-
badges.length > 0 && (
|
|
80
|
-
<div class="flex flex-wrap gap-3 py-4">
|
|
81
|
-
{badges.map((badge: any) => (
|
|
82
|
-
<span
|
|
83
|
-
class={`
|
|
84
|
-
inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium
|
|
85
|
-
bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))]
|
|
86
|
-
text-[rgb(var(--ec-page-text))]
|
|
87
|
-
`}
|
|
88
|
-
>
|
|
89
|
-
{badge.iconComponent && (
|
|
90
|
-
<badge.iconComponent className="w-4 h-4 flex-shrink-0 text-[rgb(var(--ec-icon-color))]" />
|
|
91
|
-
)}
|
|
92
|
-
<span>{badge.content}</span>
|
|
93
|
-
</span>
|
|
94
|
-
))}
|
|
95
|
-
</div>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
92
|
</div>
|
|
99
93
|
<div data-pagefind-ignore>
|
|
100
94
|
{
|
|
@@ -129,7 +123,7 @@ const pagefindAttributes =
|
|
|
129
123
|
)
|
|
130
124
|
}
|
|
131
125
|
</div>
|
|
132
|
-
<div class="prose py-
|
|
126
|
+
<div class="prose py-4 max-w-none">
|
|
133
127
|
<Content components={components(props)} />
|
|
134
128
|
</div>
|
|
135
129
|
</div>
|
|
@@ -248,6 +248,7 @@ const {
|
|
|
248
248
|
|
|
249
249
|
let friendlyCollectionName = props.collection.slice(0, props.collection.length - 1);
|
|
250
250
|
friendlyCollectionName = friendlyCollectionName === 'querie' ? 'query' : friendlyCollectionName;
|
|
251
|
+
friendlyCollectionName = friendlyCollectionName === 'entitie' ? 'entity' : friendlyCollectionName;
|
|
251
252
|
|
|
252
253
|
const schemasForResource = getSchemasFromResource(props);
|
|
253
254
|
|
|
@@ -287,38 +288,46 @@ nodeGraphs.push({
|
|
|
287
288
|
<main class="flex docs-layout min-h-full bg-[rgb(var(--ec-page-bg))]" {...pagefindAttributes}>
|
|
288
289
|
<div class="flex docs-layout w-full">
|
|
289
290
|
<div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
|
|
290
|
-
<div class="border-b border-[rgb(var(--ec-page-border))] md:pb-
|
|
291
|
+
<div class="border-b border-[rgb(var(--ec-page-border))] md:pb-6 pt-4">
|
|
291
292
|
<div>
|
|
292
|
-
<div class="flex justify-between items-
|
|
293
|
-
<div class="flex
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
<div class="flex justify-between items-end">
|
|
294
|
+
<div class="flex flex-col gap-4">
|
|
295
|
+
<span class="text-xs md:text-sm font-semibold text-[rgb(var(--ec-accent))] capitalize">
|
|
296
|
+
{friendlyCollectionName}
|
|
297
|
+
</span>
|
|
298
|
+
<div class="flex items-center gap-3">
|
|
299
|
+
{
|
|
300
|
+
headerIconSrc && (
|
|
301
|
+
<img
|
|
302
|
+
src={headerIconSrc}
|
|
303
|
+
alt={props.data.name}
|
|
304
|
+
class="w-8 h-8 md:w-10 md:h-10 object-contain rounded-md shrink-0"
|
|
305
|
+
/>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
<div class="flex items-center gap-2">
|
|
309
|
+
<h2
|
|
310
|
+
id="doc-page-header"
|
|
311
|
+
class={`text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))] ${props.data.deprecated && hasDeprecated ? 'text-red-500' : ''}`}
|
|
312
|
+
>
|
|
313
|
+
{props.data.name}
|
|
314
|
+
{props.data.latestVersion !== props.data.version && <span>(v{props.data.version})</span>}
|
|
315
|
+
</h2>
|
|
316
|
+
<FavoriteButton
|
|
317
|
+
client:load
|
|
318
|
+
nodeKey={`${collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]}:${props.data.id}:${props.data.version}`}
|
|
319
|
+
title={props.data.name}
|
|
320
|
+
badge={collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]
|
|
321
|
+
.charAt(0)
|
|
322
|
+
.toUpperCase() +
|
|
323
|
+
collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap].slice(1)}
|
|
324
|
+
href={buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}`)}
|
|
325
|
+
size="md"
|
|
300
326
|
/>
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<h2
|
|
304
|
-
id="doc-page-header"
|
|
305
|
-
class={`text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))] ${props.data.deprecated && hasDeprecated ? 'text-red-500' : ''}`}
|
|
306
|
-
>
|
|
307
|
-
{props.data.name}
|
|
308
|
-
{props.data.latestVersion !== props.data.version && <span>(v{props.data.version})</span>}
|
|
309
|
-
</h2>
|
|
310
|
-
<FavoriteButton
|
|
311
|
-
client:load
|
|
312
|
-
nodeKey={`${collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]}:${props.data.id}:${props.data.version}`}
|
|
313
|
-
title={props.data.name}
|
|
314
|
-
badge={collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]
|
|
315
|
-
.charAt(0)
|
|
316
|
-
.toUpperCase() + collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap].slice(1)}
|
|
317
|
-
href={buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}`)}
|
|
318
|
-
size="md"
|
|
319
|
-
/>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
320
329
|
</div>
|
|
321
|
-
<div class="hidden lg:block">
|
|
330
|
+
<div class="hidden lg:block mb-2">
|
|
322
331
|
<CopyAsMarkdown
|
|
323
332
|
client:only="react"
|
|
324
333
|
schemas={schemasForResource}
|
|
@@ -333,10 +342,10 @@ nodeGraphs.push({
|
|
|
333
342
|
</div>
|
|
334
343
|
</div>
|
|
335
344
|
|
|
336
|
-
<h2 class="text-base pt-
|
|
345
|
+
<h2 class="text-base pt-4 text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</h2>
|
|
337
346
|
{
|
|
338
347
|
badges && (
|
|
339
|
-
<div class="flex flex-wrap gap-3
|
|
348
|
+
<div class="flex flex-wrap gap-3 pt-6 pb-2">
|
|
340
349
|
{badges.map((badge: any) => {
|
|
341
350
|
return (
|
|
342
351
|
<span
|
|
@@ -77,7 +77,7 @@ const badges = [
|
|
|
77
77
|
<p class="text-lg pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{ubiquitousLanguage.summary}</p>
|
|
78
78
|
<!-- Add badge -->
|
|
79
79
|
{
|
|
80
|
-
badges && (
|
|
80
|
+
badges && badges.length > 0 && (
|
|
81
81
|
<div class="flex flex-wrap gap-3 py-4">
|
|
82
82
|
{badges.map((badge: any) => {
|
|
83
83
|
return (
|
|
@@ -47,7 +47,7 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
|
|
|
47
47
|
type="text"
|
|
48
48
|
id="searchInput"
|
|
49
49
|
placeholder="Search terms..."
|
|
50
|
-
class="w-full
|
|
50
|
+
class="block w-full rounded-md border-0 py-1.5 pr-4 pl-10 text-sm font-light text-[rgb(var(--ec-header-text))] bg-[rgb(var(--ec-header-bg))] shadow-xs ring-1 ring-inset ring-[rgb(var(--ec-dropdown-border))] placeholder:text-[rgb(var(--ec-icon-color))] focus:ring-2 focus:ring-[rgb(var(--ec-accent))] focus:outline-none"
|
|
51
51
|
/>
|
|
52
52
|
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
53
53
|
<svg class="h-4 w-4 text-[rgb(var(--ec-icon-color))]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -59,14 +59,6 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
|
|
|
59
59
|
</svg>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
|
-
<div class="mt-2 text-right">
|
|
63
|
-
<span
|
|
64
|
-
class="inline-flex items-center px-2 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] border border-[rgb(var(--ec-page-border))] rounded-md"
|
|
65
|
-
id="resultsCount"
|
|
66
|
-
>
|
|
67
|
-
{/* This will be updated by JavaScript */}
|
|
68
|
-
</span>
|
|
69
|
-
</div>
|
|
70
62
|
</div>
|
|
71
63
|
</div>
|
|
72
64
|
</div>
|
|
@@ -323,15 +315,11 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
|
|
|
323
315
|
function initializeSearch() {
|
|
324
316
|
const searchInput = document.getElementById('searchInput');
|
|
325
317
|
const domainSections = document.querySelectorAll('[data-domain-section]');
|
|
326
|
-
const resultsCount = document.getElementById('resultsCount');
|
|
327
318
|
|
|
328
319
|
function updateResults() {
|
|
329
320
|
//@ts-ignore
|
|
330
321
|
const searchTerm = searchInput?.value.toLowerCase() || '';
|
|
331
|
-
let totalVisibleTerms = 0;
|
|
332
|
-
let totalTerms = 0;
|
|
333
322
|
|
|
334
|
-
// Handle search for each domain section
|
|
335
323
|
domainSections.forEach((section) => {
|
|
336
324
|
const domainId = section.getAttribute('data-domain-section');
|
|
337
325
|
const domainCards = section.querySelectorAll('.term-card');
|
|
@@ -339,19 +327,14 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
|
|
|
339
327
|
let domainVisibleCount = 0;
|
|
340
328
|
|
|
341
329
|
domainCards.forEach((card) => {
|
|
342
|
-
totalTerms++;
|
|
343
330
|
const title = card.querySelector('h4')?.textContent?.toLowerCase() || '';
|
|
344
331
|
const description = card.querySelector('p')?.textContent?.toLowerCase() || '';
|
|
345
332
|
const matches = searchTerm === '' || title.includes(searchTerm) || description.includes(searchTerm);
|
|
346
333
|
|
|
347
334
|
card.classList.toggle('hidden', !matches);
|
|
348
|
-
if (matches)
|
|
349
|
-
domainVisibleCount++;
|
|
350
|
-
totalVisibleTerms++;
|
|
351
|
-
}
|
|
335
|
+
if (matches) domainVisibleCount++;
|
|
352
336
|
});
|
|
353
337
|
|
|
354
|
-
// Show/hide domain-specific no results message
|
|
355
338
|
if (domainNoResults) {
|
|
356
339
|
if (searchTerm.trim() === '') {
|
|
357
340
|
domainNoResults.classList.add('hidden');
|
|
@@ -360,16 +343,9 @@ const { subdomains, duplicateTerms } = ubiquitousLanguageData;
|
|
|
360
343
|
}
|
|
361
344
|
}
|
|
362
345
|
});
|
|
363
|
-
|
|
364
|
-
// Update results count
|
|
365
|
-
if (resultsCount) {
|
|
366
|
-
resultsCount.textContent = `Showing ${totalVisibleTerms} terms`;
|
|
367
|
-
}
|
|
368
346
|
}
|
|
369
347
|
|
|
370
348
|
searchInput?.addEventListener('input', updateResults);
|
|
371
|
-
|
|
372
|
-
// Initialize results count
|
|
373
349
|
updateResults();
|
|
374
350
|
}
|
|
375
351
|
|
|
@@ -2,7 +2,7 @@ import { getCollection } from 'astro:content';
|
|
|
2
2
|
import config from '@config';
|
|
3
3
|
import type { APIRoute } from 'astro';
|
|
4
4
|
|
|
5
|
-
import { isCustomDocsEnabled, isResourceDocsEnabled } from '@utils/feature';
|
|
5
|
+
import { isCustomDocsEnabled, isResourceDocsEnabled, isLLMSTxtEnabled } from '@utils/feature';
|
|
6
6
|
import { getUbiquitousLanguage } from '@utils/collections/domains';
|
|
7
7
|
import { getResourceDocs } from '@utils/collections/resource-docs';
|
|
8
8
|
|
|
@@ -82,6 +82,10 @@ const renderEntities = (baseUrl: string) => {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export const GET: APIRoute = async ({ params, request }) => {
|
|
85
|
+
if (!isLLMSTxtEnabled()) {
|
|
86
|
+
return new Response('llms.txt is not enabled for this Catalog.', { status: 404 });
|
|
87
|
+
}
|
|
88
|
+
|
|
85
89
|
const url = new URL(request.url);
|
|
86
90
|
const baseUrl = process.env.LLMS_TXT_BASE_URL || `${url.origin}`;
|
|
87
91
|
|
|
@@ -74,7 +74,7 @@ const pageTitle = `User | ${props.data.name}`;
|
|
|
74
74
|
<img
|
|
75
75
|
src={props.data.avatarUrl}
|
|
76
76
|
alt={`${props.data.name}'s profile picture`}
|
|
77
|
-
class="w-20 h-20 rounded-
|
|
77
|
+
class="w-20 h-20 rounded-md object-cover border-2 border-[rgb(var(--ec-page-border))]"
|
|
78
78
|
/>
|
|
79
79
|
)
|
|
80
80
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import SettingsLayout from '@layouts/SettingsLayout.astro';
|
|
5
|
+
import { AssistantSettingsForm } from '@components/Settings/AssistantSettingsForm';
|
|
6
|
+
import { isDevMode, isSSR, isEventCatalogStarterEnabled, isEventCatalogScaleEnabled } from '@utils/feature';
|
|
7
|
+
import { buildUrl } from '@utils/url-builder';
|
|
8
|
+
import config from '@config';
|
|
9
|
+
|
|
10
|
+
const canEdit = isDevMode();
|
|
11
|
+
|
|
12
|
+
const initial = {
|
|
13
|
+
llmsTxtEnabled: config?.llmsTxt?.enabled ?? true,
|
|
14
|
+
chatEnabled: config?.chat?.enabled ?? true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const projectRoot = process.env.PROJECT_DIR ?? process.cwd();
|
|
18
|
+
const hasChatConfigFile = fs.existsSync(path.join(projectRoot, 'eventcatalog.chat.js'));
|
|
19
|
+
const hasPlan = isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
|
|
20
|
+
const inSSR = isSSR();
|
|
21
|
+
const chatAvailable = hasPlan && hasChatConfigFile && inSSR;
|
|
22
|
+
|
|
23
|
+
const apiBase = buildUrl('/api/settings');
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<SettingsLayout title="Assistant Agent" active="assistant">
|
|
27
|
+
<AssistantSettingsForm
|
|
28
|
+
client:load
|
|
29
|
+
canEdit={canEdit}
|
|
30
|
+
initial={initial}
|
|
31
|
+
chatAvailable={chatAvailable}
|
|
32
|
+
hasPlan={hasPlan}
|
|
33
|
+
inSSR={inSSR}
|
|
34
|
+
hasChatConfigFile={hasChatConfigFile}
|
|
35
|
+
apiBase={apiBase}
|
|
36
|
+
/>
|
|
37
|
+
</SettingsLayout>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
import SettingsLayout from '@layouts/SettingsLayout.astro';
|
|
3
|
+
import { BillingSettingsForm, type PlanId } from '@components/Settings/BillingSettingsForm';
|
|
4
|
+
import { isEventCatalogStarterEnabled, isEventCatalogScaleEnabled } from '@utils/feature';
|
|
5
|
+
|
|
6
|
+
// Detect which plan is active. Enterprise has no env flag of its own — it includes
|
|
7
|
+
// Scale features, so an explicit Enterprise toggle would need a separate flag.
|
|
8
|
+
// For now, treat anything Scale-licensed as Scale; Enterprise users will still see
|
|
9
|
+
// upgrade copy on the Scale card but the Plans grid is the canonical source.
|
|
10
|
+
let currentPlan: PlanId = 'community';
|
|
11
|
+
if (isEventCatalogScaleEnabled()) currentPlan = 'scale';
|
|
12
|
+
else if (isEventCatalogStarterEnabled()) currentPlan = 'starter';
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<SettingsLayout title="Billing" active="billing">
|
|
16
|
+
<BillingSettingsForm client:load currentPlan={currentPlan} />
|
|
17
|
+
</SettingsLayout>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import SettingsLayout from '@layouts/SettingsLayout.astro';
|
|
3
|
+
import { GeneralSettingsForm } from '@components/Settings/GeneralSettingsForm';
|
|
4
|
+
import { isDevMode } from '@utils/feature';
|
|
5
|
+
import { buildUrl } from '@utils/url-builder';
|
|
6
|
+
import config from '@config';
|
|
7
|
+
|
|
8
|
+
const canEdit = isDevMode();
|
|
9
|
+
|
|
10
|
+
const initial = {
|
|
11
|
+
title: config?.title ?? '',
|
|
12
|
+
tagline: config?.tagline,
|
|
13
|
+
organizationName: config?.organizationName,
|
|
14
|
+
homepageLink: config?.homepageLink,
|
|
15
|
+
editUrl: config?.editUrl,
|
|
16
|
+
repositoryUrl: config?.repositoryUrl,
|
|
17
|
+
logo: config?.logo
|
|
18
|
+
? {
|
|
19
|
+
alt: config.logo.alt,
|
|
20
|
+
text: config.logo.text,
|
|
21
|
+
src: config.logo.src,
|
|
22
|
+
}
|
|
23
|
+
: undefined,
|
|
24
|
+
theme: config?.theme || 'default',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const apiBase = buildUrl('/api/settings');
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<SettingsLayout title="General" active="general">
|
|
31
|
+
<GeneralSettingsForm client:load canEdit={canEdit} initial={initial} apiBase={apiBase} />
|
|
32
|
+
</SettingsLayout>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { buildUrl } from '@utils/url-builder';
|
|
3
|
+
|
|
4
|
+
const target = buildUrl('/settings/general');
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!doctype html>
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="utf-8" />
|
|
11
|
+
<meta http-equiv="refresh" content={`0; url=${target}`} />
|
|
12
|
+
<link rel="canonical" href={target} />
|
|
13
|
+
<title>Redirecting…</title>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<p>Redirecting to <a href={target}>{target}</a>…</p>
|
|
17
|
+
<script is:inline define:vars={{ target }}>
|
|
18
|
+
window.location.replace(target);
|
|
19
|
+
</script>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|