@eventcatalog/core 3.35.1 → 3.36.2
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-D6IBLY3O.js +320 -0
- package/dist/{chunk-R4DR3YAH.js → chunk-H6TGUW5O.js} +1 -1
- package/dist/{chunk-VJ357XOI.js → chunk-IO4U4MPC.js} +1 -1
- package/dist/{chunk-JEQZWJWP.js → chunk-L723FWAT.js} +1 -1
- package/dist/{chunk-B7C4DHFE.js → chunk-R5ZDI2JO.js} +1 -1
- package/dist/{chunk-4SNN54V4.js → chunk-SEAN3UND.js} +1 -1
- package/dist/{chunk-3KXCGYET.js → chunk-ULZYHF3V.js} +5 -0
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +22 -0
- package/dist/docs/api/_category_.json +1 -1
- package/dist/docs/contributing/_category_.json +1 -1
- package/dist/docs/development/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/04-skills/_category_.json +1 -1
- package/dist/docs/development/authentication/providers/_category_.json +1 -1
- package/dist/docs/development/bring-your-own-documentation/custom-pages/_category_.json +1 -1
- package/dist/docs/development/customization/01-customize-landing-page.md +1 -1
- package/dist/docs/development/customization/03-search.md +79 -0
- package/dist/docs/development/customization/custom-components/_category_.json +1 -1
- package/dist/docs/development/customization/customize-sidebars/_category_.json +1 -1
- package/dist/docs/development/customization/customize-visualizer/_category_.json +1 -1
- package/dist/docs/development/design/_category_.json +1 -1
- package/dist/docs/development/guides/changelogs/_category_.json +1 -1
- package/dist/docs/development/guides/channels/_category_.json +1 -1
- package/dist/docs/development/guides/channels/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/channels/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/development/guides/data/_category_.json +1 -1
- package/dist/docs/development/guides/data/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/data-products/_category_.json +1 -1
- package/dist/docs/development/guides/domains/02-creating-domains/_category_.json +1 -1
- package/dist/docs/development/guides/domains/03-ownership-and-language/_category_.json +1 -1
- package/dist/docs/development/guides/domains/05-entities/_category_.json +1 -1
- package/dist/docs/development/guides/domains/_category_.json +1 -1
- package/dist/docs/development/guides/flows/_category_.json +1 -1
- package/dist/docs/development/guides/messages/_category_.json +1 -1
- package/dist/docs/development/guides/messages/commands/_category_.json +1 -1
- package/dist/docs/development/guides/messages/common/_category_.json +1 -1
- package/dist/docs/development/guides/messages/events/_category_.json +1 -1
- package/dist/docs/development/guides/messages/queries/_category_.json +1 -1
- package/dist/docs/development/guides/owners/_category_.json +1 -1
- package/dist/docs/development/guides/owners/teams/_category_.json +1 -1
- package/dist/docs/development/guides/owners/users/_category_.json +1 -1
- package/dist/docs/development/guides/schemas/_category_.json +1 -1
- package/dist/docs/development/guides/services/_category_.json +1 -1
- package/dist/docs/development/guides/services/adding-to-services/_category_.json +1 -1
- package/dist/docs/development/guides/services/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/services/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/plugins/_category_.json +1 -1
- package/dist/docs/plugins/amazon-apigateway/_category_.json +1 -1
- package/dist/docs/plugins/asyncapi/_category_.json +1 -1
- package/dist/docs/plugins/aws-glue-registry/_category_.json +1 -1
- package/dist/docs/plugins/backstage/_category_.json +1 -1
- package/dist/docs/plugins/confluent-schema-registry/_category_.json +1 -1
- package/dist/docs/plugins/eventbridge/_category_.json +1 -1
- package/dist/docs/plugins/eventcatalog-federation/_category_.json +1 -1
- package/dist/docs/plugins/github/_category_.json +1 -1
- package/dist/docs/plugins/graphql/_category_.json +1 -1
- package/dist/docs/plugins/hookdeck/_category_.json +1 -1
- package/dist/docs/plugins/openapi/_category_.json +1 -1
- package/dist/eventcatalog.cjs +434 -35
- package/dist/eventcatalog.config.d.cts +13 -1
- package/dist/eventcatalog.config.d.ts +13 -1
- package/dist/eventcatalog.js +88 -11
- package/dist/features.cjs +6 -0
- package/dist/features.d.cts +2 -1
- package/dist/features.d.ts +2 -1
- package/dist/features.js +3 -1
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/search-indexer.cjs +356 -0
- package/dist/search-indexer.d.cts +30 -0
- package/dist/search-indexer.d.ts +30 -0
- package/dist/search-indexer.js +10 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +28 -32
- package/eventcatalog/src/components/Search/SearchModal.tsx +248 -148
- package/eventcatalog/src/components/Search/search-utils.spec.ts +138 -1
- package/eventcatalog/src/components/Search/search-utils.ts +271 -0
- package/eventcatalog/src/env.d.ts +1 -0
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -7
- package/eventcatalog/src/stores/theme-store.ts +9 -13
- package/package.json +3 -2
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it, beforeEach } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyActiveFilter,
|
|
4
|
+
getIndexedResultRank,
|
|
5
|
+
getSearchFilters,
|
|
6
|
+
getUrlForSearchItem,
|
|
7
|
+
hasMeaningfulIndexedMatch,
|
|
8
|
+
highlightQuery,
|
|
9
|
+
mapPagefindResultsToSearchItems,
|
|
10
|
+
normalizeResultUrl,
|
|
11
|
+
} from './search-utils';
|
|
3
12
|
|
|
4
13
|
declare global {
|
|
5
14
|
interface Window {
|
|
@@ -34,3 +43,131 @@ describe('getUrlForSearchItem', () => {
|
|
|
34
43
|
expect(getUrlForSearchItem({}, 'unknown:Customer360:1.0.0')).toBeNull();
|
|
35
44
|
});
|
|
36
45
|
});
|
|
46
|
+
|
|
47
|
+
describe('indexed search result helpers', () => {
|
|
48
|
+
it('filters weak single-character highlighted matches for longer queries', () => {
|
|
49
|
+
expect(
|
|
50
|
+
hasMeaningfulIndexedMatch({
|
|
51
|
+
query: 'random',
|
|
52
|
+
title: 'Orders Service',
|
|
53
|
+
content: 'Order Metadata in api service server logs',
|
|
54
|
+
excerpt: 'Order Metadata in api db L -- <mark>R</mark> server',
|
|
55
|
+
})
|
|
56
|
+
).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('keeps short but meaningful marked terms', () => {
|
|
60
|
+
expect(
|
|
61
|
+
hasMeaningfulIndexedMatch({
|
|
62
|
+
query: 'db',
|
|
63
|
+
title: 'Orders Service',
|
|
64
|
+
content: 'Order Metadata in api service server logs',
|
|
65
|
+
excerpt: 'Order Metadata in api <mark>db</mark>',
|
|
66
|
+
})
|
|
67
|
+
).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('keeps exact title or content matches even without marked excerpts', () => {
|
|
71
|
+
expect(
|
|
72
|
+
hasMeaningfulIndexedMatch({
|
|
73
|
+
query: 'payments',
|
|
74
|
+
title: 'Payments Database',
|
|
75
|
+
content: '',
|
|
76
|
+
excerpt: '',
|
|
77
|
+
})
|
|
78
|
+
).toBe(true);
|
|
79
|
+
|
|
80
|
+
expect(
|
|
81
|
+
hasMeaningfulIndexedMatch({
|
|
82
|
+
query: 'idempotency',
|
|
83
|
+
title: 'Payments Database',
|
|
84
|
+
content: 'Always include an idempotency key in payment creation.',
|
|
85
|
+
excerpt: '',
|
|
86
|
+
})
|
|
87
|
+
).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('normalizes Pagefind URLs to root-relative paths', () => {
|
|
91
|
+
expect(normalizeResultUrl('docs/containers/payments-db/0.0.1')).toBe('/docs/containers/payments-db/0.0.1');
|
|
92
|
+
expect(normalizeResultUrl('/docs/containers/payments-db/0.0.1')).toBe('/docs/containers/payments-db/0.0.1');
|
|
93
|
+
expect(normalizeResultUrl('https://example.com/docs')).toBe('https://example.com/docs');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('highlights title matches while escaping unsafe title text', () => {
|
|
97
|
+
expect(highlightQuery('Payment <Service>', 'payment')).toBe('<mark>Payment</mark> <Service>');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('ranks title matches above id/url matches and content matches', () => {
|
|
101
|
+
const query = 'payment';
|
|
102
|
+
const contentRank = getIndexedResultRank({
|
|
103
|
+
query,
|
|
104
|
+
title: 'Orders Service',
|
|
105
|
+
url: '/docs/services/OrdersService/1.0.0',
|
|
106
|
+
content: 'Handles payment retries',
|
|
107
|
+
});
|
|
108
|
+
const urlRank = getIndexedResultRank({
|
|
109
|
+
query,
|
|
110
|
+
title: 'Orders Service',
|
|
111
|
+
url: '/docs/services/PaymentService/1.0.0',
|
|
112
|
+
content: 'Handles orders',
|
|
113
|
+
});
|
|
114
|
+
const titleRank = getIndexedResultRank({
|
|
115
|
+
query,
|
|
116
|
+
title: 'Payment Service',
|
|
117
|
+
url: '/docs/services/PaymentService/1.0.0',
|
|
118
|
+
content: 'Handles orders',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(titleRank).toBeGreaterThan(urlRank);
|
|
122
|
+
expect(urlRank).toBeGreaterThan(contentRank);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('applies grouped search filters', () => {
|
|
126
|
+
const items = [{ type: 'Event' }, { type: 'Command' }, { type: 'Team' }, { type: 'User' }, { type: 'Service' }];
|
|
127
|
+
|
|
128
|
+
expect(applyActiveFilter(items, 'Message')).toEqual([{ type: 'Event' }, { type: 'Command' }]);
|
|
129
|
+
expect(applyActiveFilter(items, 'Team')).toEqual([{ type: 'Team' }, { type: 'User' }]);
|
|
130
|
+
expect(applyActiveFilter(items, 'Service')).toEqual([{ type: 'Service' }]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('builds filter metadata without encoding behavior in labels', () => {
|
|
134
|
+
expect(getSearchFilters({ items: [], query: 'missing' })).toEqual([{ id: 'all', name: 'All (0)', count: 0 }]);
|
|
135
|
+
expect(getSearchFilters({ items: [{ type: 'Event' }, { type: 'Service' }], query: 'order' })).toEqual([
|
|
136
|
+
{ id: 'all', name: 'All (2)', count: 2 },
|
|
137
|
+
{ id: 'Service', name: 'Services (1)', count: 1 },
|
|
138
|
+
{ id: 'Message', name: 'Messages (1)', count: 1 },
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('maps Pagefind results into ranked search items', async () => {
|
|
143
|
+
const items = await mapPagefindResultsToSearchItems({
|
|
144
|
+
query: 'payment',
|
|
145
|
+
limit: 10,
|
|
146
|
+
results: [
|
|
147
|
+
{
|
|
148
|
+
id: 'content',
|
|
149
|
+
score: 10,
|
|
150
|
+
data: async () => ({
|
|
151
|
+
url: 'docs/services/OrdersService/1.0.0',
|
|
152
|
+
content: 'Handles payment retries',
|
|
153
|
+
excerpt: 'Handles <mark>payment</mark> retries',
|
|
154
|
+
meta: { title: 'Orders Service', type: 'Service', id: 'OrdersService' },
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'title',
|
|
159
|
+
score: 1,
|
|
160
|
+
data: async () => ({
|
|
161
|
+
url: 'docs/services/PaymentService/1.0.0',
|
|
162
|
+
content: 'Handles orders',
|
|
163
|
+
excerpt: '<mark>Payment</mark> service',
|
|
164
|
+
meta: { title: 'Payment Service', type: 'Service', id: 'PaymentService' },
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(items.map((item) => item.id)).toEqual(['title', 'content']);
|
|
171
|
+
expect(items[0].url).toBe('/docs/services/PaymentService/1.0.0');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import { buildUrl } from '@utils/url-builder';
|
|
2
2
|
|
|
3
|
+
export interface SearchNode {
|
|
4
|
+
key: string;
|
|
5
|
+
title: string;
|
|
6
|
+
badge?: string;
|
|
7
|
+
summary?: string;
|
|
8
|
+
href?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
leftIcon?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SearchItem {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
url: string;
|
|
17
|
+
type: string;
|
|
18
|
+
key?: string;
|
|
19
|
+
rawNode: {
|
|
20
|
+
title?: string;
|
|
21
|
+
badge?: string;
|
|
22
|
+
summary?: string;
|
|
23
|
+
icon?: string;
|
|
24
|
+
leftIcon?: string;
|
|
25
|
+
matchedExcerpt?: string;
|
|
26
|
+
};
|
|
27
|
+
isFavorite?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SearchFilter {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface PagefindResult {
|
|
37
|
+
id: string;
|
|
38
|
+
score?: number;
|
|
39
|
+
data: () => Promise<{
|
|
40
|
+
url: string;
|
|
41
|
+
excerpt?: string;
|
|
42
|
+
content?: string;
|
|
43
|
+
meta?: Record<string, any>;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
|
|
3
47
|
const docsPathByType: Record<string, string> = {
|
|
4
48
|
channel: 'channels',
|
|
5
49
|
command: 'commands',
|
|
@@ -32,3 +76,230 @@ export const getUrlForSearchItem = (node: { href?: string }, key: string) => {
|
|
|
32
76
|
|
|
33
77
|
return buildUrl(`/docs/${docsPath}/${id}/${version}`);
|
|
34
78
|
};
|
|
79
|
+
|
|
80
|
+
export const stripHtml = (value: string) =>
|
|
81
|
+
value
|
|
82
|
+
.replace(/<[^>]+>/g, '')
|
|
83
|
+
.replace(/\s+/g, ' ')
|
|
84
|
+
.trim();
|
|
85
|
+
|
|
86
|
+
const escapeHtml = (value: string) =>
|
|
87
|
+
value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
88
|
+
|
|
89
|
+
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
90
|
+
|
|
91
|
+
const getSearchTerms = (query: string) => [...new Set(query.trim().toLowerCase().split(/\s+/).filter(Boolean))];
|
|
92
|
+
|
|
93
|
+
const getMarkedTerms = (value: string) => {
|
|
94
|
+
return [...value.matchAll(/<mark>(.*?)<\/mark>/gi)].map((match) => stripHtml(match[1]).toLowerCase());
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const highlightQuery = (value: string, query: string) => {
|
|
98
|
+
const terms = getSearchTerms(query).sort((a, b) => b.length - a.length);
|
|
99
|
+
|
|
100
|
+
if (terms.length === 0) {
|
|
101
|
+
return escapeHtml(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const termLookup = new Set(terms);
|
|
105
|
+
const pattern = new RegExp(`(${terms.map(escapeRegExp).join('|')})`, 'gi');
|
|
106
|
+
|
|
107
|
+
return value
|
|
108
|
+
.split(pattern)
|
|
109
|
+
.map((part) => (termLookup.has(part.toLowerCase()) ? `<mark>${escapeHtml(part)}</mark>` : escapeHtml(part)))
|
|
110
|
+
.join('');
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const normalizeResultUrl = (url: string) => {
|
|
114
|
+
if (/^(https?:)?\/\//.test(url) || url.startsWith('/')) {
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return `/${url}`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const hasMeaningfulIndexedMatch = ({
|
|
122
|
+
query,
|
|
123
|
+
title,
|
|
124
|
+
content,
|
|
125
|
+
excerpt,
|
|
126
|
+
}: {
|
|
127
|
+
query: string;
|
|
128
|
+
title: string;
|
|
129
|
+
content?: string;
|
|
130
|
+
excerpt: string;
|
|
131
|
+
}) => {
|
|
132
|
+
const terms = getSearchTerms(query);
|
|
133
|
+
if (terms.length === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const searchableText = `${title} ${content || ''}`.toLowerCase();
|
|
138
|
+
if (terms.some((term) => searchableText.includes(term))) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const minimumMarkedLength = Math.min(3, Math.max(...terms.map((term) => term.length)));
|
|
143
|
+
return getMarkedTerms(excerpt).some((term) => term.length >= minimumMarkedLength);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const getIndexedResultRank = ({
|
|
147
|
+
query,
|
|
148
|
+
title,
|
|
149
|
+
id,
|
|
150
|
+
url,
|
|
151
|
+
content,
|
|
152
|
+
}: {
|
|
153
|
+
query: string;
|
|
154
|
+
title: string;
|
|
155
|
+
id?: string;
|
|
156
|
+
url: string;
|
|
157
|
+
content?: string;
|
|
158
|
+
}) => {
|
|
159
|
+
const terms = getSearchTerms(query);
|
|
160
|
+
const titleText = title.toLowerCase();
|
|
161
|
+
const identityText = `${id || ''} ${url}`.toLowerCase();
|
|
162
|
+
const contentText = (content || '').toLowerCase();
|
|
163
|
+
|
|
164
|
+
if (terms.some((term) => titleText.includes(term))) return 3;
|
|
165
|
+
if (terms.some((term) => identityText.includes(term))) return 2;
|
|
166
|
+
if (terms.some((term) => contentText.includes(term))) return 1;
|
|
167
|
+
return 0;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const applyActiveFilter = <T extends { type: string }>(items: T[], activeFilter: string) => {
|
|
171
|
+
if (activeFilter === 'all') {
|
|
172
|
+
return items;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (activeFilter === 'Message') {
|
|
176
|
+
return items.filter((item) => ['Event', 'Command', 'Query'].includes(item.type));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (activeFilter === 'Team') {
|
|
180
|
+
return items.filter((item) => ['Team', 'User'].includes(item.type));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return items.filter((item) => item.type === activeFilter);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const getSearchFilters = ({ items, query }: { items: Array<{ type: string }>; query: string }): SearchFilter[] => {
|
|
187
|
+
if (!items.length && query !== '') {
|
|
188
|
+
return [{ id: 'all', name: 'All (0)', count: 0 }];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const counts: Record<string, number> = {
|
|
192
|
+
all: items.length,
|
|
193
|
+
Domain: 0,
|
|
194
|
+
Service: 0,
|
|
195
|
+
Message: 0,
|
|
196
|
+
Team: 0,
|
|
197
|
+
Container: 0,
|
|
198
|
+
Entity: 0,
|
|
199
|
+
Design: 0,
|
|
200
|
+
Channel: 0,
|
|
201
|
+
Flow: 0,
|
|
202
|
+
'Data Product': 0,
|
|
203
|
+
'Custom Doc': 0,
|
|
204
|
+
'Resource Doc': 0,
|
|
205
|
+
Changelog: 0,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
items.forEach((item) => {
|
|
209
|
+
if (counts[item.type] !== undefined) {
|
|
210
|
+
counts[item.type]++;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (['Event', 'Command', 'Query'].includes(item.type)) {
|
|
214
|
+
counts.Message++;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (['Team', 'User'].includes(item.type)) {
|
|
218
|
+
counts.Team++;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const filters: SearchFilter[] = [{ id: 'all', name: `All (${counts.all})`, count: counts.all }];
|
|
223
|
+
const addFilter = (id: string, name: string) => {
|
|
224
|
+
if (counts[id] > 0) {
|
|
225
|
+
filters.push({ id, name: `${name} (${counts[id]})`, count: counts[id] });
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
addFilter('Domain', 'Domains');
|
|
230
|
+
addFilter('Service', 'Services');
|
|
231
|
+
addFilter('Message', 'Messages');
|
|
232
|
+
addFilter('Container', 'Data Stores');
|
|
233
|
+
addFilter('Entity', 'Entities');
|
|
234
|
+
addFilter('Channel', 'Channels');
|
|
235
|
+
addFilter('Flow', 'Flows');
|
|
236
|
+
addFilter('Data Product', 'Data Products');
|
|
237
|
+
addFilter('Custom Doc', 'Custom Docs');
|
|
238
|
+
addFilter('Resource Doc', 'Resource Docs');
|
|
239
|
+
addFilter('Changelog', 'Changelogs');
|
|
240
|
+
addFilter('Design', 'Designs');
|
|
241
|
+
addFilter('Team', 'Teams & Users');
|
|
242
|
+
|
|
243
|
+
return filters;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const mapPagefindResultsToSearchItems = async ({
|
|
247
|
+
results,
|
|
248
|
+
query,
|
|
249
|
+
limit,
|
|
250
|
+
}: {
|
|
251
|
+
results: PagefindResult[];
|
|
252
|
+
query: string;
|
|
253
|
+
limit: number;
|
|
254
|
+
}): Promise<SearchItem[]> => {
|
|
255
|
+
const mappedResults = await Promise.all(
|
|
256
|
+
results.slice(0, limit).map(async (result, resultIndex) => {
|
|
257
|
+
const data = await result.data();
|
|
258
|
+
const meta = data.meta || {};
|
|
259
|
+
const type = meta.type || 'Page';
|
|
260
|
+
const title = meta.title || data.url || 'Untitled';
|
|
261
|
+
const summary = meta.summary || stripHtml(data.excerpt || '');
|
|
262
|
+
const url = normalizeResultUrl(data.url);
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
!hasMeaningfulIndexedMatch({
|
|
266
|
+
query,
|
|
267
|
+
title,
|
|
268
|
+
content: data.content,
|
|
269
|
+
excerpt: data.excerpt || '',
|
|
270
|
+
})
|
|
271
|
+
) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
item: {
|
|
277
|
+
id: result.id,
|
|
278
|
+
name: title,
|
|
279
|
+
url,
|
|
280
|
+
type,
|
|
281
|
+
rawNode: {
|
|
282
|
+
title,
|
|
283
|
+
badge: type,
|
|
284
|
+
summary,
|
|
285
|
+
matchedExcerpt: data.excerpt || summary,
|
|
286
|
+
},
|
|
287
|
+
} satisfies SearchItem,
|
|
288
|
+
resultIndex,
|
|
289
|
+
rank: getIndexedResultRank({
|
|
290
|
+
query,
|
|
291
|
+
title,
|
|
292
|
+
id: meta.id,
|
|
293
|
+
url,
|
|
294
|
+
content: data.content,
|
|
295
|
+
}),
|
|
296
|
+
score: typeof result.score === 'number' ? result.score : 0,
|
|
297
|
+
};
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
return mappedResults
|
|
302
|
+
.filter((result): result is NonNullable<typeof result> => result !== null)
|
|
303
|
+
.sort((a, b) => b.rank - a.rank || b.score - a.score || a.resultIndex - b.resultIndex)
|
|
304
|
+
.map((result) => result.item);
|
|
305
|
+
};
|
|
@@ -20,17 +20,14 @@ const catalogTheme = config.theme || 'default';
|
|
|
20
20
|
<!-- Inline script to prevent flash of wrong theme -->
|
|
21
21
|
<script is:inline define:vars={{ catalogTheme }}>
|
|
22
22
|
(function () {
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
return 'dark';
|
|
26
|
-
}
|
|
27
|
-
return 'light';
|
|
23
|
+
function getDefaultTheme() {
|
|
24
|
+
return 'dark';
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
function applyTheme() {
|
|
31
|
-
// Check localStorage first, fall back to
|
|
28
|
+
// Check localStorage first, fall back to dark mode
|
|
32
29
|
const stored = localStorage.getItem('eventcatalog-theme');
|
|
33
|
-
const theme = stored === 'light' || stored === 'dark' ? stored :
|
|
30
|
+
const theme = stored === 'light' || stored === 'dark' ? stored : getDefaultTheme();
|
|
34
31
|
document.documentElement.setAttribute('data-theme', theme);
|
|
35
32
|
// Also ensure catalog theme is set (for page transitions)
|
|
36
33
|
document.documentElement.setAttribute('data-catalog-theme', catalogTheme);
|
|
@@ -4,14 +4,11 @@ const THEME_KEY = 'eventcatalog-theme';
|
|
|
4
4
|
|
|
5
5
|
export type Theme = 'light' | 'dark';
|
|
6
6
|
|
|
7
|
-
export const themeStore = atom<Theme>('
|
|
7
|
+
export const themeStore = atom<Theme>('dark');
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
13
|
-
}
|
|
14
|
-
return 'light';
|
|
9
|
+
// Default theme when the user has not explicitly chosen one.
|
|
10
|
+
const getDefaultTheme = (): Theme => {
|
|
11
|
+
return 'dark';
|
|
15
12
|
};
|
|
16
13
|
|
|
17
14
|
// Apply theme to document via data-theme attribute
|
|
@@ -31,15 +28,14 @@ const initStore = () => {
|
|
|
31
28
|
themeStore.set(stored);
|
|
32
29
|
applyTheme(stored);
|
|
33
30
|
} else {
|
|
34
|
-
// No stored preference, use
|
|
35
|
-
const
|
|
36
|
-
themeStore.set(
|
|
37
|
-
applyTheme(
|
|
31
|
+
// No stored preference, use the catalog default
|
|
32
|
+
const defaultTheme = getDefaultTheme();
|
|
33
|
+
themeStore.set(defaultTheme);
|
|
34
|
+
applyTheme(defaultTheme);
|
|
38
35
|
}
|
|
39
36
|
} catch (e) {
|
|
40
37
|
console.warn('Failed to load theme:', e);
|
|
41
|
-
|
|
42
|
-
applyTheme(systemTheme);
|
|
38
|
+
applyTheme(getDefaultTheme());
|
|
43
39
|
}
|
|
44
40
|
}
|
|
45
41
|
};
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.36.2",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"mdast-util-find-and-replace": "^3.0.2",
|
|
81
81
|
"mermaid": "^11.12.3",
|
|
82
82
|
"nanostores": "^1.1.0",
|
|
83
|
+
"pagefind": "^1.5.2",
|
|
83
84
|
"pako": "^2.1.0",
|
|
84
85
|
"picocolors": "^1.1.1",
|
|
85
86
|
"react": "^18.3.1",
|
|
@@ -105,8 +106,8 @@
|
|
|
105
106
|
"update-notifier": "^7.3.1",
|
|
106
107
|
"uuid": "^10.0.0",
|
|
107
108
|
"zod": "^4.3.6",
|
|
108
|
-
"@eventcatalog/linter": "1.0.22",
|
|
109
109
|
"@eventcatalog/sdk": "2.21.0",
|
|
110
|
+
"@eventcatalog/linter": "1.0.22",
|
|
110
111
|
"@eventcatalog/visualiser": "^3.20.0"
|
|
111
112
|
},
|
|
112
113
|
"devDependencies": {
|