@clidey/dory 0.1.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.
- package/.dockerignore +4 -0
- package/.github/ISSUE_TEMPLATE/anything-else.md +10 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/claude.yml +37 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +91 -0
- package/LICENSE +201 -0
- package/README.md +185 -0
- package/SECURITY.md +11 -0
- package/assets/dory_logo.png +0 -0
- package/bin/dory.js +131 -0
- package/docs/README.md +2 -0
- package/docs/configuration/app-settings/feature-flags.mdx +157 -0
- package/docs/configuration/app-settings/user-settings.mdx +201 -0
- package/docs/configuration/installation-setup/env-vars.mdx +198 -0
- package/docs/configuration/installation-setup/installation.mdx +127 -0
- package/docs/dory.json +162 -0
- package/docs/integrations/analytics-monitoring/error-tracking.mdx +199 -0
- package/docs/integrations/analytics-monitoring/notifications.mdx +189 -0
- package/docs/integrations/analytics-monitoring/user-analytics.mdx +182 -0
- package/docs/integrations/shopify-integration/shopify-setup.mdx +226 -0
- package/docs/integrations/shopify-integration/shopify-webhooks.mdx +207 -0
- package/docs/overview/developer-overview/dev-architecture.mdx +292 -0
- package/docs/overview/product-intro/app-overview.mdx +167 -0
- package/docs/overview/product-intro/architecture.mdx +203 -0
- package/docs/overview/product-intro/core-concepts.mdx +267 -0
- package/docs/platform-admin/billing-subscriptions/billing.mdx +176 -0
- package/docs/platform-admin/system-maintenance/db-migrations.mdx +184 -0
- package/docs/platform-admin/system-maintenance/health-monitoring.mdx +222 -0
- package/docs/platform-admin/system-maintenance/queue-management.mdx +234 -0
- package/docs/platform-admin/user-team-management/teams.mdx +170 -0
- package/docs/platform-admin/user-team-management/users.mdx +185 -0
- package/docs/support/legal/dependency-analysis.mdx +132 -0
- package/docs/support/legal/privacy-terms.mdx +73 -0
- package/docs/support/troubleshooting/common-issues.mdx +159 -0
- package/docs/usage/api-usage/api-inventory.mdx +213 -0
- package/docs/usage/api-usage/api-products.mdx +249 -0
- package/docs/usage/api-usage/api-purchase-orders.mdx +289 -0
- package/docs/usage/api-usage/api-reports.mdx +227 -0
- package/docs/usage/api-usage/api-suppliers.mdx +171 -0
- package/docs/usage/api-usage/api-user-management.mdx +171 -0
- package/docs/usage/ui-workflows/bundles.mdx +157 -0
- package/docs/usage/ui-workflows/demand-planning.mdx +210 -0
- package/docs/usage/ui-workflows/favorites.mdx +167 -0
- package/docs/usage/ui-workflows/inventory-management.mdx +179 -0
- package/docs/usage/ui-workflows/onboarding.mdx +215 -0
- package/docs/usage/ui-workflows/purchase-orders.mdx +200 -0
- package/docs/usage/ui-workflows/reports.mdx +257 -0
- package/index.html +54 -0
- package/k8s/Dockerfile +15 -0
- package/k8s/config/nginx-default.conf +22 -0
- package/k8s/dev.Dockerfile +16 -0
- package/package.json +71 -0
- package/src/app.css +3 -0
- package/src/app.tsx +13 -0
- package/src/components/dark-mode-toggle.tsx +25 -0
- package/src/components/dropdown.tsx +49 -0
- package/src/components/hooks.tsx +28 -0
- package/src/components/loading.tsx +23 -0
- package/src/components/mdx-provider.tsx +13 -0
- package/src/components/navigation.tsx +53 -0
- package/src/components/notification.tsx +97 -0
- package/src/components/routes.tsx +89 -0
- package/src/components/search-index.ts +43 -0
- package/src/components/search.tsx +508 -0
- package/src/components/store.ts +144 -0
- package/src/index.css +24 -0
- package/src/main.tsx +18 -0
- package/src/mdx/accordion.tsx +48 -0
- package/src/mdx/api-playground.tsx +674 -0
- package/src/mdx/api.tsx +354 -0
- package/src/mdx/callout.tsx +80 -0
- package/src/mdx/card.tsx +48 -0
- package/src/mdx/code.tsx +289 -0
- package/src/mdx/expandable.tsx +40 -0
- package/src/mdx/fence.tsx +73 -0
- package/src/mdx/icon.tsx +73 -0
- package/src/mdx/latex.tsx +26 -0
- package/src/mdx/list.tsx +7 -0
- package/src/mdx/mdx.tsx +43 -0
- package/src/mdx/mermaid.tsx +102 -0
- package/src/mdx/open-api.tsx +325 -0
- package/src/mdx/source.tsx +47 -0
- package/src/mdx/step.tsx +49 -0
- package/src/mdx/table.tsx +15 -0
- package/src/mdx/tag.tsx +66 -0
- package/src/mdx/text.tsx +98 -0
- package/src/plugins/sanitize.ts +15 -0
- package/src/theme.scss +124 -0
- package/src/ui/header.tsx +88 -0
- package/src/ui/layout.tsx +167 -0
- package/src/ui/mobile-navigation.tsx +91 -0
- package/src/ui/prev-next-link.tsx +74 -0
- package/src/ui/table-of-content.tsx +166 -0
- package/src/utils/fonts.ts +20 -0
- package/src/utils/functions.ts +12 -0
- package/src/utils/hooks.ts +46 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +32 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +25 -0
- package/vite.config.ts +27 -0
package/src/theme.scss
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
html {
|
|
2
|
+
background-color: #ffffff !important;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
h1, h2, h3, h4, h5, h6, p, li {
|
|
6
|
+
color: #000000 !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.mermaid {
|
|
10
|
+
background-color: #ffffff !important;
|
|
11
|
+
color: #000000 !important;
|
|
12
|
+
font-size: 12px !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.mermaid rect {
|
|
16
|
+
fill: #f8f8f8 !important;
|
|
17
|
+
stroke: #333333 !important;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.mermaid p {
|
|
21
|
+
color: #000000 !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.mermaid path {
|
|
25
|
+
stroke: #555555 !important;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.mermaid p {
|
|
29
|
+
background-color: #f8f8f8 !important;
|
|
30
|
+
color: #000000 !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.mermaid p, .mermaid text, .mermaid text > tspan {
|
|
34
|
+
color: #000000 !important;
|
|
35
|
+
fill: #000000 !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.mermaid line {
|
|
39
|
+
stroke: #333333 !important;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.mermaid .note {
|
|
43
|
+
fill: #ffffcc !important;
|
|
44
|
+
stroke: #999999 !important;
|
|
45
|
+
color: #000000 !important;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.docucod-mdx-code {
|
|
49
|
+
background-color: #f8f8f8 !important;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
body.dark {
|
|
53
|
+
background-color: #1e1e1e !important;
|
|
54
|
+
|
|
55
|
+
#container {
|
|
56
|
+
background-color: #1e1e1e !important;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
h1, h2, h3, h4, h5, h6, p, li, th, td {
|
|
60
|
+
color: #ffffff !important;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.mermaid {
|
|
64
|
+
background-color: #1e1e1e !important;
|
|
65
|
+
color: #ffffff !important;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.mermaid rect {
|
|
69
|
+
fill: #2a2a2a !important;
|
|
70
|
+
stroke: #aaaaaa !important;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.mermaid p {
|
|
74
|
+
color: #ffffff !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.mermaid path {
|
|
78
|
+
stroke: #cccccc !important;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.mermaid p {
|
|
82
|
+
background-color: #2a2a2a !important;
|
|
83
|
+
color: #ffffff !important;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.mermaid rect,
|
|
87
|
+
.mermaid polygon,
|
|
88
|
+
.mermaid circle,
|
|
89
|
+
.mermaid ellipse {
|
|
90
|
+
fill: #2a2a2a !important;
|
|
91
|
+
stroke: #444444 !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.mermaid p, .mermaid text, .mermaid text > tspan {
|
|
95
|
+
color: #ffffff !important;
|
|
96
|
+
fill: #ffffff !important;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.mermaid line {
|
|
100
|
+
stroke: #444444 !important;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.mermaid .note {
|
|
104
|
+
fill: #2a2a2a !important;
|
|
105
|
+
stroke: #444444 !important;
|
|
106
|
+
color: #ffffff !important;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.docucod-mdx-code {
|
|
110
|
+
background-color: #2a2a2a !important;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.docucod-mdx-code-group {
|
|
114
|
+
background-color: #2a2a2a !important;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.docucod-mdx-code-group-header {
|
|
118
|
+
background-color: #2a2a2a !important;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.prism-code {
|
|
122
|
+
background-color: transparent !important;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'preact/hooks';
|
|
3
|
+
import { Link } from 'wouter-preact';
|
|
4
|
+
import { usePathname } from 'wouter-preact/use-browser-location';
|
|
5
|
+
import docsConfig from '../../docs/dory.json';
|
|
6
|
+
import { DarkModeToggle } from '../components/dark-mode-toggle';
|
|
7
|
+
import { MobileSearch, Search } from '../components/search';
|
|
8
|
+
import { ALL_NAVIGATION } from '../components/store';
|
|
9
|
+
import { MobileNavigation } from './mobile-navigation';
|
|
10
|
+
|
|
11
|
+
function TopLevelNavItem({ href, children, isActive }: { href: string; children: string; isActive?: boolean }) {
|
|
12
|
+
return (
|
|
13
|
+
<li>
|
|
14
|
+
<Link
|
|
15
|
+
to={href}
|
|
16
|
+
className={classNames(
|
|
17
|
+
"text-sm/5 transition",
|
|
18
|
+
isActive
|
|
19
|
+
? "text-sky-500"
|
|
20
|
+
: "text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white"
|
|
21
|
+
)}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</Link>
|
|
25
|
+
</li>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function Header({ className }: { className?: string }) {
|
|
30
|
+
const [isScrolled, setIsScrolled] = useState(false);
|
|
31
|
+
const pathname = usePathname();
|
|
32
|
+
|
|
33
|
+
const { light, dark } = useMemo(() => {
|
|
34
|
+
return { light: docsConfig.logo.light, dark: docsConfig.logo.dark };
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
function onScroll() {
|
|
39
|
+
setIsScrolled(window.scrollY > 0);
|
|
40
|
+
}
|
|
41
|
+
onScroll();
|
|
42
|
+
window.addEventListener('scroll', onScroll, { passive: true });
|
|
43
|
+
return () => {
|
|
44
|
+
window.removeEventListener('scroll', onScroll);
|
|
45
|
+
}
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<header
|
|
50
|
+
className={classNames(
|
|
51
|
+
'sticky top-0 z-50 flex flex-none flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-gray-900/5 sm:px-6 lg:px-8 dark:shadow-none',
|
|
52
|
+
className,
|
|
53
|
+
{
|
|
54
|
+
'dark:bg-gray-900/95 dark:backdrop-blur-sm dark:[@supports(backdrop-filter:blur(0))]:bg-neutral-900/75': isScrolled,
|
|
55
|
+
'dark:bg-transparent': !isScrolled,
|
|
56
|
+
},
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
<div className="relative flex justify-between gap-6 sm:gap-8 md:grow items-center w-full">
|
|
60
|
+
<div className="flex items-center grow gap-4 lg:gap-6">
|
|
61
|
+
<img src={`/${light}`} alt="logo" className="w-10 h-10 hidden dark:block rounded-full" />
|
|
62
|
+
<img src={`/${dark}`} alt="logo" className="w-10 h-10 block dark:hidden rounded-full" />
|
|
63
|
+
<h1 className="text-base lg:text-2xl font-bold">{docsConfig.name}</h1>
|
|
64
|
+
</div>
|
|
65
|
+
<nav className="hidden md:block">
|
|
66
|
+
<ul role="list" className="flex items-center gap-8">
|
|
67
|
+
{ALL_NAVIGATION.map((tab) => (
|
|
68
|
+
<TopLevelNavItem
|
|
69
|
+
key={tab.title}
|
|
70
|
+
href={tab.groups[0].pages[0].href}
|
|
71
|
+
isActive={pathname.startsWith(`/${tab.groups[0].pages[0].href.split('/')[1]}`)}
|
|
72
|
+
>
|
|
73
|
+
{tab.title}
|
|
74
|
+
</TopLevelNavItem>
|
|
75
|
+
))}
|
|
76
|
+
</ul>
|
|
77
|
+
</nav>
|
|
78
|
+
<div className="hidden md:block md:h-5 md:w-px md:bg-zinc-900/10 md:dark:bg-white/15" />
|
|
79
|
+
<div className="flex items-center gap-3">
|
|
80
|
+
<MobileSearch />
|
|
81
|
+
<Search />
|
|
82
|
+
<DarkModeToggle />
|
|
83
|
+
<MobileNavigation />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</header>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { ComponentChildren } from 'preact';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
|
|
3
|
+
import { useLocation } from 'wouter-preact';
|
|
4
|
+
import { Navigation } from '../components/navigation';
|
|
5
|
+
import { ALL_NAVIGATION, ALL_OPENAPI, completeFrontMatter, loadMDXFrontMatterForPath, loadAllMDXFrontMatter, ALL_PAGES, pathFromFilename } from '../components/store';
|
|
6
|
+
import { OpenAPI } from '../mdx/open-api';
|
|
7
|
+
import { Header } from './header';
|
|
8
|
+
import { PrevNextLinks } from './prev-next-link';
|
|
9
|
+
import { TableOfContents } from './table-of-content';
|
|
10
|
+
import { Loading } from '../components/loading';
|
|
11
|
+
import Dropdown from '../components/dropdown';
|
|
12
|
+
import { useNotification } from '../components/notification';
|
|
13
|
+
|
|
14
|
+
interface LayoutProps {
|
|
15
|
+
children: ComponentChildren;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const prompt = (pathname: string) => `Please read and analyze the content from this documentation page: ${window.location.origin}${pathname}.mdx\n\n` +
|
|
19
|
+
`Then, help me understand and answer any questions I have about it. ` +
|
|
20
|
+
`Please provide clear, detailed explanations and examples where relevant.`;
|
|
21
|
+
|
|
22
|
+
export default function Layout({ children }: LayoutProps) {
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [pathname] = useLocation();
|
|
25
|
+
const { showNotification } = useNotification();
|
|
26
|
+
|
|
27
|
+
const handleOpenMDX = useCallback(() => {
|
|
28
|
+
window.open(`${window.location.origin}${pathname}.mdx`, '_blank');
|
|
29
|
+
}, [pathname]);
|
|
30
|
+
|
|
31
|
+
const handleCopyMDX = useCallback(() => {
|
|
32
|
+
ALL_PAGES[pathFromFilename(pathname)]().then((mdx) => {
|
|
33
|
+
navigator.clipboard.writeText(mdx.default.toString());
|
|
34
|
+
});
|
|
35
|
+
showNotification('Copied to clipboard');
|
|
36
|
+
}, []);
|
|
37
|
+
const handleOpenChatGPT = useCallback(() => {
|
|
38
|
+
const url = `https://chat.openai.com/?q=${encodeURIComponent(prompt(pathname))}`;
|
|
39
|
+
window.open(url, '_blank');
|
|
40
|
+
}, [pathname]);
|
|
41
|
+
const handleOpenAnthropic = useCallback(() => {
|
|
42
|
+
const url = `https://claude.ai/new?q=${encodeURIComponent(prompt(pathname))}`;
|
|
43
|
+
window.open(url, '_blank');
|
|
44
|
+
}, [pathname]);
|
|
45
|
+
|
|
46
|
+
const dropdownItems = useMemo(() => [
|
|
47
|
+
{
|
|
48
|
+
label: 'Open MDX',
|
|
49
|
+
onClick: handleOpenMDX,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Copy MDX',
|
|
53
|
+
onClick: handleCopyMDX
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Open in ChatGPT',
|
|
57
|
+
onClick: handleOpenChatGPT
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: 'Open in Anthropic',
|
|
61
|
+
onClick: handleOpenAnthropic
|
|
62
|
+
}
|
|
63
|
+
], [handleOpenMDX, handleCopyMDX, handleOpenChatGPT, handleOpenAnthropic]);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
loadMDXFrontMatterForPath(pathname).then(() => {
|
|
67
|
+
loadAllMDXFrontMatter(pathname).then(() => {
|
|
68
|
+
setLoading(false);
|
|
69
|
+
}).catch(() => {
|
|
70
|
+
setLoading(false);
|
|
71
|
+
});
|
|
72
|
+
}).catch(() => {
|
|
73
|
+
setLoading(false);
|
|
74
|
+
});
|
|
75
|
+
}, [pathname]);
|
|
76
|
+
|
|
77
|
+
const { title: group, group: title } = useMemo(() => {
|
|
78
|
+
const group = ALL_NAVIGATION.flatMap((tab) =>
|
|
79
|
+
tab.groups.find((group) =>
|
|
80
|
+
group.pages.some((page) => page.href === pathname)
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
const page = completeFrontMatter.find((page) => page.path === pathname);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (page?.title) {
|
|
87
|
+
document.title = page.title;
|
|
88
|
+
}
|
|
89
|
+
if (page?.description) {
|
|
90
|
+
const metaDescription = document.querySelector('meta[name="description"]');
|
|
91
|
+
if (metaDescription) {
|
|
92
|
+
metaDescription.setAttribute('content', page.description);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}, [page]);
|
|
96
|
+
|
|
97
|
+
return { group: group[0]?.title, title: page?.title, };
|
|
98
|
+
}, [pathname, loading]);
|
|
99
|
+
|
|
100
|
+
const tab = useMemo(() => {
|
|
101
|
+
return completeFrontMatter.find((page) => page.path === pathname);
|
|
102
|
+
}, [pathname, loading]);
|
|
103
|
+
|
|
104
|
+
const openAPIJSON = useMemo(() => {
|
|
105
|
+
if (tab == null || tab.openapi == null) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const firstDirectory = `/${tab.path.split('/')[1]}`;
|
|
109
|
+
return ALL_OPENAPI[`${firstDirectory}/openapi.json`].default;
|
|
110
|
+
}, [tab, loading]);
|
|
111
|
+
|
|
112
|
+
const { method, path } = useMemo(() => {
|
|
113
|
+
if (tab == null || tab.openapi == null) {
|
|
114
|
+
return { method: '', path: '' };
|
|
115
|
+
}
|
|
116
|
+
return { method: tab.openapi.split(' ')[0], path: tab.openapi.split(' ')[1] };
|
|
117
|
+
}, [tab, loading]);
|
|
118
|
+
|
|
119
|
+
if (loading) return <div className="h-[25vh] flex grow"><Loading /></div>;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div className="flex w-full flex-col">
|
|
123
|
+
<Header />
|
|
124
|
+
<div id="container" className="relative mx-auto flex w-full max-w-8xl flex-auto justify-center sm:px-2 lg:px-8 xl:px-32">
|
|
125
|
+
<div className="hidden lg:relative lg:block lg:flex-none">
|
|
126
|
+
<div className="sticky top-[4.75rem] -ml-0.5 h-[calc(100vh-4.75rem)] w-64 overflow-x-hidden overflow-y-auto py-16 pr-8 pl-0.5 xl:w-72 xl:pr-16">
|
|
127
|
+
<Navigation />
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<main className="flex px-4 py-16 sm:px-6 lg:px-8 grow">
|
|
131
|
+
<div className="max-w-[calc(100vw-16px)] xl:max-w-[70vw] min-w-0 px-4 lg:pr-0 lg:pl-8 xl:px-16 w-full">
|
|
132
|
+
<article className="h-full flex flex-1 flex-col">
|
|
133
|
+
<div className="flex justify-between items-center">
|
|
134
|
+
<header className="mb-8">
|
|
135
|
+
{title && (
|
|
136
|
+
<h1 className="font-display text-lg tracking-tight">
|
|
137
|
+
{title}
|
|
138
|
+
</h1>
|
|
139
|
+
)}
|
|
140
|
+
{group && (
|
|
141
|
+
<p className="text-4xl tracking-tight">
|
|
142
|
+
{group}
|
|
143
|
+
</p>
|
|
144
|
+
)}
|
|
145
|
+
</header>
|
|
146
|
+
{
|
|
147
|
+
openAPIJSON == null &&
|
|
148
|
+
<Dropdown className='min-w-[120px]' buttonLabel="AI Actions" items={dropdownItems} />
|
|
149
|
+
}
|
|
150
|
+
</div>
|
|
151
|
+
<div className="flex-1 min-h-[calc(100vh-2rem)]">
|
|
152
|
+
{openAPIJSON && tab && "openapi" in tab && method && path ?
|
|
153
|
+
<OpenAPI openAPIJson={JSON.parse(openAPIJSON)} method={method} path={path} />
|
|
154
|
+
: children}
|
|
155
|
+
</div>
|
|
156
|
+
<PrevNextLinks />
|
|
157
|
+
</article>
|
|
158
|
+
</div>
|
|
159
|
+
{
|
|
160
|
+
openAPIJSON == null &&
|
|
161
|
+
<TableOfContents />
|
|
162
|
+
}
|
|
163
|
+
</main>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogBackdrop,
|
|
6
|
+
DialogPanel,
|
|
7
|
+
TransitionChild,
|
|
8
|
+
} from '@headlessui/react'
|
|
9
|
+
import { motion } from 'motion/react'
|
|
10
|
+
import { createContext, Suspense, useContext, useState } from 'react'
|
|
11
|
+
|
|
12
|
+
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
|
13
|
+
import { Navigation } from '../components/navigation'
|
|
14
|
+
|
|
15
|
+
const IsInsideMobileNavigationContext = createContext(false)
|
|
16
|
+
|
|
17
|
+
function MobileNavigationDialog({
|
|
18
|
+
isOpen,
|
|
19
|
+
close,
|
|
20
|
+
}: {
|
|
21
|
+
isOpen: boolean
|
|
22
|
+
close: () => void
|
|
23
|
+
}) {
|
|
24
|
+
return (
|
|
25
|
+
<Dialog
|
|
26
|
+
transition
|
|
27
|
+
open={isOpen}
|
|
28
|
+
onClose={close}
|
|
29
|
+
className="fixed inset-0 z-50 lg:hidden"
|
|
30
|
+
>
|
|
31
|
+
<DialogBackdrop
|
|
32
|
+
transition
|
|
33
|
+
className="fixed inset-0 top-0 pt-8 bg-zinc-400/20 backdrop-blur-xs data-closed:opacity-0 data-enter:duration-300 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-black/40"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<DialogPanel>
|
|
37
|
+
<TransitionChild>
|
|
38
|
+
<motion.div
|
|
39
|
+
layoutScroll
|
|
40
|
+
className="fixed top-0 bottom-0 left-0 w-full overflow-y-auto bg-white px-4 pt-8 pb-4 shadow-lg ring-1 shadow-zinc-900/10 ring-zinc-900/7.5 duration-500 ease-in-out data-closed:-translate-x-full min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800 flex flex-col justify-between"
|
|
41
|
+
>
|
|
42
|
+
<Navigation />
|
|
43
|
+
<div class="flex flex-col justify-end items-end mb-4 mr-2" onClick={close}>
|
|
44
|
+
<XMarkIcon className="w-8 stroke-zinc-900 dark:stroke-white" />
|
|
45
|
+
</div>
|
|
46
|
+
</motion.div>
|
|
47
|
+
</TransitionChild>
|
|
48
|
+
</DialogPanel>
|
|
49
|
+
</Dialog>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function useIsInsideMobileNavigation() {
|
|
54
|
+
return useContext(IsInsideMobileNavigationContext)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function useMobileNavigation() {
|
|
58
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
isOpen,
|
|
62
|
+
open: () => setIsOpen(true),
|
|
63
|
+
close: () => setIsOpen(false),
|
|
64
|
+
toggle: () => setIsOpen(prev => !prev)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function MobileNavigation() {
|
|
69
|
+
const isInsideMobileNavigation = useIsInsideMobileNavigation()
|
|
70
|
+
const { isOpen, toggle, close } = useMobileNavigation()
|
|
71
|
+
const ToggleIcon = isOpen ? XMarkIcon : Bars3Icon;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<IsInsideMobileNavigationContext.Provider value={true}>
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
className="relative flex size-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5 lg:hidden"
|
|
78
|
+
aria-label="Toggle navigation"
|
|
79
|
+
onClick={toggle}
|
|
80
|
+
>
|
|
81
|
+
<span className="absolute size-12 [@media(pointer:fine)]:hidden" />
|
|
82
|
+
<ToggleIcon className="w-8 stroke-zinc-900 dark:stroke-white" />
|
|
83
|
+
</button>
|
|
84
|
+
{!isInsideMobileNavigation && (
|
|
85
|
+
<Suspense fallback={null}>
|
|
86
|
+
<MobileNavigationDialog isOpen={isOpen} close={close} />
|
|
87
|
+
</Suspense>
|
|
88
|
+
)}
|
|
89
|
+
</IsInsideMobileNavigationContext.Provider>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ArrowRightIcon } from "@heroicons/react/24/outline";
|
|
2
|
+
import classNames from "classnames";
|
|
3
|
+
import { ALL_NAVIGATION } from "../components/store";
|
|
4
|
+
import { usePathname } from "../components/hooks";
|
|
5
|
+
|
|
6
|
+
function PageLink({
|
|
7
|
+
title,
|
|
8
|
+
href,
|
|
9
|
+
dir = 'next',
|
|
10
|
+
...props
|
|
11
|
+
}: Omit<React.ComponentPropsWithoutRef<'div'>, 'dir' | 'title'> & {
|
|
12
|
+
title: string
|
|
13
|
+
href: string
|
|
14
|
+
dir?: 'previous' | 'next'
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<div {...props}>
|
|
18
|
+
<p className="font-display text-sm font-medium">
|
|
19
|
+
{dir === 'next' ? 'Next' : 'Previous'}
|
|
20
|
+
</p>
|
|
21
|
+
<div className="mt-1">
|
|
22
|
+
<a href={href} className={classNames('flex items-center gap-x-1 text-base font-semibold hover:opacity-80', {
|
|
23
|
+
'flex-row-reverse': dir === 'previous',
|
|
24
|
+
},
|
|
25
|
+
)}>
|
|
26
|
+
<p className={classNames("flex items-center gap-x-1 text-base font-semibold hover:opacity-80", {
|
|
27
|
+
"flex-row-reverse": dir === "previous"
|
|
28
|
+
})}>
|
|
29
|
+
{title}
|
|
30
|
+
<ArrowRightIcon
|
|
31
|
+
className={classNames(
|
|
32
|
+
'h-4 w-4 flex-none fill-current',
|
|
33
|
+
{
|
|
34
|
+
'rotate-180': dir === 'previous',
|
|
35
|
+
},
|
|
36
|
+
)}
|
|
37
|
+
/>
|
|
38
|
+
</p>
|
|
39
|
+
</a>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
export function PrevNextLinks() {
|
|
45
|
+
const pathname = usePathname();
|
|
46
|
+
const allLinks = ALL_NAVIGATION.flatMap(tab =>
|
|
47
|
+
tab.groups.flatMap(group =>
|
|
48
|
+
group.pages.map(page => page.href)
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
const linkIndex = allLinks.findIndex(link => link === pathname);
|
|
52
|
+
const previousPage = linkIndex > -1 ? allLinks[linkIndex - 1] : null;
|
|
53
|
+
const nextPage = linkIndex > -1 ? allLinks[linkIndex + 1] : null;
|
|
54
|
+
|
|
55
|
+
const previousPageTitle = previousPage ? ALL_NAVIGATION
|
|
56
|
+
.flatMap(tab => tab.groups)
|
|
57
|
+
.flatMap(group => group.pages)
|
|
58
|
+
.find(page => page.href === previousPage)?.title : null;
|
|
59
|
+
const nextPageTitle = nextPage ? ALL_NAVIGATION
|
|
60
|
+
.flatMap(tab => tab.groups)
|
|
61
|
+
.flatMap(group => group.pages)
|
|
62
|
+
.find(page => page.href === nextPage)?.title : null;
|
|
63
|
+
|
|
64
|
+
if (!nextPage && !previousPage) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="mt-12 pt-6 flex border-t border-black/20 dark:border-white/20">
|
|
70
|
+
{previousPage && <PageLink dir="previous" title={previousPageTitle ?? ''} href={previousPage} />}
|
|
71
|
+
{nextPage && <PageLink className="ml-auto text-right" title={nextPageTitle ?? ''} href={nextPage} />}
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|