@acmekit/docs-app 2.13.43

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.
@@ -0,0 +1,99 @@
1
+ import React, { Suspense, useEffect, useRef } from "react"
2
+ import { BrowserRouter, Routes, Route } from "react-router-dom"
3
+ import { routes, components, importers } from "virtual:acmekit/docs-routes"
4
+ import { config } from "virtual:acmekit/docs-config"
5
+ import { Loading } from "@acmekit/docs-ui"
6
+ import { DocsProviders } from "./providers/DocsProviders"
7
+ import { Layout } from "./components/Layout"
8
+ import { DocPage } from "./components/DocPage"
9
+ import { HomePage } from "./components/HomePage"
10
+ import { RouteErrorBoundary } from "./components/RouteErrorBoundary"
11
+ import { SearchModal } from "./components/SearchModal"
12
+ import type { DocsPlugin } from "./types"
13
+
14
+ type DocsAppProps = {
15
+ plugins?: DocsPlugin[]
16
+ }
17
+
18
+ export function DocsApp({ plugins = [] }: DocsAppProps) {
19
+ const allRoutes = [...routes]
20
+ const prefetched = useRef(new Set<string>())
21
+
22
+ // Merge plugin contributions
23
+ for (const plugin of plugins) {
24
+ if (plugin.routes) {
25
+ allRoutes.push(...plugin.routes)
26
+ }
27
+ }
28
+
29
+ // Prefetch chunks on link hover
30
+ useEffect(() => {
31
+ function onMouseOver(e: MouseEvent) {
32
+ const anchor = (e.target as HTMLElement).closest?.("a")
33
+ if (!anchor) return
34
+
35
+ const href = anchor.getAttribute("href")
36
+ if (!href || prefetched.current.has(href)) return
37
+
38
+ const importer = importers[href]
39
+ if (importer) {
40
+ prefetched.current.add(href)
41
+ importer()
42
+ }
43
+ }
44
+
45
+ document.addEventListener("mouseover", onMouseOver)
46
+ return () => document.removeEventListener("mouseover", onMouseOver)
47
+ }, [])
48
+
49
+ return (
50
+ <BrowserRouter basename={config.basePath}>
51
+ <DocsProviders config={config}>
52
+ <div className="h-screen w-full overflow-hidden">
53
+ <RouteErrorBoundary>
54
+ <Suspense fallback={<Loading count={8} />}>
55
+ <Routes>
56
+ <Route element={<Layout />}>
57
+ <Route index element={<HomePage />} />
58
+ {allRoutes.map((route) => {
59
+ const Component = components[route.path]
60
+ if (!Component) return null
61
+
62
+ if (route.type === "mdx") {
63
+ return (
64
+ <Route
65
+ key={route.path}
66
+ path={route.path}
67
+ element={
68
+ <DocPage frontmatter={route.frontmatter}>
69
+ <Suspense fallback={<Loading count={8} />}>
70
+ <Component />
71
+ </Suspense>
72
+ </DocPage>
73
+ }
74
+ />
75
+ )
76
+ }
77
+
78
+ return (
79
+ <Route
80
+ key={route.path}
81
+ path={route.path}
82
+ element={
83
+ <Suspense fallback={<Loading count={8} />}>
84
+ <Component />
85
+ </Suspense>
86
+ }
87
+ />
88
+ )
89
+ })}
90
+ </Route>
91
+ </Routes>
92
+ </Suspense>
93
+ </RouteErrorBoundary>
94
+ <SearchModal />
95
+ </div>
96
+ </DocsProviders>
97
+ </BrowserRouter>
98
+ )
99
+ }
@@ -0,0 +1,29 @@
1
+ import React from "react"
2
+ import { renderToString } from "react-dom/server"
3
+ import { StaticRouter } from "react-router-dom/server"
4
+ import { Routes, Route } from "react-router-dom"
5
+ import { routes, components } from "virtual:acmekit/docs-routes"
6
+
7
+ export { routes }
8
+
9
+ export function render(url: string, basePath: string): string {
10
+ return renderToString(
11
+ <StaticRouter basename={basePath} location={url}>
12
+ <div className="h-screen w-full overflow-hidden">
13
+ <Routes>
14
+ {routes.map((route) => {
15
+ const Component = components[route.path]
16
+ if (!Component) return null
17
+ return (
18
+ <Route
19
+ key={route.path}
20
+ path={route.path}
21
+ element={<Component />}
22
+ />
23
+ )
24
+ })}
25
+ </Routes>
26
+ </div>
27
+ </StaticRouter>
28
+ )
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { DocsApp } from "./docs-app"
2
+ export type { DocsPlugin } from "./types"
@@ -0,0 +1,21 @@
1
+ declare module "virtual:acmekit/docs-routes" {
2
+ import type { DocsRouteEntry } from "@acmekit/docs-shared"
3
+ export const routes: DocsRouteEntry[]
4
+ export const components: Record<string, React.ComponentType<any>>
5
+ export const importers: Record<string, () => Promise<any>>
6
+ }
7
+
8
+ declare module "virtual:acmekit/docs-search" {
9
+ import type { DocsSearchEntry } from "@acmekit/docs-shared"
10
+ export const searchIndex: DocsSearchEntry[]
11
+ }
12
+
13
+ declare module "virtual:acmekit/docs-sidebar" {
14
+ import type { DocsSidebarItem } from "@acmekit/docs-shared"
15
+ export const sidebar: DocsSidebarItem[]
16
+ }
17
+
18
+ declare module "virtual:acmekit/docs-config" {
19
+ import type { DocsConfig } from "@acmekit/docs-shared"
20
+ export const config: DocsConfig
21
+ }
@@ -0,0 +1,66 @@
1
+ import React, { useMemo } from "react"
2
+ import {
3
+ RootProviders,
4
+ SiteConfigProvider,
5
+ ScrollControllerProvider,
6
+ SidebarProvider,
7
+ NotificationProvider,
8
+ PaginationProvider,
9
+ MainNavProvider,
10
+ SearchProvider,
11
+ AiAssistantProvider,
12
+ } from "@acmekit/docs-ui"
13
+ type DocsProvidersProps = {
14
+ children: React.ReactNode
15
+ config: Record<string, any>
16
+ }
17
+
18
+ export function DocsProviders({ children, config }: DocsProvidersProps) {
19
+ const navItems = useMemo(() => {
20
+ if (!config.areas?.length) {
21
+ return []
22
+ }
23
+
24
+ return config.areas.map((area: any) => ({
25
+ type: "link" as const,
26
+ link: `/${area.id}`,
27
+ title: area.title,
28
+ }))
29
+ }, [config.areas])
30
+
31
+ return (
32
+ <RootProviders>
33
+ <SiteConfigProvider
34
+ config={{
35
+ baseUrl: "",
36
+ basePath: config.basePath,
37
+ sidebars: config.sidebars || [],
38
+ project: {
39
+ title: config.title || "Documentation",
40
+ key: "docs",
41
+ },
42
+ logo: config.logo || "",
43
+ version: config.version,
44
+ }}
45
+ >
46
+ <ScrollControllerProvider scrollableSelector="#main">
47
+ <SidebarProvider
48
+ sidebars={config.sidebars || []}
49
+ isSidebarStatic={true}
50
+ shouldHandlePathChange={true}
51
+ >
52
+ <NotificationProvider>
53
+ <PaginationProvider>
54
+ <MainNavProvider navItems={navItems}>
55
+ <SearchProvider>
56
+ <AiAssistantProvider>{children}</AiAssistantProvider>
57
+ </SearchProvider>
58
+ </MainNavProvider>
59
+ </PaginationProvider>
60
+ </NotificationProvider>
61
+ </SidebarProvider>
62
+ </ScrollControllerProvider>
63
+ </SiteConfigProvider>
64
+ </RootProviders>
65
+ )
66
+ }
@@ -0,0 +1,13 @@
1
+ import type { DocsRouteEntry } from "@acmekit/docs-shared"
2
+
3
+ export function buildRoutes(
4
+ routes: DocsRouteEntry[],
5
+ components: Record<string, React.ComponentType<any>>
6
+ ) {
7
+ return routes
8
+ .filter((route) => components[route.path])
9
+ .map((route) => ({
10
+ ...route,
11
+ component: components[route.path],
12
+ }))
13
+ }
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { DocsSidebarItem, DocsRouteEntry } from "@acmekit/docs-shared"
2
+
3
+ export type DocsPlugin = {
4
+ name: string
5
+ routes?: DocsRouteEntry[]
6
+ sidebar?: DocsSidebarItem[]
7
+ }