@eventcatalog/core 2.58.1 → 2.59.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.
Files changed (53) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-X4GKQ2ZE.js → chunk-F5WMB6Q5.js} +1 -1
  6. package/dist/{chunk-N7MEYFUO.js → chunk-SCDNNFXH.js} +1 -1
  7. package/dist/{chunk-F3YDLMMR.js → chunk-T3QXQPTB.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +1 -1
  11. package/dist/eventcatalog.js +3 -3
  12. package/eventcatalog/astro.config.mjs +13 -7
  13. package/eventcatalog/src/components/Grids/DomainGrid.tsx +67 -2
  14. package/eventcatalog/src/components/Grids/MessageGrid.tsx +157 -41
  15. package/eventcatalog/src/components/Grids/ServiceGrid.tsx +78 -14
  16. package/eventcatalog/src/components/MDX/NodeGraph/Edges/MultilineEdgeLabel.tsx +52 -0
  17. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +13 -0
  18. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -1
  19. package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Data.tsx +55 -16
  20. package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +180 -0
  21. package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +41 -0
  22. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +1 -1
  23. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +250 -59
  24. package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +3 -0
  25. package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +35 -1
  26. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
  27. package/eventcatalog/src/components/Tables/Table.tsx +22 -2
  28. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +152 -0
  29. package/eventcatalog/src/components/Tables/columns/index.tsx +3 -0
  30. package/eventcatalog/src/content.config.ts +57 -1
  31. package/eventcatalog/src/layouts/DiscoverLayout.astro +11 -1
  32. package/eventcatalog/src/pages/architecture/architecture.astro +9 -1
  33. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +1 -1
  34. package/eventcatalog/src/pages/discover/[type]/index.astro +11 -1
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +11 -1
  36. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +50 -1
  37. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  38. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -1
  39. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +19 -1
  40. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  41. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -1
  42. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +80 -0
  43. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +52 -0
  44. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +9 -2
  45. package/eventcatalog/src/types/index.ts +20 -2
  46. package/eventcatalog/src/utils/collections/containers.ts +94 -0
  47. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  48. package/eventcatalog/src/utils/collections/services.ts +15 -1
  49. package/eventcatalog/src/utils/collections/util.ts +4 -2
  50. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +155 -0
  51. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +188 -82
  52. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  53. package/package.json +3 -2
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "2.58.1";
40
+ var version = "2.59.0";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-F3YDLMMR.js";
4
- import "../chunk-N7MEYFUO.js";
3
+ } from "../chunk-T3QXQPTB.js";
4
+ import "../chunk-SCDNNFXH.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -106,7 +106,7 @@ var import_axios = __toESM(require("axios"), 1);
106
106
  var import_os = __toESM(require("os"), 1);
107
107
 
108
108
  // package.json
109
- var version = "2.58.1";
109
+ var version = "2.59.0";
110
110
 
111
111
  // src/constants.ts
112
112
  var VERSION = version;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-X4GKQ2ZE.js";
4
- import "../chunk-F3YDLMMR.js";
5
- import "../chunk-N7MEYFUO.js";
3
+ } from "../chunk-F5WMB6Q5.js";
4
+ import "../chunk-T3QXQPTB.js";
5
+ import "../chunk-SCDNNFXH.js";
6
6
  import "../chunk-UPONRQSN.js";
7
7
  export {
8
8
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-F3YDLMMR.js";
3
+ } from "./chunk-T3QXQPTB.js";
4
4
  import {
5
5
  getEventCatalogConfigFile,
6
6
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "2.58.1";
2
+ var version = "2.59.0";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-N7MEYFUO.js";
3
+ } from "./chunk-SCDNNFXH.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "2.58.1";
28
+ var version = "2.59.0";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-N7MEYFUO.js";
3
+ } from "./chunk-SCDNNFXH.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -157,7 +157,7 @@ var import_axios = __toESM(require("axios"), 1);
157
157
  var import_os = __toESM(require("os"), 1);
158
158
 
159
159
  // package.json
160
- var version = "2.58.1";
160
+ var version = "2.59.0";
161
161
 
162
162
  // src/constants.ts
163
163
  var VERSION = version;
@@ -6,8 +6,8 @@ import {
6
6
  } from "./chunk-PLNJC7NZ.js";
7
7
  import {
8
8
  log_build_default
9
- } from "./chunk-X4GKQ2ZE.js";
10
- import "./chunk-F3YDLMMR.js";
9
+ } from "./chunk-F5WMB6Q5.js";
10
+ import "./chunk-T3QXQPTB.js";
11
11
  import {
12
12
  catalogToAstro,
13
13
  checkAndConvertMdToMdx
@@ -15,7 +15,7 @@ import {
15
15
  import "./chunk-55D645EH.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-N7MEYFUO.js";
18
+ } from "./chunk-SCDNNFXH.js";
19
19
  import {
20
20
  getProjectOutDir,
21
21
  isAuthEnabled,
@@ -13,6 +13,8 @@ import remarkComment from 'remark-comment'
13
13
  import rehypeSlug from 'rehype-slug';
14
14
  import rehypeAutolinkHeadings from 'rehype-autolink-headings';
15
15
 
16
+ import rehypeExpressiveCode from 'rehype-expressive-code'
17
+
16
18
  /** @type {import('bin/eventcatalog.config').Config} */
17
19
  import config from './eventcatalog.config';
18
20
  import expressiveCode from 'astro-expressive-code';
@@ -23,6 +25,13 @@ const base = config.base || '/';
23
25
  const host = config.host || false;
24
26
  const compress = config.compress ?? true;
25
27
 
28
+ const expressiveCodeConfig = {
29
+ themes: ['andromeeda'],
30
+ defaultProps: {
31
+ wrap: true,
32
+ },
33
+ };
34
+
26
35
  // https://astro.build/config
27
36
  export default defineConfig({
28
37
  base,
@@ -53,25 +62,22 @@ export default defineConfig({
53
62
  react(),
54
63
  tailwind(),
55
64
  expressiveCode({
56
- themes: ['github-light'],
57
- defaultProps: {
58
- // Enable word wrap by default
59
- wrap: true,
60
- },
65
+ ...expressiveCodeConfig,
61
66
  }),
62
-
63
67
  mdx({
64
68
  // https://docs.astro.build/en/guides/integrations-guide/mdx/#optimize
65
69
  optimize: config.mdxOptimize || false,
66
70
  remarkPlugins: [remarkDirective, remarkDirectives, remarkComment, mermaid, plantuml],
67
71
  rehypePlugins: [
72
+ [rehypeExpressiveCode, {
73
+ ...expressiveCodeConfig,
74
+ }],
68
75
  rehypeSlug,
69
76
  [
70
77
  rehypeAutolinkHeadings,
71
78
  {
72
79
  behavior: 'append',
73
80
  properties: { className: ['anchor-link'] },
74
-
75
81
  },
76
82
  ],
77
83
  ],
@@ -1,6 +1,13 @@
1
1
  import { useState, useMemo, useEffect } from 'react';
2
- import { ServerIcon, EnvelopeIcon, RectangleGroupIcon, Squares2X2Icon, QueueListIcon } from '@heroicons/react/24/outline';
3
- import { buildUrlWithParams } from '@utils/url-builder';
2
+ import {
3
+ ServerIcon,
4
+ EnvelopeIcon,
5
+ RectangleGroupIcon,
6
+ Squares2X2Icon,
7
+ QueueListIcon,
8
+ CircleStackIcon,
9
+ } from '@heroicons/react/24/outline';
10
+ import { buildUrlWithParams, buildUrl } from '@utils/url-builder';
4
11
  import type { CollectionEntry } from 'astro:content';
5
12
  import { type CollectionMessageTypes } from '@types';
6
13
  import { getCollectionStyles } from './utils';
@@ -287,6 +294,64 @@ export default function DomainGrid({ domains, embeded }: DomainGridProps) {
287
294
  </div>
288
295
  </div>
289
296
  </div>
297
+
298
+ {/* Container lists at the bottom */}
299
+ {((service.data.readsFrom && service.data.readsFrom.length > 0) ||
300
+ (service.data.writesTo && service.data.writesTo.length > 0)) && (
301
+ <div className="mt-3 pt-3 border-t border-gray-200 grid grid-cols-2 gap-4">
302
+ {/* Reads From */}
303
+ {service.data.readsFrom && service.data.readsFrom.length > 0 && (
304
+ <div className="space-y-2">
305
+ <div className="flex items-center gap-2">
306
+ <CircleStackIcon className="h-4 w-4 text-orange-500" />
307
+ <h4 className="text-xs font-semibold text-gray-700">Reads from</h4>
308
+ </div>
309
+ <div className="flex flex-wrap gap-1">
310
+ {service.data.readsFrom.slice(0, 3).map((container: any) => (
311
+ <span
312
+ key={container.id}
313
+ className="group inline-flex items-center gap-1 px-2 py-1 bg-orange-100 border border-orange-300 rounded-md text-[11px] font-medium hover:bg-orange-200 transition-colors duration-200"
314
+ >
315
+ <CircleStackIcon className="h-3 w-3 text-orange-600" />
316
+ <span className="text-orange-800">{container.id}</span>
317
+ </span>
318
+ ))}
319
+ {service.data.readsFrom.length > 3 && (
320
+ <span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
321
+ + {service.data.readsFrom.length - 3} more
322
+ </span>
323
+ )}
324
+ </div>
325
+ </div>
326
+ )}
327
+
328
+ {/* Writes To */}
329
+ {service.data.writesTo && service.data.writesTo.length > 0 && (
330
+ <div className="space-y-2">
331
+ <div className="flex items-center gap-2">
332
+ <CircleStackIcon className="h-4 w-4 text-purple-500" />
333
+ <h4 className="text-xs font-semibold text-gray-700">Writes to</h4>
334
+ </div>
335
+ <div className="flex flex-wrap gap-1">
336
+ {service.data.writesTo.slice(0, 3).map((container: any) => (
337
+ <span
338
+ key={container.id}
339
+ className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
340
+ >
341
+ <CircleStackIcon className="h-3 w-3 text-purple-600" />
342
+ <span className="text-purple-800">{container.id}</span>
343
+ </span>
344
+ ))}
345
+ {service.data.writesTo.length > 3 && (
346
+ <span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
347
+ + {service.data.writesTo.length - 3} more
348
+ </span>
349
+ )}
350
+ </div>
351
+ </div>
352
+ )}
353
+ </div>
354
+ )}
290
355
  </div>
291
356
  ))}
292
357
  {domain.data.domains && domain.data.domains.length > 2 && (
@@ -1,5 +1,5 @@
1
1
  import { useState, useMemo, useEffect } from 'react';
2
- import { EnvelopeIcon, ChevronRightIcon, ServerIcon } from '@heroicons/react/24/outline';
2
+ import { EnvelopeIcon, ChevronRightIcon, ServerIcon, CircleStackIcon } from '@heroicons/react/24/outline';
3
3
  import { RectangleGroupIcon } from '@heroicons/react/24/outline';
4
4
  import { buildUrl, buildUrlWithParams } from '@utils/url-builder';
5
5
  import type { CollectionEntry } from 'astro:content';
@@ -9,6 +9,7 @@ import { SearchBar, TypeFilters, Pagination } from './components';
9
9
 
10
10
  interface MessageGridProps {
11
11
  messages: CollectionEntry<CollectionMessageTypes>[];
12
+ containers?: CollectionEntry<'containers'>[];
12
13
  embeded: boolean;
13
14
  }
14
15
 
@@ -18,7 +19,7 @@ interface GroupedMessages {
18
19
  receives?: CollectionEntry<CollectionMessageTypes>[];
19
20
  }
20
21
 
21
- export default function MessageGrid({ messages, embeded }: MessageGridProps) {
22
+ export default function MessageGrid({ messages, embeded, containers }: MessageGridProps) {
22
23
  const [searchQuery, setSearchQuery] = useState('');
23
24
  const [urlParams, setUrlParams] = useState<{
24
25
  serviceId?: string;
@@ -130,6 +131,19 @@ export default function MessageGrid({ messages, embeded }: MessageGridProps) {
130
131
  return { sends, receives };
131
132
  }, [filteredAndSortedMessages, urlParams]);
132
133
 
134
+ // Get the containers that are referenced by the service
135
+ const serviceContainersReferenced = useMemo(() => {
136
+ if (!urlParams?.serviceId || !containers) return { writesTo: [], readsFrom: [] };
137
+ return {
138
+ writesTo: containers.filter((container) =>
139
+ container.data.servicesThatWriteToContainer?.some((service: any) => service.data.id === urlParams.serviceId)
140
+ ),
141
+ readsFrom: containers.filter((container) =>
142
+ container.data.servicesThatReadFromContainer?.some((service: any) => service.data.id === urlParams.serviceId)
143
+ ),
144
+ };
145
+ }, [containers, urlParams]);
146
+
133
147
  const renderTypeFilters = () => {
134
148
  return (
135
149
  <div className="flex flex-col gap-4 sm:flex-row sm:items-center">
@@ -373,64 +387,166 @@ export default function MessageGrid({ messages, embeded }: MessageGridProps) {
373
387
  </div>
374
388
  </div>
375
389
  <div className="grid grid-cols-3 gap-8 relative">
376
- {/* Receives Section */}
377
- <div className="bg-blue-50 bg-opacity-50 border border-blue-300 border-dashed rounded-lg p-4">
378
- <div className="mb-6">
379
- <h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
380
- <ServerIcon className="h-5 w-5 text-blue-500" />
381
- Receives messages ({groupedMessages.receives?.length || 0})
382
- </h2>
390
+ {/* Left Column - Receives Messages & Reads From Containers */}
391
+ <div className="space-y-6">
392
+ {/* Receives Messages Section */}
393
+ <div className="bg-blue-50 bg-opacity-50 border border-blue-300 border-dashed rounded-lg p-4">
394
+ <div className="mb-6">
395
+ <h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
396
+ <ServerIcon className="h-5 w-5 text-blue-500" />
397
+ Receives ({groupedMessages.receives?.length || 0})
398
+ </h2>
399
+ </div>
400
+ {groupedMessages.receives && groupedMessages.receives.length > 0 ? (
401
+ renderMessageGrid(groupedMessages.receives)
402
+ ) : (
403
+ <div className="text-center py-12">
404
+ <p className="text-gray-500 text-sm">No messages</p>
405
+ </div>
406
+ )}
383
407
  </div>
384
- {groupedMessages.receives && groupedMessages.receives.length > 0 ? (
385
- renderMessageGrid(groupedMessages.receives)
386
- ) : (
387
- <div className="text-center py-12">
388
- <p className="text-gray-500">
389
- {selectedTypes.length > 0
390
- ? `Service does not receive ${selectedTypes.join(' or ')}`
391
- : 'Service does not receive any messages'}
392
- </p>
408
+
409
+ {/* Reads From Containers - Only show if containers exist */}
410
+ {serviceContainersReferenced.readsFrom && serviceContainersReferenced.readsFrom.length > 0 && (
411
+ <div className="bg-orange-50 border border-orange-300 border-dashed rounded-lg p-4 relative">
412
+ <div className="mb-6">
413
+ <h2
414
+ className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}
415
+ >
416
+ <CircleStackIcon className="h-5 w-5 text-orange-500" />
417
+ Reads from ({serviceContainersReferenced.readsFrom.length})
418
+ </h2>
419
+ </div>
420
+ <div className="space-y-3">
421
+ {serviceContainersReferenced.readsFrom.map((container: CollectionEntry<'containers'>) => (
422
+ <a
423
+ key={container.data.id}
424
+ href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
425
+ className="group bg-white border border-orange-200 hover:bg-orange-100 rounded-lg p-3 block transition-all duration-200"
426
+ >
427
+ <div className="flex items-center gap-2">
428
+ <CircleStackIcon className="h-4 w-4 text-orange-500" />
429
+ <h3 className="font-semibold text-gray-900 text-sm group-hover:text-orange-700">
430
+ {container.data.name}
431
+ </h3>
432
+ </div>
433
+ {container.data.summary && (
434
+ <p className="text-xs text-gray-600 mt-1 line-clamp-2">{container.data.summary}</p>
435
+ )}
436
+ </a>
437
+ ))}
438
+ </div>
439
+ {/* Arrow from Reads From to Service */}
440
+ <div className="absolute -right-8 top-1/2 -translate-y-1/2 flex items-center justify-center w-16 z-10">
441
+ <div className="absolute left-0 w-4 h-4 border-b-[3px] border-l-[3px] border-orange-200 transform rotate-45 -translate-x-1 translate-y-[-1px] shadow-[-1px_1px_0_1px_rgba(0,0,0,0.1)]"></div>
442
+ <div className="w-full h-[3px] bg-orange-200 shadow-[0_0_0_1px_rgba(0,0,0,0.1)]"></div>
443
+ </div>
393
444
  </div>
394
445
  )}
395
446
  </div>
396
447
 
397
448
  {/* Arrow from Receives to Service */}
398
- <div className="absolute left-[30%] top-1/2 -translate-y-1/2 flex items-center justify-center w-16">
449
+ <div className="absolute left-[30%] top-[25%] -translate-y-1/2 flex items-center justify-center w-16">
399
450
  <div className="w-full h-[3px] bg-blue-200 shadow-[0_0_0_1px_rgba(0,0,0,0.1)]"></div>
400
451
  <div className="absolute right-0 w-4 h-4 border-t-[3px] border-r-[3px] border-blue-200 transform rotate-45 translate-x-1 translate-y-[-1px] shadow-[1px_-1px_0_1px_rgba(0,0,0,0.1)]"></div>
401
452
  </div>
402
453
 
403
- {/* Service Information */}
404
- <div className="bg-white border-2 border-pink-100 rounded-lg p-3 flex items-center justify-center min-h-[80px]">
405
- <div className="flex flex-col items-center gap-2">
406
- <ServerIcon className="h-10 w-10 text-pink-500" />
407
- <p className="text-lg font-medium text-gray-900">{urlParams.serviceName}</p>
454
+ {/* Service Information (Center) */}
455
+ <div className="bg-white border-2 border-pink-100 rounded-lg p-6 flex flex-col justify-center">
456
+ <div className="flex flex-col items-center gap-4">
457
+ <ServerIcon className="h-12 w-12 text-pink-500" />
458
+ <p className="text-xl font-semibold text-gray-900 text-center">{urlParams.serviceName}</p>
459
+
460
+ {/* Quick Stats Grid */}
461
+ <div className="w-full grid grid-cols-2 gap-3 mt-2">
462
+ <div className="text-center p-3 bg-blue-50 rounded-lg border border-blue-200">
463
+ <div className="text-2xl font-bold text-blue-600">{groupedMessages.receives?.length || 0}</div>
464
+ <div className="text-xs text-gray-600 mt-1">Receives</div>
465
+ </div>
466
+ <div className="text-center p-3 bg-green-50 rounded-lg border border-green-200">
467
+ <div className="text-2xl font-bold text-green-600">{groupedMessages.sends?.length || 0}</div>
468
+ <div className="text-xs text-gray-600 mt-1">Sends</div>
469
+ </div>
470
+ {serviceContainersReferenced.readsFrom && serviceContainersReferenced.readsFrom.length > 0 && (
471
+ <div className="text-center p-3 bg-orange-50 rounded-lg border border-orange-200">
472
+ <div className="text-2xl font-bold text-orange-600">
473
+ {serviceContainersReferenced.readsFrom.length}
474
+ </div>
475
+ <div className="text-xs text-gray-600 mt-1">Reads from</div>
476
+ </div>
477
+ )}
478
+ {serviceContainersReferenced.writesTo && serviceContainersReferenced.writesTo.length > 0 && (
479
+ <div className="text-center p-3 bg-purple-50 rounded-lg border border-purple-200">
480
+ <div className="text-2xl font-bold text-purple-600">
481
+ {serviceContainersReferenced.writesTo.length}
482
+ </div>
483
+ <div className="text-xs text-gray-600 mt-1">Writes to</div>
484
+ </div>
485
+ )}
486
+ </div>
408
487
  </div>
409
488
  </div>
410
489
 
411
490
  {/* Arrow from Service to Sends */}
412
- <div className="absolute right-[30%] top-1/2 -translate-y-1/2 flex items-center justify-center w-16">
491
+ <div className="absolute right-[30%] top-[25%] -translate-y-1/2 flex items-center justify-center w-16">
413
492
  <div className="w-full h-[3px] bg-green-200 shadow-[0_0_0_1px_rgba(0,0,0,0.1)]"></div>
414
493
  <div className="absolute right-0 w-4 h-4 border-t-[3px] border-r-[3px] border-green-200 transform rotate-45 translate-x-1 translate-y-[-1px] shadow-[1px_-1px_0_1px_rgba(0,0,0,0.1)]"></div>
415
494
  </div>
416
495
 
417
- {/* Sends Section */}
418
- <div className="bg-green-50 border border-green-300 border-dashed rounded-lg p-4">
419
- <div className="mb-6">
420
- <h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
421
- <ServerIcon className="h-5 w-5 text-emerald-500" />
422
- Sends messages ({groupedMessages.sends?.length || 0})
423
- </h2>
496
+ {/* Right Column - Sends Messages & Writes To Containers */}
497
+ <div className="space-y-6">
498
+ {/* Sends Messages Section */}
499
+ <div className="bg-green-50 border border-green-300 border-dashed rounded-lg p-4">
500
+ <div className="mb-6">
501
+ <h2 className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}>
502
+ <ServerIcon className="h-5 w-5 text-emerald-500" />
503
+ Sends ({groupedMessages.sends?.length || 0})
504
+ </h2>
505
+ </div>
506
+ {groupedMessages.sends && groupedMessages.sends.length > 0 ? (
507
+ renderMessageGrid(groupedMessages.sends)
508
+ ) : (
509
+ <div className="text-center py-12">
510
+ <p className="text-gray-500 text-sm">No messages</p>
511
+ </div>
512
+ )}
424
513
  </div>
425
- {groupedMessages.sends && groupedMessages.sends.length > 0 ? (
426
- renderMessageGrid(groupedMessages.sends)
427
- ) : (
428
- <div className="text-center py-8">
429
- <p className="text-gray-500">
430
- {selectedTypes.length > 0
431
- ? `Service does not send ${selectedTypes.join(' or ')}`
432
- : 'Service does not send any messages'}
433
- </p>
514
+
515
+ {/* Writes To Containers - Only show if containers exist */}
516
+ {serviceContainersReferenced.writesTo && serviceContainersReferenced.writesTo.length > 0 && (
517
+ <div className="bg-purple-50 border border-purple-300 border-dashed rounded-lg p-4 relative">
518
+ {/* Arrow from Service to Writes To */}
519
+ <div className="absolute -left-8 top-1/2 -translate-y-1/2 flex items-center justify-center w-16 z-10">
520
+ <div className="w-full h-[3px] bg-purple-200 shadow-[0_0_0_1px_rgba(0,0,0,0.1)]"></div>
521
+ <div className="absolute right-0 w-4 h-4 border-t-[3px] border-r-[3px] border-purple-200 transform rotate-45 translate-x-1 translate-y-[-1px] shadow-[1px_-1px_0_1px_rgba(0,0,0,0.1)]"></div>
522
+ </div>
523
+ <div className="mb-6">
524
+ <h2
525
+ className={`font-semibold text-gray-900 flex items-center gap-2 ${embeded ? 'text-sm' : 'text-xl'}`}
526
+ >
527
+ <CircleStackIcon className="h-5 w-5 text-purple-500" />
528
+ Writes to ({serviceContainersReferenced.writesTo.length})
529
+ </h2>
530
+ </div>
531
+ <div className="space-y-3">
532
+ {serviceContainersReferenced.writesTo.map((container: CollectionEntry<'containers'>) => (
533
+ <a
534
+ key={container.data.id}
535
+ href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
536
+ className="group bg-white border border-purple-200 hover:bg-purple-100 rounded-lg p-3 block transition-all duration-200"
537
+ >
538
+ <div className="flex items-center gap-2">
539
+ <CircleStackIcon className="h-4 w-4 text-purple-500" />
540
+ <h3 className="font-semibold text-gray-900 text-sm group-hover:text-purple-700">
541
+ {container.data.name}
542
+ </h3>
543
+ </div>
544
+ {container.data.summary && (
545
+ <p className="text-xs text-gray-600 mt-1 line-clamp-2">{container.data.summary}</p>
546
+ )}
547
+ </a>
548
+ ))}
549
+ </div>
434
550
  </div>
435
551
  )}
436
552
  </div>
@@ -1,5 +1,5 @@
1
1
  import { useState, useMemo, useEffect, memo } from 'react';
2
- import { ServerIcon, ChevronRightIcon, Squares2X2Icon, QueueListIcon } from '@heroicons/react/24/outline';
2
+ import { ServerIcon, ChevronRightIcon, Squares2X2Icon, QueueListIcon, CircleStackIcon } from '@heroicons/react/24/outline';
3
3
  import { RectangleGroupIcon } from '@heroicons/react/24/outline';
4
4
  import { buildUrl, buildUrlWithParams } from '@utils/url-builder';
5
5
  import type { CollectionEntry } from 'astro:content';
@@ -110,6 +110,66 @@ const ServiceCard = memo(({ service, urlParams, selectedTypes }: { service: any;
110
110
  <MessagesContainer messages={service.data.sends} type="sends" selectedTypes={selectedTypes} />
111
111
  </div>
112
112
  )}
113
+
114
+ {/* Container lists at the bottom */}
115
+ {((service.data.readsFrom && service.data.readsFrom.length > 0) ||
116
+ (service.data.writesTo && service.data.writesTo.length > 0)) && (
117
+ <div className="mt-4 pt-4 border-t border-gray-200 grid grid-cols-2 gap-4">
118
+ {/* Reads From */}
119
+ {service.data.readsFrom && service.data.readsFrom.length > 0 && (
120
+ <div className="space-y-2">
121
+ <div className="flex items-center gap-2">
122
+ <CircleStackIcon className="h-4 w-4 text-orange-500" />
123
+ <h4 className="text-xs font-semibold text-gray-700">Reads from</h4>
124
+ </div>
125
+ <div className="flex flex-wrap gap-1">
126
+ {service.data.readsFrom.slice(0, 3).map((container: any) => (
127
+ <a
128
+ key={container.id}
129
+ href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
130
+ className="group inline-flex items-center gap-1 px-2 py-1 bg-orange-100 border border-orange-300 rounded-md text-[11px] font-medium hover:bg-orange-200 transition-colors duration-200"
131
+ >
132
+ <CircleStackIcon className="h-3 w-3 text-orange-600" />
133
+ <span className="text-orange-800">{container.data.name}</span>
134
+ </a>
135
+ ))}
136
+ {service.data.readsFrom.length > 3 && (
137
+ <span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
138
+ + {service.data.readsFrom.length - 3} more
139
+ </span>
140
+ )}
141
+ </div>
142
+ </div>
143
+ )}
144
+
145
+ {/* Writes To */}
146
+ {service.data.writesTo && service.data.writesTo.length > 0 && (
147
+ <div className="space-y-2">
148
+ <div className="flex items-center gap-2">
149
+ <CircleStackIcon className="h-4 w-4 text-purple-500" />
150
+ <h4 className="text-xs font-semibold text-gray-700">Writes to</h4>
151
+ </div>
152
+ <div className="flex flex-wrap gap-1">
153
+ {service.data.writesTo.slice(0, 3).map((container: any) => (
154
+ <a
155
+ key={container.id}
156
+ href={buildUrl(`/docs/containers/${container.data.id}/${container.data.version}`)}
157
+ className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
158
+ >
159
+ <CircleStackIcon className="h-3 w-3 text-purple-600" />
160
+ <span className="text-purple-800">{container.data.name}</span>
161
+ </a>
162
+ ))}
163
+ {service.data.writesTo.length > 3 && (
164
+ <span className="inline-flex items-center px-2 py-1 text-xs text-gray-500">
165
+ + {service.data.writesTo.length - 3} more
166
+ </span>
167
+ )}
168
+ </div>
169
+ </div>
170
+ )}
171
+ </div>
172
+ )}
113
173
  </div>
114
174
  </a>
115
175
  );
@@ -184,19 +244,23 @@ const DomainSection = memo(
184
244
 
185
245
  {/* Entities */}
186
246
  {subdomain.data.entities && subdomain.data.entities.length > 0 && (
187
- <div className="grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-1 xl:grid-cols-4 gap-6">
188
- {subdomain.data.entities.map((entity: any) => (
189
- <a
190
- key={entity.id}
191
- href={buildUrl(`/docs/entities/${entity.id}`)}
192
- className="bg-white border-2 border-dashed border-purple-400 rounded-lg p-4 space-y-4 hover:bg-purple-50 transition-colors duration-200"
193
- >
194
- <div className="flex items-center gap-2">
195
- <BoxIcon className="h-5 w-5 text-purple-500" />
196
- <h3 className="text-lg font-semibold text-gray-900">{entity.id} (Entity)</h3>
197
- </div>
198
- </a>
199
- ))}
247
+ <div className="space-y-2">
248
+ <div className="flex items-center gap-2">
249
+ <BoxIcon className="h-4 w-4 text-purple-500" />
250
+ <h4 className="text-xs font-semibold text-gray-700">Entities</h4>
251
+ </div>
252
+ <div className="flex flex-wrap gap-1">
253
+ {subdomain.data.entities.map((entity: any) => (
254
+ <a
255
+ key={entity.id}
256
+ href={buildUrl(`/docs/entities/${entity.id}`)}
257
+ className="group inline-flex items-center gap-1 px-2 py-1 bg-purple-100 border border-purple-300 rounded-md text-[11px] font-medium hover:bg-purple-200 transition-colors duration-200"
258
+ >
259
+ <BoxIcon className="h-3 w-3 text-purple-600" />
260
+ <span className="text-purple-800">{entity.id}</span>
261
+ </a>
262
+ ))}
263
+ </div>
200
264
  </div>
201
265
  )}
202
266