polen 0.11.0-next.10 → 0.11.0-next.12

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.
Files changed (193) hide show
  1. package/build/api/api.d.ts +2 -1
  2. package/build/api/api.d.ts.map +1 -1
  3. package/build/api/api.js +5 -1
  4. package/build/api/api.js.map +1 -1
  5. package/build/api/iso/$$.d.ts +2 -0
  6. package/build/api/iso/$$.d.ts.map +1 -0
  7. package/build/api/iso/$$.js +2 -0
  8. package/build/api/iso/$$.js.map +1 -0
  9. package/build/api/iso/$.d.ts +2 -0
  10. package/build/api/iso/$.d.ts.map +1 -0
  11. package/build/api/iso/$.js +2 -0
  12. package/build/api/iso/$.js.map +1 -0
  13. package/build/api/iso/schema/$$.d.ts +5 -0
  14. package/build/api/iso/schema/$$.d.ts.map +1 -0
  15. package/build/api/iso/schema/$$.js +4 -0
  16. package/build/api/iso/schema/$$.js.map +1 -0
  17. package/build/api/iso/schema/$.d.ts +2 -0
  18. package/build/api/iso/schema/$.d.ts.map +1 -0
  19. package/build/api/iso/schema/$.js +2 -0
  20. package/build/api/iso/schema/$.js.map +1 -0
  21. package/build/api/iso/schema/constants.d.ts +25 -0
  22. package/build/api/iso/schema/constants.d.ts.map +1 -0
  23. package/build/api/iso/schema/constants.js +42 -0
  24. package/build/api/iso/schema/constants.js.map +1 -0
  25. package/build/api/iso/schema/routing.d.ts +56 -0
  26. package/build/api/iso/schema/routing.d.ts.map +1 -0
  27. package/build/api/iso/schema/routing.js +97 -0
  28. package/build/api/iso/schema/routing.js.map +1 -0
  29. package/build/api/iso/schema/validation.d.ts +32 -0
  30. package/build/api/iso/schema/validation.d.ts.map +1 -0
  31. package/build/api/iso/schema/validation.js +101 -0
  32. package/build/api/iso/schema/validation.js.map +1 -0
  33. package/build/api/iso.d.ts +2 -0
  34. package/build/api/iso.d.ts.map +1 -0
  35. package/build/api/iso.js +2 -0
  36. package/build/api/iso.js.map +1 -0
  37. package/build/api/schema/schema.d.ts +3 -25
  38. package/build/api/schema/schema.d.ts.map +1 -1
  39. package/build/api/schema/schema.js +5 -42
  40. package/build/api/schema/schema.js.map +1 -1
  41. package/build/api/vite/plugins/core.d.ts.map +1 -1
  42. package/build/api/vite/plugins/core.js +3 -6
  43. package/build/api/vite/plugins/core.js.map +1 -1
  44. package/build/template/components/ArgumentList.d.ts +3 -4
  45. package/build/template/components/ArgumentList.d.ts.map +1 -1
  46. package/build/template/components/ArgumentList.js.map +1 -1
  47. package/build/template/components/Changelog.js +2 -2
  48. package/build/template/components/Changelog.js.map +1 -1
  49. package/build/template/components/DeprecationReason.d.ts +2 -2
  50. package/build/template/components/DeprecationReason.d.ts.map +1 -1
  51. package/build/template/components/DeprecationReason.js.map +1 -1
  52. package/build/template/components/Description.d.ts +2 -2
  53. package/build/template/components/Description.d.ts.map +1 -1
  54. package/build/template/components/Description.js.map +1 -1
  55. package/build/template/components/Field.d.ts +3 -4
  56. package/build/template/components/Field.d.ts.map +1 -1
  57. package/build/template/components/Field.js.map +1 -1
  58. package/build/template/components/FieldListSection.d.ts +3 -4
  59. package/build/template/components/FieldListSection.d.ts.map +1 -1
  60. package/build/template/components/FieldListSection.js.map +1 -1
  61. package/build/template/components/GraphQLInteractive/lib/parser.d.ts.map +1 -1
  62. package/build/template/components/GraphQLInteractive/lib/parser.js +32 -10
  63. package/build/template/components/GraphQLInteractive/lib/parser.js.map +1 -1
  64. package/build/template/components/HamburgerMenu.d.ts +1 -0
  65. package/build/template/components/HamburgerMenu.d.ts.map +1 -1
  66. package/build/template/components/HamburgerMenu.js +2 -2
  67. package/build/template/components/HamburgerMenu.js.map +1 -1
  68. package/build/template/components/MissingSchema.d.ts +2 -1
  69. package/build/template/components/MissingSchema.d.ts.map +1 -1
  70. package/build/template/components/MissingSchema.js.map +1 -1
  71. package/build/template/components/ReferenceLink.d.ts +9 -11
  72. package/build/template/components/ReferenceLink.d.ts.map +1 -1
  73. package/build/template/components/ReferenceLink.js +2 -4
  74. package/build/template/components/ReferenceLink.js.map +1 -1
  75. package/build/template/components/ToastContainer.d.ts +3 -0
  76. package/build/template/components/ToastContainer.d.ts.map +1 -0
  77. package/build/template/components/ToastContainer.js +44 -0
  78. package/build/template/components/ToastContainer.js.map +1 -0
  79. package/build/template/components/ToastItem.d.ts +7 -0
  80. package/build/template/components/ToastItem.d.ts.map +1 -0
  81. package/build/template/components/ToastItem.js +48 -0
  82. package/build/template/components/ToastItem.js.map +1 -0
  83. package/build/template/components/TypeAnnotation.d.ts +4 -5
  84. package/build/template/components/TypeAnnotation.d.ts.map +1 -1
  85. package/build/template/components/TypeAnnotation.js.map +1 -1
  86. package/build/template/components/VersionPicker.d.ts +8 -0
  87. package/build/template/components/VersionPicker.d.ts.map +1 -0
  88. package/build/template/components/VersionPicker.js +66 -0
  89. package/build/template/components/VersionPicker.js.map +1 -0
  90. package/build/template/components/sidebar/Sidebar.d.ts +3 -2
  91. package/build/template/components/sidebar/Sidebar.d.ts.map +1 -1
  92. package/build/template/components/sidebar/Sidebar.js +7 -6
  93. package/build/template/components/sidebar/Sidebar.js.map +1 -1
  94. package/build/template/components/sidebar/SidebarContext.d.ts +6 -0
  95. package/build/template/components/sidebar/SidebarContext.d.ts.map +1 -0
  96. package/build/template/components/sidebar/SidebarContext.js +3 -0
  97. package/build/template/components/sidebar/SidebarContext.js.map +1 -0
  98. package/build/template/components/sidebar/SidebarItem.d.ts.map +1 -1
  99. package/build/template/components/sidebar/SidebarItem.js +11 -4
  100. package/build/template/components/sidebar/SidebarItem.js.map +1 -1
  101. package/build/template/hooks/useReferencePath.d.ts +9 -0
  102. package/build/template/hooks/useReferencePath.d.ts.map +1 -0
  103. package/build/template/hooks/useReferencePath.js +18 -0
  104. package/build/template/hooks/useReferencePath.js.map +1 -0
  105. package/build/template/hooks/useVersionPath.d.ts.map +1 -1
  106. package/build/template/hooks/useVersionPath.js +3 -1
  107. package/build/template/hooks/useVersionPath.js.map +1 -1
  108. package/build/template/layouts/SidebarLayout.d.ts +3 -2
  109. package/build/template/layouts/SidebarLayout.d.ts.map +1 -1
  110. package/build/template/layouts/SidebarLayout.js +2 -2
  111. package/build/template/layouts/SidebarLayout.js.map +1 -1
  112. package/build/template/routes/reference.d.ts +24 -8
  113. package/build/template/routes/reference.d.ts.map +1 -1
  114. package/build/template/routes/reference.js +52 -61
  115. package/build/template/routes/reference.js.map +1 -1
  116. package/build/template/routes/root.d.ts +1 -0
  117. package/build/template/routes/root.d.ts.map +1 -1
  118. package/build/template/routes/root.js +16 -5
  119. package/build/template/routes/root.js.map +1 -1
  120. package/build/template/server/ssg/get-route-paths.d.ts.map +1 -1
  121. package/build/template/server/ssg/get-route-paths.js +47 -14
  122. package/build/template/server/ssg/get-route-paths.js.map +1 -1
  123. package/build/template/stores/$$.d.ts +13 -0
  124. package/build/template/stores/$$.d.ts.map +1 -0
  125. package/build/template/stores/$$.js +5 -0
  126. package/build/template/stores/$$.js.map +1 -0
  127. package/build/template/stores/$.d.ts +2 -0
  128. package/build/template/stores/$.d.ts.map +1 -0
  129. package/build/template/stores/$.js +2 -0
  130. package/build/template/stores/$.js.map +1 -0
  131. package/build/template/stores/schema.d.ts +40 -0
  132. package/build/template/stores/schema.d.ts.map +1 -0
  133. package/build/template/stores/schema.js +36 -0
  134. package/build/template/stores/schema.js.map +1 -0
  135. package/build/template/stores/toast.d.ts +103 -0
  136. package/build/template/stores/toast.d.ts.map +1 -0
  137. package/build/template/stores/toast.js +105 -0
  138. package/build/template/stores/toast.js.map +1 -0
  139. package/build/template/utils/try-with-toast.d.ts +9 -0
  140. package/build/template/utils/try-with-toast.d.ts.map +1 -0
  141. package/build/template/utils/try-with-toast.js +37 -0
  142. package/build/template/utils/try-with-toast.js.map +1 -0
  143. package/package.json +5 -1
  144. package/src/api/api.ts +7 -1
  145. package/src/api/iso/$$.ts +1 -0
  146. package/src/api/iso/$.ts +1 -0
  147. package/src/api/iso/schema/$$.ts +6 -0
  148. package/src/api/iso/schema/$.ts +1 -0
  149. package/src/api/iso/schema/constants.ts +49 -0
  150. package/src/api/iso/schema/routing.ts +142 -0
  151. package/src/api/iso/schema/validation.ts +136 -0
  152. package/src/api/iso.ts +1 -0
  153. package/src/api/schema/schema.ts +6 -53
  154. package/src/api/vite/plugins/core.ts +3 -6
  155. package/src/template/components/ArgumentList.tsx +2 -6
  156. package/src/template/components/Changelog.tsx +2 -2
  157. package/src/template/components/DeprecationReason.tsx +2 -2
  158. package/src/template/components/Description.tsx +2 -2
  159. package/src/template/components/Field.tsx +2 -6
  160. package/src/template/components/FieldListSection.tsx +2 -6
  161. package/src/template/components/GraphQLInteractive/lib/parser.ts +32 -16
  162. package/src/template/components/HamburgerMenu.tsx +3 -1
  163. package/src/template/components/MissingSchema.tsx +3 -1
  164. package/src/template/components/ReferenceLink.tsx +16 -15
  165. package/src/template/components/ToastContainer.tsx +67 -0
  166. package/src/template/components/ToastItem.tsx +119 -0
  167. package/src/template/components/TypeAnnotation.tsx +2 -6
  168. package/src/template/components/VersionPicker.tsx +94 -0
  169. package/src/template/components/sidebar/Sidebar.tsx +20 -16
  170. package/src/template/components/sidebar/SidebarContext.tsx +7 -0
  171. package/src/template/components/sidebar/SidebarItem.tsx +11 -4
  172. package/src/template/hooks/useReferencePath.ts +20 -0
  173. package/src/template/hooks/useVersionPath.ts +3 -1
  174. package/src/template/layouts/SidebarLayout.tsx +5 -3
  175. package/src/template/routes/reference.tsx +56 -73
  176. package/src/template/routes/root.tsx +29 -6
  177. package/src/template/server/ssg/get-route-paths.test.ts +13 -0
  178. package/src/template/server/ssg/get-route-paths.ts +47 -14
  179. package/src/template/stores/$$.ts +15 -0
  180. package/src/template/stores/$.ts +1 -0
  181. package/src/template/stores/schema.ts +52 -0
  182. package/src/template/stores/toast.ts +153 -0
  183. package/src/template/utils/try-with-toast.ts +41 -0
  184. package/build/template/components/VersionSelector.d.ts +0 -7
  185. package/build/template/components/VersionSelector.d.ts.map +0 -1
  186. package/build/template/components/VersionSelector.js +0 -30
  187. package/build/template/components/VersionSelector.js.map +0 -1
  188. package/build/template/lib/schema-utils/constants.d.ts +0 -5
  189. package/build/template/lib/schema-utils/constants.d.ts.map +0 -1
  190. package/build/template/lib/schema-utils/constants.js +0 -5
  191. package/build/template/lib/schema-utils/constants.js.map +0 -1
  192. package/src/template/components/VersionSelector.tsx +0 -50
  193. package/src/template/lib/schema-utils/constants.ts +0 -4
@@ -0,0 +1,94 @@
1
+ import { Api } from '#api/iso'
2
+ import type { React } from '#dep/react/index'
3
+ import { Select } from '@radix-ui/themes'
4
+ import { useNavigate } from 'react-router'
5
+ import { useReferencePath } from '../hooks/useReferencePath.js'
6
+ import { schemaSource } from '../sources/schema-source.js'
7
+ import { Stores } from '../stores/$.js'
8
+ import { tryWithToast } from '../utils/try-with-toast.js'
9
+
10
+ interface Props {
11
+ all: string[]
12
+ current: string
13
+ }
14
+
15
+ export const VersionPicker: React.FC<Props> = ({ all, current }) => {
16
+ const navigate = useNavigate()
17
+ const currentPath = useReferencePath()
18
+
19
+ // Don't show selector if only one version
20
+ if (all.length <= 1) {
21
+ return null
22
+ }
23
+
24
+ const handleVersionChange = async (newVersion: string) => {
25
+ const error = await tryWithToast(async () => {
26
+ // Check if current path exists in target version
27
+ const targetSchema = await schemaSource.get(newVersion)
28
+ // Find fallback path if needed
29
+ const fallbackPath = Api.Schema.Validation.findFallbackPath(targetSchema, currentPath)
30
+ // Get redirect description if path changed
31
+ const redirectDescription = Api.Schema.Validation.getRedirectDescription(
32
+ targetSchema,
33
+ currentPath,
34
+ fallbackPath,
35
+ newVersion,
36
+ )
37
+ // Create the new path
38
+ const newPath = Api.Schema.Routing.createReferencePath({
39
+ version: newVersion,
40
+ type: fallbackPath.type,
41
+ field: fallbackPath.field,
42
+ })
43
+ // Show toast notification if schema location redirect will occur
44
+ if (redirectDescription) {
45
+ Stores.Toast.store.info(redirectDescription, {
46
+ duration: 160_000,
47
+ actions: [
48
+ {
49
+ label: 'Go back',
50
+ onClick() {
51
+ navigate(-1)
52
+ },
53
+ },
54
+ {
55
+ label: 'View changelog',
56
+ onClick() {
57
+ // Navigate to changelog page
58
+ navigate('/changelog')
59
+ },
60
+ },
61
+ ],
62
+ })
63
+ }
64
+
65
+ navigate(newPath)
66
+ }, 'Failed to switch version')
67
+
68
+ // Fallback logic if error occurred
69
+ if (error) {
70
+ // Fallback to simple navigation if schema loading fails
71
+ const newPath = Api.Schema.Routing.createReferencePath({
72
+ version: newVersion,
73
+ type: currentPath.type,
74
+ field: currentPath.field,
75
+ })
76
+ navigate(newPath)
77
+ }
78
+ }
79
+
80
+ return (
81
+ <Select.Root value={current} onValueChange={handleVersionChange}>
82
+ <Select.Trigger>
83
+ {current === Api.Schema.VERSION_LATEST ? `Latest` : current}
84
+ </Select.Trigger>
85
+ <Select.Content>
86
+ {all.map(version => (
87
+ <Select.Item key={version} value={version}>
88
+ {version === Api.Schema.VERSION_LATEST ? `Latest` : version}
89
+ </Select.Item>
90
+ ))}
91
+ </Select.Content>
92
+ </Select.Root>
93
+ )
94
+ }
@@ -1,28 +1,32 @@
1
1
  import type { Content } from '#api/content/$'
2
2
  import { Box } from '@radix-ui/themes'
3
3
  import type { BoxOwnProps, LayoutProps, MarginProps } from '@radix-ui/themes/props'
4
+ import { SidebarContext } from './SidebarContext.js'
4
5
  import { Items } from './SidebarItem.js'
5
6
 
6
- interface SidebarProps extends LayoutProps, MarginProps, BoxOwnProps {
7
+ interface Props extends LayoutProps, MarginProps, BoxOwnProps {
7
8
  data: Content.Item[]
9
+ basePath?: string
8
10
  style?: React.CSSProperties
9
11
  }
10
12
 
11
- export const Sidebar = ({ data, ...props }: SidebarProps) => {
13
+ export const Sidebar = ({ data, basePath, ...props }: Props) => {
12
14
  return (
13
- <Box
14
- data-testid='sidebar'
15
- role='Sidebar'
16
- {...props}
17
- >
18
- <style>
19
- {`
20
- div[role="Sidebar"] a:not([data-active]):hover {
21
- background-color: var(--iris-2) !important;
22
- }
23
- `}
24
- </style>
25
- <Items items={data} />
26
- </Box>
15
+ <SidebarContext.Provider value={{ basePath }}>
16
+ <Box
17
+ data-testid='sidebar'
18
+ role='Sidebar'
19
+ {...props}
20
+ >
21
+ <style>
22
+ {`
23
+ div[role="Sidebar"] a:not([data-active]):hover {
24
+ background-color: var(--iris-2) !important;
25
+ }
26
+ `}
27
+ </style>
28
+ <Items items={data} />
29
+ </Box>
30
+ </SidebarContext.Provider>
27
31
  )
28
32
  }
@@ -0,0 +1,7 @@
1
+ import { createContext } from 'react'
2
+
3
+ interface SidebarContextValue {
4
+ basePath?: string
5
+ }
6
+
7
+ export const SidebarContext = createContext<SidebarContextValue>({})
@@ -1,9 +1,12 @@
1
1
  import type { Content } from '#api/content/$'
2
+ import { Api } from '#api/iso'
2
3
  import type { React } from '#dep/react/index'
3
4
  import { Texts } from '#template/components/Texts/index'
4
5
  import { Box, Flex } from '@radix-ui/themes'
6
+ import { useContext } from 'react'
5
7
  import { useLocation } from 'react-router'
6
8
  import { getPathActiveReport, Link } from '../Link.js'
9
+ import { SidebarContext } from './SidebarContext.js'
7
10
 
8
11
  export const Items: React.FC<{ items: Content.Item[] }> = ({ items }) => {
9
12
  return (
@@ -50,15 +53,17 @@ const SBLink: React.FC<{
50
53
  link: Content.ItemLink | Content.ItemSection
51
54
  }> = ({ link }) => {
52
55
  const location = useLocation()
56
+ const { basePath } = useContext(SidebarContext)
53
57
  const currentPathExp = location.pathname
54
- const active = getPathActiveReport(link.pathExp, currentPathExp)
58
+ const href = Api.Schema.Routing.joinSegmentsAndPaths(basePath, link.pathExp)
59
+ const active = getPathActiveReport(href, currentPathExp)
55
60
 
56
61
  return (
57
62
  <Link
58
63
  role='Sidebar Link'
59
64
  color={active.is ? `iris` : `gray`}
60
65
  data-testid={`sidebar-link-${link.pathExp}`}
61
- to={`/${link.pathExp}`}
66
+ to={href}
62
67
  style={{
63
68
  display: `block`,
64
69
  textDecoration: `none`,
@@ -135,12 +140,14 @@ const LinkedSection: React.FC<{
135
140
 
136
141
  const SectionLink: React.FC<{ link: Content.ItemLink }> = ({ link }) => {
137
142
  const location = useLocation()
138
- const active = getPathActiveReport(link.pathExp, location.pathname)
143
+ const { basePath } = useContext(SidebarContext)
144
+ const href = Api.Schema.Routing.joinSegmentsAndPaths(basePath, link.pathExp)
145
+ const active = getPathActiveReport(href, location.pathname)
139
146
 
140
147
  return (
141
148
  <Link
142
149
  role='Sidebar Link'
143
- to={`/` + link.pathExp}
150
+ to={href}
144
151
  color={active.is ? `iris` : `gray`}
145
152
  style={{
146
153
  textDecoration: `none`,
@@ -0,0 +1,20 @@
1
+ import { Api } from '#api/iso'
2
+ import { useLocation, useParams } from 'react-router'
3
+
4
+ /**
5
+ * Hook that returns parsed reference path parameters
6
+ *
7
+ * @throws {Error} If not currently on a reference route
8
+ * @returns Parsed reference path object
9
+ */
10
+ export const useReferencePath = (): Api.Schema.Validation.PathValidation => {
11
+ const params = useParams()
12
+ const location = useLocation()
13
+
14
+ Api.Schema.Routing.assertReferenceRoute({
15
+ pathname: location.pathname,
16
+ params,
17
+ })
18
+
19
+ return params
20
+ }
@@ -1,3 +1,4 @@
1
+ import { Api } from '#api/iso'
1
2
  import { useParams } from 'react-router'
2
3
 
3
4
  /**
@@ -6,5 +7,6 @@ import { useParams } from 'react-router'
6
7
  */
7
8
  export const useVersionPath = (): string => {
8
9
  const params = useParams()
9
- return params[`version`] ? `version/${params[`version`]}/` : ``
10
+ const version = params[`version`] || Api.Schema.VERSION_LATEST
11
+ return Api.Schema.Routing.createReferenceVersionPath(version)
10
12
  }
@@ -6,12 +6,13 @@ import { useLocation } from 'react-router'
6
6
  import { HamburgerMenu } from '../components/HamburgerMenu.js'
7
7
  import { Sidebar } from '../components/sidebar/Sidebar.js'
8
8
 
9
- interface SidebarLayoutProps {
9
+ interface Props {
10
10
  children: React.ReactNode
11
11
  sidebar: Content.Item[]
12
+ basePath?: string
12
13
  }
13
14
 
14
- export const SidebarLayout: React.FC<SidebarLayoutProps> = ({ children, sidebar }) => {
15
+ export const SidebarLayout: React.FC<Props> = ({ children, sidebar, basePath }) => {
15
16
  const location = useLocation()
16
17
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
17
18
 
@@ -45,6 +46,7 @@ export const SidebarLayout: React.FC<SidebarLayoutProps> = ({ children, sidebar
45
46
  setMobileMenuOpen(false)
46
47
  }}
47
48
  sidebarData={sidebar}
49
+ basePath={basePath}
48
50
  />
49
51
  </Box>
50
52
  )}
@@ -56,7 +58,7 @@ export const SidebarLayout: React.FC<SidebarLayoutProps> = ({ children, sidebar
56
58
  gridColumn='1 / 3'
57
59
  gridRow='1 / auto'
58
60
  >
59
- <Sidebar data={sidebar} />
61
+ <Sidebar data={sidebar} basePath={basePath} />
60
62
  </Box>
61
63
  )}
62
64
 
@@ -1,23 +1,24 @@
1
1
  import type { Content } from '#api/content/$'
2
+ import { Api } from '#api/iso'
3
+ import type { React } from '#dep/react/index'
2
4
  import { GrafaidOld } from '#lib/grafaid-old/index'
3
- import { Grafaid } from '#lib/grafaid/index'
4
5
  import { route, routeIndex } from '#lib/react-router-aid/react-router-aid'
5
6
  import { createLoader, useLoaderData } from '#lib/react-router-loader/react-router-loader'
6
7
  import { Box } from '@radix-ui/themes'
7
- import { Outlet, useParams } from 'react-router'
8
+ import { neverCase } from '@wollybeard/kit/language'
9
+ import { useParams } from 'react-router'
8
10
  import { Field } from '../components/Field.js'
9
11
  import { MissingSchema } from '../components/MissingSchema.js'
10
12
  import { NamedType } from '../components/NamedType.js'
11
- import { VersionSelector } from '../components/VersionSelector.js'
13
+ import { VersionPicker } from '../components/VersionPicker.js'
12
14
  import { SidebarLayout } from '../layouts/index.js'
13
- import { VERSION_LATEST } from '../lib/schema-utils/constants.js'
14
15
  import { schemaSource } from '../sources/schema-source.js'
15
16
 
16
17
  export const loader = createLoader(async ({ params }) => {
17
18
  // Handle both versioned and unversioned routes:
18
19
  // - Versioned: /reference/version/:version/:type → params.version exists
19
20
  // - Unversioned: /reference/:type → params.version is undefined, defaults to latest
20
- const currentVersion = params.version ?? VERSION_LATEST
21
+ const currentVersion = params.version ?? Api.Schema.VERSION_LATEST
21
22
 
22
23
  const schema = await schemaSource.get(currentVersion)
23
24
  const availableVersions = schemaSource.versions
@@ -29,7 +30,9 @@ export const loader = createLoader(async ({ params }) => {
29
30
  }
30
31
  })
31
32
 
32
- const RouteReferenceComponent = () => {
33
+ // Single component that handles all reference route variations
34
+ const ReferenceView = () => {
35
+ const params = useParams() as { version?: string; type?: string; field?: string }
33
36
  const data = useLoaderData<typeof loader>()
34
37
 
35
38
  if (!data.schema) {
@@ -42,12 +45,6 @@ const RouteReferenceComponent = () => {
42
45
  const sidebarItems: Content.Item[] = []
43
46
  const kindEntries = Object.entries(kindMap.list).filter(([_, types]) => types.length > 0)
44
47
 
45
- // Build path prefix based on current version from loader data
46
- // This ensures sidebar links always match the current version being viewed
47
- const versionPath = data.currentVersion === VERSION_LATEST
48
- ? ``
49
- : `version/${data.currentVersion}/`
50
-
51
48
  for (const [title, types] of kindEntries) {
52
49
  sidebarItems.push({
53
50
  type: `ItemSection` as const,
@@ -57,96 +54,82 @@ const RouteReferenceComponent = () => {
57
54
  links: types.map(type => ({
58
55
  type: `ItemLink` as const,
59
56
  title: type.name,
60
- pathExp: `reference/${versionPath}${type.name}`,
57
+ pathExp: type.name, // Just the type name, basePath will be prepended
61
58
  })),
62
59
  })
63
60
  }
64
61
 
62
+ // Calculate basePath based on current version
63
+ const basePath = Api.Schema.Routing.createReferenceBasePath(data.currentVersion)
64
+
65
+ // Determine view type and render appropriate content
66
+ const viewType = Api.Schema.Routing.getReferenceViewType({
67
+ schema: data.schema,
68
+ type: params.type,
69
+ field: params.field,
70
+ })
71
+
72
+ const content: React.ReactNode = (() => {
73
+ if (viewType === 'index') {
74
+ return <div>Select a type from the sidebar to view its documentation.</div>
75
+ } else if (viewType === 'type-missing' || viewType === 'field-missing') {
76
+ return <MissingSchema />
77
+ } else if (viewType === 'type') {
78
+ const type = data.schema.getType(params.type!)!
79
+ return <NamedType data={type} />
80
+ } else if (viewType === 'field') {
81
+ const type = data.schema.getType(params.type!)!
82
+ const fields = (type as any).getFields()
83
+ const field = fields[params.field!]
84
+ return <Field data={field} />
85
+ } else {
86
+ neverCase(viewType)
87
+ }
88
+ })()
89
+
65
90
  return (
66
- <SidebarLayout sidebar={sidebarItems}>
91
+ <SidebarLayout sidebar={sidebarItems} basePath={basePath}>
67
92
  <Box mb={`4`}>
68
- <VersionSelector
69
- availableVersions={data.availableVersions}
70
- currentVersion={data.currentVersion}
93
+ <VersionPicker
94
+ all={[...data.availableVersions]} // Convert readonly to mutable
95
+ current={data.currentVersion}
71
96
  />
72
97
  </Box>
73
- <Outlet />
98
+ {content}
74
99
  </SidebarLayout>
75
100
  )
76
101
  }
77
102
 
78
- // Shared hooks for schema data validation and retrieval
79
- const useReferenceSchema = () => {
80
- const data = useLoaderData<typeof loader>('reference')
81
- if (!data?.schema) {
82
- throw new Error('Schema not found')
83
- }
84
- return data
85
- }
86
-
87
- const useSchemaType = (typeName: string) => {
88
- const { schema } = useReferenceSchema()
89
- const type = schema.getType(typeName)
90
- if (!type) {
91
- throw new Error(`Could not find type ${typeName}`)
92
- }
93
- return type
94
- }
95
-
96
- const useSchemaField = (typeName: string, fieldName: string) => {
97
- const type = useSchemaType(typeName)
98
- if (!Grafaid.Schema.TypesLike.isFielded(type)) {
99
- throw new Error(`Type ${typeName} does not have fields`)
100
- }
101
-
102
- const fields = type.getFields()
103
- const field = fields[fieldName]
104
- if (!field) {
105
- throw new Error(`Could not find field ${fieldName} on type ${typeName}`)
106
- }
107
- return field
108
- }
109
-
110
- const RouteComponentIndex = () => {
111
- return <div>Select a type from the sidebar to view its documentation.</div>
112
- }
113
-
114
- const RouteComponentType = () => {
115
- const params = useParams() as { type: string }
116
- const type = useSchemaType(params.type)
117
- return <NamedType data={type} />
118
- }
119
-
120
- const RouteComponentTypeField = () => {
121
- const params = useParams() as { type: string; field: string }
122
- const field = useSchemaField(params.type, params.field)
123
- return <Field data={field} />
124
- }
125
-
103
+ // Define routes that handle type and field params
126
104
  const typeAndFieldRoutes = [
127
- routeIndex(RouteComponentIndex),
105
+ routeIndex({
106
+ Component: ReferenceView,
107
+ loader,
108
+ }),
128
109
  route({
129
110
  path: `:type`,
130
- Component: RouteComponentType,
111
+ Component: ReferenceView,
131
112
  errorElement: <MissingSchema />,
113
+ loader,
132
114
  children: [
133
115
  route({
134
116
  path: `:field`,
135
- Component: RouteComponentTypeField,
117
+ Component: ReferenceView,
136
118
  errorElement: <MissingSchema />,
119
+ loader,
137
120
  }),
138
121
  ],
139
122
  }),
140
123
  ]
141
124
 
142
125
  /**
143
- * Reference documentation with proper nested structure - all routes in one file
126
+ * Reference documentation routes using proper React Router patterns
127
+ * - Parent routes have no components (automatically render Outlet)
128
+ * - Leaf routes have components and loaders that always run fresh
129
+ * - Single ReferenceView component handles all variations
144
130
  */
145
131
  export const reference = route({
146
- id: 'reference',
147
132
  path: `reference`,
148
- loader,
149
- Component: RouteReferenceComponent,
150
133
  children: [
151
134
  ...typeAndFieldRoutes,
152
135
  route({
@@ -1,5 +1,7 @@
1
1
  import type { ReactRouter } from '#dep/react-router/index'
2
2
  import { route } from '#lib/react-router-aid/react-router-aid'
3
+ import { createLoader } from '#lib/react-router-loader/react-router-loader'
4
+ import type { Stores } from '#template/stores/$'
3
5
  import { Box, Flex, Theme } from '@radix-ui/themes'
4
6
  import { Link as LinkReactRouter } from 'react-router'
5
7
  import { Outlet, ScrollRestoration } from 'react-router'
@@ -11,6 +13,7 @@ import { Link as PolenLink } from '../components/Link.js'
11
13
  import { Logo } from '../components/Logo.js'
12
14
  import { NotFound } from '../components/NotFound.js'
13
15
  import { ThemeToggle } from '../components/ThemeToggle.js'
16
+ import { ToastContainer } from '../components/ToastContainer.js'
14
17
  import { ThemeProvider, useTheme } from '../contexts/ThemeContext.js'
15
18
  import { changelog } from './changelog.js'
16
19
  import { index } from './index.js'
@@ -46,7 +49,12 @@ const Layout = () => {
46
49
  style={{ color: `inherit`, textDecoration: `none` }}
47
50
  >
48
51
  <Box display={{ initial: `block`, md: `block` }}>
49
- <Logo src={logoSrc} title={templateVariables.title} height={30} showTitle={true} />
52
+ <Logo
53
+ src={logoSrc}
54
+ title={templateVariables.title}
55
+ height={30}
56
+ showTitle={true}
57
+ />
50
58
  </Box>
51
59
  </LinkReactRouter>
52
60
  <Flex direction='row' gap='4' style={{ flex: 1 }}>
@@ -61,7 +69,7 @@ const Layout = () => {
61
69
  )
62
70
 
63
71
  return (
64
- <Theme asChild appearance={appearance}>
72
+ <Theme asChild appearance={appearance} radius='none'>
65
73
  <Box
66
74
  width={{ initial: `100%`, sm: `100%`, md: `var(--container-4)` }}
67
75
  maxWidth='100vw'
@@ -72,15 +80,13 @@ const Layout = () => {
72
80
  >
73
81
  {header}
74
82
  <Outlet />
83
+ <ToastContainer />
75
84
  </Box>
76
85
  </Theme>
77
86
  )
78
87
  }
79
88
 
80
- const children: ReactRouter.RouteObject[] = [
81
- index,
82
- pages,
83
- ]
89
+ const children: ReactRouter.RouteObject[] = [index, pages]
84
90
 
85
91
  //
86
92
  //
@@ -122,8 +128,25 @@ children.push(notFoundRoute)
122
128
  //
123
129
  //
124
130
 
131
+ const storeModules = import.meta.glob('../stores/!($.*)*.ts', { eager: true }) as Record<
132
+ string,
133
+ Stores.StoreModule
134
+ >
135
+
125
136
  export const root = route({
126
137
  path: `/`,
127
138
  Component,
139
+ loader: createLoader(async () => {
140
+ // Reset all stores on SSR to prevent cross-request pollution
141
+ if (import.meta.env.SSR) {
142
+ for (const module of Object.values(storeModules)) {
143
+ if (module.store?.reset) {
144
+ module.store.reset()
145
+ }
146
+ }
147
+ }
148
+
149
+ return {}
150
+ }),
128
151
  children,
129
152
  })
@@ -50,6 +50,19 @@ vi.mock('#api/index', () => ({
50
50
  Schema: {
51
51
  getMetadata: vi.fn(),
52
52
  VERSION_LATEST: 'latest',
53
+ Routing: {
54
+ createReferencePath: vi.fn((parts) => {
55
+ const version = parts.version || 'latest'
56
+ const basePath = version === 'latest' ? '/reference' : `/reference/version/${version}`
57
+ if (parts.type) {
58
+ return parts.field ? `${basePath}/${parts.type}/${parts.field}` : `${basePath}/${parts.type}`
59
+ }
60
+ return basePath
61
+ }),
62
+ createReferenceBasePath: vi.fn((version) => {
63
+ return version === 'latest' ? '/reference' : `/reference/version/${version}`
64
+ }),
65
+ },
53
66
  },
54
67
  },
55
68
  }))