@xyd-js/plugin-docs 0.1.0-build.160

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,340 @@
1
+ import { useEffect, useMemo } from "react";
2
+ import {
3
+ Outlet,
4
+ useLoaderData,
5
+ useLocation,
6
+ useNavigate,
7
+ useNavigation,
8
+ useMatches
9
+ } from "react-router";
10
+
11
+ import { UXNode } from "openux-js";
12
+
13
+ import { mapSettingsToProps } from "@xyd-js/framework/hydration";
14
+
15
+ import type { Metadata, MetadataMap, Settings, Theme as ThemeSettings } from "@xyd-js/core";
16
+ import type { INavLinks, IBreadcrumb } from "@xyd-js/ui";
17
+ import { Framework, FwLink, FwLogo, useSettings, type FwSidebarItemProps } from "@xyd-js/framework/react";
18
+ import { ReactContent } from "@xyd-js/components/content";
19
+ import { Atlas, AtlasContext, type VariantToggleConfig } from "@xyd-js/atlas";
20
+ import AtlasXydPlugin from "@xyd-js/atlas/xydPlugin";
21
+
22
+ import { Surfaces, pageMetaLayout } from "@xyd-js/framework";
23
+ import { Composer } from "@xyd-js/composer";
24
+ import { Analytics, useAnalytics } from "@xyd-js/analytics";
25
+ // @ts-ignore
26
+ import { iconSet } from 'virtual:xyd-icon-set';
27
+
28
+ // @ts-ignore
29
+ import virtualSettings from "virtual:xyd-settings";
30
+ // @ts-ignore
31
+ const { settings: getSettings, settingsClone, userPreferences } = virtualSettings
32
+
33
+ // const settings = globalThis.__xydSettings
34
+ import Theme from "virtual:xyd-theme";
35
+ // @ts-ignore
36
+ import { loadProvider } from 'virtual:xyd-analytics-providers'
37
+
38
+ // @ts-ignore
39
+ import "virtual:xyd-theme/index.css"
40
+ import "virtual:xyd-theme-override/index.css"
41
+ import 'katex/dist/katex.min.css'
42
+
43
+ // @ts-ignore
44
+ import { components as userComponents } from 'virtual:xyd-user-components';
45
+
46
+ import { PageContext } from "./context";
47
+ import React from "react";
48
+
49
+ import { markdownPlugins } from "@xyd-js/content/md";
50
+ import { ContentFS } from "@xyd-js/content";
51
+ import { Icon, IconProvider } from "@xyd-js/components/writer";
52
+ import { CoderProvider } from "@xyd-js/components/coder";
53
+ import { SearchButton } from "@xyd-js/components/system"
54
+
55
+ globalThis.__xydSettings = getSettings
56
+ globalThis.__xydSettingsClone = settingsClone
57
+ globalThis.__xydUserComponents = userComponents // Add user components to global scope TODO: problematic
58
+ globalThis.__xydUserPreferences = userPreferences
59
+
60
+ const settings = globalThis.__xydSettings as Settings
61
+
62
+ // console.log(JSON.stringify(settings?.navigation?.sidebar, null, 2), "settings?.navigation?.sidebar")
63
+
64
+ const surfaces = new Surfaces()
65
+ const atlasXyd = AtlasXydPlugin()(settings) // TODO: in the future via standard plugin API
66
+ const SidebarItemRight = atlasXyd?.customComponents?.["AtlasSidebarItemRight"]
67
+
68
+ if (SidebarItemRight) {
69
+ surfaces.define(
70
+ SidebarItemRight.surface,
71
+ SidebarItemRight.component,
72
+ )
73
+ }
74
+
75
+ const reactContent = new ReactContent(settings, {
76
+ Link: FwLink,
77
+ components: {
78
+ Atlas,
79
+ },
80
+ useLocation, // // TODO: !!!! BETTER API !!!!!
81
+ useNavigate,
82
+ useNavigation
83
+ })
84
+ globalThis.__xydThemeSettings = settings?.theme
85
+ globalThis.__xydNavigation = settings?.navigation
86
+ globalThis.__xydWebeditor = settings?.webeditor
87
+ globalThis.__xydReactContent = reactContent
88
+ globalThis.__xydSurfaces = surfaces
89
+
90
+ const theme = new Theme()
91
+ //@ts-ignore TODO: in the future better api like PageLoad interface or something like that
92
+ if (theme.mergeUserAppearance) {
93
+ // its needed after user declaration
94
+ //@ts-ignore
95
+ theme.mergeUserAppearance()
96
+ }
97
+
98
+ if (
99
+ settings?.theme?.appearance?.sidebar?.scrollbar === "secondary"
100
+ ) {
101
+ import("@xyd-js/themes/decorators/sidebar-scroll.css").catch(() => {
102
+ // Ignore CSS import errors during development
103
+ });
104
+ }
105
+
106
+ const { Layout: BaseThemeLayout } = theme
107
+
108
+ interface LoaderData {
109
+ sidebarGroups: FwSidebarItemProps[]
110
+ breadcrumbs: IBreadcrumb[],
111
+ toc: MetadataMap,
112
+ slug: string
113
+ metadata: Metadata | null
114
+ navlinks?: INavLinks,
115
+ bannerContentCode?: string
116
+ }
117
+
118
+ export async function loader({ request }: { request: any }) {
119
+ globalThis.__xydFrontmatterNotExists = {}
120
+
121
+ new Composer() // TODO: better API
122
+
123
+ const slug = getPathname(request.url || "index") || "index"
124
+
125
+ const {
126
+ groups: sidebarGroups,
127
+ breadcrumbs,
128
+ navlinks,
129
+ metadata
130
+ } = await mapSettingsToProps(
131
+ settings,
132
+ globalThis.__xydPagePathMapping,
133
+ slug,
134
+ )
135
+
136
+ let bannerContentCode = ""
137
+
138
+ const mdPlugins = await markdownPlugins({
139
+ maxDepth: metadata?.maxTocDepth || settings?.theme?.writer?.maxTocDepth || 2,
140
+ }, settings)
141
+ const contentFs = new ContentFS(settings, mdPlugins.remarkPlugins, mdPlugins.rehypePlugins, mdPlugins.recmaPlugins)
142
+
143
+ if (settings?.components?.banner?.content && typeof settings?.components?.banner?.content === "string") {
144
+ bannerContentCode = await contentFs.compileContent(
145
+ settings?.components?.banner?.content,
146
+ )
147
+ }
148
+
149
+ // TODO: IN THE FUTURE BETTER API
150
+ const layout = pageMetaLayout(metadata)
151
+ if (metadata && layout) {
152
+ metadata.layout = layout
153
+ }
154
+
155
+ return {
156
+ sidebarGroups,
157
+ breadcrumbs,
158
+ navlinks,
159
+ slug,
160
+ metadata,
161
+ bannerContentCode
162
+ } as LoaderData
163
+ }
164
+
165
+ export default function Layout() {
166
+ const loaderData = useLoaderData<LoaderData>()
167
+ const matches = useMatches()
168
+
169
+ const lastMatchId = matches[matches.length - 1]?.id || null
170
+
171
+ let atlasVariantToggles: VariantToggleConfig[] = [];
172
+
173
+ // TODO: BETTER HANDLE THAT
174
+ if (loaderData.metadata?.openapi) {
175
+ atlasVariantToggles = [
176
+ { key: "status", defaultValue: "200" },
177
+ { key: "contentType", defaultValue: "application/json" }
178
+ ];
179
+ } else {
180
+ atlasVariantToggles = [
181
+ { key: "symbolName", defaultValue: "" }
182
+ ];
183
+ }
184
+
185
+ let bannerContent: any = null
186
+ // TODO: !!!! BETTER API !!!!
187
+ if (loaderData.bannerContentCode) {
188
+ const content = mdxContent(loaderData.bannerContentCode)
189
+ const BannerContent = MemoMDXComponent(content.component)
190
+
191
+ bannerContent = function () {
192
+ return <BannerContent components={theme.reactContentComponents()} />
193
+ }
194
+ }
195
+
196
+ const userComponents = (globalThis.__xydUserComponents || []).reduce((acc, component) => {
197
+ acc[component.name] = component.component;
198
+ return acc;
199
+ }, {});
200
+
201
+ return <>
202
+ <Analytics settings={settings} loader={loadProvider}>
203
+ <IconProvider value={{
204
+ iconSet: iconSet
205
+ }}>
206
+ {/* TOOD: better solution for roto ux node cuz for example in user components then its not defined but neede to use analyitcs hooks (but should be optional?) */}
207
+ <UXNode
208
+ name="Framework"
209
+ props={{
210
+ location: "",
211
+ }}
212
+ >
213
+ <Framework
214
+ settings={settings || globalThis.__xydSettings}
215
+ sidebarGroups={loaderData.sidebarGroups || []}
216
+ metadata={loaderData.metadata || {}}
217
+ surfaces={surfaces}
218
+ BannerContent={bannerContent}
219
+ components={{
220
+ Search: SearchButton,
221
+ Logo: FwLogo,
222
+ ...userComponents
223
+ }}
224
+ >
225
+ <AtlasContext
226
+ value={{
227
+ Link: FwLink,
228
+ syntaxHighlight: settings?.theme?.coder?.syntaxHighlight || null,
229
+ baseMatch: lastMatchId || "",
230
+ variantToggles: atlasVariantToggles
231
+ }}
232
+ >
233
+ <CoderProvider lines={settings?.theme?.coder?.lines} scroll={settings?.theme?.coder?.scroll}>
234
+ <BaseThemeLayout>
235
+ <PageContext value={{ theme }}>
236
+ <PostLayout>
237
+ <Outlet />
238
+ </PostLayout>
239
+ </PageContext>
240
+ </BaseThemeLayout>
241
+ </CoderProvider>
242
+ </AtlasContext>
243
+ </Framework>
244
+ </UXNode>
245
+
246
+ </IconProvider>
247
+ </Analytics>
248
+ </>
249
+ }
250
+
251
+ function PostLayout({ children }: { children: React.ReactNode }) {
252
+ const analytics = useAnalytics()
253
+
254
+ useEffect(() => {
255
+ // @ts-ignore
256
+ window.analytics = analytics
257
+ }, [])
258
+
259
+ return children
260
+ }
261
+
262
+ function getPathname(url: string) {
263
+ const parsedUrl = new URL(url);
264
+ return parsedUrl.pathname.replace(/^\//, '');
265
+ }
266
+
267
+
268
+ // TODO: move to content?
269
+ function mdxExport(code: string) {
270
+ // Create a wrapper around React.createElement that adds keys to elements in lists
271
+ const scope = {
272
+ Fragment: React.Fragment,
273
+ jsxs: createElementWithKeys,
274
+ jsx: createElementWithKeys,
275
+ jsxDEV: createElementWithKeys,
276
+ }
277
+ const fn = new Function(...Object.keys(scope), code)
278
+
279
+ return fn(scope)
280
+ }
281
+
282
+
283
+ // // TODO: move to content?
284
+ function mdxContent(code: string) {
285
+ const content = mdxExport(code) // TODO: fix any
286
+ if (!mdxExport) {
287
+ return {}
288
+ }
289
+
290
+ return {
291
+ component: content?.default,
292
+ }
293
+ }
294
+
295
+ const createElementWithKeys = (type: any, props: any) => {
296
+ // Process children to add keys to all elements
297
+ const processChildren = (childrenArray: any[]): any[] => {
298
+ return childrenArray.map((child, index) => {
299
+ // If the child is a React element and doesn't have a key, add one
300
+ if (React.isValidElement(child) && !child.key) {
301
+ return React.cloneElement(child, { key: `mdx-${index}` });
302
+ }
303
+ // If the child is an array, process it recursively
304
+ if (Array.isArray(child)) {
305
+ return processChildren(child);
306
+ }
307
+ return child;
308
+ });
309
+ };
310
+
311
+ // Handle both cases: children as separate args or as props.children
312
+ let processedChildren;
313
+
314
+ if (props && props.children) {
315
+ if (Array.isArray(props.children)) {
316
+ processedChildren = processChildren(props.children);
317
+ } else if (React.isValidElement(props.children) && !props.children.key) {
318
+ // Single child without key
319
+ processedChildren = React.cloneElement(props.children, { key: 'mdx-child' });
320
+ } else {
321
+ // Single child with key or non-React element
322
+ processedChildren = props.children;
323
+ }
324
+ } else {
325
+ processedChildren = [];
326
+ }
327
+
328
+ // Create the element with processed children
329
+ return React.createElement(type, {
330
+ ...props,
331
+ children: processedChildren
332
+ });
333
+ };
334
+
335
+ function MemoMDXComponent(codeComponent: any) {
336
+ return useMemo(
337
+ () => codeComponent ? codeComponent : null,
338
+ [codeComponent]
339
+ )
340
+ }
@@ -0,0 +1,96 @@
1
+ import { MetaTags } from "@xyd-js/core";
2
+
3
+ export const SUPPORTED_META_TAGS: MetaTags = {
4
+ "robots": "name",
5
+ "charset": "name",
6
+ "viewport": "name",
7
+ "description": "name",
8
+ "keywords": "name",
9
+ "author": "name",
10
+ "googlebot": "name",
11
+ "google": "name",
12
+ "google-site-verification": "name",
13
+ "generator": "name",
14
+ "theme-color": "name",
15
+ "color-scheme": "name",
16
+ "format-detection": "name",
17
+ "referrer": "name",
18
+ "refresh": "name",
19
+ "rating": "name",
20
+ "revisit-after": "name",
21
+ "language": "name",
22
+ "copyright": "name",
23
+ "reply-to": "name",
24
+ "distribution": "name",
25
+ "coverage": "name",
26
+ "category": "name",
27
+ "target": "name",
28
+ "HandheldFriendly": "name",
29
+ "MobileOptimized": "name",
30
+ "apple-mobile-web-app-capable": "name",
31
+ "apple-mobile-web-app-status-bar-style": "name",
32
+ "apple-mobile-web-app-title": "name",
33
+ "application-name": "name",
34
+ "msapplication-TileColor": "name",
35
+ "msapplication-TileImage": "name",
36
+ "msapplication-config": "name",
37
+
38
+ "og:title": "property",
39
+ "og:type": "property",
40
+ "og:url": "property",
41
+ "og:image": "property",
42
+ "og:description": "property",
43
+ "og:site_name": "property",
44
+ "og:locale": "property",
45
+ "og:video": "property",
46
+ "og:audio": "property",
47
+
48
+ "twitter:card": "property",
49
+ "twitter:site": "property",
50
+ "twitter:creator": "property",
51
+ "twitter:title": "property",
52
+ "twitter:description": "property",
53
+ "twitter:image": "property",
54
+ "twitter:image:alt": "property",
55
+ "twitter:player": "property",
56
+ "twitter:player:width": "property",
57
+ "twitter:player:height": "property",
58
+ "twitter:app:name:iphone": "property",
59
+ "twitter:app:id:iphone": "property",
60
+ "twitter:app:url:iphone": "property",
61
+
62
+ "article:published_time": "property",
63
+ "article:modified_time": "property",
64
+ "article:expiration_time": "property",
65
+ "article:author": "property",
66
+ "article:section": "property",
67
+ "article:tag": "property",
68
+
69
+ "book:author": "property",
70
+ "book:isbn": "property",
71
+ "book:release_date": "property",
72
+ "book:tag": "property",
73
+
74
+ "profile:first_name": "property",
75
+ "profile:last_name": "property",
76
+ "profile:username": "property",
77
+ "profile:gender": "property",
78
+
79
+ "music:duration": "property",
80
+ "music:album": "property",
81
+ "music:album:disc": "property",
82
+ "music:album:track": "property",
83
+ "music:musician": "property",
84
+ "music:song": "property",
85
+ "music:song:disc": "property",
86
+ "music:song:track": "property",
87
+
88
+ "video:actor": "property",
89
+ "video:actor:role": "property",
90
+ "video:director": "property",
91
+ "video:writer": "property",
92
+ "video:duration": "property",
93
+ "video:release_date": "property",
94
+ "video:tag": "property",
95
+ "video:series": "property"
96
+ }