@jaamd/jaad 0.11.0 → 1.0.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.
Files changed (138) hide show
  1. package/README.md +8 -2
  2. package/dist/jaad.css +232 -347
  3. package/docs/00-overview.md +25 -0
  4. package/docs/01-getting-started/01-installation.md +216 -0
  5. package/docs/01-getting-started/02-pages.md +109 -0
  6. package/docs/01-getting-started/03-deployment.md +100 -0
  7. package/docs/02-configurations/01-reference.md +215 -0
  8. package/docs/02-configurations/02-frontmatter.md +105 -0
  9. package/docs/02-configurations/03-i18n.md +210 -0
  10. package/docs/02-configurations/04-search.md +58 -0
  11. package/docs/02-configurations/05-themes.md +104 -0
  12. package/docs/02-configurations/06-styles.md +156 -0
  13. package/docs/02-configurations/07-custom-pages.md +119 -0
  14. package/docs/02-configurations/08-advanced-layout.md +139 -0
  15. package/docs/03-upgrading.md +208 -0
  16. package/index.ts +8 -22
  17. package/package.json +10 -9
  18. package/site.ts +18 -1
  19. package/src/config.ts +25 -176
  20. package/src/{advanced.ts → docs/advanced.ts} +6 -4
  21. package/src/docs/collection.ts +109 -0
  22. package/src/docs/components/Breadcrumbs.astro +29 -0
  23. package/src/docs/components/DocsMobileNav.astro +90 -0
  24. package/src/docs/components/DocsNavTree.astro +57 -0
  25. package/src/docs/components/DocsPage.astro +66 -0
  26. package/src/{components/docs → docs/components}/DocsPageNav.astro +4 -4
  27. package/src/docs/components/DocsSidebarLeft.astro +53 -0
  28. package/src/{components/docs → docs/components}/PageActions.astro +10 -8
  29. package/src/{components/docs → docs/components}/StructuredData.astro +3 -3
  30. package/src/{components/docs → docs/components}/TableOfContents.astro +20 -22
  31. package/src/docs/config.ts +167 -0
  32. package/src/docs/content.ts +36 -0
  33. package/src/docs/endpoints.ts +89 -0
  34. package/src/docs/integration.ts +249 -0
  35. package/src/{layouts → docs/layouts}/DefaultDocsFrame.astro +7 -5
  36. package/src/{layouts → docs/layouts}/Docs.astro +44 -43
  37. package/src/docs/layouts/Page.astro +18 -0
  38. package/src/docs/link-plugins.ts +52 -0
  39. package/src/docs/links.ts +42 -0
  40. package/src/{locale-tree.ts → docs/locale-tree.ts} +1 -1
  41. package/src/docs/model/ids.ts +87 -0
  42. package/src/docs/model/rendered.ts +185 -0
  43. package/src/docs/model/tree.ts +124 -0
  44. package/src/docs/model/validate.ts +75 -0
  45. package/src/docs/navigation.ts +75 -0
  46. package/src/{routes → docs/routes}/404.astro +1 -1
  47. package/src/docs/routes/docs-index.astro +19 -0
  48. package/src/docs/routes/docs-slug.astro +54 -0
  49. package/src/{routes → docs/routes}/docs-slug.md.ts +8 -10
  50. package/src/{routes → docs/routes}/llms-locale.txt.ts +1 -1
  51. package/src/{routes → docs/routes}/llms.txt.ts +1 -1
  52. package/src/{routes → docs/routes}/search-index-locale.json.ts +1 -1
  53. package/src/{routes → docs/routes}/search-index.json.ts +1 -1
  54. package/src/docs/runtime.ts +17 -0
  55. package/src/docs/search-index.ts +39 -0
  56. package/src/docs/styles/components/breadcrumbs.css +26 -0
  57. package/src/docs/styles/components/docs-mobile-nav.css +47 -0
  58. package/src/docs/styles/components/docs-page-nav.css +41 -0
  59. package/src/docs/styles/components/docs-sidebar.css +59 -0
  60. package/src/docs/styles/components/page-actions.css +51 -0
  61. package/src/docs/styles/components/toc.css +38 -0
  62. package/src/docs/styles/components.css +7 -0
  63. package/src/docs/styles/docs.css +81 -0
  64. package/src/{@types/docs.ts → docs/types.ts} +8 -16
  65. package/src/{urls.ts → docs/urls.ts} +2 -16
  66. package/src/env.d.ts +1 -1
  67. package/src/integration.ts +15 -249
  68. package/src/{components → site/components}/Footer.astro +1 -1
  69. package/src/{components → site/components}/Header.astro +14 -11
  70. package/src/{components → site/components}/LocaleSwitcher.astro +2 -2
  71. package/src/{components → site/components}/NavMobile.astro +1 -1
  72. package/src/{components → site/components}/SearchPalette.astro +160 -79
  73. package/src/{components → site/components}/ThemeSwitcher.astro +1 -1
  74. package/src/site/config.ts +149 -0
  75. package/src/{infer.ts → site/infer.ts} +3 -3
  76. package/src/site/integration.ts +59 -0
  77. package/src/{layouts/Page.astro → site/layouts/Shell.astro} +6 -3
  78. package/src/{locales.ts → site/locales.ts} +9 -1
  79. package/src/{docs-config.ts → site/runtime.ts} +1 -12
  80. package/src/site/search.ts +227 -0
  81. package/src/{social.ts → site/social.ts} +2 -2
  82. package/src/site/styles/components/header.css +31 -0
  83. package/src/{styles → site/styles}/components/search.css +21 -1
  84. package/src/{styles → site/styles}/components/shell.css +4 -0
  85. package/src/site/styles/components.css +10 -0
  86. package/src/{styles → site/styles}/global.css +4 -26
  87. package/src/{themes/index.ts → site/themes.ts} +1 -1
  88. package/src/{ui → site/ui}/en.ts +3 -5
  89. package/src/{ui → site/ui}/it.ts +2 -4
  90. package/src/site/urls.ts +14 -0
  91. package/src/{virtual.ts → site/virtual.ts} +12 -18
  92. package/src/styles.css +5 -0
  93. package/src/collection.ts +0 -97
  94. package/src/components/ScrollToTop.astro +0 -43
  95. package/src/components/docs/Breadcrumbs.astro +0 -36
  96. package/src/components/docs/DocsMobileNav.astro +0 -93
  97. package/src/components/docs/DocsPage.astro +0 -68
  98. package/src/components/docs/DocsSidebarLeft.astro +0 -82
  99. package/src/content.ts +0 -42
  100. package/src/navigation.ts +0 -97
  101. package/src/routes/docs-index.astro +0 -19
  102. package/src/routes/docs-slug.astro +0 -58
  103. package/src/styles/components/breadcrumbs.css +0 -13
  104. package/src/styles/components/docs-mobile-nav.css +0 -9
  105. package/src/styles/components/docs-page-nav.css +0 -31
  106. package/src/styles/components/docs-sidebar.css +0 -42
  107. package/src/styles/components/header.css +0 -20
  108. package/src/styles/components/page-actions.css +0 -13
  109. package/src/styles/components/scroll-to-top.css +0 -11
  110. package/src/styles/components/toc.css +0 -21
  111. package/src/styles/docs.css +0 -112
  112. package/src/utils/docs.ts +0 -300
  113. package/src/utils/endpoints.ts +0 -80
  114. package/src/utils/search.ts +0 -83
  115. /package/src/{styles → docs/styles}/components/docs-page.css +0 -0
  116. /package/src/{styles → docs/styles}/jaamd.css +0 -0
  117. /package/src/{components → site/components}/Head.astro +0 -0
  118. /package/src/{components → site/components}/Nav.astro +0 -0
  119. /package/src/{components → site/components}/SocialLinks.astro +0 -0
  120. /package/src/{components → site/components}/locale-switcher.ts +0 -0
  121. /package/src/{fonts → site/fonts}/OFL.txt +0 -0
  122. /package/src/{fonts → site/fonts}/inter.woff2 +0 -0
  123. /package/src/{fonts → site/fonts}/merriweather-italic.woff2 +0 -0
  124. /package/src/{fonts → site/fonts}/merriweather.woff2 +0 -0
  125. /package/src/{fonts.ts → site/fonts.ts} +0 -0
  126. /package/src/{forges.ts → site/forges.ts} +0 -0
  127. /package/src/{utils → site}/helpers.ts +0 -0
  128. /package/src/{styles → site/styles}/components/footer.css +0 -0
  129. /package/src/{styles → site/styles}/components/locale-switcher.css +0 -0
  130. /package/src/{styles → site/styles}/components/nav-mobile.css +0 -0
  131. /package/src/{styles → site/styles}/components/nav.css +0 -0
  132. /package/src/{styles → site/styles}/components/not-found.css +0 -0
  133. /package/src/{styles → site/styles}/components/social-links.css +0 -0
  134. /package/src/{styles → site/styles}/components/theme-switcher.css +0 -0
  135. /package/src/{styles → site/styles}/jaamd-forward.css +0 -0
  136. /package/src/{styles → site/styles}/jaamd-reverse.css +0 -0
  137. /package/src/{styles → site/styles}/tailwind.css +0 -0
  138. /package/src/{ui.ts → site/ui.ts} +0 -0
@@ -1,43 +0,0 @@
1
- ---
2
- import { ui } from '../docs-config.ts'
3
-
4
- const t = ui(Astro.props.locale)
5
- ---
6
-
7
- <jaad-scroll-top>
8
- <button class="jaad-scroll-top-button" aria-label={t('scrollTop')}>
9
- <svg class="jaad-scroll-top-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
10
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
11
- </svg>
12
- </button>
13
- </jaad-scroll-top>
14
-
15
- <script>
16
- const SHOW_AFTER = 300
17
-
18
- class JaadScrollTop extends HTMLElement {
19
- #button: HTMLButtonElement | null = null
20
- #onScroll = () => {
21
- const shown = window.scrollY > SHOW_AFTER
22
- this.#button?.toggleAttribute('data-visible', shown)
23
- }
24
-
25
- connectedCallback() {
26
- this.#button = this.querySelector('button')
27
- this.#button?.addEventListener('click', () => {
28
- const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches
29
- window.scrollTo({ top: 0, behavior: reduced ? 'auto' : 'smooth' })
30
- })
31
- window.addEventListener('scroll', this.#onScroll, { passive: true })
32
- this.#onScroll()
33
- }
34
-
35
- disconnectedCallback() {
36
- window.removeEventListener('scroll', this.#onScroll)
37
- }
38
- }
39
-
40
- if (!customElements.get('jaad-scroll-top')) {
41
- customElements.define('jaad-scroll-top', JaadScrollTop)
42
- }
43
- </script>
@@ -1,36 +0,0 @@
1
- ---
2
- import config from 'virtual:jaad/config'
3
- import { docsUrls, ui } from '../../docs-config.ts'
4
- import { docsPageHref } from '../../urls.ts'
5
- import { formatChapterTitle } from '../../utils/docs.ts'
6
-
7
- interface Props {
8
- chapter?: string
9
- title: string
10
- locale?: string
11
- }
12
-
13
- const { chapter, title, locale } = Astro.props
14
- const t = ui(locale)
15
- const chapterTitle = formatChapterTitle(chapter)
16
-
17
- const rootSegment = config.docsBase.split('/').filter(Boolean).at(-1)
18
- const rootLabel = formatChapterTitle(rootSegment) ?? t('breadcrumb.root')
19
- const rootHref = docsPageHref('', docsUrls(locale))
20
- ---
21
-
22
- <nav aria-label={t('breadcrumb.label')} class="docs-breadcrumbs">
23
- <a href={rootHref} class="docs-breadcrumbs-link">
24
- {rootLabel}
25
- </a>
26
- {chapterTitle && (
27
- <>
28
- <span aria-hidden="true">/</span>
29
- <span>{chapterTitle}</span>
30
- </>
31
- )}
32
- <span aria-hidden="true">/</span>
33
- <span class="docs-breadcrumbs-current" aria-current="page">
34
- {title}
35
- </span>
36
- </nav>
@@ -1,93 +0,0 @@
1
- ---
2
- import type { DocsHeadings } from '../../@types/docs.ts'
3
- import type { NavSection } from '../../navigation.ts'
4
- import { ui } from '../../docs-config.ts'
5
- import { formatChapterTitle } from '../../utils/docs.ts'
6
- import ScrollToTop from '../ScrollToTop.astro'
7
-
8
- interface Props {
9
- locale?: string
10
- sections: NavSection[]
11
- headings: DocsHeadings[]
12
- }
13
-
14
- const { sections, headings, locale } = Astro.props
15
- const t = ui(locale)
16
- ---
17
-
18
- <div class="docs-mobile-nav">
19
- <div class="docs-select">
20
- <label for="page-select" class="jaad-sr-only">
21
- {t('nav.selectPage')}
22
- </label>
23
- <select id="page-select" class="docs-select-field">
24
- {sections.map((section) =>
25
- section.type === 'page' ? (
26
- <option value={section.item.href} selected={section.item.isActive}>
27
- {section.item.title}
28
- </option>
29
- ) : (
30
- <optgroup label={formatChapterTitle(section.chapter) || section.chapter}>
31
- {section.items.map((page) => (
32
- <option value={page.href} selected={page.isActive}>
33
- {page.title}
34
- </option>
35
- ))}
36
- </optgroup>
37
- ),
38
- )}
39
- </select>
40
- </div>
41
-
42
- {headings.length > 0 && (
43
- <div id="section-select-wrapper" class="docs-select">
44
- <label for="section-select" class="jaad-sr-only">
45
- Jump to section
46
- </label>
47
- <select id="section-select" class="docs-select-field">
48
- <option value="">{t('nav.jumpToSection')}</option>
49
- {headings.map((heading) => {
50
- const prefix = heading.depth === 3 ? '\u00A0\u00A0\u00A0\u00A0' : ''
51
- return (
52
- <option value={`#${heading.slug}`}>
53
- {prefix}
54
- {heading.text}
55
- </option>
56
- )
57
- })}
58
- </select>
59
- </div>
60
- )}
61
- </div>
62
-
63
- <ScrollToTop {locale} />
64
-
65
- <script>
66
- import { navigate } from 'astro:transitions/client'
67
-
68
- function initMobileNav(): void {
69
- const pageSelect = document.querySelector<HTMLSelectElement>('#page-select')
70
- const sectionSelect = document.querySelector<HTMLSelectElement>('#section-select')
71
-
72
- if (pageSelect && !pageSelect.dataset.navInit) {
73
- pageSelect.dataset.navInit = '1'
74
- pageSelect.addEventListener('change', () => {
75
- if (pageSelect.value) navigate(pageSelect.value)
76
- })
77
- }
78
-
79
- if (sectionSelect && !sectionSelect.dataset.navInit) {
80
- sectionSelect.dataset.navInit = '1'
81
- sectionSelect.addEventListener('change', () => {
82
- const target = sectionSelect.value
83
- if (!target) return
84
- document.querySelector(target)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
85
- // Reset so picking the same section twice in a row fires again.
86
- setTimeout(() => (sectionSelect.value = ''), 500)
87
- })
88
- }
89
- }
90
-
91
- initMobileNav()
92
- document.addEventListener('astro:page-load', initMobileNav)
93
- </script>
@@ -1,68 +0,0 @@
1
- ---
2
- import type { DocsEntry } from '../../@types/docs.ts'
3
- import MarkdownContent from '@jaamd/jaamd/components'
4
- import config from 'virtual:jaad/config'
5
- import { DEFAULT_LOCALE, LOCALES, docsUrls, ui } from '../../docs-config.ts'
6
- import DocsLayout from '../../layouts/Docs.astro'
7
- import { buildDocsNavigation } from '../../navigation.ts'
8
- import {
9
- getCleanSlug,
10
- parseDocCollectionId,
11
- docTitle,
12
- extractHeadingsFromMarkdown,
13
- routedId,
14
- } from '../../utils/docs.ts'
15
- import Breadcrumbs from './Breadcrumbs.astro'
16
- import DocsMobileNav from './DocsMobileNav.astro'
17
- import DocsPageNav from './DocsPageNav.astro'
18
- import PageActions from './PageActions.astro'
19
-
20
- interface Props {
21
- entry: DocsEntry
22
- sortedPages: DocsEntry[]
23
- /** Left out on the opening page, whose title is the site name alone. */
24
- headTitle?: string
25
- }
26
-
27
- const { entry, sortedPages, headTitle } = Astro.props
28
-
29
- const cleanSlug = getCleanSlug(routedId(entry))
30
- const { chapter } = parseDocCollectionId(routedId(entry))
31
- const title = docTitle(entry)
32
- const headings = extractHeadingsFromMarkdown(entry.body || '')
33
- const urls = docsUrls(entry.locale)
34
- const t = ui(entry.locale)
35
- const language = LOCALES.find((known) => known.code === (entry.locale ?? DEFAULT_LOCALE))?.tag ?? config.lang
36
- const updated = entry.data.lastUpdated
37
- const navigation = buildDocsNavigation(sortedPages, entry.id, urls)
38
- const contentAttrs = { id: 'content' }
39
- ---
40
-
41
- <DocsLayout title={headTitle} chapter={chapter} entry={entry} headings={headings} {navigation}>
42
- <DocsMobileNav slot="mobile-navigation" sections={navigation.sections} headings={headings} locale={entry.locale} />
43
-
44
- <Breadcrumbs slot="breadcrumbs" chapter={chapter} title={title} locale={entry.locale} />
45
- <PageActions slot="page-actions" id={entry.id} cleanSlug={cleanSlug} locale={entry.locale} />
46
-
47
- <Fragment slot="content">
48
- <MarkdownContent {...contentAttrs} class="docs-content">
49
- <slot />
50
- </MarkdownContent>
51
-
52
- {updated && (
53
- <p class="docs-updated">
54
- {t('page.lastUpdated')}{' '}
55
- <time datetime={updated.toISOString()}>
56
- {new Intl.DateTimeFormat(language, { dateStyle: 'long' }).format(updated)}
57
- </time>
58
- </p>
59
- )}
60
- </Fragment>
61
-
62
- <DocsPageNav
63
- slot="page-navigation"
64
- previousPage={navigation.previousPage}
65
- nextPage={navigation.nextPage}
66
- locale={entry.locale}
67
- />
68
- </DocsLayout>
@@ -1,82 +0,0 @@
1
- ---
2
- import type { NavSection } from '../../navigation.ts'
3
- import { ui } from '../../docs-config.ts'
4
- import { formatChapterTitle } from '../../utils/docs.ts'
5
-
6
- interface Props {
7
- sections: NavSection[]
8
- locale?: string
9
- }
10
-
11
- const { sections, locale } = Astro.props
12
- const t = ui(locale)
13
- ---
14
-
15
- <aside class="docs-sidebar-left jaad-sidebar">
16
- <nav aria-label={t('nav.documentation')}>
17
- {sections.map((section) =>
18
- section.type === 'page' ? (
19
- <div class="docs-sidebar-page">
20
- <a
21
- href={section.item.href}
22
- class="docs-sidebar-link"
23
- aria-current={section.item.isActive ? 'page' : undefined}
24
- >
25
- {section.item.title}
26
- </a>
27
- </div>
28
- ) : (
29
- <div class="docs-sidebar-chapter">
30
- <h3 class="docs-sidebar-chapter-title">{formatChapterTitle(section.chapter)}</h3>
31
- <ul class="docs-sidebar-chapter-list">
32
- {section.items.map((item) => (
33
- <li>
34
- <a
35
- href={item.href}
36
- class="docs-sidebar-sublink"
37
- aria-current={item.isActive ? 'page' : undefined}
38
- >
39
- {item.title}
40
- </a>
41
- </li>
42
- ))}
43
- </ul>
44
- </div>
45
- ),
46
- )}
47
- </nav>
48
- </aside>
49
-
50
- <script>
51
- const SELECTOR = '.docs-sidebar-left'
52
- let scrollTop = 0
53
-
54
- const sidebar = () => document.querySelector<HTMLElement>(SELECTOR)
55
-
56
- /** Scrolls the sidebar only, never the document, and only when needed. */
57
- function revealCurrent() {
58
- const el = sidebar()
59
- const current = el?.querySelector('[aria-current="page"]')
60
- if (!el || !current) return
61
-
62
- const box = el.getBoundingClientRect()
63
- const item = current.getBoundingClientRect()
64
- if (item.top >= box.top && item.bottom <= box.bottom) return
65
-
66
- el.scrollTop += item.top - box.top - (box.height - item.height) / 2
67
- }
68
-
69
- document.addEventListener('astro:before-swap', () => {
70
- scrollTop = sidebar()?.scrollTop ?? 0
71
- })
72
-
73
- document.addEventListener('astro:after-swap', () => {
74
- const el = sidebar()
75
- if (!el) return
76
- el.scrollTop = scrollTop
77
- // A deep link, or a page whose entry sits outside the restored window.
78
- revealCurrent()
79
- })
80
-
81
- revealCurrent()
82
- </script>
package/src/content.ts DELETED
@@ -1,42 +0,0 @@
1
- import { glob } from 'astro/loaders'
2
- import { z } from 'astro/zod'
3
- import { defineCollection } from 'astro:content'
4
- import config from 'virtual:jaad/config'
5
- import { getDocCollectionId } from './utils/docs.ts'
6
-
7
- const docsPages = defineCollection({
8
- loader: glob({
9
- pattern: '**/*.md',
10
- base: config.docsDir,
11
- // Keep URLs file-driven even if markdown contains an unrelated slug field.
12
- generateId: ({ entry }) => getDocCollectionId(entry),
13
- }),
14
- // Every field is optional: markdown without frontmatter works as-is.
15
- schema: z.object({
16
- title: z.string().optional(),
17
- description: z.string().optional(),
18
- /** Shorter label, in navigation only. */
19
- label: z.string().optional(),
20
- /** Pairs this page with its translations when the slugs differ. */
21
- translationKey: z.string().optional(),
22
- /** Replaces the filename prefix number. */
23
- order: z.number().int().optional(),
24
- /** Left out of production builds. */
25
- draft: z.boolean().default(false),
26
- /** Metadata and search weight; never rendered. */
27
- keywords: z.array(z.string()).default([]),
28
- author: z.string().optional(),
29
- /** Public path or URL; false drops the site's. */
30
- ogImage: z
31
- .union([
32
- z.string().refine((v) => v.startsWith('/') || /^https?:\/\//.test(v), {
33
- message: 'ogImage must be a public url, such as "/og/page.png"',
34
- }),
35
- z.literal(false),
36
- ])
37
- .optional(),
38
- lastUpdated: z.coerce.date().optional(),
39
- }),
40
- })
41
-
42
- export const collections = { docsPages }
package/src/navigation.ts DELETED
@@ -1,97 +0,0 @@
1
- import type { DocsNavItem } from './@types/docs.ts'
2
- import { docsPageHref, type DocsUrlOptions } from './urls.ts'
3
- import { docLabel, getCanonicalDocSlug, parseDocCollectionId, routedId, type DocsPageLike } from './utils/docs.ts'
4
-
5
- export type NavSection =
6
- | { type: 'page'; order: number; item: DocsNavItem }
7
- | { type: 'chapter'; order: number; chapter: string; items: DocsNavItem[] }
8
-
9
- export interface DocsNavigation {
10
- sections: NavSection[]
11
- previousPage: DocsNavItem | null
12
- nextPage: DocsNavItem | null
13
- }
14
-
15
- function groupByChapter(items: DocsNavItem[]): NavSection[] {
16
- const chapters = new Map<string, Extract<NavSection, { type: 'chapter' }>>()
17
- const sections: NavSection[] = []
18
-
19
- for (const item of items) {
20
- if (!item.chapter) {
21
- sections.push({ type: 'page', order: item.primaryOrder, item })
22
- continue
23
- }
24
-
25
- let chapter = chapters.get(item.chapter)
26
- if (!chapter) {
27
- chapter = {
28
- type: 'chapter',
29
- order: item.primaryOrder,
30
- chapter: item.chapter,
31
- items: [],
32
- }
33
- chapters.set(item.chapter, chapter)
34
- sections.push(chapter)
35
- }
36
- chapter.items.push(item)
37
- }
38
-
39
- return sections.toSorted((a, b) => a.order - b.order)
40
- }
41
-
42
- /** Build every navigation view from one ordered collection traversal. */
43
- interface Model {
44
- items: DocsNavItem[]
45
- positions: Map<string, number>
46
- }
47
-
48
- // Only isActive and the neighbours vary between the pages of one locale. The
49
- // hrefs do not, as long as the url options are the same ones.
50
- const models = new WeakMap<object, Map<string, Model>>()
51
-
52
- function modelFor(sortedPages: DocsPageLike[], urls: DocsUrlOptions): Model {
53
- const key = `${urls.docsBase}\u0000${urls.deploymentBase ?? ''}\u0000${urls.locale ?? ''}\u0000${urls.defaultLocale ?? ''}`
54
- let byUrls = models.get(sortedPages)
55
- if (!byUrls) {
56
- byUrls = new Map()
57
- models.set(sortedPages, byUrls)
58
- }
59
- const cached = byUrls.get(key)
60
- if (cached) return cached
61
-
62
- const model: Model = {
63
- items: sortedPages.map((page, index): DocsNavItem => {
64
- const parsed = parseDocCollectionId(routedId(page))
65
- return {
66
- title: docLabel(page),
67
- chapter: parsed.chapter,
68
- primaryOrder: parsed.orderChapter ?? page.data?.order ?? parsed.order,
69
- href: docsPageHref(getCanonicalDocSlug(page, index), urls),
70
- isActive: false,
71
- }
72
- }),
73
- positions: new Map(sortedPages.map((page, index) => [page.id, index])),
74
- }
75
-
76
- byUrls.set(key, model)
77
- return model
78
- }
79
-
80
- export function buildDocsNavigation(
81
- sortedPages: DocsPageLike[],
82
- currentId: string,
83
- urls: DocsUrlOptions,
84
- ): DocsNavigation {
85
- const { items, positions } = modelFor(sortedPages, urls)
86
- const currentIndex = positions.get(currentId) ?? -1
87
- const marked = items.map((item, index) => ({
88
- ...item,
89
- isActive: index === currentIndex,
90
- }))
91
-
92
- return {
93
- sections: groupByChapter(marked),
94
- previousPage: currentIndex > 0 ? marked[currentIndex - 1] : null,
95
- nextPage: currentIndex >= 0 && currentIndex < marked.length - 1 ? marked[currentIndex + 1] : null,
96
- }
97
- }
@@ -1,19 +0,0 @@
1
- ---
2
- import { render } from 'astro:content'
3
- import { getSortedDocsPages } from '../collection.ts'
4
- import DocsPage from '../components/docs/DocsPage.astro'
5
- import { withDeploymentBase } from '../urls.ts'
6
-
7
- const sortedPages = await getSortedDocsPages()
8
-
9
- const firstPage = sortedPages[0]
10
- if (!firstPage) {
11
- return Astro.redirect(withDeploymentBase('/404', import.meta.env.BASE_URL))
12
- }
13
-
14
- const { Content } = await render(firstPage)
15
- ---
16
-
17
- <DocsPage entry={firstPage} {sortedPages}>
18
- <Content />
19
- </DocsPage>
@@ -1,58 +0,0 @@
1
- ---
2
- import { render } from 'astro:content'
3
- import { getSortedDocsPages } from '../collection.ts'
4
- import DocsPage from '../components/docs/DocsPage.astro'
5
- import { buildLocales, docsUrls, localePrefix } from '../docs-config.ts'
6
- import { docsPageHref, withDeploymentBase } from '../urls.ts'
7
- import { getCanonicalDocSlug, getCleanSlug, docTitle, routedId } from '../utils/docs.ts'
8
-
9
- interface DocsPath {
10
- params: { slug: string }
11
- props: { id: string; locale?: string; isOpening: boolean }
12
- }
13
-
14
- export async function getStaticPaths() {
15
- const paths: DocsPath[] = []
16
-
17
- for (const locale of buildLocales()) {
18
- const sortedPages = await getSortedDocsPages(locale)
19
- const prefix = localePrefix(locale)
20
-
21
- sortedPages.forEach((page, index) => {
22
- const isOpening = getCanonicalDocSlug(page, index) === ''
23
- paths.push({
24
- params: { slug: `${prefix}${getCleanSlug(routedId(page))}` },
25
- props: { id: page.id, locale, isOpening },
26
- })
27
-
28
- // A prefixed locale has no index route of its own: /it is this page.
29
- if (isOpening && prefix) {
30
- paths.push({
31
- params: { slug: locale! },
32
- props: { id: page.id, locale, isOpening: false },
33
- })
34
- }
35
- })
36
- }
37
-
38
- return paths
39
- }
40
-
41
- const { id, locale, isOpening } = Astro.props
42
-
43
- if (isOpening) {
44
- return Astro.redirect(docsPageHref('', docsUrls(locale)), 301)
45
- }
46
-
47
- const sortedPages = await getSortedDocsPages(locale)
48
- const docPage = sortedPages.find((page) => page.id === id)
49
- if (!docPage) {
50
- return Astro.redirect(withDeploymentBase('/404', import.meta.env.BASE_URL))
51
- }
52
-
53
- const { Content } = await render(docPage)
54
- ---
55
-
56
- <DocsPage entry={docPage} {sortedPages} headTitle={docTitle(docPage)}>
57
- <Content />
58
- </DocsPage>
@@ -1,13 +0,0 @@
1
- @layer components {
2
- .docs-breadcrumbs {
3
- @apply text-foreground-muted mb-4 flex items-center gap-1.5 text-xs;
4
- }
5
-
6
- .docs-breadcrumbs-link {
7
- @apply hover:text-foreground-bright no-underline;
8
- }
9
-
10
- .docs-breadcrumbs-current {
11
- @apply text-foreground-bright font-medium;
12
- }
13
- }
@@ -1,9 +0,0 @@
1
- @layer components {
2
- .docs-mobile-nav {
3
- @apply mb-8 space-y-4 lg:hidden;
4
- }
5
-
6
- .docs-select-field {
7
- @apply border-primary/20 bg-surface text-foreground focus:border-primary w-full appearance-none rounded-lg border py-3 pr-10 pl-4 transition-colors focus:outline-none;
8
- }
9
- }
@@ -1,31 +0,0 @@
1
- @layer components {
2
- .docs-page-nav {
3
- @apply py-8;
4
- }
5
-
6
- .docs-page-nav-row {
7
- @apply flex items-center justify-between gap-4;
8
- }
9
-
10
- .docs-page-nav-link {
11
- @apply border-border-dark bg-background-tertiary text-foreground-bright hover:border-primary hover:bg-border-dark hover:text-primary flex max-w-[calc(45%-1rem)] min-w-0 items-center gap-2 rounded-lg border px-4 py-3 no-underline transition-all duration-300;
12
- }
13
-
14
- .docs-page-nav-icon {
15
- @apply h-4 w-4 shrink-0 transition-transform;
16
- }
17
-
18
- @media (hover: hover) {
19
- .docs-page-nav-link:hover .docs-page-nav-icon-prev {
20
- @apply -translate-x-1;
21
- }
22
-
23
- .docs-page-nav-link:hover .docs-page-nav-icon-next {
24
- @apply translate-x-1;
25
- }
26
- }
27
-
28
- .docs-page-nav-label {
29
- @apply overflow-hidden font-medium text-ellipsis whitespace-nowrap;
30
- }
31
- }
@@ -1,42 +0,0 @@
1
- @layer components {
2
- .docs-sidebar-page {
3
- @apply mb-0.5;
4
- }
5
-
6
- .docs-sidebar-page:not(:first-child) {
7
- @apply mt-0.5;
8
- }
9
-
10
- .docs-sidebar-link,
11
- .docs-sidebar-sublink {
12
- @apply text-foreground hover:bg-primary/10 hover:text-primary block px-3 py-1.5 text-sm no-underline transition-colors;
13
- }
14
-
15
- .docs-sidebar-link {
16
- @apply rounded;
17
- }
18
-
19
- .docs-sidebar-sublink {
20
- @apply rounded-r;
21
- }
22
-
23
- .docs-sidebar-link[aria-current='page'] {
24
- @apply bg-primary/10 text-primary font-semibold;
25
- }
26
-
27
- .docs-sidebar-sublink[aria-current='page'] {
28
- @apply border-primary bg-primary/10 text-primary -ml-px border-l-2 pl-2.75 font-semibold;
29
- }
30
-
31
- .docs-sidebar-chapter {
32
- @apply mt-5 mb-5;
33
- }
34
-
35
- .docs-sidebar-chapter-title {
36
- @apply text-primary/50 mb-1 px-3 text-[0.825rem] font-semibold tracking-widest uppercase;
37
- }
38
-
39
- .docs-sidebar-chapter-list {
40
- @apply border-border/40 ml-3 space-y-0.5 border-l pl-0;
41
- }
42
- }
@@ -1,20 +0,0 @@
1
- @layer components {
2
- .jaad-header-inner {
3
- @apply container mx-auto p-4;
4
- }
5
- .jaad-header-row {
6
- @apply flex items-center gap-4;
7
- }
8
- .jaad-brand {
9
- @apply text-foreground-bright text-xl;
10
- }
11
- .jaad-brand-link {
12
- @apply text-primary no-underline;
13
- }
14
- .jaad-logo {
15
- @apply h-7;
16
- }
17
- .jaad-header-actions {
18
- @apply ml-auto flex items-center gap-2;
19
- }
20
- }
@@ -1,13 +0,0 @@
1
- @layer components {
2
- .docs-page-actions {
3
- @apply mb-4 flex items-center gap-2 text-xs;
4
- }
5
-
6
- .docs-page-action {
7
- @apply border-border bg-surface hover:bg-surface-hover text-foreground-secondary hover:text-foreground-bright flex cursor-pointer items-center gap-1.5 rounded-lg border px-3 py-1.5 no-underline transition-colors;
8
- }
9
-
10
- .docs-page-action-icon {
11
- @apply h-3.5 w-3.5 shrink-0;
12
- }
13
- }