@eventcatalog/core 3.29.2 → 3.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) 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-36IA4UE4.js → chunk-7IGMIOQF.js} +1 -1
  6. package/dist/{chunk-EGQGCB2B.js → chunk-HVOLSUC2.js} +1 -1
  7. package/dist/{chunk-DB4IQ3GB.js → chunk-LWVHWR77.js} +1 -1
  8. package/dist/{chunk-VEUNSJ6Z.js → chunk-QIJOBQZ7.js} +1 -1
  9. package/dist/{chunk-MEJOYC5Z.js → chunk-UY5QDWK7.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.js +5 -5
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/astro.config.mjs +11 -7
  19. package/eventcatalog/public/logo.png +0 -0
  20. package/eventcatalog/src/components/CopyAsMarkdown.tsx +29 -24
  21. package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
  22. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
  23. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
  24. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
  25. package/eventcatalog/src/components/Header.astro +9 -19
  26. package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
  27. package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
  28. package/eventcatalog/src/components/MDX/Design/Design.astro +1 -1
  29. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
  30. package/eventcatalog/src/components/MDX/Tiles/Tile.astro +11 -8
  31. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
  32. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
  33. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
  34. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
  35. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
  36. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
  37. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
  38. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
  39. package/eventcatalog/src/components/Search/Search.astro +1 -1
  40. package/eventcatalog/src/components/Seo.astro +1 -1
  41. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +218 -0
  42. package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +265 -0
  43. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +371 -0
  44. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +183 -0
  45. package/eventcatalog/src/components/Settings/LogoUpload.tsx +137 -0
  46. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +91 -0
  47. package/eventcatalog/src/components/Settings/ReadOnlyBanner.tsx +18 -0
  48. package/eventcatalog/src/components/Settings/Row.tsx +59 -0
  49. package/eventcatalog/src/components/Settings/SettingsShared.tsx +176 -0
  50. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
  51. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +233 -261
  52. package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +116 -68
  53. package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +2 -2
  54. package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
  55. package/eventcatalog/src/components/Tables/Table.tsx +21 -18
  56. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
  57. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
  58. package/eventcatalog/src/content.config.ts +1 -1
  59. package/eventcatalog/src/enterprise/auth/error.astro +1 -1
  60. package/eventcatalog/src/enterprise/auth/login.astro +1 -1
  61. package/eventcatalog/src/enterprise/auth/middleware/middleware-auth.ts +11 -7
  62. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +97 -95
  63. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +232 -181
  64. package/eventcatalog/src/enterprise/feature.ts +2 -1
  65. package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
  66. package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
  67. package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
  68. package/eventcatalog/src/layouts/SettingsLayout.astro +116 -0
  69. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +562 -141
  70. package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
  71. package/eventcatalog/src/pages/_index.astro +253 -256
  72. package/eventcatalog/src/pages/api/settings/ai.ts +57 -0
  73. package/eventcatalog/src/pages/api/settings/general.ts +71 -0
  74. package/eventcatalog/src/pages/api/settings/logo.ts +113 -0
  75. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
  76. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
  77. package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
  78. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +130 -30
  79. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +147 -53
  80. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
  81. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
  82. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +22 -19
  83. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +71 -61
  84. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
  85. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +3 -3
  86. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +6 -32
  87. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +5 -1
  88. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
  89. package/eventcatalog/src/pages/docs/users/[id]/index.astro +12 -5
  90. package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
  91. package/eventcatalog/src/pages/settings/assistant.astro +37 -0
  92. package/eventcatalog/src/pages/settings/billing.astro +17 -0
  93. package/eventcatalog/src/pages/settings/general.astro +32 -0
  94. package/eventcatalog/src/pages/settings/index.astro +21 -0
  95. package/eventcatalog/src/pages/settings/llm-access.astro +34 -0
  96. package/eventcatalog/src/pages/settings/mcp.astro +14 -0
  97. package/eventcatalog/src/pages/studio.astro +1 -1
  98. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
  99. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
  100. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
  101. package/eventcatalog/src/styles/theme.css +95 -30
  102. package/eventcatalog/src/styles/themes/forest.css +17 -9
  103. package/eventcatalog/src/styles/themes/ocean.css +10 -2
  104. package/eventcatalog/src/styles/themes/sapphire.css +10 -2
  105. package/eventcatalog/src/styles/themes/sunset.css +25 -17
  106. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
  107. package/eventcatalog/src/utils/eventcatalog-config/config-schema.ts +49 -0
  108. package/eventcatalog/src/utils/eventcatalog-config/config-writer.ts +149 -0
  109. package/eventcatalog/src/utils/url-builder.ts +4 -2
  110. package/package.json +7 -5
  111. package/eventcatalog/public/logo.svg +0 -14
  112. package/eventcatalog/src/enterprise/plans/index.astro +0 -319
  113. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +0 -81
@@ -0,0 +1,91 @@
1
+ import { ExternalLink, Server, ServerCog } from 'lucide-react';
2
+ import { Row } from './Row';
3
+ import { LiveCard, MCP_DOCS_URL, UpgradeRequired, UrlPanel } from './SettingsShared';
4
+
5
+ interface Props {
6
+ hasScalePlan: boolean;
7
+ inSSR: boolean;
8
+ mcpUrl: string;
9
+ }
10
+
11
+ export const McpSettingsForm = ({ hasScalePlan, inSSR, mcpUrl }: Props) => {
12
+ const mcpAvailable = hasScalePlan && inSSR;
13
+
14
+ return (
15
+ <div className="divide-y divide-[rgb(var(--ec-page-border))]">
16
+ <Row
17
+ title="MCP Server"
18
+ description="Expose your catalog over the Model Context Protocol so AI agents (Claude Desktop, Cursor, etc.) can query your architecture as a live data source."
19
+ canEdit={false}
20
+ dirty={false}
21
+ >
22
+ {mcpAvailable ? (
23
+ <McpAvailable url={mcpUrl} />
24
+ ) : !hasScalePlan ? (
25
+ <UpgradeRequired
26
+ tier="Scale"
27
+ blurb="The MCP Server is a Scale-plan feature. Upgrade to expose your catalog to AI agents over the Model Context Protocol."
28
+ docsUrl={MCP_DOCS_URL}
29
+ />
30
+ ) : (
31
+ <McpNeedsSSR />
32
+ )}
33
+ </Row>
34
+ </div>
35
+ );
36
+ };
37
+
38
+ const McpAvailable = ({ url }: { url: string }) => (
39
+ <div className="space-y-3">
40
+ <LiveCard
41
+ icon={<Server className="h-4 w-4" aria-hidden />}
42
+ title="MCP server is live"
43
+ description="Point Claude Desktop, Cursor, or any MCP-aware client at the endpoint below."
44
+ />
45
+ <div className="rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg)/0.4)] px-4 py-3">
46
+ <p className="text-[12px] font-medium text-[rgb(var(--ec-page-text))]">Endpoint</p>
47
+ <UrlPanel url={url} />
48
+ <a
49
+ href={MCP_DOCS_URL}
50
+ target="_blank"
51
+ rel="noreferrer"
52
+ className="mt-3 inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-accent))] hover:underline"
53
+ >
54
+ Connect a client
55
+ <ExternalLink className="h-3 w-3" aria-hidden />
56
+ </a>
57
+ </div>
58
+ </div>
59
+ );
60
+
61
+ const McpNeedsSSR = () => (
62
+ <div className="overflow-hidden rounded-lg border border-[rgb(var(--ec-accent)/0.4)] bg-gradient-to-br from-[rgb(var(--ec-accent)/0.1)] via-[rgb(var(--ec-accent)/0.05)] to-transparent">
63
+ <div className="flex items-start gap-3 px-4 py-3.5">
64
+ <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-[rgb(var(--ec-accent)/0.4)] bg-[rgb(var(--ec-accent)/0.1)] text-[rgb(var(--ec-accent))]">
65
+ <ServerCog className="h-4 w-4" aria-hidden />
66
+ </span>
67
+ <div className="flex-1 min-w-0">
68
+ <div className="flex items-center gap-2">
69
+ <p className="text-[13px] font-semibold text-[rgb(var(--ec-page-text))]">Server output mode required</p>
70
+ <span className="inline-flex items-center gap-1 rounded-full border border-[rgb(var(--ec-accent)/0.4)] bg-[rgb(var(--ec-accent)/0.1)] px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[rgb(var(--ec-accent))]">
71
+ SSR
72
+ </span>
73
+ </div>
74
+ <p className="mt-1 text-[12px] leading-snug text-[rgb(var(--ec-page-text-muted))]">
75
+ The MCP Server requires your catalog to run in server (SSR) mode. Follow the setup guide to switch.
76
+ </p>
77
+ <div className="mt-3 flex flex-wrap items-center gap-3">
78
+ <a
79
+ href={MCP_DOCS_URL}
80
+ target="_blank"
81
+ rel="noreferrer"
82
+ className="inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-accent))] px-3 py-1.5 text-[12px] font-semibold text-white shadow-sm transition-colors hover:bg-[rgb(var(--ec-accent-hover))]"
83
+ >
84
+ MCP setup guide
85
+ <ExternalLink className="h-3 w-3" aria-hidden />
86
+ </a>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ );
@@ -0,0 +1,18 @@
1
+ import { Lock } from 'lucide-react';
2
+
3
+ export const ReadOnlyBanner = () => (
4
+ <div
5
+ role="status"
6
+ className="mb-6 flex items-start gap-3 rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg,var(--ec-page-bg)))] p-4 text-sm text-[rgb(var(--ec-page-text))]"
7
+ >
8
+ <Lock className="mt-0.5 h-4 w-4 flex-shrink-0 text-[rgb(var(--ec-page-text-muted))]" aria-hidden />
9
+ <div className="space-y-1">
10
+ <p className="font-medium">Read-only</p>
11
+ <p className="text-[rgb(var(--ec-page-text-muted))]">
12
+ To edit these settings, run EventCatalog locally or update them in your{' '}
13
+ <code className="rounded bg-[rgb(var(--ec-page-bg)/0.78)] px-1 py-0.5 font-mono text-xs">eventcatalog.config.js</code>{' '}
14
+ file.
15
+ </p>
16
+ </div>
17
+ </div>
18
+ );
@@ -0,0 +1,59 @@
1
+ import { AlertCircle } from 'lucide-react';
2
+
3
+ export const cn = (...parts: Array<string | false | undefined | null>): string => parts.filter(Boolean).join(' ');
4
+
5
+ export const inputBase =
6
+ 'block w-full rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-input-bg,var(--ec-page-bg)))] px-3 py-2 text-[13px] text-[rgb(var(--ec-page-text))] placeholder:text-[rgb(var(--ec-page-text-muted)/0.7)] transition-colors focus:border-[rgb(var(--ec-accent)/0.6)] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent)/0.25)] disabled:cursor-not-allowed disabled:opacity-60';
7
+
8
+ export const monoInput = 'font-mono text-[12.5px] tracking-tight';
9
+
10
+ export const inputError = 'border-red-500/70 focus:border-red-500 focus:ring-red-500/20';
11
+
12
+ interface RowProps {
13
+ title: string;
14
+ description: string;
15
+ canEdit: boolean;
16
+ dirty: boolean;
17
+ saving?: boolean;
18
+ onSave?: () => void;
19
+ error?: string;
20
+ children: React.ReactNode;
21
+ }
22
+
23
+ export const Row = ({ title, description, canEdit, dirty, saving, onSave, error, children }: RowProps) => (
24
+ <section className="grid grid-cols-1 gap-6 py-8 md:grid-cols-[minmax(0,_18rem)_minmax(0,_1fr)] md:gap-12">
25
+ <header className="space-y-1.5">
26
+ <h3 className="text-[14px] font-semibold tracking-tight text-[rgb(var(--ec-page-text))]">{title}</h3>
27
+ <p className="text-[13px] leading-relaxed text-[rgb(var(--ec-content-text-muted,var(--ec-page-text-muted)))]">
28
+ {description}
29
+ </p>
30
+ </header>
31
+ <div className="space-y-2.5">
32
+ {children}
33
+ {error && (
34
+ <p className="flex items-center gap-1.5 text-[12px] text-red-500">
35
+ <AlertCircle className="h-3 w-3 flex-shrink-0" aria-hidden />
36
+ {error}
37
+ </p>
38
+ )}
39
+ {onSave && canEdit && (
40
+ <div className="pt-1">
41
+ <button
42
+ type="button"
43
+ onClick={onSave}
44
+ disabled={!dirty || saving}
45
+ className={cn(
46
+ 'inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-[12px] font-medium transition-colors',
47
+ dirty && !saving
48
+ ? 'border-[rgb(var(--ec-accent)/0.5)] bg-[rgb(var(--ec-accent-subtle))] text-[rgb(var(--ec-accent-text))] hover:bg-[rgb(var(--ec-accent-subtle)/0.8)]'
49
+ : 'border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))]',
50
+ 'disabled:cursor-not-allowed disabled:opacity-60'
51
+ )}
52
+ >
53
+ {saving ? 'Saving…' : 'Save changes'}
54
+ </button>
55
+ </div>
56
+ )}
57
+ </div>
58
+ </section>
59
+ );
@@ -0,0 +1,176 @@
1
+ import { useState } from 'react';
2
+ import { toast } from 'sonner';
3
+ import { ExternalLink, Lock, Sparkles, Copy, Check as CheckIcon } from 'lucide-react';
4
+ import { cn } from './Row';
5
+
6
+ export const ASSISTANT_DOCS_URL =
7
+ 'https://www.eventcatalog.dev/docs/development/ask-your-architecture/eventcatalog-assistant/what-is-eventcatalog-assistant';
8
+ export const ASSISTANT_CONFIGURATION_DOCS_URL =
9
+ 'https://www.eventcatalog.dev/docs/development/ask-your-architecture/eventcatalog-assistant/configuration';
10
+ export const MCP_DOCS_URL = 'https://www.eventcatalog.dev/docs/development/ask-your-architecture/mcp-server/getting-started';
11
+ export const PRICING_URL = 'https://www.eventcatalog.dev/pricing';
12
+
13
+ interface UpgradeRequiredProps {
14
+ tier: string;
15
+ blurb: string;
16
+ docsUrl?: string;
17
+ }
18
+
19
+ export const UpgradeRequired = ({ tier, blurb, docsUrl }: UpgradeRequiredProps) => (
20
+ <div className="overflow-hidden rounded-lg border border-amber-500/40 bg-gradient-to-br from-amber-500/10 via-amber-500/5 to-transparent">
21
+ <div className="flex items-start gap-3 px-4 py-3.5">
22
+ <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-amber-500/40 bg-amber-500/10 text-amber-500">
23
+ <Lock className="h-4 w-4" aria-hidden />
24
+ </span>
25
+ <div className="flex-1 min-w-0">
26
+ <div className="flex items-center gap-2">
27
+ <p className="text-[13px] font-semibold text-[rgb(var(--ec-page-text))]">Available on {tier}</p>
28
+ <span className="inline-flex items-center gap-1 rounded-full border border-amber-500/40 bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-500">
29
+ <Sparkles className="h-2.5 w-2.5" aria-hidden />
30
+ Upgrade
31
+ </span>
32
+ </div>
33
+ <p className="mt-1 text-[12px] leading-snug text-[rgb(var(--ec-page-text-muted))]">{blurb}</p>
34
+ <div className="mt-3 flex flex-wrap items-center gap-3">
35
+ <a
36
+ href={PRICING_URL}
37
+ target="_blank"
38
+ rel="noreferrer"
39
+ className="inline-flex items-center gap-1 rounded-md bg-amber-500 px-3 py-1.5 text-[12px] font-semibold text-white shadow-sm transition-colors hover:bg-amber-600"
40
+ >
41
+ View plans
42
+ <ExternalLink className="h-3 w-3" aria-hidden />
43
+ </a>
44
+ {docsUrl && (
45
+ <a
46
+ href={docsUrl}
47
+ target="_blank"
48
+ rel="noreferrer"
49
+ className="inline-flex items-center gap-1 text-[12px] font-medium text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]"
50
+ >
51
+ Learn more
52
+ <ExternalLink className="h-3 w-3" aria-hidden />
53
+ </a>
54
+ )}
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ );
60
+
61
+ interface UrlPanelProps {
62
+ url: string;
63
+ }
64
+
65
+ export const UrlPanel = ({ url }: UrlPanelProps) => {
66
+ const [copied, setCopied] = useState(false);
67
+ const copy = async () => {
68
+ try {
69
+ await navigator.clipboard.writeText(new URL(url, window.location.origin).href);
70
+ setCopied(true);
71
+ setTimeout(() => setCopied(false), 1600);
72
+ } catch {
73
+ toast.error('Could not copy URL');
74
+ }
75
+ };
76
+ return (
77
+ <div className="mt-2 flex items-center gap-2">
78
+ <a
79
+ href={url}
80
+ target="_blank"
81
+ rel="noreferrer"
82
+ className="flex-1 truncate rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] px-2.5 py-1.5 font-mono text-[12px] text-[rgb(var(--ec-page-text))] transition-colors hover:border-[rgb(var(--ec-accent)/0.5)] hover:text-[rgb(var(--ec-accent))]"
83
+ >
84
+ {url}
85
+ </a>
86
+ <button
87
+ type="button"
88
+ onClick={copy}
89
+ aria-label="Copy URL"
90
+ className="inline-flex h-7 w-7 items-center justify-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))] transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]"
91
+ >
92
+ {copied ? <CheckIcon className="h-3.5 w-3.5 text-green-500" aria-hidden /> : <Copy className="h-3.5 w-3.5" aria-hidden />}
93
+ </button>
94
+ <a
95
+ href={url}
96
+ target="_blank"
97
+ rel="noreferrer"
98
+ aria-label="Open in new tab"
99
+ className="inline-flex h-7 w-7 items-center justify-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))] transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.78)] hover:text-[rgb(var(--ec-page-text))]"
100
+ >
101
+ <ExternalLink className="h-3.5 w-3.5" aria-hidden />
102
+ </a>
103
+ </div>
104
+ );
105
+ };
106
+
107
+ interface LiveCardProps {
108
+ icon: React.ReactNode;
109
+ title: string;
110
+ description: string;
111
+ }
112
+
113
+ export const LiveCard = ({ icon, title, description }: LiveCardProps) => (
114
+ <div className="flex items-center gap-3 rounded-lg border border-emerald-500/30 bg-emerald-500/5 px-4 py-3">
115
+ <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-emerald-500/30 bg-emerald-500/10 text-emerald-500">
116
+ {icon}
117
+ </span>
118
+ <div className="flex-1 min-w-0">
119
+ <p className="text-[13px] font-medium text-[rgb(var(--ec-page-text))]">{title}</p>
120
+ <p className="text-[12px] leading-snug text-[rgb(var(--ec-page-text-muted))]">{description}</p>
121
+ </div>
122
+ <span className="flex-shrink-0 rounded-full border border-emerald-500/30 bg-emerald-500/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-500">
123
+ Live
124
+ </span>
125
+ </div>
126
+ );
127
+
128
+ interface ToggleProps {
129
+ checked: boolean;
130
+ disabled?: boolean;
131
+ onChange: (v: boolean) => void;
132
+ }
133
+
134
+ export const Toggle = ({ checked, disabled, onChange }: ToggleProps) => (
135
+ <button
136
+ type="button"
137
+ role="switch"
138
+ aria-checked={checked}
139
+ disabled={disabled}
140
+ onClick={() => onChange(!checked)}
141
+ className={cn(
142
+ 'relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer items-center rounded-full transition-colors',
143
+ checked ? 'bg-[rgb(var(--ec-accent))]' : 'bg-[rgb(var(--ec-page-text-muted)/0.35)]',
144
+ disabled && 'cursor-not-allowed opacity-50'
145
+ )}
146
+ >
147
+ <span
148
+ className={cn(
149
+ 'inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform',
150
+ checked ? 'translate-x-[1.125rem]' : 'translate-x-[0.1875rem]'
151
+ )}
152
+ />
153
+ </button>
154
+ );
155
+
156
+ interface ToggleRowProps {
157
+ icon: React.ReactNode;
158
+ label: string;
159
+ hint: string;
160
+ checked: boolean;
161
+ disabled?: boolean;
162
+ onChange: (v: boolean) => void;
163
+ }
164
+
165
+ export const ToggleRow = ({ icon, label, hint, checked, disabled, onChange }: ToggleRowProps) => (
166
+ <div className="flex items-center gap-3 rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-input-bg,var(--ec-page-bg)))] px-4 py-3">
167
+ <span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-page-bg))] text-[rgb(var(--ec-page-text-muted))]">
168
+ {icon}
169
+ </span>
170
+ <div className="flex-1 min-w-0">
171
+ <p className="text-[13px] font-medium text-[rgb(var(--ec-page-text))]">{label}</p>
172
+ <p className="text-[12px] leading-snug text-[rgb(var(--ec-page-text-muted))]">{hint}</p>
173
+ </div>
174
+ <Toggle checked={checked} disabled={disabled} onChange={onChange} />
175
+ </div>
176
+ );
@@ -181,7 +181,7 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
181
181
  return (
182
182
  <>
183
183
  {/* Search Input */}
184
- <div className="px-3 py-2 bg-[rgb(var(--ec-content-bg))] border-b border-[rgb(var(--ec-content-border))]">
184
+ <div className="px-4 py-3 bg-[rgb(var(--ec-content-bg))] border-b border-[rgb(var(--ec-content-border))]">
185
185
  <div className="flex gap-2">
186
186
  <div className="relative flex-1">
187
187
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[rgb(var(--ec-input-placeholder))]" />
@@ -190,7 +190,7 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
190
190
  placeholder="Search resources..."
191
191
  value={searchQuery}
192
192
  onChange={(e) => handleSearchChange(e.target.value)}
193
- className="w-full pl-9! pr-8! py-2! text-sm! bg-[rgb(var(--ec-input-bg))]! border! border-[rgb(var(--ec-input-border))]! rounded-lg! focus:outline-hidden! focus:ring-2! focus:ring-[rgb(var(--ec-accent))]! focus:border-transparent! text-[rgb(var(--ec-input-text))]! placeholder:text-[rgb(var(--ec-input-placeholder))]!"
193
+ className="w-full pl-9! pr-8! py-2.5! text-sm! bg-[rgb(var(--ec-input-bg))]! border! border-[rgb(var(--ec-input-border))]! rounded-xl! focus:outline-hidden! focus:ring-2! focus:ring-[rgb(var(--ec-accent))]! focus:border-transparent! text-[rgb(var(--ec-input-text))]! placeholder:text-[rgb(var(--ec-input-placeholder))]!"
194
194
  />
195
195
  {searchQuery && (
196
196
  <button
@@ -207,7 +207,7 @@ export default function SearchBar({ nodes, onSelectResult, onSearchChange }: Pro
207
207
  <button
208
208
  onClick={() => setShowFilterDropdown(!showFilterDropdown)}
209
209
  className={cn(
210
- 'flex items-center justify-center w-10 h-10 rounded-lg border transition-colors',
210
+ 'flex items-center justify-center w-10 h-10 rounded-xl border transition-colors',
211
211
  searchFilters.size > 0
212
212
  ? 'bg-[rgb(var(--ec-accent)/0.1)] border-[rgb(var(--ec-accent)/0.3)] text-[rgb(var(--ec-accent))]'
213
213
  : 'bg-[rgb(var(--ec-input-bg))] border-[rgb(var(--ec-input-border))] text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-icon-hover))] hover:bg-[rgb(var(--ec-content-hover))]'