@eventcatalog/core 3.6.0 → 3.6.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/README.md +1 -1
- 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-2DSMO5BZ.js → chunk-BWIAPGUD.js} +1 -1
- package/dist/{chunk-O7ZZX4CS.js → chunk-KOD6UMZD.js} +1 -1
- package/dist/{chunk-YQ2LO4G6.js → chunk-LFXU3MEP.js} +1 -1
- package/dist/{chunk-XTN3M6CM.js → chunk-NI3M6IVT.js} +1 -1
- package/dist/{chunk-O3LNFOFS.js → chunk-S3764NRV.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 +2 -1
- package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +477 -0
- package/eventcatalog/src/components/MDX/components.tsx +2 -0
- package/eventcatalog/src/components/Search/SearchModal.tsx +1 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +12 -2
- package/eventcatalog/src/enterprise/plans/index.astro +125 -98
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +8 -1
- package/eventcatalog/src/pages/docs/custom/feature.astro +45 -39
- package/eventcatalog/src/remark-plugins/resource-ref.ts +51 -0
- package/package.json +5 -3
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Inline resource reference component with hover tooltip.
|
|
4
|
+
*
|
|
5
|
+
* Usage via remark plugin:
|
|
6
|
+
* [[service|OrderService]] -> Links to OrderService with tooltip
|
|
7
|
+
* [[Order]] -> Shorthand, defaults to entity type
|
|
8
|
+
*/
|
|
9
|
+
import { buildUrl } from '@utils/url-builder';
|
|
10
|
+
import {
|
|
11
|
+
getItemsFromCollectionByIdAndSemverOrLatest,
|
|
12
|
+
resourceToCollectionMap,
|
|
13
|
+
getDeprecatedDetails,
|
|
14
|
+
} from '@utils/collections/util';
|
|
15
|
+
import { getCollection } from 'astro:content';
|
|
16
|
+
import { getServiceSpecifications, getSpecUrl, getSpecLabel } from '@components/Grids/specification-utils';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
type:
|
|
20
|
+
| 'entity'
|
|
21
|
+
| 'service'
|
|
22
|
+
| 'event'
|
|
23
|
+
| 'command'
|
|
24
|
+
| 'query'
|
|
25
|
+
| 'domain'
|
|
26
|
+
| 'flow'
|
|
27
|
+
| 'channel'
|
|
28
|
+
| 'diagram'
|
|
29
|
+
| 'container'
|
|
30
|
+
| 'user'
|
|
31
|
+
| 'team';
|
|
32
|
+
version?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const { type = 'entity', version } = Astro.props;
|
|
36
|
+
|
|
37
|
+
// Get resource ID from slot content
|
|
38
|
+
const slotContent = await Astro.slots.render('default');
|
|
39
|
+
const resourceId = slotContent.trim();
|
|
40
|
+
|
|
41
|
+
// Map type to collection name
|
|
42
|
+
const collection = resourceToCollectionMap[type as keyof typeof resourceToCollectionMap];
|
|
43
|
+
|
|
44
|
+
// Type-specific styling using CSS variables from theme.css
|
|
45
|
+
// Maps to --ec-badge-{type}-text variables for consistency
|
|
46
|
+
const typeStyles: Record<string, { borderColor: string; label: string }> = {
|
|
47
|
+
entity: { borderColor: 'var(--ec-badge-query-text)', label: 'Entity' }, // purple
|
|
48
|
+
service: { borderColor: 'var(--ec-badge-service-text)', label: 'Service' }, // pink
|
|
49
|
+
event: { borderColor: 'var(--ec-badge-event-text)', label: 'Event' }, // amber/orange
|
|
50
|
+
command: { borderColor: 'var(--ec-badge-command-text)', label: 'Command' }, // pink
|
|
51
|
+
query: { borderColor: 'var(--ec-badge-query-text)', label: 'Query' }, // purple
|
|
52
|
+
domain: { borderColor: 'var(--ec-badge-domain-text)', label: 'Domain' }, // yellow
|
|
53
|
+
flow: { borderColor: 'var(--ec-badge-design-text)', label: 'Flow' }, // teal
|
|
54
|
+
channel: { borderColor: 'var(--ec-badge-channel-text)', label: 'Channel' }, // indigo
|
|
55
|
+
diagram: { borderColor: 'var(--ec-badge-default-text)', label: 'Diagram' }, // gray
|
|
56
|
+
container: { borderColor: 'var(--ec-badge-default-text)', label: 'Container' }, // gray
|
|
57
|
+
user: { borderColor: 'var(--ec-badge-default-text)', label: 'User' }, // gray
|
|
58
|
+
team: { borderColor: 'var(--ec-badge-default-text)', label: 'Team' }, // gray
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// SVG icons for each type (from heroicons outline)
|
|
62
|
+
const typeIcons: Record<string, string> = {
|
|
63
|
+
entity:
|
|
64
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />', // Box/cube
|
|
65
|
+
service:
|
|
66
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />', // Server
|
|
67
|
+
event:
|
|
68
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" />', // Bolt
|
|
69
|
+
command:
|
|
70
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" />', // ChatBubble
|
|
71
|
+
query:
|
|
72
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />', // MagnifyingGlass
|
|
73
|
+
domain:
|
|
74
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z" />', // RectangleGroup
|
|
75
|
+
flow: '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 010 3.75H5.625a1.875 1.875 0 010-3.75z" />', // QueueList
|
|
76
|
+
channel:
|
|
77
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" />', // ArrowsRightLeft
|
|
78
|
+
diagram:
|
|
79
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M9 6.75V15m6-6v8.25m.503 3.498l4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 00-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0z" />', // Map
|
|
80
|
+
container:
|
|
81
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />', // Database
|
|
82
|
+
user: '<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />', // User
|
|
83
|
+
team: '<path stroke-linecap="round" stroke-linejoin="round" d="M18 18.72a9.094 9.094 0 003.741-.479 3 3 0 00-4.682-2.72m.94 3.198l.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0112 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 016 18.719m12 0a5.971 5.971 0 00-.941-3.197m0 0A5.995 5.995 0 0012 12.75a5.995 5.995 0 00-5.058 2.772m0 0a3 3 0 00-4.681 2.72 8.986 8.986 0 003.74.477m.94-3.197a5.971 5.971 0 00-.94 3.197M15 6.75a3 3 0 11-6 0 3 3 0 016 0zm6 3a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0zm-13.5 0a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z" />', // UserGroup
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const style = typeStyles[type] || typeStyles.entity;
|
|
87
|
+
const iconPath = typeIcons[type] || typeIcons.entity;
|
|
88
|
+
|
|
89
|
+
let resource: any = null;
|
|
90
|
+
let href = '#';
|
|
91
|
+
let hasError = false;
|
|
92
|
+
let errorMessage = '';
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
if (!collection) {
|
|
96
|
+
throw new Error(`Unknown resource type: ${type}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const resourcesCollection = (await getCollection(collection as any)) as { data: { id: string; version: string } }[];
|
|
100
|
+
const resources = getItemsFromCollectionByIdAndSemverOrLatest(resourcesCollection, resourceId, version);
|
|
101
|
+
|
|
102
|
+
if (resources.length === 0) {
|
|
103
|
+
throw new Error(`Resource not found: ${resourceId}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
resource = resources[0];
|
|
107
|
+
// Diagrams use /diagrams/ path, other resources use /docs/{collection}/
|
|
108
|
+
href =
|
|
109
|
+
type === 'diagram'
|
|
110
|
+
? buildUrl(`/diagrams/${resourceId}/${resource.data.version}`)
|
|
111
|
+
: buildUrl(`/docs/${collection}/${resourceId}/${resource.data.version}`);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
hasError = true;
|
|
114
|
+
errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Truncate summary if too long
|
|
118
|
+
const maxSummaryLength = 120;
|
|
119
|
+
const summary = resource?.data?.summary || '';
|
|
120
|
+
const truncatedSummary = summary.length > maxSummaryLength ? summary.slice(0, maxSummaryLength) + '...' : summary;
|
|
121
|
+
|
|
122
|
+
// Only these types have visualizers
|
|
123
|
+
const hasVisualizer = ['domain', 'service', 'event', 'query', 'command', 'container'].includes(type);
|
|
124
|
+
|
|
125
|
+
// Check deprecation status
|
|
126
|
+
const deprecation = resource ? getDeprecatedDetails(resource) : null;
|
|
127
|
+
const isDeprecated = deprecation?.isMarkedAsDeprecated || false;
|
|
128
|
+
|
|
129
|
+
// Get owners (first 2)
|
|
130
|
+
const owners = resource?.data?.owners?.slice(0, 2) || [];
|
|
131
|
+
|
|
132
|
+
// Check if message type has a schema
|
|
133
|
+
const isMessageType = ['event', 'command', 'query'].includes(type);
|
|
134
|
+
const hasSchema = isMessageType && resource?.data?.schemaPath;
|
|
135
|
+
|
|
136
|
+
// Check if resource has a repository URL
|
|
137
|
+
const repositoryUrl = resource?.data?.repository?.url;
|
|
138
|
+
|
|
139
|
+
// For services: get messages (sends/receives) with resolved versions
|
|
140
|
+
const maxMessages = 3;
|
|
141
|
+
const sends = resource?.data?.sends || [];
|
|
142
|
+
const receives = resource?.data?.receives || [];
|
|
143
|
+
const isService = type === 'service';
|
|
144
|
+
|
|
145
|
+
// Helper to resolve message version and collection - use specified version or fetch from collection
|
|
146
|
+
const resolveMessage = async (msg: any): Promise<{ version: string | null; collection: string | null }> => {
|
|
147
|
+
// If version is specified and not "latest", use it (assume event as default collection)
|
|
148
|
+
if (msg.version && msg.version !== 'latest') {
|
|
149
|
+
return { version: msg.version, collection: 'events' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Try to find the message in events, commands, or queries collections
|
|
153
|
+
const collections = ['events', 'commands', 'queries'];
|
|
154
|
+
for (const col of collections) {
|
|
155
|
+
try {
|
|
156
|
+
const items = (await getCollection(col as any)) as { data: { id: string; version: string } }[];
|
|
157
|
+
const found = getItemsFromCollectionByIdAndSemverOrLatest(items, msg.id);
|
|
158
|
+
if (found.length > 0 && found[0].data.version && found[0].data.version !== 'latest') {
|
|
159
|
+
return { version: found[0].data.version, collection: col };
|
|
160
|
+
}
|
|
161
|
+
} catch (e) {
|
|
162
|
+
// Collection might not exist or item not found, continue
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { version: null, collection: null };
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Resolve versions and collections for messages to show
|
|
169
|
+
const sendsWithVersions = await Promise.all(
|
|
170
|
+
sends.slice(0, maxMessages).map(async (msg: any) => {
|
|
171
|
+
const resolved = await resolveMessage(msg);
|
|
172
|
+
return { ...msg, resolvedVersion: resolved.version, resolvedCollection: resolved.collection };
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
const receivesWithVersions = await Promise.all(
|
|
176
|
+
receives.slice(0, maxMessages).map(async (msg: any) => {
|
|
177
|
+
const resolved = await resolveMessage(msg);
|
|
178
|
+
return { ...msg, resolvedVersion: resolved.version, resolvedCollection: resolved.collection };
|
|
179
|
+
})
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// Get specifications for services
|
|
183
|
+
const specifications = isService ? getServiceSpecifications(resource?.data) : [];
|
|
184
|
+
const hasSpecifications = specifications.length > 0;
|
|
185
|
+
|
|
186
|
+
// Generate unique ID for this instance
|
|
187
|
+
const tooltipId = `ref-tooltip-${Math.random().toString(36).slice(2, 9)}`;
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
<span class="resource-ref-wrapper inline">
|
|
191
|
+
{
|
|
192
|
+
hasError ? (
|
|
193
|
+
<span
|
|
194
|
+
class="text-[rgb(var(--ec-page-text-muted))] underline decoration-wavy decoration-red-400/50 underline-offset-2 cursor-help"
|
|
195
|
+
title={`Resource not found: ${resourceId}`}
|
|
196
|
+
>
|
|
197
|
+
{resourceId}
|
|
198
|
+
</span>
|
|
199
|
+
) : (
|
|
200
|
+
<>
|
|
201
|
+
<a
|
|
202
|
+
href={href}
|
|
203
|
+
class:list={[
|
|
204
|
+
'resource-ref-trigger underline decoration-dotted hover:decoration-solid underline-offset-2 font-medium transition-colors',
|
|
205
|
+
isDeprecated
|
|
206
|
+
? 'text-amber-600 decoration-amber-500/50'
|
|
207
|
+
: 'text-[rgb(var(--ec-accent))] hover:text-[rgb(var(--ec-accent-hover))]',
|
|
208
|
+
]}
|
|
209
|
+
data-tooltip-id={tooltipId}
|
|
210
|
+
>
|
|
211
|
+
{resource?.data?.name || resourceId}
|
|
212
|
+
{isDeprecated && ' (deprecated)'}
|
|
213
|
+
</a>
|
|
214
|
+
<span
|
|
215
|
+
id={tooltipId}
|
|
216
|
+
class="resource-ref-tooltip fixed w-80 bg-[rgb(var(--ec-card-bg))] border border-[rgb(var(--ec-page-border))] border-l-[3px] rounded-r-lg rounded-l-none shadow-lg shadow-black/8 z-[9999] text-left overflow-hidden opacity-0 pointer-events-none transition-all duration-150 scale-95 origin-top-left"
|
|
217
|
+
style={`border-left-color: rgb(${style.borderColor});`}
|
|
218
|
+
>
|
|
219
|
+
{/* Deprecation banner */}
|
|
220
|
+
{isDeprecated && (
|
|
221
|
+
<span class="flex items-center gap-1.5 px-3 py-1.5 bg-[rgb(var(--ec-badge-event-bg))] text-[rgb(var(--ec-badge-event-text))] text-xs font-medium">
|
|
222
|
+
<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
223
|
+
<path
|
|
224
|
+
stroke-linecap="round"
|
|
225
|
+
stroke-linejoin="round"
|
|
226
|
+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
227
|
+
/>
|
|
228
|
+
</svg>
|
|
229
|
+
Deprecated
|
|
230
|
+
</span>
|
|
231
|
+
)}
|
|
232
|
+
|
|
233
|
+
<span class="block p-3">
|
|
234
|
+
{/* Header: Name with icon top-right */}
|
|
235
|
+
<span class="flex justify-between items-start mb-2">
|
|
236
|
+
<span class="flex flex-col">
|
|
237
|
+
<span class="flex items-baseline gap-2">
|
|
238
|
+
<span class="font-semibold text-[rgb(var(--ec-page-text))] text-[0.95rem] leading-tight">
|
|
239
|
+
{resource?.data?.name || resourceId}
|
|
240
|
+
</span>
|
|
241
|
+
<span class="text-[0.65rem] text-[rgb(var(--ec-page-text-muted))] uppercase tracking-wide">{style.label}</span>
|
|
242
|
+
</span>
|
|
243
|
+
<span class="text-[0.7rem] font-mono text-[rgb(var(--ec-page-text-muted))] mt-0.5">
|
|
244
|
+
v{resource?.data?.version}
|
|
245
|
+
</span>
|
|
246
|
+
</span>
|
|
247
|
+
<svg
|
|
248
|
+
class="w-5 h-5 flex-shrink-0"
|
|
249
|
+
style={`color: rgb(${style.borderColor});`}
|
|
250
|
+
fill="none"
|
|
251
|
+
viewBox="0 0 24 24"
|
|
252
|
+
stroke="currentColor"
|
|
253
|
+
stroke-width="1.5"
|
|
254
|
+
set:html={iconPath}
|
|
255
|
+
/>
|
|
256
|
+
</span>
|
|
257
|
+
|
|
258
|
+
{/* Summary */}
|
|
259
|
+
{truncatedSummary && (
|
|
260
|
+
<span class="block text-[rgb(var(--ec-page-text-muted))] text-[0.8rem] leading-relaxed mb-3">
|
|
261
|
+
{truncatedSummary}
|
|
262
|
+
</span>
|
|
263
|
+
)}
|
|
264
|
+
|
|
265
|
+
{/* Metadata */}
|
|
266
|
+
{(isService && (sends.length > 0 || receives.length > 0)) || hasSpecifications || owners.length > 0 ? (
|
|
267
|
+
<span class="block text-[0.7rem] mb-3 space-y-1">
|
|
268
|
+
{isService && sends.length > 0 && (
|
|
269
|
+
<span class="flex items-baseline gap-2">
|
|
270
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">Publishes</span>
|
|
271
|
+
<span class="font-mono text-[rgb(var(--ec-page-text))]">
|
|
272
|
+
{sendsWithVersions.slice(0, 2).map((msg: any, idx: number) => (
|
|
273
|
+
<>
|
|
274
|
+
{msg.resolvedVersion && msg.resolvedCollection ? (
|
|
275
|
+
<a
|
|
276
|
+
href={buildUrl(`/docs/${msg.resolvedCollection}/${msg.id}/${msg.resolvedVersion}`)}
|
|
277
|
+
class="hover:underline hover:text-[rgb(var(--ec-accent))]"
|
|
278
|
+
>
|
|
279
|
+
{msg.id}
|
|
280
|
+
</a>
|
|
281
|
+
) : (
|
|
282
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">{msg.id}</span>
|
|
283
|
+
)}
|
|
284
|
+
{idx < Math.min(sendsWithVersions.length, 2) - 1 && ', '}
|
|
285
|
+
</>
|
|
286
|
+
))}
|
|
287
|
+
{sends.length > 2 && <span class="text-[rgb(var(--ec-page-text-muted))]"> +{sends.length - 2}</span>}
|
|
288
|
+
</span>
|
|
289
|
+
</span>
|
|
290
|
+
)}
|
|
291
|
+
{isService && receives.length > 0 && (
|
|
292
|
+
<span class="flex items-baseline gap-2">
|
|
293
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">Subscribes</span>
|
|
294
|
+
<span class="font-mono text-[rgb(var(--ec-page-text))]">
|
|
295
|
+
{receivesWithVersions.slice(0, 2).map((msg: any, idx: number) => (
|
|
296
|
+
<>
|
|
297
|
+
{msg.resolvedVersion && msg.resolvedCollection ? (
|
|
298
|
+
<a
|
|
299
|
+
href={buildUrl(`/docs/${msg.resolvedCollection}/${msg.id}/${msg.resolvedVersion}`)}
|
|
300
|
+
class="hover:underline hover:text-[rgb(var(--ec-accent))]"
|
|
301
|
+
>
|
|
302
|
+
{msg.id}
|
|
303
|
+
</a>
|
|
304
|
+
) : (
|
|
305
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">{msg.id}</span>
|
|
306
|
+
)}
|
|
307
|
+
{idx < Math.min(receivesWithVersions.length, 2) - 1 && ', '}
|
|
308
|
+
</>
|
|
309
|
+
))}
|
|
310
|
+
{receives.length > 2 && <span class="text-[rgb(var(--ec-page-text-muted))]"> +{receives.length - 2}</span>}
|
|
311
|
+
</span>
|
|
312
|
+
</span>
|
|
313
|
+
)}
|
|
314
|
+
{hasSpecifications && (
|
|
315
|
+
<span class="flex items-baseline gap-2">
|
|
316
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">APIs</span>
|
|
317
|
+
<span class="font-mono text-[rgb(var(--ec-page-text))]">
|
|
318
|
+
{specifications.map((spec: any, idx: number) => (
|
|
319
|
+
<>
|
|
320
|
+
<a
|
|
321
|
+
href={getSpecUrl(spec, resourceId, resource?.data?.version)}
|
|
322
|
+
class="hover:underline hover:text-[rgb(var(--ec-accent))]"
|
|
323
|
+
>
|
|
324
|
+
{getSpecLabel(spec.type)}
|
|
325
|
+
</a>
|
|
326
|
+
{idx < specifications.length - 1 && ', '}
|
|
327
|
+
</>
|
|
328
|
+
))}
|
|
329
|
+
</span>
|
|
330
|
+
</span>
|
|
331
|
+
)}
|
|
332
|
+
{owners.length > 0 && (
|
|
333
|
+
<span class="flex items-baseline gap-2">
|
|
334
|
+
<span class="text-[rgb(var(--ec-page-text-muted))]">Owner</span>
|
|
335
|
+
<span class="font-mono text-[rgb(var(--ec-page-text))]">
|
|
336
|
+
{owners.map((o: any, idx: number) => {
|
|
337
|
+
const ownerId = typeof o === 'string' ? o : o.id;
|
|
338
|
+
return (
|
|
339
|
+
<>
|
|
340
|
+
<a
|
|
341
|
+
href={buildUrl(`/docs/users/${ownerId}`)}
|
|
342
|
+
class="hover:underline hover:text-[rgb(var(--ec-accent))]"
|
|
343
|
+
>
|
|
344
|
+
{ownerId}
|
|
345
|
+
</a>
|
|
346
|
+
{idx < owners.length - 1 && ', '}
|
|
347
|
+
</>
|
|
348
|
+
);
|
|
349
|
+
})}
|
|
350
|
+
</span>
|
|
351
|
+
</span>
|
|
352
|
+
)}
|
|
353
|
+
</span>
|
|
354
|
+
) : null}
|
|
355
|
+
|
|
356
|
+
{/* Actions */}
|
|
357
|
+
<span class="flex items-center justify-between pt-2 border-t border-[rgb(var(--ec-page-border))] text-[0.7rem]">
|
|
358
|
+
<span class="flex items-center gap-3">
|
|
359
|
+
{type === 'flow' && (
|
|
360
|
+
<a
|
|
361
|
+
href={buildUrl(`/visualiser/${collection}/${resourceId}/${resource?.data?.version}`)}
|
|
362
|
+
class="text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
|
|
363
|
+
>
|
|
364
|
+
View flow
|
|
365
|
+
</a>
|
|
366
|
+
)}
|
|
367
|
+
{hasSchema && (
|
|
368
|
+
<a
|
|
369
|
+
href={buildUrl(`/schemas/${collection}/${resourceId}/${resource?.data?.version}`)}
|
|
370
|
+
class="text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
|
|
371
|
+
>
|
|
372
|
+
Schema
|
|
373
|
+
</a>
|
|
374
|
+
)}
|
|
375
|
+
{hasVisualizer && type !== 'flow' && (
|
|
376
|
+
<a
|
|
377
|
+
href={buildUrl(`/visualiser/${collection}/${resourceId}/${resource?.data?.version}`)}
|
|
378
|
+
class="text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
|
|
379
|
+
>
|
|
380
|
+
Map
|
|
381
|
+
</a>
|
|
382
|
+
)}
|
|
383
|
+
{repositoryUrl && (
|
|
384
|
+
<a
|
|
385
|
+
href={repositoryUrl}
|
|
386
|
+
target="_blank"
|
|
387
|
+
rel="noopener noreferrer"
|
|
388
|
+
class="text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
|
|
389
|
+
>
|
|
390
|
+
Repo
|
|
391
|
+
</a>
|
|
392
|
+
)}
|
|
393
|
+
</span>
|
|
394
|
+
<a href={href} class="text-[rgb(var(--ec-accent))] hover:underline font-medium">
|
|
395
|
+
{type === 'diagram' ? 'View diagram' : 'View docs'}
|
|
396
|
+
</a>
|
|
397
|
+
</span>
|
|
398
|
+
</span>
|
|
399
|
+
</span>
|
|
400
|
+
</>
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
</span>
|
|
404
|
+
|
|
405
|
+
<script>
|
|
406
|
+
// Initialize all resource ref tooltips
|
|
407
|
+
function initResourceRefTooltips() {
|
|
408
|
+
const triggers = document.querySelectorAll('.resource-ref-trigger');
|
|
409
|
+
|
|
410
|
+
triggers.forEach((trigger) => {
|
|
411
|
+
const tooltipId = trigger.getAttribute('data-tooltip-id');
|
|
412
|
+
if (!tooltipId) return;
|
|
413
|
+
|
|
414
|
+
const tooltip = document.getElementById(tooltipId);
|
|
415
|
+
if (!tooltip) return;
|
|
416
|
+
|
|
417
|
+
let hideTimeout: ReturnType<typeof setTimeout>;
|
|
418
|
+
|
|
419
|
+
const showTooltip = () => {
|
|
420
|
+
clearTimeout(hideTimeout);
|
|
421
|
+
|
|
422
|
+
// Get trigger position
|
|
423
|
+
const rect = trigger.getBoundingClientRect();
|
|
424
|
+
const tooltipWidth = 320; // w-80 = 20rem = 320px
|
|
425
|
+
const padding = 16;
|
|
426
|
+
|
|
427
|
+
// Calculate left position - try to align with trigger, but keep within viewport
|
|
428
|
+
let left = rect.left;
|
|
429
|
+
|
|
430
|
+
// If tooltip would overflow right edge, align to right edge of viewport
|
|
431
|
+
if (left + tooltipWidth > window.innerWidth - padding) {
|
|
432
|
+
left = window.innerWidth - tooltipWidth - padding;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// If tooltip would overflow left edge, align to left edge
|
|
436
|
+
if (left < padding) {
|
|
437
|
+
left = padding;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Position below the trigger
|
|
441
|
+
const top = rect.bottom + 8;
|
|
442
|
+
|
|
443
|
+
tooltip.style.left = `${left}px`;
|
|
444
|
+
tooltip.style.top = `${top}px`;
|
|
445
|
+
tooltip.classList.remove('opacity-0', 'pointer-events-none', 'scale-95');
|
|
446
|
+
tooltip.classList.add('opacity-100', 'pointer-events-auto', 'scale-100');
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const hideTooltip = () => {
|
|
450
|
+
hideTimeout = setTimeout(() => {
|
|
451
|
+
tooltip.classList.remove('opacity-100', 'pointer-events-auto', 'scale-100');
|
|
452
|
+
tooltip.classList.add('opacity-0', 'pointer-events-none', 'scale-95');
|
|
453
|
+
}, 100);
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const keepTooltipOpen = () => {
|
|
457
|
+
clearTimeout(hideTimeout);
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Trigger events
|
|
461
|
+
trigger.addEventListener('mouseenter', showTooltip);
|
|
462
|
+
trigger.addEventListener('mouseleave', hideTooltip);
|
|
463
|
+
trigger.addEventListener('focus', showTooltip);
|
|
464
|
+
trigger.addEventListener('blur', hideTooltip);
|
|
465
|
+
|
|
466
|
+
// Tooltip events - keep open when hovering tooltip
|
|
467
|
+
tooltip.addEventListener('mouseenter', keepTooltipOpen);
|
|
468
|
+
tooltip.addEventListener('mouseleave', hideTooltip);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Run on initial load
|
|
473
|
+
initResourceRefTooltips();
|
|
474
|
+
|
|
475
|
+
// Re-run after Astro page transitions
|
|
476
|
+
document.addEventListener('astro:page-load', initResourceRefTooltips);
|
|
477
|
+
</script>
|
|
@@ -20,6 +20,7 @@ import EntityPropertiesTable from '@components/MDX/EntityPropertiesTable/EntityP
|
|
|
20
20
|
import Tabs from '@components/MDX/Tabs/Tabs.astro';
|
|
21
21
|
import TabItem from '@components/MDX/Tabs/TabItem.astro';
|
|
22
22
|
import ResourceLink from '@components/MDX/ResourceLink/ResourceLink.astro';
|
|
23
|
+
import ResourceRef from '@components/MDX/ResourceRef/ResourceRef.astro';
|
|
23
24
|
import Link from '@components/MDX/Link/Link.astro';
|
|
24
25
|
import Miro from '@components/MDX/Miro/Miro.astro';
|
|
25
26
|
import Lucid from '@components/MDX/Lucid/Lucid.astro';
|
|
@@ -54,6 +55,7 @@ const components = (props: any) => {
|
|
|
54
55
|
OpenAPI,
|
|
55
56
|
ResourceGroupTable: (mdxProp: any) => jsx(ResourceGroupTable, { ...props, ...mdxProp }),
|
|
56
57
|
ResourceLink: (mdxProp: any) => jsx(ResourceLink, { ...props, ...mdxProp }),
|
|
58
|
+
ResourceRef: (mdxProp: any) => jsx(ResourceRef, { ...props, ...mdxProp }),
|
|
57
59
|
Schema: (mdxProp: any) => jsx(Schema, { ...props, ...mdxProp }),
|
|
58
60
|
SchemaViewer: (mdxProp: any) => SchemaViewerPortal({ ...props.data, ...mdxProp }),
|
|
59
61
|
Step,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { StarIcon as StarIconSolid, CircleStackIcon } from '@heroicons/react/24/solid';
|
|
26
26
|
import { useStore } from '@nanostores/react';
|
|
27
27
|
import { sidebarStore } from '../../stores/sidebar-store';
|
|
28
|
+
import type { NavNode } from '../../stores/sidebar-store/state';
|
|
28
29
|
import { favoritesStore, toggleFavorite as toggleFavoriteAction } from '../../stores/favorites-store';
|
|
29
30
|
import { buildUrl } from '@utils/url-builder';
|
|
30
31
|
|
|
@@ -8,7 +8,12 @@ import SearchBar from './SearchBar';
|
|
|
8
8
|
import { saveState, loadState, saveCollapsedSections, loadCollapsedSections } from './storage';
|
|
9
9
|
import { useStore } from '@nanostores/react';
|
|
10
10
|
import { sidebarStore } from '@stores/sidebar-store';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
favoritesStore,
|
|
13
|
+
toggleFavorite as toggleFavoriteAction,
|
|
14
|
+
removeFavorite as removeFavoriteAction,
|
|
15
|
+
type FavoriteItem,
|
|
16
|
+
} from '@stores/favorites-store';
|
|
12
17
|
import { getBadgeClasses } from './utils';
|
|
13
18
|
|
|
14
19
|
const cn = (...classes: (string | false | undefined)[]) => classes.filter(Boolean).join(' ');
|
|
@@ -1098,7 +1103,12 @@ export default function NestedSideBar() {
|
|
|
1098
1103
|
<div
|
|
1099
1104
|
onClick={(e) => {
|
|
1100
1105
|
e.stopPropagation();
|
|
1101
|
-
if (node)
|
|
1106
|
+
if (node) {
|
|
1107
|
+
toggleFavorite(fav.nodeKey, node);
|
|
1108
|
+
} else {
|
|
1109
|
+
// Node no longer exists, remove directly using nodeKey
|
|
1110
|
+
removeFavoriteAction(fav.nodeKey);
|
|
1111
|
+
}
|
|
1102
1112
|
}}
|
|
1103
1113
|
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors cursor-pointer"
|
|
1104
1114
|
>
|