@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.
- 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-36IA4UE4.js → chunk-7IGMIOQF.js} +1 -1
- package/dist/{chunk-EGQGCB2B.js → chunk-HVOLSUC2.js} +1 -1
- package/dist/{chunk-DB4IQ3GB.js → chunk-LWVHWR77.js} +1 -1
- package/dist/{chunk-VEUNSJ6Z.js → chunk-QIJOBQZ7.js} +1 -1
- package/dist/{chunk-MEJOYC5Z.js → chunk-UY5QDWK7.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 +11 -7
- package/eventcatalog/public/logo.png +0 -0
- package/eventcatalog/src/components/CopyAsMarkdown.tsx +29 -24
- package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
- package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
- package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
- package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
- package/eventcatalog/src/components/Header.astro +9 -19
- package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
- package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
- package/eventcatalog/src/components/MDX/Design/Design.astro +1 -1
- package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
- package/eventcatalog/src/components/MDX/Tiles/Tile.astro +11 -8
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
- package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
- package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
- package/eventcatalog/src/components/Search/Search.astro +1 -1
- package/eventcatalog/src/components/Seo.astro +1 -1
- package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +218 -0
- package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +265 -0
- package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +371 -0
- package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +183 -0
- package/eventcatalog/src/components/Settings/LogoUpload.tsx +137 -0
- package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +91 -0
- package/eventcatalog/src/components/Settings/ReadOnlyBanner.tsx +18 -0
- package/eventcatalog/src/components/Settings/Row.tsx +59 -0
- package/eventcatalog/src/components/Settings/SettingsShared.tsx +176 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +233 -261
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +116 -68
- package/eventcatalog/src/components/Tables/Discover/FilterComponents.tsx +2 -2
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
- package/eventcatalog/src/components/Tables/Table.tsx +21 -18
- package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
- package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
- package/eventcatalog/src/content.config.ts +1 -1
- package/eventcatalog/src/enterprise/auth/error.astro +1 -1
- package/eventcatalog/src/enterprise/auth/login.astro +1 -1
- package/eventcatalog/src/enterprise/auth/middleware/middleware-auth.ts +11 -7
- package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +97 -95
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +232 -181
- package/eventcatalog/src/enterprise/feature.ts +2 -1
- package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
- package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
- package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
- package/eventcatalog/src/layouts/SettingsLayout.astro +116 -0
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +562 -141
- package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
- package/eventcatalog/src/pages/_index.astro +253 -256
- package/eventcatalog/src/pages/api/settings/ai.ts +57 -0
- package/eventcatalog/src/pages/api/settings/general.ts +71 -0
- package/eventcatalog/src/pages/api/settings/logo.ts +113 -0
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
- package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
- package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +130 -30
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +147 -53
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +22 -19
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +71 -61
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +3 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +6 -32
- package/eventcatalog/src/pages/docs/llm/llms.txt.ts +5 -1
- package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +12 -5
- package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
- package/eventcatalog/src/pages/settings/assistant.astro +37 -0
- package/eventcatalog/src/pages/settings/billing.astro +17 -0
- package/eventcatalog/src/pages/settings/general.astro +32 -0
- package/eventcatalog/src/pages/settings/index.astro +21 -0
- package/eventcatalog/src/pages/settings/llm-access.astro +34 -0
- package/eventcatalog/src/pages/settings/mcp.astro +14 -0
- package/eventcatalog/src/pages/studio.astro +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
- package/eventcatalog/src/styles/theme.css +95 -30
- package/eventcatalog/src/styles/themes/forest.css +17 -9
- package/eventcatalog/src/styles/themes/ocean.css +10 -2
- package/eventcatalog/src/styles/themes/sapphire.css +10 -2
- package/eventcatalog/src/styles/themes/sunset.css +25 -17
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
- package/eventcatalog/src/utils/eventcatalog-config/config-schema.ts +49 -0
- package/eventcatalog/src/utils/eventcatalog-config/config-writer.ts +149 -0
- package/eventcatalog/src/utils/url-builder.ts +4 -2
- package/package.json +7 -5
- package/eventcatalog/public/logo.svg +0 -14
- package/eventcatalog/src/enterprise/plans/index.astro +0 -319
- package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +0 -81
|
@@ -4,7 +4,6 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
|
4
4
|
import * as LucideIcons from 'lucide-react';
|
|
5
5
|
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star, FileQuestion } from 'lucide-react';
|
|
6
6
|
import type { NavNode, ChildRef } from '@stores/sidebar-store/state';
|
|
7
|
-
import SearchBar from './SearchBar';
|
|
8
7
|
import { saveState, loadState, saveCollapsedSections, loadCollapsedSections } from './storage';
|
|
9
8
|
import { useStore } from '@nanostores/react';
|
|
10
9
|
import { sidebarStore } from '@stores/sidebar-store';
|
|
@@ -61,7 +60,6 @@ export default function NestedSideBar() {
|
|
|
61
60
|
const [collapsedSections, setCollapsedSections] = useState<Set<string>>(new Set());
|
|
62
61
|
const [showPathPreview, setShowPathPreview] = useState(false);
|
|
63
62
|
const [showFullPath, setShowFullPath] = useState(false);
|
|
64
|
-
const [isSearching, setIsSearching] = useState(false);
|
|
65
63
|
|
|
66
64
|
// Build a lookup map for faster URL navigation
|
|
67
65
|
// Map format: "type:id" -> "nodeKey"
|
|
@@ -503,13 +501,13 @@ export default function NestedSideBar() {
|
|
|
503
501
|
// Show loading state if no data yet
|
|
504
502
|
if (!data || roots.length === 0) {
|
|
505
503
|
return (
|
|
506
|
-
<aside className="w-
|
|
504
|
+
<aside className="w-full min-h-full flex-1 flex flex-col font-sans bg-[rgb(var(--ec-rail-bg))]">
|
|
507
505
|
{/* Search skeleton */}
|
|
508
|
-
<div className="px-
|
|
509
|
-
<div className="h-
|
|
506
|
+
<div className="px-4 py-3 border-b border-[rgb(var(--ec-content-border))] bg-[rgb(var(--ec-rail-bg))]">
|
|
507
|
+
<div className="h-10 bg-[rgb(var(--ec-content-hover))] rounded-xl animate-pulse" />
|
|
510
508
|
</div>
|
|
511
509
|
{/* Content skeleton */}
|
|
512
|
-
<div className="p-
|
|
510
|
+
<div className="p-4 space-y-3">
|
|
513
511
|
{/* Group header skeleton */}
|
|
514
512
|
<div className="flex items-center gap-2 px-2 py-1.5">
|
|
515
513
|
<div className="w-3.5 h-3.5 bg-[rgb(var(--ec-content-hover))] rounded animate-pulse" />
|
|
@@ -665,32 +663,6 @@ export default function NestedSideBar() {
|
|
|
665
663
|
|
|
666
664
|
const isTopLevel = navigationStack.length === 1;
|
|
667
665
|
|
|
668
|
-
/**
|
|
669
|
-
* Navigate to a search result
|
|
670
|
-
*/
|
|
671
|
-
const navigateToSearchResult = (nodeKey: string, node: NavNode) => {
|
|
672
|
-
// If it's a leaf node with href, navigate directly
|
|
673
|
-
if (node.href && (!node.pages || node.pages.length === 0)) {
|
|
674
|
-
window.location.href = node.href;
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// If it has children, drill down to it
|
|
679
|
-
if (node.pages && node.pages.length > 0) {
|
|
680
|
-
setSlideDirection('forward');
|
|
681
|
-
setAnimationKey((prev) => prev + 1);
|
|
682
|
-
setNavigationStack([
|
|
683
|
-
{ key: null, entries: roots, title: 'Documentation' },
|
|
684
|
-
{ key: nodeKey, entries: node.pages, title: node.title, badge: node.badge },
|
|
685
|
-
]);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
setIsSearching(false);
|
|
689
|
-
// Reset hover states
|
|
690
|
-
setShowPathPreview(false);
|
|
691
|
-
setShowFullPath(false);
|
|
692
|
-
};
|
|
693
|
-
|
|
694
666
|
/**
|
|
695
667
|
* Render a list of child refs (resolving keys as needed)
|
|
696
668
|
*/
|
|
@@ -777,10 +749,9 @@ export default function NestedSideBar() {
|
|
|
777
749
|
)}
|
|
778
750
|
<span
|
|
779
751
|
className={cn(
|
|
780
|
-
'tracking-tight',
|
|
781
752
|
isSubtleGroup
|
|
782
|
-
? 'text-[
|
|
783
|
-
: 'text-[
|
|
753
|
+
? 'text-[10px] font-semibold uppercase tracking-[0.12em] text-[rgb(var(--ec-content-text-muted))]'
|
|
754
|
+
: 'text-[12px] font-semibold tracking-tight text-[rgb(var(--ec-content-text))]'
|
|
784
755
|
)}
|
|
785
756
|
>
|
|
786
757
|
{group.title}
|
|
@@ -799,7 +770,7 @@ export default function NestedSideBar() {
|
|
|
799
770
|
);
|
|
800
771
|
|
|
801
772
|
return (
|
|
802
|
-
<div key={`group-${groupKey || index}`} className={cn(isSubtleGroup ? 'mb-
|
|
773
|
+
<div key={`group-${groupKey || index}`} className={cn(isSubtleGroup ? 'mb-2 last:mb-1' : 'mb-5 last:mb-2')}>
|
|
803
774
|
{canCollapse ? (
|
|
804
775
|
<button
|
|
805
776
|
onClick={() => toggleSectionCollapse(groupId)}
|
|
@@ -821,7 +792,7 @@ export default function NestedSideBar() {
|
|
|
821
792
|
<div
|
|
822
793
|
className={cn(
|
|
823
794
|
'flex flex-col gap-0.5 border-[rgb(var(--ec-content-border))]',
|
|
824
|
-
isSubtleGroup ? '
|
|
795
|
+
isSubtleGroup ? 'mt-0.5' : shouldFlattenSubtleChildren ? 'mt-1' : 'border-l ml-4 mt-1'
|
|
825
796
|
)}
|
|
826
797
|
>
|
|
827
798
|
{visibleChildren.map((childRef, childIndex) => {
|
|
@@ -891,7 +862,7 @@ export default function NestedSideBar() {
|
|
|
891
862
|
{item.leftIcon && <img src={resolveIconUrl(item.leftIcon)} alt="" loading="lazy" className="w-4 h-4 flex-shrink-0" />}
|
|
892
863
|
<span
|
|
893
864
|
className={cn(
|
|
894
|
-
'text-[
|
|
865
|
+
'text-[12px] truncate',
|
|
895
866
|
isActive
|
|
896
867
|
? 'text-[rgb(var(--ec-accent-text))] font-medium'
|
|
897
868
|
: 'text-[rgb(var(--ec-content-text-secondary))] group-hover:text-[rgb(var(--ec-content-text))]'
|
|
@@ -924,11 +895,9 @@ export default function NestedSideBar() {
|
|
|
924
895
|
);
|
|
925
896
|
|
|
926
897
|
const baseClasses =
|
|
927
|
-
'group flex items-center justify-between w-full px-3 py-1.5
|
|
898
|
+
'group flex items-center justify-between w-full px-3 py-1.5 border border-transparent cursor-pointer text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] active:bg-[rgb(var(--ec-content-hover))]';
|
|
928
899
|
const parentClasses = itemHasChildren ? 'font-medium' : '';
|
|
929
|
-
const activeClasses = isActive
|
|
930
|
-
? 'bg-[rgb(var(--ec-accent-subtle))] hover:bg-[rgb(var(--ec-accent-subtle))] rounded-none!'
|
|
931
|
-
: '';
|
|
900
|
+
const activeClasses = isActive ? 'bg-[rgb(var(--ec-rail-active-bg))] hover:bg-[rgb(var(--ec-rail-active-bg))]' : '';
|
|
932
901
|
|
|
933
902
|
// Leaf item with href → render as link
|
|
934
903
|
if (item.href && !itemHasChildren) {
|
|
@@ -966,251 +935,254 @@ export default function NestedSideBar() {
|
|
|
966
935
|
};
|
|
967
936
|
|
|
968
937
|
return (
|
|
969
|
-
<aside className="w-
|
|
970
|
-
{
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
938
|
+
<aside className="w-full min-h-full flex-1 flex flex-col font-sans bg-[rgb(var(--ec-rail-bg))]">
|
|
939
|
+
{isTopLevel && (
|
|
940
|
+
<div className="flex h-[60px] items-center px-6 bg-[rgb(var(--ec-rail-bg)/0.98)] backdrop-blur-sm border-b border-[rgb(var(--ec-content-border))] sticky top-0 z-10">
|
|
941
|
+
<span className="text-[0.65rem] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-sidebar-text)/0.5)] truncate">
|
|
942
|
+
All resources
|
|
943
|
+
</span>
|
|
944
|
+
</div>
|
|
945
|
+
)}
|
|
946
|
+
|
|
947
|
+
{!isTopLevel && (
|
|
948
|
+
<div
|
|
949
|
+
className="flex h-[60px] items-center px-4 bg-[rgb(var(--ec-rail-bg)/0.98)] backdrop-blur-sm border-b border-[rgb(var(--ec-content-border))] sticky top-0 z-10"
|
|
950
|
+
onMouseEnter={() => !isTopLevel && setShowPathPreview(true)}
|
|
951
|
+
onMouseLeave={() => {
|
|
952
|
+
setShowPathPreview(false);
|
|
953
|
+
setShowFullPath(false);
|
|
954
|
+
}}
|
|
955
|
+
>
|
|
956
|
+
<button
|
|
957
|
+
onClick={navigateBack}
|
|
958
|
+
disabled={isTopLevel}
|
|
959
|
+
className={cn(
|
|
960
|
+
'flex items-center gap-2 w-full px-2 py-1.5 -mx-2 rounded-md transition-colors',
|
|
961
|
+
!isTopLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
962
|
+
isTopLevel && 'cursor-default'
|
|
963
|
+
)}
|
|
964
|
+
>
|
|
965
|
+
<span
|
|
966
|
+
className={cn(
|
|
967
|
+
'flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] transition-all',
|
|
968
|
+
isTopLevel && 'opacity-0',
|
|
969
|
+
!isTopLevel && 'group-hover:-translate-x-0.5'
|
|
970
|
+
)}
|
|
984
971
|
>
|
|
985
|
-
<
|
|
986
|
-
|
|
987
|
-
|
|
972
|
+
<ChevronLeft className="w-4 h-4" />
|
|
973
|
+
</span>
|
|
974
|
+
<span className="text-[0.65rem] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-sidebar-text))] truncate">
|
|
975
|
+
{currentLevel.title}
|
|
976
|
+
</span>
|
|
977
|
+
{currentLevel.badge && (
|
|
978
|
+
<span
|
|
988
979
|
className={cn(
|
|
989
|
-
'
|
|
990
|
-
|
|
991
|
-
isTopLevel && 'cursor-default'
|
|
980
|
+
'ml-auto px-2 py-0.5 text-[8px] font-semibold uppercase tracking-wide rounded',
|
|
981
|
+
getBadgeClasses(currentLevel.badge)
|
|
992
982
|
)}
|
|
993
983
|
>
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
!isTopLevel && 'group-hover:-translate-x-0.5'
|
|
999
|
-
)}
|
|
1000
|
-
>
|
|
1001
|
-
<ChevronLeft className="w-4 h-4" />
|
|
1002
|
-
</span>
|
|
1003
|
-
<span className="text-sm font-semibold text-[rgb(var(--ec-content-text))] truncate">{currentLevel.title}</span>
|
|
1004
|
-
{currentLevel.badge && (
|
|
1005
|
-
<span
|
|
1006
|
-
className={cn(
|
|
1007
|
-
'ml-auto px-2 py-0.5 text-[9px] font-semibold uppercase tracking-wide rounded',
|
|
1008
|
-
getBadgeClasses(currentLevel.badge)
|
|
1009
|
-
)}
|
|
1010
|
-
>
|
|
1011
|
-
{currentLevel.badge}
|
|
1012
|
-
</span>
|
|
1013
|
-
)}
|
|
1014
|
-
</button>
|
|
1015
|
-
|
|
1016
|
-
{/* Path Preview Dropdown */}
|
|
1017
|
-
{showPathPreview && navigationStack.length > 1 && (
|
|
1018
|
-
<div className="absolute left-0 right-0 top-full bg-[rgb(var(--ec-content-bg))] border-b border-[rgb(var(--ec-content-border))] shadow-lg z-20">
|
|
1019
|
-
<div className="px-3 py-2">
|
|
1020
|
-
<div className="text-[10px] font-medium text-[rgb(var(--ec-content-text-muted))] uppercase tracking-wide mb-2">
|
|
1021
|
-
Navigation Path
|
|
1022
|
-
</div>
|
|
1023
|
-
<div className="flex flex-col gap-0.5">
|
|
1024
|
-
{(() => {
|
|
1025
|
-
const SHOW_FIRST = 2; // Show first N items
|
|
1026
|
-
const SHOW_LAST = 2; // Show last N items (including current)
|
|
1027
|
-
const totalItems = navigationStack.length;
|
|
1028
|
-
const hiddenCount = totalItems - SHOW_FIRST - SHOW_LAST;
|
|
1029
|
-
const shouldTruncate = hiddenCount > 0 && !showFullPath;
|
|
1030
|
-
|
|
1031
|
-
const renderPathItem = (level: NavigationLevel, index: number, displayIndex: number) => {
|
|
1032
|
-
const isCurrentLevel = index === navigationStack.length - 1;
|
|
1033
|
-
return (
|
|
1034
|
-
<button
|
|
1035
|
-
key={`path-${index}`}
|
|
1036
|
-
onClick={() => navigateToLevel(index)}
|
|
1037
|
-
disabled={isCurrentLevel}
|
|
1038
|
-
className={cn(
|
|
1039
|
-
'flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors',
|
|
1040
|
-
!isCurrentLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
1041
|
-
isCurrentLevel && 'bg-[rgb(var(--ec-content-hover))] cursor-default'
|
|
1042
|
-
)}
|
|
1043
|
-
style={{ paddingLeft: `${displayIndex * 12 + 8}px` }}
|
|
1044
|
-
>
|
|
1045
|
-
{index === 0 ? (
|
|
1046
|
-
<Home className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] flex-shrink-0" />
|
|
1047
|
-
) : (
|
|
1048
|
-
<ChevronRight className="w-3.5 h-3.5 text-[rgb(var(--ec-content-text-muted))] flex-shrink-0" />
|
|
1049
|
-
)}
|
|
1050
|
-
<span
|
|
1051
|
-
className={cn(
|
|
1052
|
-
'text-sm truncate',
|
|
1053
|
-
isCurrentLevel
|
|
1054
|
-
? 'font-medium text-[rgb(var(--ec-content-text))]'
|
|
1055
|
-
: 'text-[rgb(var(--ec-content-text-secondary))]'
|
|
1056
|
-
)}
|
|
1057
|
-
>
|
|
1058
|
-
{level.title}
|
|
1059
|
-
</span>
|
|
1060
|
-
{level.badge && (
|
|
1061
|
-
<span
|
|
1062
|
-
className={cn(
|
|
1063
|
-
'ml-auto px-1.5 py-0.5 text-[8px] font-semibold uppercase tracking-wide rounded flex-shrink-0',
|
|
1064
|
-
getBadgeClasses(level.badge)
|
|
1065
|
-
)}
|
|
1066
|
-
>
|
|
1067
|
-
{level.badge}
|
|
1068
|
-
</span>
|
|
1069
|
-
)}
|
|
1070
|
-
</button>
|
|
1071
|
-
);
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
if (shouldTruncate) {
|
|
1075
|
-
return (
|
|
1076
|
-
<>
|
|
1077
|
-
{/* First N items */}
|
|
1078
|
-
{navigationStack.slice(0, SHOW_FIRST).map((level, index) => renderPathItem(level, index, index))}
|
|
1079
|
-
|
|
1080
|
-
{/* Collapsed middle section */}
|
|
1081
|
-
<button
|
|
1082
|
-
onClick={(e) => {
|
|
1083
|
-
e.stopPropagation();
|
|
1084
|
-
setShowFullPath(true);
|
|
1085
|
-
}}
|
|
1086
|
-
className="flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer"
|
|
1087
|
-
style={{ paddingLeft: `${SHOW_FIRST * 12 + 8}px` }}
|
|
1088
|
-
>
|
|
1089
|
-
<span className="flex items-center justify-center w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))]">
|
|
1090
|
-
<span className="text-xs">•••</span>
|
|
1091
|
-
</span>
|
|
1092
|
-
<span className="text-sm text-[rgb(var(--ec-content-text-muted))]">
|
|
1093
|
-
{hiddenCount} more level{hiddenCount > 1 ? 's' : ''}
|
|
1094
|
-
</span>
|
|
1095
|
-
<ChevronDown className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] ml-auto" />
|
|
1096
|
-
</button>
|
|
1097
|
-
|
|
1098
|
-
{/* Last N items */}
|
|
1099
|
-
{navigationStack.slice(-SHOW_LAST).map((level, sliceIndex) => {
|
|
1100
|
-
const actualIndex = totalItems - SHOW_LAST + sliceIndex;
|
|
1101
|
-
return renderPathItem(level, actualIndex, SHOW_FIRST + 1 + sliceIndex);
|
|
1102
|
-
})}
|
|
1103
|
-
</>
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// Show full path
|
|
1108
|
-
return navigationStack.map((level, index) => renderPathItem(level, index, index));
|
|
1109
|
-
})()}
|
|
1110
|
-
</div>
|
|
1111
|
-
</div>
|
|
1112
|
-
</div>
|
|
1113
|
-
)}
|
|
1114
|
-
</div>
|
|
1115
|
-
)}
|
|
984
|
+
{currentLevel.badge}
|
|
985
|
+
</span>
|
|
986
|
+
)}
|
|
987
|
+
</button>
|
|
1116
988
|
|
|
1117
|
-
{/*
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
scrollbarColor: 'rgb(var(--ec-content-border)) transparent',
|
|
1124
|
-
}}
|
|
1125
|
-
>
|
|
1126
|
-
{/* Favorites Section */}
|
|
1127
|
-
{favorites.length > 0 && isTopLevel && (
|
|
1128
|
-
<div className="mb-6">
|
|
1129
|
-
<div className="flex items-center px-2 py-1.5">
|
|
1130
|
-
<Star className="w-3.5 h-3.5 mr-2 text-amber-400 fill-current" />
|
|
1131
|
-
<span className="text-sm text-[rgb(var(--ec-content-text))] font-semibold">Favorites</span>
|
|
989
|
+
{/* Path Preview Dropdown */}
|
|
990
|
+
{showPathPreview && navigationStack.length > 1 && (
|
|
991
|
+
<div className="absolute left-0 right-0 top-full bg-[rgb(var(--ec-page-bg))] border-b border-[rgb(var(--ec-content-border))] shadow-lg z-20">
|
|
992
|
+
<div className="px-4 py-3">
|
|
993
|
+
<div className="text-[9px] font-medium text-[rgb(var(--ec-content-text-muted))] uppercase tracking-wide mb-2">
|
|
994
|
+
Navigation Path
|
|
1132
995
|
</div>
|
|
1133
|
-
<div className="flex flex-col gap-0.5
|
|
1134
|
-
{
|
|
1135
|
-
const
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
996
|
+
<div className="flex flex-col gap-0.5">
|
|
997
|
+
{(() => {
|
|
998
|
+
const SHOW_FIRST = 2; // Show first N items
|
|
999
|
+
const SHOW_LAST = 2; // Show last N items (including current)
|
|
1000
|
+
const totalItems = navigationStack.length;
|
|
1001
|
+
const hiddenCount = totalItems - SHOW_FIRST - SHOW_LAST;
|
|
1002
|
+
const shouldTruncate = hiddenCount > 0 && !showFullPath;
|
|
1003
|
+
|
|
1004
|
+
const renderPathItem = (level: NavigationLevel, index: number, displayIndex: number) => {
|
|
1005
|
+
const isCurrentLevel = index === navigationStack.length - 1;
|
|
1006
|
+
return (
|
|
1007
|
+
<button
|
|
1008
|
+
key={`path-${index}`}
|
|
1009
|
+
onClick={() => navigateToLevel(index)}
|
|
1010
|
+
disabled={isCurrentLevel}
|
|
1011
|
+
className={cn(
|
|
1012
|
+
'flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors',
|
|
1013
|
+
!isCurrentLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
1014
|
+
isCurrentLevel && 'bg-[rgb(var(--ec-content-hover))] cursor-default'
|
|
1015
|
+
)}
|
|
1016
|
+
style={{ paddingLeft: `${displayIndex * 12 + 8}px` }}
|
|
1017
|
+
>
|
|
1018
|
+
{index === 0 ? (
|
|
1019
|
+
<Home className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] flex-shrink-0" />
|
|
1020
|
+
) : (
|
|
1021
|
+
<ChevronRight className="w-3.5 h-3.5 text-[rgb(var(--ec-content-text-muted))] flex-shrink-0" />
|
|
1022
|
+
)}
|
|
1148
1023
|
<span
|
|
1149
1024
|
className={cn(
|
|
1150
|
-
'text-[
|
|
1151
|
-
|
|
1152
|
-
? 'text-[rgb(var(--ec-
|
|
1153
|
-
: 'text-[rgb(var(--ec-content-text-secondary))]
|
|
1025
|
+
'text-[12px] truncate',
|
|
1026
|
+
isCurrentLevel
|
|
1027
|
+
? 'font-medium text-[rgb(var(--ec-content-text))]'
|
|
1028
|
+
: 'text-[rgb(var(--ec-content-text-secondary))]'
|
|
1154
1029
|
)}
|
|
1155
1030
|
>
|
|
1156
|
-
{
|
|
1031
|
+
{level.title}
|
|
1157
1032
|
</span>
|
|
1158
|
-
|
|
1159
|
-
<div className="flex items-center gap-1 flex-shrink-0">
|
|
1160
|
-
{fav.badge && (
|
|
1033
|
+
{level.badge && (
|
|
1161
1034
|
<span
|
|
1162
1035
|
className={cn(
|
|
1163
|
-
'px-1.5 py-0.5 text-[
|
|
1164
|
-
getBadgeClasses(
|
|
1036
|
+
'ml-auto px-1.5 py-0.5 text-[7px] font-semibold uppercase tracking-wide rounded flex-shrink-0',
|
|
1037
|
+
getBadgeClasses(level.badge)
|
|
1165
1038
|
)}
|
|
1166
1039
|
>
|
|
1167
|
-
{
|
|
1040
|
+
{level.badge}
|
|
1168
1041
|
</span>
|
|
1169
1042
|
)}
|
|
1170
|
-
|
|
1043
|
+
</button>
|
|
1044
|
+
);
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
if (shouldTruncate) {
|
|
1048
|
+
return (
|
|
1049
|
+
<>
|
|
1050
|
+
{/* First N items */}
|
|
1051
|
+
{navigationStack.slice(0, SHOW_FIRST).map((level, index) => renderPathItem(level, index, index))}
|
|
1052
|
+
|
|
1053
|
+
{/* Collapsed middle section */}
|
|
1054
|
+
<button
|
|
1171
1055
|
onClick={(e) => {
|
|
1172
1056
|
e.stopPropagation();
|
|
1173
|
-
|
|
1174
|
-
toggleFavorite(fav.nodeKey, node);
|
|
1175
|
-
} else {
|
|
1176
|
-
// Node no longer exists, remove directly using nodeKey
|
|
1177
|
-
removeFavoriteAction(fav.nodeKey);
|
|
1178
|
-
}
|
|
1057
|
+
setShowFullPath(true);
|
|
1179
1058
|
}}
|
|
1180
|
-
className="flex items-center
|
|
1059
|
+
className="flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer"
|
|
1060
|
+
style={{ paddingLeft: `${SHOW_FIRST * 12 + 8}px` }}
|
|
1181
1061
|
>
|
|
1182
|
-
<
|
|
1183
|
-
|
|
1184
|
-
{node?.pages && node.pages.length > 0 && (
|
|
1185
|
-
<span className="flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] group-hover:text-[rgb(var(--ec-content-text))]">
|
|
1186
|
-
<ChevronRight className="w-4 h-4" />
|
|
1062
|
+
<span className="flex items-center justify-center w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))]">
|
|
1063
|
+
<span className="text-xs">•••</span>
|
|
1187
1064
|
</span>
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1065
|
+
<span className="text-[12px] text-[rgb(var(--ec-content-text-muted))]">
|
|
1066
|
+
{hiddenCount} more level{hiddenCount > 1 ? 's' : ''}
|
|
1067
|
+
</span>
|
|
1068
|
+
<ChevronDown className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] ml-auto" />
|
|
1069
|
+
</button>
|
|
1070
|
+
|
|
1071
|
+
{/* Last N items */}
|
|
1072
|
+
{navigationStack.slice(-SHOW_LAST).map((level, sliceIndex) => {
|
|
1073
|
+
const actualIndex = totalItems - SHOW_LAST + sliceIndex;
|
|
1074
|
+
return renderPathItem(level, actualIndex, SHOW_FIRST + 1 + sliceIndex);
|
|
1075
|
+
})}
|
|
1076
|
+
</>
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Show full path
|
|
1081
|
+
return navigationStack.map((level, index) => renderPathItem(level, index, index));
|
|
1082
|
+
})()}
|
|
1193
1083
|
</div>
|
|
1194
1084
|
</div>
|
|
1195
|
-
|
|
1085
|
+
</div>
|
|
1086
|
+
)}
|
|
1087
|
+
</div>
|
|
1088
|
+
)}
|
|
1196
1089
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1090
|
+
{/* Navigation Content */}
|
|
1091
|
+
<nav
|
|
1092
|
+
key={animationKey}
|
|
1093
|
+
className={cn('flex-1 overflow-y-auto overflow-x-hidden p-4 px-2', getAnimationClass())}
|
|
1094
|
+
style={{
|
|
1095
|
+
scrollbarWidth: 'thin',
|
|
1096
|
+
scrollbarColor: 'rgb(var(--ec-content-border)) transparent',
|
|
1097
|
+
}}
|
|
1098
|
+
>
|
|
1099
|
+
{/* Favorites Section */}
|
|
1100
|
+
{favorites.length > 0 && isTopLevel && (
|
|
1101
|
+
<div className="mb-6">
|
|
1102
|
+
<div className="flex items-center px-2 py-1.5">
|
|
1103
|
+
<Star className="w-3.5 h-3.5 mr-2 text-amber-400 fill-current" />
|
|
1104
|
+
<span className="text-[12px] text-[rgb(var(--ec-content-text))] font-semibold">Favorites</span>
|
|
1105
|
+
</div>
|
|
1106
|
+
<div className="flex flex-col gap-0.5 border-l ml-3.5 border-amber-200">
|
|
1107
|
+
{favorites.map((fav, index) => {
|
|
1108
|
+
const node = resolveRef(fav.nodeKey);
|
|
1109
|
+
const isActive = fav.href && currentPath === fav.href;
|
|
1209
1110
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1111
|
+
return (
|
|
1112
|
+
<button
|
|
1113
|
+
key={`fav-${index}`}
|
|
1114
|
+
onClick={() => navigateToFavorite(fav)}
|
|
1115
|
+
className={cn(
|
|
1116
|
+
'group flex items-center justify-between w-full px-3 py-2 border border-transparent cursor-pointer text-left transition-colors hover:bg-amber-500/10 active:bg-amber-500/20',
|
|
1117
|
+
isActive &&
|
|
1118
|
+
'border-[rgb(var(--ec-accent)/0.16)] bg-[rgb(var(--ec-accent-subtle))] hover:bg-[rgb(var(--ec-accent-subtle))] shadow-sm'
|
|
1119
|
+
)}
|
|
1120
|
+
>
|
|
1121
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
|
1122
|
+
<span
|
|
1123
|
+
className={cn(
|
|
1124
|
+
'text-[12px] truncate',
|
|
1125
|
+
isActive
|
|
1126
|
+
? 'text-[rgb(var(--ec-accent-text))] font-medium'
|
|
1127
|
+
: 'text-[rgb(var(--ec-content-text-secondary))] group-hover:text-[rgb(var(--ec-content-text))]'
|
|
1128
|
+
)}
|
|
1129
|
+
>
|
|
1130
|
+
{fav.title}
|
|
1131
|
+
</span>
|
|
1132
|
+
</div>
|
|
1133
|
+
<div className="flex items-center gap-1 flex-shrink-0">
|
|
1134
|
+
{fav.badge && (
|
|
1135
|
+
<span
|
|
1136
|
+
className={cn(
|
|
1137
|
+
'px-1.5 py-0.5 text-[7px] font-semibold uppercase tracking-wide rounded',
|
|
1138
|
+
getBadgeClasses(fav.badge)
|
|
1139
|
+
)}
|
|
1140
|
+
>
|
|
1141
|
+
{fav.badge}
|
|
1142
|
+
</span>
|
|
1143
|
+
)}
|
|
1144
|
+
<div
|
|
1145
|
+
onClick={(e) => {
|
|
1146
|
+
e.stopPropagation();
|
|
1147
|
+
if (node) {
|
|
1148
|
+
toggleFavorite(fav.nodeKey, node);
|
|
1149
|
+
} else {
|
|
1150
|
+
// Node no longer exists, remove directly using nodeKey
|
|
1151
|
+
removeFavoriteAction(fav.nodeKey);
|
|
1152
|
+
}
|
|
1153
|
+
}}
|
|
1154
|
+
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors cursor-pointer"
|
|
1155
|
+
>
|
|
1156
|
+
<Star className="w-3.5 h-3.5 fill-current" />
|
|
1157
|
+
</div>
|
|
1158
|
+
{node?.pages && node.pages.length > 0 && (
|
|
1159
|
+
<span className="flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] group-hover:text-[rgb(var(--ec-content-text))]">
|
|
1160
|
+
<ChevronRight className="w-4 h-4" />
|
|
1161
|
+
</span>
|
|
1162
|
+
)}
|
|
1163
|
+
</div>
|
|
1164
|
+
</button>
|
|
1165
|
+
);
|
|
1166
|
+
})}
|
|
1167
|
+
</div>
|
|
1168
|
+
</div>
|
|
1169
|
+
)}
|
|
1170
|
+
|
|
1171
|
+
{/* Empty State */}
|
|
1172
|
+
{currentLevel.entries.length === 0 && favorites.length === 0 && (
|
|
1173
|
+
<div className="flex flex-col items-center justify-center px-6 py-12 text-center">
|
|
1174
|
+
<div className="mb-4 p-3 rounded-full bg-[rgb(var(--ec-group-icon-bg))]">
|
|
1175
|
+
<FileQuestion className="w-8 h-8 text-[rgb(var(--ec-icon-color))]" />
|
|
1176
|
+
</div>
|
|
1177
|
+
<h3 className="text-[12px] font-semibold text-[rgb(var(--ec-content-text))] mb-2">Your catalog is empty</h3>
|
|
1178
|
+
<p className="text-[11px] text-[rgb(var(--ec-content-text-muted))] leading-relaxed max-w-[240px]">
|
|
1179
|
+
Navigation will appear here when you add resources to your EventCatalog.
|
|
1180
|
+
</p>
|
|
1181
|
+
</div>
|
|
1182
|
+
)}
|
|
1183
|
+
|
|
1184
|
+
{currentLevel.entries.length > 0 && renderEntries(currentLevel.entries)}
|
|
1185
|
+
</nav>
|
|
1214
1186
|
|
|
1215
1187
|
{/* Animation keyframes */}
|
|
1216
1188
|
<style>{`
|