@raystack/chronicle 0.5.3 → 0.6.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 (74) hide show
  1. package/dist/cli/index.js +260 -81
  2. package/package.json +8 -6
  3. package/src/cli/commands/build.ts +5 -8
  4. package/src/cli/commands/dev.ts +5 -6
  5. package/src/cli/commands/init.test.ts +77 -0
  6. package/src/cli/commands/init.ts +73 -40
  7. package/src/cli/commands/serve.ts +6 -9
  8. package/src/cli/commands/start.ts +5 -5
  9. package/src/cli/utils/config.ts +6 -12
  10. package/src/cli/utils/scaffold.test.ts +179 -0
  11. package/src/cli/utils/scaffold.ts +70 -9
  12. package/src/components/api/field-row.tsx +1 -1
  13. package/src/components/api/field-section.tsx +2 -2
  14. package/src/components/mdx/index.tsx +1 -1
  15. package/src/components/mdx/mermaid.tsx +24 -21
  16. package/src/components/ui/breadcrumbs.tsx +4 -2
  17. package/src/components/ui/client-theme-switcher.tsx +21 -4
  18. package/src/components/ui/search.module.css +16 -41
  19. package/src/components/ui/search.tsx +30 -41
  20. package/src/lib/config.test.ts +493 -0
  21. package/src/lib/config.ts +123 -22
  22. package/src/lib/head.tsx +23 -5
  23. package/src/lib/llms.test.ts +94 -0
  24. package/src/lib/llms.ts +41 -0
  25. package/src/lib/navigation.test.ts +94 -0
  26. package/src/lib/navigation.ts +51 -0
  27. package/src/lib/page-context.tsx +79 -32
  28. package/src/lib/route-resolver.test.ts +173 -0
  29. package/src/lib/route-resolver.ts +73 -0
  30. package/src/lib/source.ts +94 -1
  31. package/src/lib/version-source.test.ts +163 -0
  32. package/src/lib/version-source.ts +101 -0
  33. package/src/pages/ApiPage.tsx +1 -1
  34. package/src/pages/DocsLayout.tsx +24 -3
  35. package/src/pages/DocsPage.tsx +7 -7
  36. package/src/pages/LandingPage.module.css +56 -0
  37. package/src/pages/LandingPage.tsx +39 -0
  38. package/src/pages/NotFound.module.css +3 -0
  39. package/src/pages/NotFound.tsx +9 -12
  40. package/src/server/App.tsx +21 -23
  41. package/src/server/api/{page/[...slug].ts → page.ts} +7 -3
  42. package/src/server/api/search.ts +51 -24
  43. package/src/server/api/specs.ts +17 -5
  44. package/src/server/entry-client.tsx +42 -14
  45. package/src/server/entry-server.tsx +35 -13
  46. package/src/server/plugins/telemetry.ts +47 -7
  47. package/src/server/routes/[...slug].md.ts +0 -6
  48. package/src/server/routes/[version]/llms.txt.ts +26 -0
  49. package/src/server/routes/llms.txt.ts +10 -13
  50. package/src/server/routes/og.tsx +2 -2
  51. package/src/server/routes/sitemap.xml.ts +14 -6
  52. package/src/server/vite-config.ts +5 -5
  53. package/src/themes/default/ContentDirButtons.tsx +66 -0
  54. package/src/themes/default/Layout.module.css +187 -40
  55. package/src/themes/default/Layout.tsx +166 -65
  56. package/src/themes/default/OpenInAI.tsx +112 -0
  57. package/src/themes/default/Page.module.css +30 -0
  58. package/src/themes/default/Page.tsx +1 -3
  59. package/src/themes/default/SidebarLogo.tsx +26 -0
  60. package/src/themes/default/Toc.module.css +102 -25
  61. package/src/themes/default/Toc.tsx +56 -10
  62. package/src/themes/default/VersionSwitcher.tsx +59 -0
  63. package/src/themes/paper/ContentDirDropdown.tsx +47 -0
  64. package/src/themes/paper/Layout.module.css +7 -0
  65. package/src/themes/paper/Layout.tsx +20 -13
  66. package/src/themes/paper/VersionSwitcher.tsx +60 -0
  67. package/src/types/config.ts +146 -23
  68. package/src/types/content.ts +11 -1
  69. package/src/types/theme.ts +1 -0
  70. package/src/components/ui/footer.module.css +0 -27
  71. package/src/components/ui/footer.tsx +0 -30
  72. package/src/server/api/metrics.ts +0 -23
  73. package/src/server/api/page/index.ts +0 -1
  74. package/src/server/telemetry.ts +0 -49
@@ -1,6 +1,5 @@
1
- 'use client'
2
-
3
- import { useEffect, useId, useState } from 'react'
1
+ import { renderMermaidSVG } from 'beautiful-mermaid'
2
+ import { useMemo } from 'react'
4
3
  import styles from './mermaid.module.css'
5
4
 
6
5
  interface MermaidProps {
@@ -8,30 +7,34 @@ interface MermaidProps {
8
7
  }
9
8
 
10
9
  export function Mermaid({ chart }: MermaidProps) {
11
- const mermaidId = useId().replace(/:/g, '-')
12
- const [svg, setSvg] = useState<string>('')
13
-
14
- useEffect(() => {
15
- let cancelled = false
16
-
17
- async function render() {
18
- const { default: mermaid } = await import('mermaid')
19
- mermaid.initialize({ startOnLoad: false, theme: 'default' })
20
- const { svg: rendered } = await mermaid.render(
21
- mermaidId,
22
- chart
23
- )
24
- if (!cancelled) setSvg(rendered)
10
+ const { svg, error } = useMemo(() => {
11
+ try {
12
+ return {
13
+ svg: renderMermaidSVG(chart, {
14
+ bg: 'var(--rs-color-background-base-primary)',
15
+ fg: 'var(--rs-color-foreground-base-primary)',
16
+ line: 'var(--rs-color-border-base-focus)',
17
+ accent: 'var(--rs-color-foreground-accent-primary)',
18
+ muted: 'var(--rs-color-foreground-base-secondary)',
19
+ surface: 'var(--rs-color-background-neutral-secondary)',
20
+ border: 'var(--rs-color-border-base-tertiary)',
21
+ transparent: true,
22
+ }),
23
+ error: null,
24
+ }
25
+ } catch (err) {
26
+ return {
27
+ svg: null,
28
+ error: err instanceof Error ? err : new Error(String(err)),
29
+ }
25
30
  }
26
-
27
- render()
28
- return () => { cancelled = true }
29
31
  }, [chart])
30
32
 
33
+ if (error) return <pre className={styles.error}>{error.message}</pre>
31
34
  return (
32
35
  <div
33
36
  className={styles.mermaid}
34
- dangerouslySetInnerHTML={{ __html: svg }}
37
+ dangerouslySetInnerHTML={{ __html: svg! }}
35
38
  />
36
39
  )
37
40
  }
@@ -3,6 +3,7 @@
3
3
  import { Breadcrumb } from '@raystack/apsara'
4
4
  import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb'
5
5
  import type { Root } from 'fumadocs-core/page-tree'
6
+ import { Link as RouterLink } from 'react-router'
6
7
 
7
8
  interface BreadcrumbsProps {
8
9
  slug: string[]
@@ -18,11 +19,12 @@ export function Breadcrumbs({ slug, tree }: BreadcrumbsProps) {
18
19
  return (
19
20
  <Breadcrumb size="small">
20
21
  {items.flatMap((item, index) => {
22
+ const isCurrent = index === items.length - 1
21
23
  const breadcrumbItem = (
22
24
  <Breadcrumb.Item
23
25
  key={`item-${index}`}
24
- href={item.url}
25
- current={index === items.length - 1}
26
+ current={isCurrent}
27
+ render={isCurrent ? undefined : <RouterLink to={item.url} />}
26
28
  >
27
29
  {item.name}
28
30
  </Breadcrumb.Item>
@@ -1,18 +1,35 @@
1
1
  'use client'
2
2
 
3
- import { ThemeSwitcher } from '@raystack/apsara'
4
- import { useState, useEffect } from 'react'
3
+ import { MoonIcon, SunIcon } from '@heroicons/react/24/outline'
4
+ import { IconButton, useTheme } from '@raystack/apsara'
5
+ import { useEffect, useState } from 'react'
5
6
 
6
7
  interface ClientThemeSwitcherProps {
7
8
  size?: number
8
9
  }
9
10
 
10
- export function ClientThemeSwitcher({ size }: ClientThemeSwitcherProps) {
11
+ export function ClientThemeSwitcher({ size = 16 }: ClientThemeSwitcherProps) {
11
12
  const [isClient, setIsClient] = useState(false)
13
+ const { resolvedTheme, setTheme } = useTheme()
12
14
 
13
15
  useEffect(() => {
14
16
  setIsClient(true)
15
17
  }, [])
16
18
 
17
- return isClient ? <ThemeSwitcher size={size} /> : null
19
+ if (!isClient) return null
20
+
21
+ const isDark = resolvedTheme === 'dark'
22
+ return (
23
+ <IconButton
24
+ size={3}
25
+ aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}
26
+ onClick={() => setTheme(isDark ? 'light' : 'dark')}
27
+ >
28
+ {isDark ? (
29
+ <SunIcon width={size} height={size} />
30
+ ) : (
31
+ <MoonIcon width={size} height={size} />
32
+ )}
33
+ </IconButton>
34
+ )
18
35
  }
@@ -1,63 +1,38 @@
1
- .trigger {
2
- gap: 8px;
3
- color: var(--rs-color-foreground-base-secondary);
4
- cursor: pointer;
5
- }
6
-
7
- .kbd {
8
- padding: 2px 6px;
9
- border-radius: 4px;
10
- border: 1px solid var(--rs-color-border-base-primary);
11
- font-size: 12px;
12
- }
13
-
14
1
  .dialogContent {
2
+ border-radius: var(--rs-radius-4);
3
+ padding: 0px;
4
+ width: 80%;
15
5
  max-width: 600px;
16
- padding: 0;
17
- min-height: 0;
18
6
  position: fixed;
19
7
  top: 20%;
20
- left: 50%;
21
- transform: translateX(-50%);
22
- border-radius: var(--rs-radius-4);
23
8
  }
24
9
 
25
10
  .input {
26
- flex: 1;
27
- border: none;
28
- outline: none;
29
- background: transparent;
30
- font-size: 16px;
31
- color: var(--rs-color-foreground-base-primary);
11
+ font-size: var(--rs-font-size-small);
32
12
  }
33
13
 
34
14
  .list {
35
- max-height: 300px;
36
- overflow: auto;
37
- padding: 0;
15
+ max-height: 400px;
38
16
  }
39
17
 
40
- .visuallyHidden {
41
- position: absolute;
42
- width: 1px;
43
- height: 1px;
44
- padding: 0;
45
- margin: -1px;
46
- overflow: hidden;
47
- clip: rect(0, 0, 0, 0);
48
- white-space: nowrap;
49
- border: 0;
18
+ .list :global([cmdk-group-heading]) {
19
+ color: var(--rs-color-foreground-base-tertiary);
20
+ font-size: var(--rs-font-size-mini);
21
+ font-weight: var(--rs-font-weight-medium);
22
+ letter-spacing: var(--rs-letter-spacing-mini);
23
+ padding: var(--rs-space-6) var(--rs-space-5) var(--rs-space-3);
50
24
  }
51
25
 
52
26
  .item {
53
- padding: 12px 16px;
27
+ height: 32px;
28
+ padding: var(--rs-space-3);
29
+ gap: var(--rs-space-3);
30
+ border-radius: var(--rs-radius-2);
54
31
  cursor: pointer;
55
- border-radius: 6px;
56
32
  }
57
33
 
58
34
  .item[data-selected="true"] {
59
- background: var(--rs-color-background-base-secondary);
60
- color: var(--rs-color-foreground-accent-primary-hover);
35
+ background: var(--rs-color-background-base-primary-hover);
61
36
  }
62
37
 
63
38
  .itemContent {
@@ -1,28 +1,17 @@
1
- import { DocumentIcon, HashtagIcon } from '@heroicons/react/24/outline';
2
- import { Button, Command, Dialog, Text } from '@raystack/apsara';
3
- import { cx } from 'class-variance-authority';
1
+ import {
2
+ DocumentIcon,
3
+ HashtagIcon,
4
+ MagnifyingGlassIcon
5
+ } from '@heroicons/react/24/outline';
6
+ import { Command, IconButton, Text } from '@raystack/apsara';
4
7
  import type { SortedResult } from 'fumadocs-core/search';
5
8
  import { useDocsSearch } from 'fumadocs-core/search/client';
6
9
  import { useCallback, useEffect, useState } from 'react';
7
10
  import { useNavigate } from 'react-router';
8
11
  import { MethodBadge } from '@/components/api/method-badge';
12
+ import { usePageContext } from '@/lib/page-context';
9
13
  import styles from './search.module.css';
10
14
 
11
- function SearchShortcutKey({ className }: { className?: string }) {
12
- const [key, setKey] = useState('⌘');
13
-
14
- useEffect(() => {
15
- const isMac = navigator.platform?.toUpperCase().includes('MAC');
16
- setKey(isMac ? '⌘' : 'Ctrl');
17
- }, []);
18
-
19
- return (
20
- <kbd className={className} suppressHydrationWarning>
21
- {key} K
22
- </kbd>
23
- );
24
- }
25
-
26
15
  interface SearchProps {
27
16
  className?: string;
28
17
  }
@@ -30,10 +19,12 @@ interface SearchProps {
30
19
  export function Search({ className }: SearchProps) {
31
20
  const [open, setOpen] = useState(false);
32
21
  const navigate = useNavigate();
22
+ const { version } = usePageContext();
33
23
 
34
24
  const { search, setSearch, query } = useDocsSearch({
35
25
  type: 'fetch',
36
26
  api: '/api/search',
27
+ tag: version.dir ?? undefined,
37
28
  delayMs: 100,
38
29
  allowEmpty: true
39
30
  });
@@ -64,31 +55,28 @@ export function Search({ className }: SearchProps) {
64
55
 
65
56
  return (
66
57
  <>
67
- <Button
68
- variant='outline'
69
- color='neutral'
70
- size='small'
58
+ <IconButton
59
+ size={3}
60
+ aria-label='Search'
61
+ title='Search (Ctrl/⌘K)'
71
62
  onClick={() => setOpen(true)}
72
- className={cx(styles.trigger, className)}
73
- trailingIcon={<SearchShortcutKey className={styles.kbd} />}
63
+ className={className}
74
64
  >
75
- Search...
76
- </Button>
77
-
78
- <Dialog open={open} onOpenChange={setOpen}>
79
- <Dialog.Content className={styles.dialogContent}>
80
- <Dialog.Title className={styles.visuallyHidden}>
81
- Search documentation
82
- </Dialog.Title>
83
- <Command loop>
65
+ <MagnifyingGlassIcon width={16} height={16} />
66
+ </IconButton>
67
+
68
+ <Command.Dialog open={open} onOpenChange={setOpen}>
69
+ <Command.DialogContent className={styles.dialogContent}>
70
+ <Command>
84
71
  <Command.Input
85
72
  placeholder='Search'
73
+ leadingIcon={<MagnifyingGlassIcon width={16} height={16} />}
86
74
  value={search}
87
- onValueChange={setSearch}
75
+ onChange={(e) => setSearch(e.target.value)}
88
76
  className={styles.input}
89
77
  />
90
78
 
91
- <Command.List className={styles.list}>
79
+ <Command.Content className={styles.list}>
92
80
  {query.isLoading && <Command.Empty>Loading...</Command.Empty>}
93
81
  {!query.isLoading &&
94
82
  search.length > 0 &&
@@ -98,12 +86,13 @@ export function Search({ className }: SearchProps) {
98
86
  {!query.isLoading &&
99
87
  search.length === 0 &&
100
88
  results.length > 0 && (
101
- <Command.Group heading='Suggestions'>
89
+ <Command.Group>
90
+ <Command.Label>Suggestions</Command.Label>
102
91
  {results.slice(0, 8).map((result: SortedResult) => (
103
92
  <Command.Item
104
93
  key={result.id}
105
94
  value={result.id}
106
- onSelect={() => onSelect(result.url)}
95
+ onClick={() => onSelect(result.url)}
107
96
  className={styles.item}
108
97
  >
109
98
  <div className={styles.itemContent}>
@@ -123,7 +112,7 @@ export function Search({ className }: SearchProps) {
123
112
  <Command.Item
124
113
  key={result.id}
125
114
  value={result.id}
126
- onSelect={() => onSelect(result.url)}
115
+ onClick={() => onSelect(result.url)}
127
116
  className={styles.item}
128
117
  >
129
118
  <div className={styles.itemContent}>
@@ -152,10 +141,10 @@ export function Search({ className }: SearchProps) {
152
141
  </div>
153
142
  </Command.Item>
154
143
  ))}
155
- </Command.List>
144
+ </Command.Content>
156
145
  </Command>
157
- </Dialog.Content>
158
- </Dialog>
146
+ </Command.DialogContent>
147
+ </Command.Dialog>
159
148
  </>
160
149
  );
161
150
  }