@eventcatalog/core 3.29.1 → 3.30.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 (82) 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-H5UC2A5F.js → chunk-6UG4JMUV.js} +1 -1
  6. package/dist/{chunk-4MSAPCV3.js → chunk-ATRBVTJ6.js} +1 -1
  7. package/dist/{chunk-PLNJC7NZ.js → chunk-K3ZVEX2Y.js} +13 -2
  8. package/dist/{chunk-V4OTI3PF.js → chunk-MVZKHUX2.js} +1 -1
  9. package/dist/{chunk-FVKDNLZK.js → chunk-RRBDF4MM.js} +1 -1
  10. package/dist/{chunk-24NGK43A.js → chunk-Z26P4PCB.js} +1 -1
  11. package/dist/constants.cjs +1 -1
  12. package/dist/constants.js +1 -1
  13. package/dist/eventcatalog.cjs +14 -3
  14. package/dist/eventcatalog.js +6 -6
  15. package/dist/generate.cjs +1 -1
  16. package/dist/generate.js +3 -3
  17. package/dist/utils/cli-logger.cjs +1 -1
  18. package/dist/utils/cli-logger.js +2 -2
  19. package/dist/watcher.cjs +13 -2
  20. package/dist/watcher.js +1 -1
  21. package/eventcatalog/astro.config.mjs +1 -1
  22. package/eventcatalog/public/logo.png +0 -0
  23. package/eventcatalog/src/components/CopyAsMarkdown.tsx +2 -2
  24. package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
  25. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
  26. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
  27. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
  28. package/eventcatalog/src/components/Header.astro +9 -19
  29. package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
  30. package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
  31. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
  32. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
  33. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
  34. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
  35. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
  36. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
  37. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
  38. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
  39. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
  40. package/eventcatalog/src/components/Search/Search.astro +1 -1
  41. package/eventcatalog/src/components/Seo.astro +1 -1
  42. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
  43. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +229 -256
  44. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +78 -59
  45. package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
  46. package/eventcatalog/src/components/Tables/Table.tsx +21 -18
  47. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
  48. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
  49. package/eventcatalog/src/enterprise/auth/error.astro +1 -1
  50. package/eventcatalog/src/enterprise/auth/login.astro +1 -1
  51. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +95 -93
  52. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +174 -136
  53. package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
  54. package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
  55. package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
  56. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +528 -146
  57. package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
  58. package/eventcatalog/src/pages/_index.astro +5 -3
  59. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
  60. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
  61. package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
  62. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +129 -29
  63. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +129 -29
  64. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
  65. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
  66. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +21 -18
  67. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +33 -32
  68. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
  69. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +2 -2
  70. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +4 -6
  71. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
  72. package/eventcatalog/src/pages/docs/users/[id]/index.astro +11 -4
  73. package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
  74. package/eventcatalog/src/pages/studio.astro +1 -1
  75. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
  76. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
  77. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
  78. package/eventcatalog/src/styles/theme.css +68 -12
  79. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
  80. package/package.json +1 -1
  81. package/eventcatalog/public/logo.svg +0 -14
  82. package/eventcatalog/src/enterprise/plans/index.astro +0 -319
@@ -2,13 +2,21 @@
2
2
 
3
3
  ---
4
4
 
5
- <div class="accordian-group border border-gray-200 rounded-md shadow-xs accordion my-2">
5
+ <div
6
+ class="accordion-group border border-[rgb(var(--ec-page-border))] rounded-lg overflow-hidden bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] my-3"
7
+ >
6
8
  <slot />
7
9
  </div>
8
10
 
9
11
  <style is:global>
10
- .accordian-group .accordion {
12
+ .accordion-group .accordion {
11
13
  border: none;
12
- border-bottom: 1px solid #e2e8f0;
14
+ border-radius: 0;
15
+ box-shadow: none;
16
+ margin: 0;
17
+ background: transparent;
18
+ }
19
+ .accordion-group .accordion + .accordion {
20
+ border-top: 1px solid rgb(var(--ec-page-border));
13
21
  }
14
22
  </style>
@@ -248,15 +248,25 @@ const tooltipId = `ref-tooltip-${Math.random().toString(36).slice(2, 9)}`;
248
248
  <a
249
249
  href={href}
250
250
  class:list={[
251
- 'resource-ref-trigger underline decoration-dotted hover:decoration-solid underline-offset-2 font-medium transition-colors',
251
+ 'resource-ref-trigger inline-flex items-center gap-1 px-1 -mx-1 rounded font-medium underline decoration-dotted decoration-2 underline-offset-[3px] transition-colors',
252
252
  isDeprecated
253
- ? 'text-amber-600 decoration-amber-500/50'
254
- : 'text-[rgb(var(--ec-accent))] hover:text-[rgb(var(--ec-accent-hover))]',
253
+ ? 'text-amber-700 dark:text-amber-400 decoration-amber-500/60'
254
+ : 'text-[rgb(var(--ec-accent-text))] decoration-[rgb(var(--ec-primary))] hover:bg-[rgb(var(--ec-accent-subtle))]',
255
255
  ]}
256
256
  data-tooltip-id={tooltipId}
257
257
  >
258
- {resource?.data?.name || resourceId}
259
- {isDeprecated && ' (deprecated)'}
258
+ <svg
259
+ class="w-3.5 h-3.5 flex-shrink-0 text-[rgb(var(--ec-primary))]"
260
+ fill="none"
261
+ viewBox="0 0 24 24"
262
+ stroke="currentColor"
263
+ stroke-width="1.75"
264
+ set:html={iconPath}
265
+ />
266
+ <span>
267
+ {resource?.data?.name || resourceId}
268
+ {isDeprecated && ' (deprecated)'}
269
+ </span>
260
270
  </a>
261
271
  <span
262
272
  id={tooltipId}
@@ -1,5 +1,10 @@
1
- import { ClipboardDocumentIcon, CheckIcon } from '@heroicons/react/24/outline';
1
+ import { ClipboardDocumentIcon, CheckIcon, GlobeAltIcon, DocumentTextIcon } from '@heroicons/react/24/outline';
2
2
  import { LockClosedIcon } from '@heroicons/react/24/solid';
3
+ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4
+ import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5
+ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
6
+ import { getLanguageForHighlight, getSchemaTypeLabel } from './utils';
7
+ import { useDarkMode } from './useDarkMode';
3
8
  import type { SchemaItem } from './types';
4
9
 
5
10
  interface ApiContentViewerProps {
@@ -10,7 +15,14 @@ interface ApiContentViewerProps {
10
15
  }
11
16
 
12
17
  export default function ApiContentViewer({ message, onCopy, copiedId, apiAccessEnabled = false }: ApiContentViewerProps) {
13
- // Generate API path based on collection type
18
+ const isDarkMode = useDarkMode();
19
+ const dividerColor = isDarkMode ? 'rgb(var(--ec-page-text-muted) / 0.13)' : 'rgb(var(--ec-page-border))';
20
+ const boxBorderColor = isDarkMode ? 'rgb(var(--ec-page-text-muted) / 0.17)' : 'rgb(var(--ec-page-border))';
21
+ const insetDividerColor = isDarkMode ? 'rgb(var(--ec-page-text-muted) / 0.11)' : 'rgb(var(--ec-page-border) / 0.8)';
22
+ const headingClass = 'text-sm font-semibold text-[rgb(var(--ec-page-text))]';
23
+ const labelClass = 'text-sm text-[rgb(var(--ec-page-text-muted))]';
24
+ const valueClass = 'text-sm text-[rgb(var(--ec-page-text-muted))]';
25
+
14
26
  let apiPath = '';
15
27
  if (message.collection === 'services') {
16
28
  const specType = message.specType || 'openapi';
@@ -20,17 +32,20 @@ export default function ApiContentViewer({ message, onCopy, copiedId, apiAccessE
20
32
  }
21
33
 
22
34
  const fullUrl = typeof window !== 'undefined' ? `${window.location.origin}${apiPath}` : apiPath;
23
- const curlCommand = `curl ${fullUrl}`;
35
+ const exampleResponse = message.schemaContent || '{}';
36
+ const schemaTypeLabel = getSchemaTypeLabel(message.schemaExtension);
37
+ const lineCount = exampleResponse.split('\n').length;
38
+ const byteSize = new TextEncoder().encode(exampleResponse).length;
24
39
 
25
40
  if (!apiAccessEnabled) {
26
41
  return (
27
- <div className="h-full flex items-center justify-center p-8">
42
+ <div className="flex h-full items-center justify-center p-8">
28
43
  <div className="max-w-md text-center">
29
- <div className="flex items-center justify-center w-16 h-16 mx-auto mb-4 rounded-2xl bg-[rgb(var(--ec-accent-subtle))] border border-[rgb(var(--ec-accent)/0.3)]">
44
+ <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl border border-[rgb(var(--ec-accent)/0.3)] bg-[rgb(var(--ec-accent-subtle))]">
30
45
  <LockClosedIcon className="h-8 w-8 text-[rgb(var(--ec-accent))]" />
31
46
  </div>
32
- <h3 className="text-lg font-semibold text-[rgb(var(--ec-page-text))] mb-2">API Access</h3>
33
- <p className="text-sm text-[rgb(var(--ec-page-text-muted))] mb-6">
47
+ <h3 className="mb-2 text-lg font-semibold text-[rgb(var(--ec-page-text))]">API Access</h3>
48
+ <p className="mb-6 text-sm text-[rgb(var(--ec-page-text-muted))]">
34
49
  Access your schemas programmatically via REST API. Perfect for CI/CD pipelines, automation, and integrations with your
35
50
  development workflow.
36
51
  </p>
@@ -38,94 +53,190 @@ export default function ApiContentViewer({ message, onCopy, copiedId, apiAccessE
38
53
  href="https://eventcatalog.cloud"
39
54
  target="_blank"
40
55
  rel="noopener noreferrer"
41
- className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-[rgb(var(--ec-accent))] rounded-lg hover:bg-[rgb(var(--ec-accent-hover))] transition-colors"
56
+ className="inline-flex items-center gap-2 rounded-lg bg-[rgb(var(--ec-accent))] px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-[rgb(var(--ec-accent-hover))]"
42
57
  >
43
58
  Upgrade to Scale
44
59
  <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
45
60
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
46
61
  </svg>
47
62
  </a>
48
- <p className="text-xs text-[rgb(var(--ec-page-text-muted))] mt-3">Start your 14-day free trial</p>
63
+ <p className="mt-3 text-xs text-[rgb(var(--ec-page-text-muted))]">Start your 14-day free trial</p>
49
64
  </div>
50
65
  </div>
51
66
  );
52
67
  }
53
68
 
54
69
  return (
55
- <div className="h-full overflow-auto">
56
- <div className="space-y-4">
57
- {/* Endpoint */}
58
- <div className="rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-code-bg))]">
59
- <div className="flex items-center justify-between px-4 py-2.5 border-b border-[rgb(var(--ec-page-border))]">
60
- <div className="flex items-center gap-2">
61
- <span className="px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-emerald-400 bg-emerald-400/10 rounded">
62
- GET
63
- </span>
64
- <span className="text-xs font-medium text-[rgb(var(--ec-page-text-muted))]">Endpoint</span>
70
+ <div className="h-full overflow-auto pr-1">
71
+ <div className="space-y-4 pb-3">
72
+ <section className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg)/0.66)]">
73
+ <div className="flex items-start justify-between gap-4 px-4 py-4">
74
+ <div className="min-w-0">
75
+ <div className="flex items-center gap-2">
76
+ <div className="flex h-6 w-6 items-center justify-center rounded-md bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent))]">
77
+ <GlobeAltIcon className="h-3.5 w-3.5" />
78
+ </div>
79
+ <h3 className={headingClass}>HTTP Endpoint</h3>
80
+ </div>
81
+ <p className={`mt-2 ${labelClass}`}>
82
+ Use this endpoint to interact with the{' '}
83
+ <span className="text-[rgb(var(--ec-page-text))]">{message.data.name}</span>.
84
+ </p>
65
85
  </div>
66
86
  <CopyButton
67
- label="Copy URL"
87
+ label="Copy"
68
88
  isCopied={copiedId === `${message.data.id}-url`}
69
89
  onClick={() => onCopy(fullUrl, `${message.data.id}-url`)}
90
+ borderColor={boxBorderColor}
91
+ variant="ghost"
70
92
  />
71
93
  </div>
72
- <div className="px-4 py-3">
73
- <code className="text-[13px] text-[rgb(var(--ec-page-text))] font-mono break-all leading-relaxed">{apiPath}</code>
94
+
95
+ <div className="px-4 pb-4">
96
+ <div
97
+ className="flex items-center gap-2 rounded-lg border bg-[rgb(var(--ec-content-hover)/0.45)] p-2"
98
+ style={{ borderColor: boxBorderColor }}
99
+ >
100
+ <span className="rounded-md bg-emerald-500/15 px-3 py-2 text-xs font-bold uppercase tracking-wide text-emerald-400">
101
+ GET
102
+ </span>
103
+ <code className="min-w-0 flex-1 truncate text-[13px] text-[rgb(var(--ec-page-text-muted))]">{apiPath}</code>
104
+ </div>
105
+
106
+ <div className="mt-4 pt-4">
107
+ <div className="mb-2 flex items-center gap-3">
108
+ <span className={labelClass}>Full URL</span>
109
+ <div className="h-px flex-1" style={{ backgroundColor: insetDividerColor }} />
110
+ </div>
111
+ <div className="flex items-center gap-4">
112
+ <code className="min-w-0 flex-1 break-all text-[13px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">
113
+ {fullUrl}
114
+ </code>
115
+ <CopyButton
116
+ label="Copy"
117
+ isCopied={copiedId === `${message.data.id}-full-url`}
118
+ onClick={() => onCopy(fullUrl, `${message.data.id}-full-url`)}
119
+ borderColor={boxBorderColor}
120
+ />
121
+ </div>
122
+ </div>
74
123
  </div>
75
- </div>
124
+ </section>
125
+
126
+ <section className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg)/0.66)]">
127
+ <div className="px-4 py-4">
128
+ <h3 className={headingClass}>Response</h3>
129
+ </div>
130
+ <div className="border-t" style={{ borderColor: dividerColor }}>
131
+ <div className="flex items-center justify-between gap-4 px-4 py-3">
132
+ <span className={labelClass}>Content-Type</span>
133
+ <code
134
+ className="rounded-md border bg-[rgb(var(--ec-content-hover)/0.45)] px-2.5 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))]"
135
+ style={{ borderColor: boxBorderColor }}
136
+ >
137
+ application/json
138
+ </code>
139
+ </div>
140
+ <div className="flex items-center justify-between gap-4 border-t px-4 py-3" style={{ borderColor: dividerColor }}>
141
+ <span className={labelClass}>Returns</span>
142
+ <span className={valueClass}>Raw schema content</span>
143
+ </div>
144
+ </div>
145
+ </section>
76
146
 
77
- {/* cURL */}
78
- <div className="rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-code-bg))]">
79
- <div className="flex items-center justify-between px-4 py-2.5 border-b border-[rgb(var(--ec-page-border))]">
80
- <span className="text-xs font-medium text-[rgb(var(--ec-page-text-muted))]">cURL</span>
147
+ <section className="overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg)/0.66)]">
148
+ <div className="flex items-center justify-between gap-4 px-4 py-4">
149
+ <div className="flex items-center gap-2">
150
+ <div
151
+ className="flex h-6 w-6 items-center justify-center rounded-md border bg-[rgb(var(--ec-content-hover)/0.45)] text-[rgb(var(--ec-page-text-muted))]"
152
+ style={{ borderColor: boxBorderColor }}
153
+ >
154
+ <DocumentTextIcon className="h-3.5 w-3.5" />
155
+ </div>
156
+ <h3 className={headingClass}>Example Response</h3>
157
+ </div>
81
158
  <CopyButton
82
159
  label="Copy"
83
- isCopied={copiedId === `${message.data.id}-curl`}
84
- onClick={() => onCopy(curlCommand, `${message.data.id}-curl`)}
160
+ isCopied={copiedId === `${message.data.id}-response`}
161
+ onClick={() => onCopy(exampleResponse, `${message.data.id}-response`)}
162
+ borderColor={boxBorderColor}
163
+ variant="ghost"
85
164
  />
86
165
  </div>
87
- <div className="px-4 py-3">
88
- <code className="text-[13px] text-[rgb(var(--ec-page-text))] font-mono break-all leading-relaxed">
89
- <span className="text-[rgb(var(--ec-accent))]">curl</span> {fullUrl}
90
- </code>
91
- </div>
92
- </div>
93
166
 
94
- {/* Response details */}
95
- <div className="rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden">
96
- <div className="px-4 py-2.5 border-b border-[rgb(var(--ec-page-border))]">
97
- <span className="text-xs font-medium text-[rgb(var(--ec-page-text-muted))]">Response</span>
167
+ <div className="border-t" style={{ borderColor: dividerColor }}>
168
+ <SyntaxHighlighter
169
+ language={getLanguageForHighlight(message.schemaExtension)}
170
+ style={isDarkMode ? oneDark : oneLight}
171
+ customStyle={{
172
+ margin: 0,
173
+ padding: '1rem 1.25rem',
174
+ borderRadius: 0,
175
+ fontSize: '0.8125rem',
176
+ lineHeight: '1.625',
177
+ background: 'transparent',
178
+ }}
179
+ showLineNumbers={true}
180
+ wrapLines={true}
181
+ wrapLongLines={true}
182
+ >
183
+ {exampleResponse}
184
+ </SyntaxHighlighter>
98
185
  </div>
99
- <div className="divide-y divide-[rgb(var(--ec-page-border))]">
100
- <div className="flex items-center justify-between px-4 py-2.5">
101
- <span className="text-xs text-[rgb(var(--ec-page-text-muted))]">Content-Type</span>
102
- <code className="text-xs font-mono text-[rgb(var(--ec-page-text))] bg-[rgb(var(--ec-content-hover))] px-2 py-0.5 rounded">
103
- application/json
104
- </code>
186
+
187
+ <div className="flex items-center justify-between gap-4 border-t px-4 py-3" style={{ borderColor: dividerColor }}>
188
+ <div className="inline-flex items-center gap-2 rounded-md bg-emerald-500/10 px-3 py-1.5 text-xs font-medium text-emerald-400">
189
+ <span className="h-2 w-2 rounded-full bg-emerald-400" />
190
+ Valid {schemaTypeLabel}
105
191
  </div>
106
- <div className="flex items-center justify-between px-4 py-2.5">
107
- <span className="text-xs text-[rgb(var(--ec-page-text-muted))]">Returns</span>
108
- <span className="text-xs text-[rgb(var(--ec-page-text))]">Raw schema content</span>
192
+ <div className="flex items-center gap-4 text-xs text-[rgb(var(--ec-page-text-muted))]">
193
+ <span>{lineCount} lines</span>
194
+ <span>{formatBytes(byteSize)}</span>
109
195
  </div>
110
196
  </div>
111
- </div>
197
+ </section>
112
198
  </div>
113
199
  </div>
114
200
  );
115
201
  }
116
202
 
117
- function CopyButton({ label, isCopied, onClick }: { label: string; isCopied: boolean; onClick: () => void }) {
203
+ function CopyButton({
204
+ label,
205
+ isCopied,
206
+ onClick,
207
+ borderColor,
208
+ variant = 'outline',
209
+ }: {
210
+ label: string;
211
+ isCopied: boolean;
212
+ onClick: () => void;
213
+ borderColor: string;
214
+ variant?: 'outline' | 'ghost';
215
+ }) {
216
+ const classes =
217
+ variant === 'ghost'
218
+ ? 'inline-flex items-center gap-2 rounded-md px-1 py-1 text-xs font-medium transition-colors'
219
+ : 'inline-flex items-center gap-2 rounded-md border px-3 py-2 text-xs font-medium transition-colors';
220
+
118
221
  return (
119
222
  <button
120
223
  onClick={onClick}
121
- className={`inline-flex items-center gap-1 px-2 py-1 text-[11px] font-medium rounded transition-colors ${
224
+ style={variant === 'outline' ? { borderColor } : undefined}
225
+ className={`${classes} ${
122
226
  isCopied
123
227
  ? 'text-emerald-400'
124
- : 'text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] hover:bg-[rgb(var(--ec-content-hover))]'
228
+ : 'text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-content-hover)/0.45)] hover:text-[rgb(var(--ec-page-text))]'
125
229
  }`}
126
230
  >
127
- {isCopied ? <CheckIcon className="h-3 w-3" /> : <ClipboardDocumentIcon className="h-3 w-3" />}
231
+ {isCopied ? <CheckIcon className="h-3.5 w-3.5" /> : <ClipboardDocumentIcon className="h-3.5 w-3.5" />}
128
232
  {isCopied ? 'Copied' : label}
129
233
  </button>
130
234
  );
131
235
  }
236
+
237
+ function formatBytes(bytes: number) {
238
+ if (bytes < 1024) return `${bytes} B`;
239
+ const kb = bytes / 1024;
240
+ if (kb < 1024) return `${kb.toFixed(1)} KB`;
241
+ return `${(kb / 1024).toFixed(1)} MB`;
242
+ }
@@ -118,7 +118,7 @@ export default function DiffViewer({ diffs, onOpenFullscreen, apiAccessEnabled =
118
118
  if (diffs.length === 0) return null;
119
119
 
120
120
  return (
121
- <div className="h-full overflow-auto p-5">
121
+ <div className="h-full overflow-auto p-2">
122
122
  {isDark && <style dangerouslySetInnerHTML={{ __html: DIFF_DARK_STYLES }} />}
123
123
  <div className="mb-5 flex items-start justify-between">
124
124
  <div className="flex-1">
@@ -73,9 +73,9 @@ export default function ExamplesViewer({ examples }: ExamplesViewerProps) {
73
73
  {/* Right: code + usage */}
74
74
  <div className="flex-1 flex flex-col min-w-0 overflow-auto">
75
75
  {/* Code window */}
76
- <div className="flex flex-col rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-code-bg))]">
76
+ <div className="flex flex-col rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-dropdown-bg)/0.66)]">
77
77
  {/* Window title bar */}
78
- <div className="flex items-center justify-between px-4 py-2.5 bg-[rgb(var(--ec-content-hover))] border-b border-[rgb(var(--ec-page-border))]">
78
+ <div className="flex items-center justify-between border-b border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-content-hover)/0.45)] px-4 py-2.5">
79
79
  <div className="flex items-center gap-3">
80
80
  <div className="flex items-center gap-1.5">
81
81
  <div className="w-3 h-3 rounded-full bg-[#FF5F56]" />
@@ -121,8 +121,8 @@ export default function ExamplesViewer({ examples }: ExamplesViewerProps) {
121
121
  {/* Usage section */}
122
122
  {selected.usage && (
123
123
  <div className="flex-shrink-0 mt-3">
124
- <div className="rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-code-bg))]">
125
- <div className="flex items-center justify-between px-4 py-2 bg-[rgb(var(--ec-content-hover))] border-b border-[rgb(var(--ec-page-border))]">
124
+ <div className="rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-dropdown-bg)/0.66)]">
125
+ <div className="flex items-center justify-between border-b border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-content-hover)/0.45)] px-4 py-2">
126
126
  <span className="text-xs font-medium text-[rgb(var(--ec-page-text-muted))]">Usage</span>
127
127
  <button
128
128
  onClick={() => handleCopy(selected.usage!, `usage-${selected.fileName}`)}
@@ -10,26 +10,28 @@ export default function Pagination({ currentPage, totalPages, onPageChange }: Pa
10
10
  if (totalPages <= 1) return null;
11
11
 
12
12
  return (
13
- <div className="flex-shrink-0 border-t border-[rgb(var(--ec-page-border))] px-3.5 py-2">
14
- <div className="flex items-center justify-between">
13
+ <div className="flex-shrink-0 border-t border-[rgb(var(--ec-page-border))] px-5 py-4">
14
+ <div className="flex items-center justify-center gap-4">
15
15
  <button
16
16
  onClick={() => onPageChange(Math.max(1, currentPage - 1))}
17
17
  disabled={currentPage === 1}
18
- className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] disabled:opacity-30 disabled:cursor-not-allowed rounded-md hover:bg-[rgb(var(--ec-content-hover))] transition-colors"
18
+ className="inline-flex h-9 w-9 items-center justify-center rounded-xl border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg))] text-[rgb(var(--ec-page-text-muted))] transition-colors hover:text-[rgb(var(--ec-page-text))] disabled:cursor-not-allowed disabled:opacity-30"
19
+ aria-label="Previous page"
19
20
  >
20
- <ChevronLeftIcon className="h-3.5 w-3.5" />
21
- Prev
21
+ <ChevronLeftIcon className="h-4 w-4" />
22
22
  </button>
23
- <span className="text-xs tabular-nums text-[rgb(var(--ec-page-text-muted))]">
24
- {currentPage} / {totalPages}
23
+
24
+ <span className="min-w-[5.5rem] text-center text-[12px] font-medium tabular-nums text-[rgb(var(--ec-page-text-muted))]">
25
+ {currentPage} of {totalPages}
25
26
  </span>
27
+
26
28
  <button
27
29
  onClick={() => onPageChange(Math.min(totalPages, currentPage + 1))}
28
30
  disabled={currentPage === totalPages}
29
- className="inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] disabled:opacity-30 disabled:cursor-not-allowed rounded-md hover:bg-[rgb(var(--ec-content-hover))] transition-colors"
31
+ className="inline-flex h-9 w-9 items-center justify-center rounded-xl border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg))] text-[rgb(var(--ec-page-text-muted))] transition-colors hover:text-[rgb(var(--ec-page-text))] disabled:cursor-not-allowed disabled:opacity-30"
32
+ aria-label="Next page"
30
33
  >
31
- Next
32
- <ChevronRightIcon className="h-3.5 w-3.5" />
34
+ <ChevronRightIcon className="h-4 w-4" />
33
35
  </button>
34
36
  </div>
35
37
  </div>
@@ -5,7 +5,7 @@ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5
5
  import { buildUrl } from '@utils/url-builder';
6
6
  import JSONSchemaViewer from './JSONSchemaViewer';
7
7
  import AvroSchemaViewer from './AvroSchemaViewer';
8
- import { getLanguageForHighlight, getSchemaTypeLabel, ICON_SPECS } from './utils';
8
+ import { getLanguageForHighlight } from './utils';
9
9
  import type { SchemaItem } from './types';
10
10
  import { useDarkMode } from './useDarkMode';
11
11
 
@@ -50,96 +50,67 @@ export default function SchemaContentViewer({
50
50
  }
51
51
  }
52
52
 
53
- const ext = message.schemaExtension?.toLowerCase() || '';
54
- const iconSpec = ICON_SPECS[ext];
55
- const filename = message.data.schemaPath
56
- ? message.data.schemaPath.split('/').pop() || `${message.data.id}.${ext || 'json'}`
57
- : `${message.data.id}.${ext || 'json'}`;
58
-
59
53
  return (
60
- <div className="h-full flex flex-col rounded-lg border border-[rgb(var(--ec-page-border))] overflow-hidden bg-[rgb(var(--ec-code-bg))]">
61
- {/* Window title bar */}
62
- <div className="flex items-center justify-between px-4 py-2.5 bg-[rgb(var(--ec-content-hover))] border-b border-[rgb(var(--ec-page-border))]">
63
- <div className="flex items-center gap-3">
64
- {/* Traffic light dots */}
65
- <div className="flex items-center gap-1.5">
66
- <div className="w-3 h-3 rounded-full bg-[#FF5F56]" />
67
- <div className="w-3 h-3 rounded-full bg-[#FFBD2E]" />
68
- <div className="w-3 h-3 rounded-full bg-[#27C93F]" />
69
- </div>
70
-
71
- {/* Filename */}
72
- <span className="text-xs font-mono text-[rgb(var(--ec-page-text-muted))]">{filename}</span>
73
- </div>
54
+ <div className="relative flex h-full min-h-0 flex-col overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-code-bg))]">
55
+ <div className="pointer-events-none absolute right-4 top-4 z-10 flex items-center gap-1.5">
56
+ {message.collection === 'services' &&
57
+ (() => {
58
+ const specType = message.specType || 'openapi';
59
+ const specFilename = message.specFilenameWithoutExtension || 'schema';
74
60
 
75
- {/* Action buttons */}
76
- <div className="flex items-center gap-1.5">
77
- {message.collection === 'services' &&
78
- (() => {
79
- const specType = message.specType || 'openapi';
80
- const specFilename = message.specFilenameWithoutExtension || 'schema';
61
+ let urlSegment = 'spec';
62
+ if (specType === 'asyncapi') {
63
+ urlSegment = 'asyncapi';
64
+ } else if (specType === 'graphql') {
65
+ urlSegment = 'graphql';
66
+ }
81
67
 
82
- let urlSegment = 'spec';
83
- if (specType === 'asyncapi') {
84
- urlSegment = 'asyncapi';
85
- } else if (specType === 'graphql') {
86
- urlSegment = 'graphql';
87
- }
68
+ const specUrl = buildUrl(`/docs/services/${message.data.id}/${message.data.version}/${urlSegment}/${specFilename}`);
88
69
 
89
- const specUrl = buildUrl(`/docs/services/${message.data.id}/${message.data.version}/${urlSegment}/${specFilename}`);
90
-
91
- return (
92
- <a
93
- href={specUrl}
94
- className="inline-flex items-center gap-1 px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-page-bg)/0.5)] hover:text-[rgb(var(--ec-page-text))] rounded transition-colors"
95
- title="View full specification"
96
- >
97
- <svg
98
- xmlns="http://www.w3.org/2000/svg"
99
- className="h-3 w-3"
100
- fill="none"
101
- viewBox="0 0 24 24"
102
- stroke="currentColor"
103
- >
104
- <path
105
- strokeLinecap="round"
106
- strokeLinejoin="round"
107
- strokeWidth={2}
108
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
109
- />
110
- </svg>
111
- View Spec
112
- </a>
113
- );
114
- })()}
115
- {onOpenFullscreen && (
116
- <button
117
- onClick={onOpenFullscreen}
118
- className="inline-flex items-center p-1 text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-page-bg)/0.5)] hover:text-[rgb(var(--ec-page-text))] rounded transition-colors"
119
- title="Open in fullscreen"
120
- >
121
- <ArrowsPointingOutIcon className="h-3.5 w-3.5" />
122
- </button>
123
- )}
70
+ return (
71
+ <a
72
+ href={specUrl}
73
+ className="pointer-events-auto inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
74
+ title="View full specification"
75
+ >
76
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
77
+ <path
78
+ strokeLinecap="round"
79
+ strokeLinejoin="round"
80
+ strokeWidth={2}
81
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
82
+ />
83
+ </svg>
84
+ View Spec
85
+ </a>
86
+ );
87
+ })()}
88
+ {onOpenFullscreen && (
124
89
  <button
125
- onClick={onCopy}
126
- className="inline-flex items-center gap-1 px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] hover:bg-[rgb(var(--ec-page-bg)/0.5)] hover:text-[rgb(var(--ec-page-text))] rounded transition-colors"
127
- title="Copy code"
90
+ onClick={onOpenFullscreen}
91
+ className="pointer-events-auto inline-flex items-center rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] p-1.5 text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
92
+ title="Open in fullscreen"
128
93
  >
129
- <ClipboardDocumentIcon className="h-3 w-3" />
130
- {isCopied ? 'Copied!' : 'Copy'}
94
+ <ArrowsPointingOutIcon className="h-3.5 w-3.5" />
131
95
  </button>
132
- </div>
96
+ )}
97
+ <button
98
+ onClick={onCopy}
99
+ className="pointer-events-auto inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] px-2 py-1.5 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
100
+ title="Copy code"
101
+ >
102
+ <ClipboardDocumentIcon className="h-3 w-3" />
103
+ {isCopied ? 'Copied!' : 'Copy'}
104
+ </button>
133
105
  </div>
134
106
 
135
- {/* Code content */}
136
- <div className="flex-1 overflow-auto">
107
+ <div className="min-h-0 flex-1 overflow-auto">
137
108
  <SyntaxHighlighter
138
109
  language={getLanguageForHighlight(message.schemaExtension)}
139
110
  style={isDarkMode ? oneDark : oneLight}
140
111
  customStyle={{
141
112
  margin: 0,
142
- padding: '1rem 1.25rem',
113
+ padding: '1.25rem',
143
114
  borderRadius: 0,
144
115
  fontSize: '0.8125rem',
145
116
  lineHeight: '1.625',