@ciderpress/ui 1.0.0-rc.12 → 1.0.0-rc.14
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/dist/head/css/themes/honeycrisp.css +1 -1
- package/dist/node.mjs +17 -13
- package/dist/plugins/mermaid/MermaidRenderer.tsx +2 -2
- package/dist/theme/components/home/hero-banner.tsx +225 -0
- package/dist/theme/components/home/hero-demo.css +492 -61
- package/dist/theme/components/home/hero-demo.test.tsx +159 -0
- package/dist/theme/components/home/hero-demo.tsx +335 -32
- package/dist/theme/components/home/hero.css +142 -9
- package/dist/theme/components/home/hero.test.tsx +105 -0
- package/dist/theme/components/home/hero.tsx +201 -4
- package/dist/theme/components/home/home-visual.css +98 -0
- package/dist/theme/components/home/home-visual.test.tsx +27 -0
- package/dist/theme/components/home/home-visual.tsx +20 -13
- package/dist/theme/components/home/layout.tsx +125 -1
- package/dist/theme/components/home/split.css +2 -2
- package/dist/theme/components/home/tabs.css +1 -1
- package/dist/theme/components/home/trust-strip.css +12 -0
- package/dist/theme/components/home/trust-strip.test.tsx +33 -0
- package/dist/theme/components/home/trust-strip.tsx +76 -15
- package/dist/theme/components/nav/layout.tsx +10 -14
- package/dist/theme/components/nav/nav-logo.tsx +2 -3
- package/dist/theme/components/sidebar/sidebar-filter.test.ts +49 -9
- package/dist/theme/components/sidebar/sidebar-filter.ts +24 -4
- package/dist/theme/index.tsx +8 -1
- package/dist/theme/lib/rich-text-parse.ts +0 -2
- package/dist/theme/lib/theme-favicon.ts +0 -3
- package/dist/theme/styles/themes/honeycrisp.css +20 -20
- package/package.json +20 -19
- package/src/theme/components/home/hero-banner.tsx +225 -0
- package/src/theme/components/home/hero-demo.css +492 -61
- package/src/theme/components/home/hero-demo.test.tsx +159 -0
- package/src/theme/components/home/hero-demo.tsx +335 -32
- package/src/theme/components/home/hero.css +142 -9
- package/src/theme/components/home/hero.test.tsx +105 -0
- package/src/theme/components/home/hero.tsx +201 -4
- package/src/theme/components/home/home-visual.css +98 -0
- package/src/theme/components/home/home-visual.test.tsx +27 -0
- package/src/theme/components/home/home-visual.tsx +20 -13
- package/src/theme/components/home/layout.tsx +125 -1
- package/src/theme/components/home/split.css +2 -2
- package/src/theme/components/home/tabs.css +1 -1
- package/src/theme/components/home/trust-strip.css +12 -0
- package/src/theme/components/home/trust-strip.test.tsx +33 -0
- package/src/theme/components/home/trust-strip.tsx +76 -15
- package/src/theme/components/nav/layout.tsx +10 -14
- package/src/theme/components/nav/nav-logo.tsx +2 -3
- package/src/theme/components/sidebar/sidebar-filter.test.ts +49 -9
- package/src/theme/components/sidebar/sidebar-filter.ts +24 -4
- package/src/theme/index.tsx +8 -1
- package/src/theme/lib/rich-text-parse.ts +0 -2
- package/src/theme/lib/theme-favicon.ts +0 -3
- package/src/theme/styles/themes/honeycrisp.css +20 -20
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act } from 'react'
|
|
4
|
+
import { createRoot } from 'react-dom/client'
|
|
5
|
+
import type { Root } from 'react-dom/client'
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { HeroDemo } from './hero-demo.tsx'
|
|
9
|
+
|
|
10
|
+
describe('hero demo', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.useFakeTimers()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.useRealTimers()
|
|
17
|
+
document.body.replaceChildren()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should explain the repository discovery workflow', async () => {
|
|
21
|
+
expect.assertions(4)
|
|
22
|
+
const rendered = await renderHeroDemo()
|
|
23
|
+
const output = rendered.container.textContent
|
|
24
|
+
expect(output).toContain('ciderpress dev')
|
|
25
|
+
expect(output).not.toContain('pnpm')
|
|
26
|
+
expect(output).toContain('docs/**/*.md')
|
|
27
|
+
expect(output).toContain('generated navigation from repository structure')
|
|
28
|
+
await unmountHeroDemo(rendered)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should switch previews through keyboard-focusable tab controls', async () => {
|
|
32
|
+
expect.assertions(4)
|
|
33
|
+
const rendered = await renderHeroDemo()
|
|
34
|
+
const cli = getButton({ container: rendered.container, label: 'CLI' })
|
|
35
|
+
const docs = getButton({ container: rendered.container, label: 'View docs' })
|
|
36
|
+
docs.focus()
|
|
37
|
+
expect(document.activeElement).toBe(docs)
|
|
38
|
+
await click(docs)
|
|
39
|
+
expect(docs.getAttribute('aria-selected')).toBe('true')
|
|
40
|
+
expect(cli.getAttribute('aria-selected')).toBe('false')
|
|
41
|
+
expect(getButton({ container: rendered.container, label: 'rerun' }).tabIndex).toBe(0)
|
|
42
|
+
await unmountHeroDemo(rendered)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should include the generated Acme Corp documentation preview', async () => {
|
|
46
|
+
expect.assertions(4)
|
|
47
|
+
const rendered = await renderHeroDemo()
|
|
48
|
+
const output = rendered.container.textContent
|
|
49
|
+
expect(output).toContain('http://localhost:6174')
|
|
50
|
+
expect(output).toContain('Acme Docs')
|
|
51
|
+
expect(output).toContain('Getting Started')
|
|
52
|
+
expect(output).toContain('On this page')
|
|
53
|
+
await unmountHeroDemo(rendered)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should advance timed stages and reset them when rerun', async () => {
|
|
57
|
+
expect.assertions(6)
|
|
58
|
+
const rendered = await renderHeroDemo()
|
|
59
|
+
const log = getLog(rendered.container)
|
|
60
|
+
const starting = getTextElement({ container: rendered.container, text: '◒ Starting' })
|
|
61
|
+
const ready = getTextElement({ container: rendered.container, text: '● Ready' })
|
|
62
|
+
expect(log.getAttribute('aria-busy')).toBe('true')
|
|
63
|
+
expect(starting.getAttribute('aria-hidden')).toBe('false')
|
|
64
|
+
expect(ready.getAttribute('aria-hidden')).toBe('true')
|
|
65
|
+
await act(() => vi.advanceTimersByTime(4_100))
|
|
66
|
+
expect(log.getAttribute('aria-busy')).toBe('false')
|
|
67
|
+
expect(ready.getAttribute('aria-hidden')).toBe('false')
|
|
68
|
+
await click(getButton({ container: rendered.container, label: 'rerun' }))
|
|
69
|
+
expect(log.getAttribute('aria-busy')).toBe('true')
|
|
70
|
+
await unmountHeroDemo(rendered)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
interface RenderedHeroDemo {
|
|
75
|
+
readonly container: HTMLDivElement
|
|
76
|
+
readonly root: Root
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Render the interactive hero demo into the test document.
|
|
81
|
+
*
|
|
82
|
+
* @private
|
|
83
|
+
* @returns Mounted component controls.
|
|
84
|
+
*/
|
|
85
|
+
async function renderHeroDemo(): Promise<RenderedHeroDemo> {
|
|
86
|
+
const container = document.createElement('div')
|
|
87
|
+
document.body.append(container)
|
|
88
|
+
const root = createRoot(container)
|
|
89
|
+
await act(async () => root.render(<HeroDemo />))
|
|
90
|
+
return { container, root }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Unmount a rendered hero demo and flush effect cleanup.
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
97
|
+
* @param rendered - Mounted component controls.
|
|
98
|
+
* @returns Promise resolved after cleanup.
|
|
99
|
+
*/
|
|
100
|
+
async function unmountHeroDemo(rendered: RenderedHeroDemo): Promise<void> {
|
|
101
|
+
return act(async () => rendered.root.unmount())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Click a native button and flush React updates.
|
|
106
|
+
*
|
|
107
|
+
* @private
|
|
108
|
+
* @param button - Button to activate.
|
|
109
|
+
* @returns Promise resolved after event handling.
|
|
110
|
+
*/
|
|
111
|
+
async function click(button: HTMLButtonElement): Promise<void> {
|
|
112
|
+
return act(async () => button.click())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Find a button by its accessible text.
|
|
117
|
+
*
|
|
118
|
+
* @private
|
|
119
|
+
* @param params - Rendered component container and visible button label.
|
|
120
|
+
* @returns Matching button.
|
|
121
|
+
*/
|
|
122
|
+
function getButton(params: {
|
|
123
|
+
readonly container: HTMLElement
|
|
124
|
+
readonly label: string
|
|
125
|
+
}): HTMLButtonElement {
|
|
126
|
+
const button = Array.from(params.container.querySelectorAll('button')).find(
|
|
127
|
+
(candidate) => candidate.textContent.trim() === params.label
|
|
128
|
+
)
|
|
129
|
+
return button as HTMLButtonElement
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Find the live terminal output region.
|
|
134
|
+
*
|
|
135
|
+
* @private
|
|
136
|
+
* @param container - Rendered component container.
|
|
137
|
+
* @returns Terminal log element.
|
|
138
|
+
*/
|
|
139
|
+
function getLog(container: HTMLElement): HTMLElement {
|
|
140
|
+
const log = container.querySelector<HTMLElement>('[role="log"]')
|
|
141
|
+
return log as HTMLElement
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Find an element by exact text content.
|
|
146
|
+
*
|
|
147
|
+
* @private
|
|
148
|
+
* @param params - Rendered component container and exact visible text.
|
|
149
|
+
* @returns Matching element.
|
|
150
|
+
*/
|
|
151
|
+
function getTextElement(params: {
|
|
152
|
+
readonly container: HTMLElement
|
|
153
|
+
readonly text: string
|
|
154
|
+
}): HTMLElement {
|
|
155
|
+
const element = Array.from(params.container.querySelectorAll<HTMLElement>('*')).find(
|
|
156
|
+
(candidate) => candidate.children.length === 0 && candidate.textContent.trim() === params.text
|
|
157
|
+
)
|
|
158
|
+
return element as HTMLElement
|
|
159
|
+
}
|
|
@@ -1,46 +1,349 @@
|
|
|
1
|
+
import { match } from 'massaman/match'
|
|
1
2
|
import type React from 'react'
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
2
4
|
|
|
3
5
|
import './hero-demo.css'
|
|
4
6
|
|
|
7
|
+
type DemoView = 'cli' | 'docs'
|
|
8
|
+
|
|
9
|
+
const FINAL_RUN_STEP = 5
|
|
10
|
+
const INITIAL_RUN_DELAY = 800
|
|
11
|
+
const RUN_DELAYS = [1300, 1850, 2300, 2700, 3300] as const
|
|
12
|
+
const activity = [
|
|
13
|
+
['23:18:04', 'synced', 'docs/getting-started/introduction.md', '12ms', 3],
|
|
14
|
+
['23:18:04', 'synced', 'packages/api/docs/authentication.md', '8ms', 4],
|
|
15
|
+
['23:18:05', 'restarted', 'openapi/acme.yaml', '41ms', 4],
|
|
16
|
+
] as const
|
|
17
|
+
|
|
5
18
|
/**
|
|
6
|
-
*
|
|
19
|
+
* Interactive hero demo showing Ciderpress turning repository Markdown into an internal docs site.
|
|
7
20
|
*
|
|
8
|
-
*
|
|
9
|
-
* so consumers can render it directly in MDX or via HomeLayout.
|
|
10
|
-
*
|
|
11
|
-
* @returns React element.
|
|
21
|
+
* @returns CLI and generated documentation preview
|
|
12
22
|
*/
|
|
13
23
|
export function HeroDemo(): React.ReactElement {
|
|
24
|
+
const [activeView, setActiveView] = useState<DemoView>('cli')
|
|
25
|
+
const [runId, setRunId] = useState(0)
|
|
26
|
+
const [runStep, setRunStep] = useState(0)
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (activeView === 'docs') {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setRunStep(0)
|
|
34
|
+
if (globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
35
|
+
setRunStep(FINAL_RUN_STEP)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const runDelay = match(runId)
|
|
40
|
+
.with(0, () => INITIAL_RUN_DELAY)
|
|
41
|
+
.otherwise(() => 0)
|
|
42
|
+
const timers = RUN_DELAYS.map((delay, index) =>
|
|
43
|
+
globalThis.setTimeout(() => setRunStep(index + 1), runDelay + delay)
|
|
44
|
+
)
|
|
45
|
+
return function clearRunTimers(): void {
|
|
46
|
+
return timers.reduce<undefined>((_, timer) => {
|
|
47
|
+
globalThis.clearTimeout(timer)
|
|
48
|
+
return undefined
|
|
49
|
+
}, undefined)
|
|
50
|
+
}
|
|
51
|
+
}, [activeView, runId])
|
|
52
|
+
|
|
53
|
+
function showCli(): void {
|
|
54
|
+
setActiveView('cli')
|
|
55
|
+
setRunId((current) => current + 1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function showDocs(): void {
|
|
59
|
+
setActiveView('docs')
|
|
60
|
+
}
|
|
61
|
+
|
|
14
62
|
return (
|
|
15
63
|
<div className="cp-hero-demo">
|
|
16
|
-
<div className="cp-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
64
|
+
<div className="cp-window cp-window--terminal" hidden={activeView !== 'cli'}>
|
|
65
|
+
<div className="cp-window__titlebar">
|
|
66
|
+
<WindowDots />
|
|
67
|
+
<span className="cp-window__title">ciderpress dev — ~/code/acme-platform</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div className="cp-window__content">
|
|
70
|
+
<div
|
|
71
|
+
className="cp-hero-demo__terminal"
|
|
72
|
+
role="log"
|
|
73
|
+
aria-label="Ciderpress development server output"
|
|
74
|
+
aria-live="polite"
|
|
75
|
+
aria-busy={runStep < FINAL_RUN_STEP}
|
|
76
|
+
>
|
|
77
|
+
<span className="cp-term-line cp-term-line--command">
|
|
78
|
+
<span className="cp-hero-demo__prompt" aria-hidden="true">
|
|
79
|
+
$
|
|
80
|
+
</span>
|
|
81
|
+
<span className="cp-hero-demo__typing" data-initial-run={runId === 0} key={runId}>
|
|
82
|
+
ciderpress dev
|
|
83
|
+
</span>
|
|
84
|
+
</span>
|
|
85
|
+
<span
|
|
86
|
+
className="cp-hero-demo__loading"
|
|
87
|
+
data-initial-run={runId === 0}
|
|
88
|
+
data-visible={runStep === 0}
|
|
89
|
+
aria-hidden={runStep !== 0}
|
|
90
|
+
>
|
|
91
|
+
◒ loading
|
|
92
|
+
</span>
|
|
93
|
+
<div
|
|
94
|
+
className="cp-hero-demo__run-output"
|
|
95
|
+
data-visible={runStep >= 1}
|
|
96
|
+
aria-hidden={runStep < 1}
|
|
97
|
+
>
|
|
98
|
+
<pre className="cp-hero-demo__cli-banner" aria-label="Ciderpress">
|
|
99
|
+
{'█▀▀ █ █▀▄ █▀▀ █▀█ █▀█ █▀█ █▀▀ █▀▀ █▀▀\n█▄▄ █ █▄▀ ██▄ █▀▄ █▀▀ █▀▄ ██▄ ▄▄█ ▄▄█'}
|
|
100
|
+
</pre>
|
|
101
|
+
<div className="cp-hero-demo__status-row">
|
|
102
|
+
<span className="cp-term-text--cyan">http://localhost:6174</span>
|
|
103
|
+
<span className="cp-hero-demo__status">
|
|
104
|
+
<span
|
|
105
|
+
className="cp-term-text--yellow"
|
|
106
|
+
data-visible={runStep < FINAL_RUN_STEP}
|
|
107
|
+
aria-hidden={runStep >= FINAL_RUN_STEP}
|
|
108
|
+
>
|
|
109
|
+
◒ Starting
|
|
110
|
+
</span>
|
|
111
|
+
<span
|
|
112
|
+
className="cp-term-text--green"
|
|
113
|
+
data-visible={runStep >= FINAL_RUN_STEP}
|
|
114
|
+
aria-hidden={runStep < FINAL_RUN_STEP}
|
|
115
|
+
>
|
|
116
|
+
● Ready
|
|
117
|
+
</span>
|
|
118
|
+
</span>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<div
|
|
122
|
+
className="cp-hero-demo__run-output"
|
|
123
|
+
data-visible={runStep >= 2}
|
|
124
|
+
aria-hidden={runStep < 2}
|
|
125
|
+
>
|
|
126
|
+
<div className="cp-hero-demo__rule" />
|
|
127
|
+
<div className="cp-hero-demo__watching">
|
|
128
|
+
<span className="cp-term-text--dim">watching </span>
|
|
129
|
+
<span className="cp-term-text--white">docs/**/*.md</span>
|
|
130
|
+
<span className="cp-term-text--dim"> across 12 workspaces</span>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<div className="cp-hero-demo__activity">
|
|
134
|
+
{activity.map(([time, action, file, elapsed, visibleAt], index) => (
|
|
135
|
+
<div
|
|
136
|
+
className="cp-hero-demo__activity-row cp-hero-demo__run-output"
|
|
137
|
+
data-visible={runStep >= visibleAt}
|
|
138
|
+
aria-hidden={runStep < visibleAt}
|
|
139
|
+
key={file}
|
|
140
|
+
>
|
|
141
|
+
<span
|
|
142
|
+
className={match(index)
|
|
143
|
+
.with(0, () => '')
|
|
144
|
+
.otherwise(() => 'cp-term-text--dim')}
|
|
145
|
+
>
|
|
146
|
+
{time}
|
|
147
|
+
</span>
|
|
148
|
+
<span
|
|
149
|
+
className={match({ action, index })
|
|
150
|
+
.with({ action: 'restarted', index: 0 }, () => 'cp-term-text--yellow')
|
|
151
|
+
.with({ action: 'restarted' }, () => 'cp-term-text--yellow cp-term-text--dim')
|
|
152
|
+
.with({ index: 0 }, () => 'cp-term-text--green')
|
|
153
|
+
.otherwise(() => 'cp-term-text--green cp-term-text--dim')}
|
|
154
|
+
>
|
|
155
|
+
{action}
|
|
156
|
+
</span>
|
|
157
|
+
<span
|
|
158
|
+
className={match(index)
|
|
159
|
+
.with(0, () => '')
|
|
160
|
+
.otherwise(() => 'cp-term-text--dim')}
|
|
161
|
+
>
|
|
162
|
+
{file}
|
|
163
|
+
</span>
|
|
164
|
+
<span className="cp-term-text--dim">{elapsed}</span>
|
|
165
|
+
</div>
|
|
166
|
+
))}
|
|
167
|
+
</div>
|
|
168
|
+
<div
|
|
169
|
+
className="cp-hero-demo__run-output"
|
|
170
|
+
data-visible={runStep >= FINAL_RUN_STEP}
|
|
171
|
+
aria-hidden={runStep < FINAL_RUN_STEP}
|
|
172
|
+
>
|
|
173
|
+
<div className="cp-hero-demo__rule" />
|
|
174
|
+
<div className="cp-hero-demo__stats">
|
|
175
|
+
<span>
|
|
176
|
+
<span className="cp-term-text--green">84</span>
|
|
177
|
+
<span className="cp-term-text--dim"> written · </span>
|
|
178
|
+
<span className="cp-term-text--yellow">0</span>
|
|
179
|
+
<span className="cp-term-text--dim"> skipped · </span>
|
|
180
|
+
<span className="cp-term-text--red">0</span>
|
|
181
|
+
<span className="cp-term-text--dim"> removed · 412ms</span>
|
|
182
|
+
</span>
|
|
183
|
+
<button className="cp-hero-demo__open" type="button" onClick={showDocs}>
|
|
184
|
+
<span className="cp-term-text--cyan">o</span>
|
|
185
|
+
<span> open docs</span>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<span className="cp-hero-demo__sr-only">
|
|
190
|
+
generated navigation from repository structure
|
|
191
|
+
</span>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div className="cp-window cp-window--browser" hidden={activeView !== 'docs'}>
|
|
197
|
+
<div className="cp-window__titlebar">
|
|
198
|
+
<WindowDots />
|
|
199
|
+
<div className="cp-browser__tab">
|
|
200
|
+
<span className="cp-browser__tab-title">Acme Corp · Introduction</span>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div className="cp-browser__url-bar">
|
|
204
|
+
<BrowserNav />
|
|
205
|
+
<span className="cp-browser__url">http://localhost:6174</span>
|
|
206
|
+
<svg className="cp-browser__menu-icon" viewBox="0 0 16 16" fill="currentColor">
|
|
207
|
+
<circle cx="8" cy="3" r="1.2" />
|
|
208
|
+
<circle cx="8" cy="8" r="1.2" />
|
|
209
|
+
<circle cx="8" cy="13" r="1.2" />
|
|
210
|
+
</svg>
|
|
211
|
+
</div>
|
|
212
|
+
<div className="cp-window__content">
|
|
213
|
+
<div className="cp-hero-demo__site">
|
|
214
|
+
<header className="cp-hero-demo__site-header">
|
|
215
|
+
<strong className="cp-hero-demo__site-wordmark">Acme Docs</strong>
|
|
216
|
+
<div className="cp-hero-demo__search">
|
|
217
|
+
Search <kbd>⌘K</kbd>
|
|
218
|
+
</div>
|
|
219
|
+
<nav className="cp-hero-demo__site-nav" aria-label="Generated documentation">
|
|
220
|
+
<strong>Getting Started</strong>
|
|
221
|
+
<span>Concepts</span>
|
|
222
|
+
<span>Guides</span>
|
|
223
|
+
<span>API</span>
|
|
224
|
+
</nav>
|
|
225
|
+
</header>
|
|
226
|
+
<div className="cp-hero-demo__docs-layout">
|
|
227
|
+
<aside className="cp-hero-demo__sidebar">
|
|
228
|
+
<span className="cp-hero-demo__sidebar-menu">▣ Menu</span>
|
|
229
|
+
<span>⌂ Home</span>
|
|
230
|
+
<span>▱ Changelog</span>
|
|
231
|
+
<span className="cp-hero-demo__sidebar-label">Getting started</span>
|
|
232
|
+
<strong>Introduction</strong>
|
|
233
|
+
<span>Quick Start</span>
|
|
234
|
+
<span className="cp-hero-demo__sidebar-label">Concepts</span>
|
|
235
|
+
<span>Content</span>
|
|
236
|
+
<span>Navigation</span>
|
|
237
|
+
<span>Publishing</span>
|
|
238
|
+
<span>Themes</span>
|
|
239
|
+
</aside>
|
|
240
|
+
<main className="cp-hero-demo__document">
|
|
241
|
+
<div className="cp-hero-demo__breadcrumb">⌂ › Getting Started › Introduction</div>
|
|
242
|
+
<article>
|
|
243
|
+
<h2>Introduction</h2>
|
|
244
|
+
<div className="cp-hero-demo__document-rule" />
|
|
245
|
+
<h3>What is Acme Docs?</h3>
|
|
246
|
+
<p>
|
|
247
|
+
Acme Docs is the source of truth for building, shipping, and operating the
|
|
248
|
+
platform. Content stays beside the code that it documents.
|
|
249
|
+
</p>
|
|
250
|
+
<code>ciderpress dev</code>
|
|
251
|
+
<p>One command, working docs site.</p>
|
|
252
|
+
<h3>Why Acme Docs?</h3>
|
|
253
|
+
</article>
|
|
254
|
+
</main>
|
|
255
|
+
<aside className="cp-hero-demo__toc">
|
|
256
|
+
<strong>On this page</strong>
|
|
257
|
+
<span>What is Acme Docs?</span>
|
|
258
|
+
<span>Why Acme Docs?</span>
|
|
259
|
+
<span>Features</span>
|
|
260
|
+
</aside>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
23
264
|
</div>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
265
|
+
|
|
266
|
+
<div className="cp-hero-demo__switcher">
|
|
267
|
+
<div role="tablist" aria-label="Ciderpress preview">
|
|
268
|
+
<button
|
|
269
|
+
type="button"
|
|
270
|
+
role="tab"
|
|
271
|
+
aria-selected={activeView === 'cli'}
|
|
272
|
+
className={match(activeView)
|
|
273
|
+
.with('cli', () => 'is-active')
|
|
274
|
+
.otherwise(() => '')}
|
|
275
|
+
onClick={showCli}
|
|
276
|
+
>
|
|
277
|
+
CLI
|
|
278
|
+
</button>
|
|
279
|
+
<button
|
|
280
|
+
type="button"
|
|
281
|
+
role="tab"
|
|
282
|
+
aria-selected={activeView === 'docs'}
|
|
283
|
+
className={match(activeView)
|
|
284
|
+
.with('docs', () => 'is-active')
|
|
285
|
+
.otherwise(() => '')}
|
|
286
|
+
onClick={showDocs}
|
|
287
|
+
>
|
|
288
|
+
View docs
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
<button className="cp-hero-demo__rerun" type="button" onClick={showCli}>
|
|
293
|
+
<svg aria-hidden="true" viewBox="0 0 16 16" fill="none">
|
|
294
|
+
<path
|
|
295
|
+
d="M13 8a5 5 0 1 1-1.46-3.54L13 6M13 3v3h-3"
|
|
296
|
+
stroke="currentColor"
|
|
297
|
+
strokeWidth="1.5"
|
|
298
|
+
strokeLinecap="round"
|
|
299
|
+
strokeLinejoin="round"
|
|
300
|
+
/>
|
|
301
|
+
</svg>
|
|
302
|
+
rerun
|
|
303
|
+
</button>
|
|
304
|
+
</div>
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Render canonical desktop-window traffic lights.
|
|
310
|
+
*
|
|
311
|
+
* @private
|
|
312
|
+
* @returns Decorative window controls
|
|
313
|
+
*/
|
|
314
|
+
function WindowDots(): React.ReactElement {
|
|
315
|
+
return (
|
|
316
|
+
<div className="cp-window__dots" aria-hidden="true">
|
|
317
|
+
<span className="cp-window__dot cp-window__dot--close" />
|
|
318
|
+
<span className="cp-window__dot cp-window__dot--minimize" />
|
|
319
|
+
<span className="cp-window__dot cp-window__dot--maximize" />
|
|
320
|
+
</div>
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Render compact browser navigation controls.
|
|
326
|
+
*
|
|
327
|
+
* @private
|
|
328
|
+
* @returns Decorative browser controls
|
|
329
|
+
*/
|
|
330
|
+
function BrowserNav(): React.ReactElement {
|
|
331
|
+
return (
|
|
332
|
+
<div className="cp-browser__nav" aria-hidden="true">
|
|
333
|
+
<svg className="cp-browser__nav-icon" viewBox="0 0 16 16" fill="currentColor">
|
|
334
|
+
<path d="M10.5 3 5.5 8l5 5" stroke="currentColor" strokeWidth="1.5" fill="none" />
|
|
335
|
+
</svg>
|
|
336
|
+
<svg className="cp-browser__nav-icon" viewBox="0 0 16 16" fill="currentColor">
|
|
337
|
+
<path d="m5.5 3 5 5-5 5" stroke="currentColor" strokeWidth="1.5" fill="none" />
|
|
338
|
+
</svg>
|
|
339
|
+
<svg className="cp-browser__nav-icon" viewBox="0 0 16 16" fill="currentColor">
|
|
340
|
+
<path
|
|
341
|
+
d="M13 8A5 5 0 1 1 3 8a5 5 0 0 1 10 0Z"
|
|
342
|
+
stroke="currentColor"
|
|
343
|
+
strokeWidth="1.5"
|
|
344
|
+
fill="none"
|
|
345
|
+
/>
|
|
346
|
+
</svg>
|
|
44
347
|
</div>
|
|
45
348
|
)
|
|
46
349
|
}
|