@brainfish-ai/devdoc 0.1.21 → 0.1.23

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.
@@ -120,6 +120,20 @@ import {
120
120
  PDF,
121
121
  Audio,
122
122
  Download,
123
+ // Landing Page Components
124
+ Hero,
125
+ Pre,
126
+ Tagline,
127
+ Headline,
128
+ Description,
129
+ CommandBox,
130
+ Section,
131
+ Center,
132
+ FeatureGrid,
133
+ FeatureItem,
134
+ ButtonLink,
135
+ Spacer,
136
+ Divider,
123
137
  } from '../../docs/mdx/index'
124
138
 
125
139
  // MDX components mapping
@@ -190,6 +204,21 @@ const mdxComponents = {
190
204
  Audio,
191
205
  Download,
192
206
 
207
+ // Landing Page
208
+ Hero,
209
+ Pre,
210
+ Tagline,
211
+ Headline,
212
+ Description,
213
+ CommandBox,
214
+ Section,
215
+ Center,
216
+ FeatureGrid,
217
+ FeatureItem,
218
+ ButtonLink,
219
+ Spacer,
220
+ Divider,
221
+
193
222
  // Links
194
223
  a: MdxLink,
195
224
  }
@@ -204,6 +233,11 @@ interface DocPageData {
204
233
  title: string
205
234
  description?: string
206
235
  icon?: string
236
+ // Page mode and layout options
237
+ mode?: 'default' | 'wide' | 'custom'
238
+ hideHeader?: boolean
239
+ fullWidth?: boolean
240
+ background?: string
207
241
  }
208
242
  mdxSource: MDXRemoteSerializeResult
209
243
  }
@@ -244,7 +278,7 @@ export function DocPage({ slug }: DocPageProps) {
244
278
 
245
279
  if (loading) {
246
280
  return (
247
- <div className="docs-page docs-page-loading max-w-4xl mx-auto px-4 py-6 sm:px-8 sm:py-8">
281
+ <div className="docs-page docs-page-loading w-full min-h-[200px]">
248
282
  <div className="docs-loading flex items-center justify-center py-12">
249
283
  <Spinner className="h-6 w-6 animate-spin text-muted-foreground" />
250
284
  </div>
@@ -254,7 +288,7 @@ export function DocPage({ slug }: DocPageProps) {
254
288
 
255
289
  if (error || !pageData) {
256
290
  return (
257
- <div className="docs-page docs-page-error max-w-4xl mx-auto px-4 py-6 sm:px-8 sm:py-8">
291
+ <div className="docs-page docs-page-error w-full min-h-[200px]">
258
292
  <div className="docs-error text-center py-12">
259
293
  <p className="text-destructive">{error || 'Page not found'}</p>
260
294
  </div>
@@ -262,19 +296,52 @@ export function DocPage({ slug }: DocPageProps) {
262
296
  )
263
297
  }
264
298
 
265
- return (
266
- <div ref={contentRef} className="docs-page docs-content max-w-4xl mx-auto px-4 py-6 sm:px-8 sm:py-8">
267
- {/* Page header */}
268
- <div className="docs-page-header mb-6">
269
- <h1 className="docs-content-title text-2xl sm:text-3xl font-bold mb-2 text-foreground">
270
- {pageData.frontmatter.title}
271
- </h1>
272
- {pageData.frontmatter.description && (
273
- <p className="docs-content-description text-lg text-muted-foreground">
274
- {pageData.frontmatter.description}
275
- </p>
276
- )}
299
+ const { mode = 'default', hideHeader, fullWidth, background } = pageData.frontmatter
300
+ const isCustomMode = mode === 'custom'
301
+ const isWideMode = mode === 'wide'
302
+ const showHeader = !hideHeader && !isCustomMode
303
+
304
+ // Custom mode: Full-width layout for landing pages
305
+ if (isCustomMode) {
306
+ return (
307
+ <div
308
+ ref={contentRef}
309
+ className="docs-page docs-page-custom docs-content w-full min-h-full"
310
+ style={{
311
+ background: background || 'var(--background)',
312
+ // Ensure the content fills the entire viewport width within the content area
313
+ marginLeft: 0,
314
+ marginRight: 0,
315
+ }}
316
+ >
317
+ {/* Custom pages render MDX content directly without container constraints */}
318
+ <div className="docs-custom-content [&>*]:w-full">
319
+ <MDXRemote {...pageData.mdxSource} components={mdxComponents} />
320
+ </div>
277
321
  </div>
322
+ )
323
+ }
324
+
325
+ // Default/Wide mode: Standard documentation layout
326
+ const containerClass = isWideMode
327
+ ? 'max-w-6xl' // Wide mode
328
+ : 'max-w-4xl' // Default mode
329
+
330
+ return (
331
+ <div ref={contentRef} className={`docs-page docs-content ${containerClass} mx-auto px-4 py-6 sm:px-8 sm:py-8`}>
332
+ {/* Page header - shown unless hideHeader is true */}
333
+ {showHeader && (
334
+ <div className="docs-page-header mb-6">
335
+ <h1 className="docs-content-title text-2xl sm:text-3xl font-bold mb-2 text-foreground">
336
+ {pageData.frontmatter.title}
337
+ </h1>
338
+ {pageData.frontmatter.description && (
339
+ <p className="docs-content-description text-lg text-muted-foreground">
340
+ {pageData.frontmatter.description}
341
+ </p>
342
+ )}
343
+ </div>
344
+ )}
278
345
 
279
346
  {/* Page content - MDX rendered with custom components */}
280
347
  <div className="docs-prose prose prose-sm max-w-none prose-headings:text-foreground prose-p:text-muted-foreground prose-strong:text-foreground prose-code:text-foreground prose-pre:bg-muted prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-pre:overflow-x-auto prose-table:w-full prose-th:text-left prose-th:p-3 prose-th:bg-muted prose-td:p-3 prose-td:border-b prose-td:border-border">
@@ -7,7 +7,6 @@ import { DocsSidebar } from './sidebar'
7
7
  import { ApiPlayground } from './playground'
8
8
  import type { DebugContext } from './playground/response-viewer'
9
9
  import { RightSidebar } from './sidebar/right-sidebar'
10
- import { Introduction } from './content/introduction'
11
10
  import { RequestDetails } from './content/request-details'
12
11
  import { DocPage } from './content/doc-page'
13
12
  import { ChangelogPage } from './content/changelog-page'
@@ -241,6 +240,53 @@ function findRequestById(collection: BrainfishCollection, id: string): Brainfish
241
240
  return null
242
241
  }
243
242
 
243
+ // Helper to get the first endpoint from collection
244
+ function getFirstEndpoint(collection: BrainfishCollection): BrainfishRESTRequest | null {
245
+ // Check direct requests first
246
+ if (collection.requests.length > 0) {
247
+ return collection.requests[0]
248
+ }
249
+
250
+ // Check folders recursively
251
+ for (const folder of collection.folders) {
252
+ const firstInFolder = getFirstEndpoint(folder)
253
+ if (firstInFolder) return firstInFolder
254
+ }
255
+ return null
256
+ }
257
+
258
+ // Helper to check if a doc group belongs to a specific tab
259
+ // Group IDs are formatted as: group-{tab-id}-{group-name}
260
+ function isGroupForTab(groupId: string, tabId: string): boolean {
261
+ // Check if the group ID starts with "group-{tabId}-"
262
+ const prefix = `group-${tabId}-`
263
+ return groupId.startsWith(prefix)
264
+ }
265
+
266
+ // Helper to check if a tab has doc groups (MDX pages)
267
+ function hasDocGroupsForTab(
268
+ docGroups: BrainfishDocGroup[] | undefined,
269
+ tabId: string
270
+ ): boolean {
271
+ if (!docGroups || docGroups.length === 0) return false
272
+
273
+ // Check if any doc group belongs to this tab and has pages
274
+ return docGroups.some(g => isGroupForTab(g.id, tabId) && g.pages.length > 0)
275
+ }
276
+
277
+ // Helper to get the first page from doc groups for a tab
278
+ function getFirstDocPageForTab(
279
+ docGroups: BrainfishDocGroup[] | undefined,
280
+ tabId: string
281
+ ): string | null {
282
+ if (!docGroups || docGroups.length === 0) return null
283
+
284
+ const tabDocGroup = docGroups.find(g => isGroupForTab(g.id, tabId) && g.pages.length > 0)
285
+
286
+ return tabDocGroup?.pages[0]?.slug || null
287
+ }
288
+
289
+
244
290
  // API version
245
291
  interface ApiVersion {
246
292
  version: string
@@ -391,19 +437,21 @@ function DocsContent() {
391
437
  const navigateToHash = useCallback((collectionData: BrainfishCollection) => {
392
438
  const hash = window.location.hash.slice(1) // Remove #
393
439
 
394
- console.log('[Docs] Navigating to hash:', hash)
395
-
396
440
  if (!hash) {
397
- // No hash - show Introduction by default
398
- setSelectedRequest(null)
441
+ // No hash - auto-select first endpoint
399
442
  setSelectedDocSection(null)
400
443
  setSelectedDocPage(null)
444
+ const firstEndpoint = getFirstEndpoint(collectionData)
445
+ if (firstEndpoint) {
446
+ setSelectedRequest(firstEndpoint)
447
+ } else {
448
+ setSelectedRequest(null)
449
+ }
401
450
  return
402
451
  }
403
452
 
404
453
  // Notes mode is handled by ModeContext, just clear API selection
405
454
  if (hash === 'notes' || hash.startsWith('notes/')) {
406
- console.log('[Docs] Notes mode detected, clearing API selection')
407
455
  setSelectedRequest(null)
408
456
  setSelectedDocSection(null)
409
457
  setSelectedDocPage(null)
@@ -428,9 +476,7 @@ function DocsContent() {
428
476
  const actualId = id || (legacyType ? hash.replace(`${legacyType}/`, '') : null)
429
477
 
430
478
  if (actualType === 'endpoint' && actualId) {
431
- console.log('[Docs] Looking for endpoint:', actualId)
432
479
  const request = findRequestById(collectionData, actualId)
433
- console.log('[Docs] Found request:', request?.name || 'NOT FOUND')
434
480
  if (request) {
435
481
  setSelectedRequest(request)
436
482
  setSelectedDocSection(null)
@@ -598,18 +644,55 @@ function DocsContent() {
598
644
  // Reset mode to docs when switching tabs (exit sandbox/api-client mode)
599
645
  switchToDocs()
600
646
 
601
- if (tabId === 'api-reference') {
602
- // Switch to API Reference - clear doc page and show introduction (no endpoint selected)
603
- setSelectedDocPage(null)
604
- setSelectedRequest(null)
605
- setSelectedDocSection(null)
606
- updateUrlHash('', tabId)
607
- } else if (tabId === 'changelog') {
647
+ // Find the tab config to check its type
648
+ const tabConfig = collection?.navigationTabs?.find(t => t.id === tabId)
649
+ const isApiTab = tabConfig?.type === 'openapi' || tabConfig?.type === 'graphql' || tabId === 'api-reference'
650
+
651
+ if (tabId === 'changelog') {
608
652
  // Switch to Changelog tab
609
653
  setSelectedDocPage(null)
610
654
  setSelectedRequest(null)
611
655
  setSelectedDocSection(null)
612
656
  updateUrlHash('', tabId)
657
+ } else if (isApiTab) {
658
+ // API Reference or GraphQL tab
659
+ setSelectedDocSection(null)
660
+
661
+ // Check if there are doc groups for this tab (MDX pages)
662
+ const hasGroups = hasDocGroupsForTab(collection?.docGroups, tabId)
663
+
664
+ if (hasGroups) {
665
+ // Has doc groups - select the first page from groups
666
+ const firstPage = getFirstDocPageForTab(collection?.docGroups, tabId)
667
+ if (firstPage) {
668
+ setSelectedDocPage(firstPage)
669
+ setSelectedRequest(null)
670
+ updateUrlHash(`page/${firstPage}`, tabId)
671
+ switchToDocs()
672
+ } else {
673
+ // No pages in groups - auto-select first endpoint
674
+ setSelectedDocPage(null)
675
+ const firstEndpoint = collection ? getFirstEndpoint(collection) : null
676
+ if (firstEndpoint) {
677
+ setSelectedRequest(firstEndpoint)
678
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, tabId)
679
+ } else {
680
+ setSelectedRequest(null)
681
+ updateUrlHash('', tabId)
682
+ }
683
+ }
684
+ } else {
685
+ // No doc groups - auto-select first endpoint
686
+ setSelectedDocPage(null)
687
+ const firstEndpoint = collection ? getFirstEndpoint(collection) : null
688
+ if (firstEndpoint) {
689
+ setSelectedRequest(firstEndpoint)
690
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, tabId)
691
+ } else {
692
+ setSelectedRequest(null)
693
+ updateUrlHash('', tabId)
694
+ }
695
+ }
613
696
  } else {
614
697
  // Switch to a doc group tab - find and select the first page in that tab
615
698
  setSelectedRequest(null)
@@ -617,10 +700,7 @@ function DocsContent() {
617
700
 
618
701
  // Find the first doc group for this tab and select its first page
619
702
  if (collection?.docGroups) {
620
- const tabDocGroup = collection.docGroups.find(g => {
621
- const groupTabPart = g.id.replace('group-', '').split('-')[0]
622
- return groupTabPart === tabId
623
- })
703
+ const tabDocGroup = collection.docGroups.find(g => isGroupForTab(g.id, tabId))
624
704
 
625
705
  if (tabDocGroup && tabDocGroup.pages.length > 0) {
626
706
  const firstPage = tabDocGroup.pages[0]
@@ -733,8 +813,6 @@ function DocsContent() {
733
813
  // Also update shortcut icon and apple-touch-icon
734
814
  updateFaviconLink('shortcut icon', faviconPath)
735
815
  updateFaviconLink('apple-touch-icon', faviconPath)
736
-
737
- console.log('[Docs] Set favicon to:', faviconPath)
738
816
  }, [collection?.docsFavicon])
739
817
 
740
818
  useEffect(() => {
@@ -761,16 +839,6 @@ function DocsContent() {
761
839
  }
762
840
 
763
841
  const data = await response.json()
764
-
765
- console.log('[Docs] Collection data:', JSON.stringify({
766
- hasData: !!data,
767
- name: data?.name,
768
- requestsCount: data?.requests?.length || 0,
769
- foldersCount: data?.folders?.length || 0,
770
- apiVersions: data?.apiVersions?.length || 0,
771
- selectedVersion: data?.selectedApiVersion,
772
- }, null, 2))
773
-
774
842
  setCollection(data)
775
843
 
776
844
  // Set initial API version if not already set
@@ -793,24 +861,60 @@ function DocsContent() {
793
861
  // Use setTimeout to ensure state is set before navigation
794
862
  setTimeout(() => {
795
863
  const hash = window.location.hash.slice(1)
796
- console.log('[Docs] Initial hash:', hash)
797
864
 
798
865
  if (!hash) {
799
866
  // No hash - set URL to initial tab
800
- updateUrlHash('', initialTabId)
801
- setSelectedRequest(null)
802
867
  setSelectedDocSection(null)
803
868
 
804
- if (initialTabId === 'api-reference') {
805
- // API Reference tab - show introduction
806
- setSelectedDocPage(null)
869
+ // Find the tab config to check its type
870
+ const tabConfig = data.navigationTabs?.find((t: NavigationTab) => t.id === initialTabId)
871
+ const isApiTab = tabConfig?.type === 'openapi' || tabConfig?.type === 'graphql' || initialTabId === 'api-reference'
872
+
873
+ if (isApiTab) {
874
+ // API Reference or GraphQL tab
875
+ setSelectedDocSection(null)
876
+
877
+ // Check if there are doc groups for this tab (MDX pages)
878
+ const hasGroups = hasDocGroupsForTab(data.docGroups, initialTabId)
879
+
880
+ if (hasGroups) {
881
+ // Has doc groups - select the first page from groups
882
+ const firstPage = getFirstDocPageForTab(data.docGroups, initialTabId)
883
+ if (firstPage) {
884
+ setSelectedDocPage(firstPage)
885
+ setSelectedRequest(null)
886
+ updateUrlHash(`page/${firstPage}`, initialTabId)
887
+ } else {
888
+ // No pages in groups - auto-select first endpoint
889
+ setSelectedDocPage(null)
890
+ const firstEndpoint = getFirstEndpoint(data)
891
+ if (firstEndpoint) {
892
+ setSelectedRequest(firstEndpoint)
893
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, initialTabId)
894
+ } else {
895
+ setSelectedRequest(null)
896
+ updateUrlHash('', initialTabId)
897
+ }
898
+ }
899
+ } else {
900
+ // No doc groups - auto-select first endpoint
901
+ setSelectedDocPage(null)
902
+ const firstEndpoint = getFirstEndpoint(data)
903
+ if (firstEndpoint) {
904
+ setSelectedRequest(firstEndpoint)
905
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, initialTabId)
906
+ } else {
907
+ setSelectedRequest(null)
908
+ updateUrlHash('', initialTabId)
909
+ }
910
+ }
807
911
  switchToDocs()
808
912
  } else {
809
913
  // Doc group tab - select first page
810
- const tabDocGroup = data.docGroups?.find((g: BrainfishDocGroup) => {
811
- const groupTabPart = g.id.replace('group-', '').split('-')[0]
812
- return groupTabPart === initialTabId
813
- })
914
+ setSelectedRequest(null)
915
+ const tabDocGroup = data.docGroups?.find((g: BrainfishDocGroup) =>
916
+ isGroupForTab(g.id, initialTabId)
917
+ )
814
918
 
815
919
  if (tabDocGroup && tabDocGroup.pages.length > 0) {
816
920
  const firstPage = tabDocGroup.pages[0]
@@ -819,12 +923,12 @@ function DocsContent() {
819
923
  switchToDocs()
820
924
  } else {
821
925
  setSelectedDocPage(null)
926
+ updateUrlHash('', initialTabId)
822
927
  switchToDocs()
823
928
  }
824
929
  }
825
930
  } else if (hash === 'notes' || hash.startsWith('notes/')) {
826
931
  // Notes mode - handled by ModeContext, just clear API selection
827
- console.log('[Docs] Initial hash is notes mode')
828
932
  setSelectedRequest(null)
829
933
  setSelectedDocSection(null)
830
934
  } else {
@@ -846,14 +950,13 @@ function DocsContent() {
846
950
  const actualId = isLegacyFormat ? parts.slice(1).join('/') : hashId
847
951
 
848
952
  if (actualType === 'endpoint' && actualId) {
849
- console.log('[Docs] Looking for endpoint on load:', actualId)
850
953
  const request = findRequestById(data, actualId)
851
- console.log('[Docs] Found:', request?.name || 'NOT FOUND')
852
954
  if (request) {
853
955
  setSelectedRequest(request)
854
956
  setSelectedDocSection(null)
855
957
  setSelectedDocPage(null)
856
958
  switchToDocs()
959
+ return
857
960
  } else {
858
961
  setSelectedRequest(null)
859
962
  setSelectedDocSection(null)
@@ -878,9 +981,49 @@ function DocsContent() {
878
981
  }, 100)
879
982
  } else if (hashTab && !hashType) {
880
983
  // Just a tab, show its default content
881
- setSelectedRequest(null)
882
984
  setSelectedDocSection(null)
883
- setSelectedDocPage(null)
985
+
986
+ // Check if this is an API tab
987
+ const tabConfig = data.navigationTabs?.find((t: NavigationTab) => t.id === hashTab)
988
+ const isApiTab = tabConfig?.type === 'openapi' || tabConfig?.type === 'graphql' || hashTab === 'api-reference'
989
+
990
+ if (isApiTab) {
991
+ // Check if there are doc groups for this tab (MDX pages)
992
+ const hasGroups = hasDocGroupsForTab(data.docGroups, hashTab)
993
+
994
+ if (hasGroups) {
995
+ // Has doc groups - select the first page from groups
996
+ const firstPage = getFirstDocPageForTab(data.docGroups, hashTab)
997
+ if (firstPage) {
998
+ setSelectedDocPage(firstPage)
999
+ setSelectedRequest(null)
1000
+ updateUrlHash(`page/${firstPage}`, hashTab)
1001
+ } else {
1002
+ // No pages in groups - auto-select first endpoint
1003
+ setSelectedDocPage(null)
1004
+ const firstEndpoint = getFirstEndpoint(data)
1005
+ if (firstEndpoint) {
1006
+ setSelectedRequest(firstEndpoint)
1007
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, hashTab)
1008
+ } else {
1009
+ setSelectedRequest(null)
1010
+ }
1011
+ }
1012
+ } else {
1013
+ // No doc groups - auto-select first endpoint
1014
+ setSelectedDocPage(null)
1015
+ const firstEndpoint = getFirstEndpoint(data)
1016
+ if (firstEndpoint) {
1017
+ setSelectedRequest(firstEndpoint)
1018
+ updateUrlHash(`endpoint/${firstEndpoint.id}`, hashTab)
1019
+ } else {
1020
+ setSelectedRequest(null)
1021
+ }
1022
+ }
1023
+ } else {
1024
+ setSelectedDocPage(null)
1025
+ setSelectedRequest(null)
1026
+ }
884
1027
  switchToDocs()
885
1028
  }
886
1029
  }
@@ -1170,17 +1313,13 @@ function DocsWithMode({
1170
1313
  // Find the active tab's config
1171
1314
  const activeTabConfig = collection.navigationTabs?.find(t => t.id === activeTab)
1172
1315
  const activeTabType = activeTabConfig?.type || 'docs'
1173
- // Use the tab name for page header
1174
- const activeTabTitle = activeTabConfig?.tab || 'Documentation'
1175
1316
 
1176
1317
 
1177
1318
  // Filter doc groups for sidebar based on active tab
1178
1319
  // Group IDs are like "group-guides-getting-started", tab IDs are like "guides"
1179
- const filteredDocGroups = collection.docGroups?.filter(g => {
1180
- // Extract the tab part from the group ID (e.g., "guides" from "group-guides-getting-started")
1181
- const groupTabPart = g.id.replace('group-', '').split('-')[0]
1182
- return groupTabPart === activeTab
1183
- }) || []
1320
+ // Filter doc groups for sidebar based on active tab
1321
+ // Group IDs are formatted as: group-{tab-id}-{group-name}
1322
+ const filteredDocGroups = collection.docGroups?.filter(g => isGroupForTab(g.id, activeTab)) || []
1184
1323
 
1185
1324
  // Show endpoints in sidebar only when OpenAPI tab is active
1186
1325
  const showEndpoints = activeTabType === 'openapi'
@@ -1285,7 +1424,7 @@ function DocsWithMode({
1285
1424
  }, [collection?.defaultTheme, setTheme])
1286
1425
 
1287
1426
  return (
1288
- <>
1427
+ <div className="docs-viewer-root flex flex-col h-screen overflow-hidden">
1289
1428
  {/* Notice */}
1290
1429
  {collection.notice && (
1291
1430
  <Notice config={collection.notice} storageKey="docs-notice" />
@@ -1313,7 +1452,7 @@ function DocsWithMode({
1313
1452
  docsNavbar={collection.docsNavbar}
1314
1453
  />
1315
1454
 
1316
- <div className="docs-layout flex h-[calc(100vh-6rem)] overflow-hidden relative z-0">
1455
+ <div className="docs-layout flex flex-1 overflow-hidden relative z-0 min-h-0">
1317
1456
  {/* Left Sidebar - Hidden for changelog */}
1318
1457
  {!showChangelog && (
1319
1458
  <DocsSidebar
@@ -1344,9 +1483,9 @@ function DocsWithMode({
1344
1483
  )}
1345
1484
 
1346
1485
  {/* Center - Toggles between API Client/Playground and Notes */}
1347
- <div className="docs-main flex-1 flex flex-col overflow-hidden min-w-0">
1486
+ <div className="docs-main flex-1 flex flex-col overflow-hidden" style={{ minWidth: 0, flexBasis: 0, flexGrow: 1 }}>
1348
1487
  {/* Mode Toggle Header */}
1349
- <div className="docs-main-header flex items-center justify-end px-3 sm:px-4 h-[41px] border-b border-border bg-muted/30">
1488
+ <div className="docs-main-header flex items-center justify-end px-3 sm:px-4 h-[41px] border-b border-border bg-muted/30 flex-shrink-0">
1350
1489
  <ModeToggleTabs hasEndpoint={!!selectedRequest} />
1351
1490
  </div>
1352
1491
 
@@ -1386,7 +1525,12 @@ function DocsWithMode({
1386
1525
  ) : selectedDocPage ? (
1387
1526
  <DocPage slug={selectedDocPage} />
1388
1527
  ) : (
1389
- <Introduction collection={collection} />
1528
+ <div className="flex-1 flex items-center justify-center bg-background">
1529
+ <div className="text-center text-muted-foreground">
1530
+ <Book className="h-12 w-12 mx-auto mb-3 opacity-40" />
1531
+ <p className="text-sm">Select an endpoint from the sidebar</p>
1532
+ </div>
1533
+ </div>
1390
1534
  )}
1391
1535
  </div>
1392
1536
  </DocsNavigationProvider>
@@ -1446,7 +1590,7 @@ function DocsWithMode({
1446
1590
  open={showAuthModal}
1447
1591
  onClose={() => setShowAuthModal(false)}
1448
1592
  />
1449
- </>
1593
+ </div>
1450
1594
  )
1451
1595
  }
1452
1596