camelmind 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camelmind",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Create and manage CamelMind documentation sites",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -102,11 +102,14 @@ export function MobileDrawer({
102
102
  <nav className="flex-1 px-3 py-4 space-y-1">
103
103
  {nav.map((item) => {
104
104
  if (isNavGroup(item)) {
105
- if ((item.noDropdown || !item.items) && item.slug) {
105
+ const visibleItems = (item.items ?? []).filter((i) => canSee(i.roles))
106
+ const directHref = item.slug ?? visibleItems[0]?.slug
107
+
108
+ if ((item.noDropdown || !item.items) && directHref) {
106
109
  return (
107
110
  <Link
108
111
  key={item.label}
109
- href={item.slug}
112
+ href={directHref}
110
113
  className="block px-3 py-2.5 text-sm font-medium text-gray-200 hover:text-white hover:bg-gray-800 rounded-lg"
111
114
  >
112
115
  {item.label}
@@ -114,7 +117,6 @@ export function MobileDrawer({
114
117
  )
115
118
  }
116
119
 
117
- const visibleItems = (item.items ?? []).filter((i) => canSee(i.roles))
118
120
  if (visibleItems.length === 0) return null
119
121
  const isExpanded = expandedGroup === item.label
120
122
 
@@ -60,15 +60,17 @@ export function TopNav({ nav, userRoles, userName, authEnabled = false, versions
60
60
  <div className="hidden md:flex items-center gap-6 flex-1">
61
61
  {nav.map((item) => {
62
62
  if (isNavGroup(item)) {
63
- if ((item.noDropdown || !item.items) && item.slug) {
63
+ const visibleItems = (item.items ?? []).filter((i) => canSee(i.roles))
64
+ const directHref = item.slug ?? visibleItems[0]?.slug
65
+
66
+ if ((item.noDropdown || !item.items) && directHref) {
64
67
  return (
65
- <Link key={item.label} href={item.slug} className="text-sm font-medium hover:text-gray-300">
68
+ <Link key={item.label} href={directHref} className="text-sm font-medium hover:text-gray-300">
66
69
  {item.label}
67
70
  </Link>
68
71
  )
69
72
  }
70
73
 
71
- const visibleItems = (item.items ?? []).filter((i) => canSee(i.roles))
72
74
  if (visibleItems.length === 0) return null
73
75
 
74
76
  return (
@@ -164,15 +164,25 @@ function GroupSidebar({
164
164
  return (
165
165
  <aside className="hidden md:block w-72 shrink-0 border-r border-gray-200 dark:border-gray-800 overflow-y-auto bg-white dark:bg-gray-950">
166
166
  <div className="px-4 py-4">
167
- {visibleItems.map((entry) => (
168
- <CategoryBlock
169
- key={entry.slug}
170
- entry={entry}
171
- currentSlug={currentSlug}
172
- userRoles={userRoles}
173
- authEnabled={authEnabled}
174
- />
175
- ))}
167
+ {visibleItems.map((entry) =>
168
+ entry.section ? (
169
+ <CategoryBlock
170
+ key={entry.slug}
171
+ entry={entry}
172
+ currentSlug={currentSlug}
173
+ userRoles={userRoles}
174
+ authEnabled={authEnabled}
175
+ />
176
+ ) : (
177
+ <SectionRow
178
+ key={entry.slug}
179
+ section={entry as NavChild}
180
+ currentSlug={currentSlug}
181
+ userRoles={userRoles}
182
+ authEnabled={authEnabled}
183
+ />
184
+ )
185
+ )}
176
186
  </div>
177
187
  </aside>
178
188
  )
@@ -11,14 +11,15 @@ export type NavEntry = {
11
11
  slug: string
12
12
  file: string
13
13
  roles: string[]
14
- section?: NavChild[]
14
+ section?: NavChild[] // renders as ALL CAPS category block with children below
15
+ children?: NavChild[] // renders as a plain collapsible row (no category label)
15
16
  }
16
17
 
17
18
  export type NavGroup = {
18
19
  label: string
19
20
  dropdown: boolean
20
21
  noDropdown?: boolean // show as direct link in top nav, not a dropdown button
21
- slug?: string // href for the direct link when noDropdown is true
22
+ slug?: string // optional direct-link href; falls back to the first visible item
22
23
  items: NavEntry[]
23
24
  }
24
25
 
@@ -29,6 +29,7 @@ function stripNavPrefix(nav: NavConfig, prefix: string): NavConfig {
29
29
  ...e,
30
30
  slug: s(e.slug),
31
31
  section: e.section ? walkChildren(e.section) : undefined,
32
+ children: e.children ? walkChildren(e.children) : undefined,
32
33
  }))
33
34
 
34
35
  return {
@@ -83,6 +84,11 @@ export function getSlugsFromConfig(nav: NavConfig): string[] {
83
84
  slugs.push(child.slug)
84
85
  }
85
86
  }
87
+ if (entry.children) {
88
+ for (const child of flattenChildren(entry.children)) {
89
+ slugs.push(child.slug)
90
+ }
91
+ }
86
92
  }
87
93
  } else if ("slug" in item) {
88
94
  slugs.push((item as NavEntry).slug)
@@ -112,6 +118,10 @@ export function getEntryBySlugFromConfig(nav: NavConfig, slug: string): (NavEntr
112
118
  const match = flattenChildren(entry.section).find((c) => c.slug === normalized)
113
119
  if (match) return match
114
120
  }
121
+ if (entry.children) {
122
+ const match = flattenChildren(entry.children).find((c) => c.slug === normalized)
123
+ if (match) return match
124
+ }
115
125
  }
116
126
  } else if ("slug" in item) {
117
127
  const entry = item as NavEntry
@@ -140,6 +150,10 @@ export function getGroupForSlugFromConfig(nav: NavConfig, slug: string): NavGrou
140
150
  const match = flattenChildren(entry.section).find((c) => c.slug === normalized)
141
151
  if (match) return group
142
152
  }
153
+ if (entry.children) {
154
+ const match = flattenChildren(entry.children).find((c) => c.slug === normalized)
155
+ if (match) return group
156
+ }
143
157
  }
144
158
  }
145
159
 
@@ -162,6 +176,10 @@ export function getSectionForSlugFromConfig(nav: NavConfig, slug: string): NavEn
162
176
  const match = flattenChildren(entry.section).find((c) => c.slug === normalized)
163
177
  if (match) return entry
164
178
  }
179
+ if (entry.children) {
180
+ const match = flattenChildren(entry.children).find((c) => c.slug === normalized)
181
+ if (match) return entry
182
+ }
165
183
  }
166
184
  }
167
185
 
@@ -184,6 +202,11 @@ export function getAllPublicEntries(nav: NavConfig): (NavEntry | NavChild)[] {
184
202
  if (child.roles.length === 0) entries.push(child)
185
203
  }
186
204
  }
205
+ if (entry.children) {
206
+ for (const child of flattenChildren(entry.children)) {
207
+ if (child.roles.length === 0) entries.push(child)
208
+ }
209
+ }
187
210
  }
188
211
  } else if ("slug" in item) {
189
212
  const entry = item as NavEntry