@ciderpress/ui 1.0.0-rc.1 → 1.0.0-rc.10
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/index.d.ts +1 -1
- package/dist/index.mjs +4 -3
- package/dist/node.d.ts +1 -1
- package/dist/node.mjs +335 -51
- package/dist/plugins/mermaid/MermaidRenderer.tsx +49 -6
- package/dist/theme/components/footer/site-footer.tsx +68 -9
- package/dist/theme/components/home/feature-card.tsx +5 -8
- package/dist/theme/components/home/feature.tsx +31 -11
- package/dist/theme/components/home/hero-demo-custom.tsx +116 -0
- package/dist/theme/components/home/hero-demo.css +27 -0
- package/dist/theme/components/home/layout.tsx +149 -26
- package/dist/theme/components/home/split-visual-custom.tsx +26 -0
- package/dist/theme/components/home/workspaces.tsx +39 -3
- package/dist/theme/components/nav/branch-tag.tsx +7 -7
- package/dist/theme/components/nav/ciderpress-docs-bar.css +13 -1
- package/dist/theme/components/nav/ciderpress-docs-bar.tsx +33 -2
- package/dist/theme/components/nav/ciderpress-header.css +2 -1
- package/dist/theme/components/nav/ciderpress-header.tsx +26 -3
- package/dist/theme/components/nav/ciderpress-nav-hamburger.css +50 -0
- package/dist/theme/components/nav/ciderpress-nav-hamburger.tsx +95 -9
- package/dist/theme/components/nav/ciderpress-nav-menu.css +88 -0
- package/dist/theme/components/nav/ciderpress-nav-menu.tsx +397 -38
- package/dist/theme/components/nav/ciderpress-nav-social-links.tsx +27 -22
- package/dist/theme/components/nav/floating-branch-indicator.tsx +7 -7
- package/dist/theme/components/nav/header-icon.css +24 -0
- package/dist/theme/components/nav/header-icon.tsx +119 -0
- package/dist/theme/components/nav/header-logo.css +16 -0
- package/dist/theme/components/nav/header-logo.tsx +200 -0
- package/dist/theme/components/nav/layout.tsx +117 -16
- package/dist/theme/components/nav/nav-logo.tsx +16 -19
- package/dist/theme/components/openapi/index.ts +0 -2
- package/dist/theme/components/openapi/openapi.css +0 -14
- package/dist/theme/components/shared/card-icon.tsx +37 -0
- package/dist/theme/components/shared/ciderpress-logo.tsx +5 -1
- package/dist/theme/components/shared/icon.tsx +120 -35
- package/dist/theme/components/shared/resolve-card-icon.ts +53 -12
- package/dist/theme/components/shared/section-card.tsx +37 -6
- package/dist/theme/components/sidebar/sidebar-badge.css +50 -0
- package/dist/theme/components/sidebar/sidebar-badge.tsx +153 -0
- package/dist/theme/components/sidebar/sidebar-links.tsx +6 -2
- package/dist/theme/components/sidebar/sidebar-scope.tsx +24 -1
- package/dist/theme/components/theme-provider.tsx +27 -11
- package/dist/theme/components/workspaces/card.tsx +7 -9
- package/dist/theme/hooks/use-ciderpress.ts +65 -5
- package/dist/theme/hooks/use-nav-items.ts +82 -11
- package/dist/theme/index.tsx +5 -3
- package/dist/theme/lib/read-social-links.ts +4 -3
- package/dist/theme/lib/theme-favicon.ts +1 -1
- package/dist/theme/lib/with-mount-base.ts +31 -0
- package/dist/theme/styles/overrides/footnotes.css +54 -0
- package/dist/theme/styles/overrides/lists.css +83 -0
- package/dist/theme/styles/overrides/rail.css +27 -10
- package/dist/theme/styles/overrides/section-card.css +9 -0
- package/dist/theme/styles/overrides/sidebar.css +22 -0
- package/package.json +18 -16
- package/src/theme/components/footer/site-footer.tsx +68 -9
- package/src/theme/components/home/feature-card.tsx +5 -8
- package/src/theme/components/home/feature.tsx +31 -11
- package/src/theme/components/home/hero-demo-custom.tsx +116 -0
- package/src/theme/components/home/hero-demo.css +27 -0
- package/src/theme/components/home/layout.tsx +149 -26
- package/src/theme/components/home/split-visual-custom.tsx +26 -0
- package/src/theme/components/home/workspaces.tsx +39 -3
- package/src/theme/components/nav/branch-tag.tsx +7 -7
- package/src/theme/components/nav/ciderpress-docs-bar.css +13 -1
- package/src/theme/components/nav/ciderpress-docs-bar.tsx +33 -2
- package/src/theme/components/nav/ciderpress-header.css +2 -1
- package/src/theme/components/nav/ciderpress-header.tsx +26 -3
- package/src/theme/components/nav/ciderpress-nav-hamburger.css +50 -0
- package/src/theme/components/nav/ciderpress-nav-hamburger.tsx +95 -9
- package/src/theme/components/nav/ciderpress-nav-menu.css +88 -0
- package/src/theme/components/nav/ciderpress-nav-menu.tsx +397 -38
- package/src/theme/components/nav/ciderpress-nav-social-links.tsx +27 -22
- package/src/theme/components/nav/floating-branch-indicator.tsx +7 -7
- package/src/theme/components/nav/header-icon.css +24 -0
- package/src/theme/components/nav/header-icon.tsx +119 -0
- package/src/theme/components/nav/header-logo.css +16 -0
- package/src/theme/components/nav/header-logo.tsx +200 -0
- package/src/theme/components/nav/layout.tsx +117 -16
- package/src/theme/components/nav/nav-logo.tsx +16 -19
- package/src/theme/components/openapi/index.ts +0 -2
- package/src/theme/components/openapi/openapi.css +0 -14
- package/src/theme/components/shared/card-icon.tsx +37 -0
- package/src/theme/components/shared/ciderpress-logo.tsx +5 -1
- package/src/theme/components/shared/icon.tsx +120 -35
- package/src/theme/components/shared/resolve-card-icon.ts +53 -12
- package/src/theme/components/shared/section-card.tsx +37 -6
- package/src/theme/components/sidebar/sidebar-badge.css +50 -0
- package/src/theme/components/sidebar/sidebar-badge.tsx +153 -0
- package/src/theme/components/sidebar/sidebar-links.tsx +6 -2
- package/src/theme/components/sidebar/sidebar-scope.tsx +24 -1
- package/src/theme/components/theme-provider.tsx +27 -11
- package/src/theme/components/workspaces/card.tsx +7 -9
- package/src/theme/hooks/use-ciderpress.ts +65 -5
- package/src/theme/hooks/use-nav-items.ts +82 -11
- package/src/theme/index.tsx +5 -3
- package/src/theme/lib/read-social-links.ts +4 -3
- package/src/theme/lib/theme-favicon.ts +1 -1
- package/src/theme/lib/with-mount-base.ts +31 -0
- package/src/theme/styles/overrides/footnotes.css +54 -0
- package/src/theme/styles/overrides/lists.css +83 -0
- package/src/theme/styles/overrides/rail.css +27 -10
- package/src/theme/styles/overrides/section-card.css +9 -0
- package/src/theme/styles/overrides/sidebar.css +22 -0
- package/dist/theme/components/openapi/copy-markdown-button.tsx +0 -41
- package/src/theme/components/openapi/copy-markdown-button.tsx +0 -41
|
@@ -2,7 +2,8 @@ import { match, P } from 'massaman/match'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
|
|
4
4
|
import { Card } from '../shared/card'
|
|
5
|
-
import {
|
|
5
|
+
import { CardIcon } from '../shared/card-icon'
|
|
6
|
+
import type { CardIconInput } from '../shared/resolve-card-icon'
|
|
6
7
|
import { resolveCardIcon } from '../shared/resolve-card-icon'
|
|
7
8
|
import { TechTag } from '../shared/tech-tag'
|
|
8
9
|
|
|
@@ -16,9 +17,10 @@ export interface WorkspaceCardProps {
|
|
|
16
17
|
*/
|
|
17
18
|
readonly href: string
|
|
18
19
|
/**
|
|
19
|
-
* Icon config — Iconify identifier string
|
|
20
|
+
* Icon config — Iconify identifier string, `{ id, color }` object, or
|
|
21
|
+
* `{ kind: 'image', src, alt }` for a static image asset.
|
|
20
22
|
*/
|
|
21
|
-
readonly icon?:
|
|
23
|
+
readonly icon?: CardIconInput
|
|
22
24
|
/**
|
|
23
25
|
* Scope prefix shown above the name (e.g. "apps/").
|
|
24
26
|
*/
|
|
@@ -67,13 +69,9 @@ export function WorkspaceCard({
|
|
|
67
69
|
const resolved = resolveCardIcon(icon)
|
|
68
70
|
|
|
69
71
|
const iconEl = match(resolved)
|
|
70
|
-
.with(P.nonNullable, (r) => <
|
|
72
|
+
.with(P.nonNullable, (r) => <CardIcon resolved={r} className="cp-card__icon" />)
|
|
71
73
|
.otherwise(() => null)
|
|
72
74
|
|
|
73
|
-
const iconColor = match(resolved)
|
|
74
|
-
.with(P.nonNullable, (r) => r.color)
|
|
75
|
-
.otherwise(() => 'purple')
|
|
76
|
-
|
|
77
75
|
const scopeEl = match(scope)
|
|
78
76
|
.with(
|
|
79
77
|
P.when((s): s is string => s !== undefined && s.length > 0),
|
|
@@ -117,7 +115,7 @@ export function WorkspaceCard({
|
|
|
117
115
|
<Card href={href} className="cp-workspace-card">
|
|
118
116
|
<div className="cp-workspace-card__header">
|
|
119
117
|
<div className="cp-workspace-card__identity">
|
|
120
|
-
|
|
118
|
+
{iconEl}
|
|
121
119
|
<div className="cp-workspace-card__title">
|
|
122
120
|
{scopeEl}
|
|
123
121
|
<span
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type { FooterConfig, HomeConfig,
|
|
1
|
+
import type { BadgeConfig, FooterConfig, HomeConfig, SerializedIcon } from '@ciderpress/config'
|
|
2
2
|
import { useSite } from '@rspress/core/runtime'
|
|
3
3
|
|
|
4
4
|
export interface CiderpressSidebarItem {
|
|
5
5
|
readonly text?: string
|
|
6
6
|
readonly link?: string
|
|
7
|
-
readonly icon?:
|
|
7
|
+
readonly icon?: SerializedIcon
|
|
8
8
|
readonly items?: readonly CiderpressSidebarItem[]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface CiderpressSidebarLink {
|
|
12
12
|
readonly text: string
|
|
13
13
|
readonly link: string
|
|
14
|
-
readonly icon?:
|
|
14
|
+
readonly icon?: SerializedIcon
|
|
15
15
|
readonly style?: 'brand' | 'alt' | 'ghost'
|
|
16
16
|
readonly shape?: 'square' | 'rounded' | 'circle'
|
|
17
17
|
}
|
|
@@ -19,7 +19,7 @@ export interface CiderpressSidebarLink {
|
|
|
19
19
|
export interface WorkspaceCardData {
|
|
20
20
|
readonly title: string
|
|
21
21
|
readonly href: string
|
|
22
|
-
readonly icon:
|
|
22
|
+
readonly icon: SerializedIcon | undefined
|
|
23
23
|
readonly scope: string | undefined
|
|
24
24
|
readonly description: string | undefined
|
|
25
25
|
readonly tags: readonly string[]
|
|
@@ -33,6 +33,65 @@ export interface WorkspaceGroupData {
|
|
|
33
33
|
readonly cards: readonly WorkspaceCardData[]
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Serialised view of the unified `CiderpressConfig` that the runtime
|
|
38
|
+
* theme components still read through. The `site` field is rebuilt in
|
|
39
|
+
* `packages/ui/src/config.ts` (`buildSiteBlock`) from the new top-level
|
|
40
|
+
* `editLink` / `reportLink` / `topbar` / `sidebar` / `footer` / `version`
|
|
41
|
+
* config — kept under the legacy `site.*` name so theme components don't
|
|
42
|
+
* need to be rewired to look up each field individually.
|
|
43
|
+
*/
|
|
44
|
+
export interface CiderpressSiteBlock {
|
|
45
|
+
readonly version: string | undefined
|
|
46
|
+
readonly edit:
|
|
47
|
+
| {
|
|
48
|
+
readonly repo: string
|
|
49
|
+
readonly branch?: string
|
|
50
|
+
readonly directory?: string
|
|
51
|
+
readonly label?: string
|
|
52
|
+
}
|
|
53
|
+
| undefined
|
|
54
|
+
readonly report:
|
|
55
|
+
| {
|
|
56
|
+
readonly repo: string
|
|
57
|
+
readonly branch?: string
|
|
58
|
+
readonly directory?: string
|
|
59
|
+
readonly label?: string
|
|
60
|
+
}
|
|
61
|
+
| undefined
|
|
62
|
+
readonly topbarCta: { readonly text: string; readonly href: string } | undefined
|
|
63
|
+
readonly sidebarPromo:
|
|
64
|
+
| {
|
|
65
|
+
readonly title: string
|
|
66
|
+
readonly body: string
|
|
67
|
+
readonly cta: { readonly text: string; readonly href: string }
|
|
68
|
+
}
|
|
69
|
+
| undefined
|
|
70
|
+
readonly announcement:
|
|
71
|
+
| {
|
|
72
|
+
readonly id?: string
|
|
73
|
+
readonly lead?: string
|
|
74
|
+
readonly message: string
|
|
75
|
+
readonly cta?: { readonly href: string; readonly label: string }
|
|
76
|
+
readonly persistent?: boolean
|
|
77
|
+
}
|
|
78
|
+
| undefined
|
|
79
|
+
readonly footer:
|
|
80
|
+
| {
|
|
81
|
+
readonly columns?: readonly {
|
|
82
|
+
readonly heading: string
|
|
83
|
+
readonly links: readonly { readonly text: string; readonly href: string }[]
|
|
84
|
+
}[]
|
|
85
|
+
readonly tagline?: string
|
|
86
|
+
readonly brandMark?: string
|
|
87
|
+
}
|
|
88
|
+
| undefined
|
|
89
|
+
readonly feedback: {
|
|
90
|
+
readonly enabled: boolean
|
|
91
|
+
readonly question: string | undefined
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
36
95
|
interface CiderpressThemeConfig {
|
|
37
96
|
readonly sidebar: Record<string, readonly CiderpressSidebarItem[]>
|
|
38
97
|
readonly sidebarAbove: readonly CiderpressSidebarLink[] | undefined
|
|
@@ -41,7 +100,8 @@ interface CiderpressThemeConfig {
|
|
|
41
100
|
readonly standaloneScopePaths: readonly string[] | undefined
|
|
42
101
|
readonly home: HomeConfig | undefined
|
|
43
102
|
readonly ciderpressFooter: FooterConfig | undefined
|
|
44
|
-
readonly site:
|
|
103
|
+
readonly site: CiderpressSiteBlock | undefined
|
|
104
|
+
readonly pageBadges: Record<string, readonly BadgeConfig[]> | undefined
|
|
45
105
|
}
|
|
46
106
|
|
|
47
107
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { removeBase } from '@rspress/core/runtime'
|
|
1
2
|
import { useEffect, useState } from 'react'
|
|
2
3
|
|
|
3
4
|
import type { CiderpressNavMenuItem } from '../components/nav/ciderpress-nav-menu'
|
|
@@ -41,34 +42,104 @@ export function useNavItems(): readonly CiderpressNavMenuItem[] {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
45
|
+
* Reconstruct the primary nav from Rspress's hidden `.rp-nav-menu`,
|
|
46
|
+
* preserving dropdowns. Each top-level `.rp-nav-menu__item` is either a
|
|
47
|
+
* leaf (its container is an anchor) or a dropdown parent (it wraps a
|
|
48
|
+
* `.rp-hover-group` of child links). Items with empty text, or dropdown
|
|
49
|
+
* parents with no usable children, are dropped.
|
|
47
50
|
*
|
|
48
51
|
* @private
|
|
49
52
|
* @returns Nav items currently in the DOM (empty array when not mounted).
|
|
50
53
|
*/
|
|
51
54
|
function scrapeNavItems(): readonly CiderpressNavMenuItem[] {
|
|
52
|
-
|
|
55
|
+
return navMenuRoots()
|
|
56
|
+
.map(scrapeNavItem)
|
|
57
|
+
.filter((item): item is CiderpressNavMenuItem => item !== null)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Collect the top-level `.rp-nav-menu__item` `<li>`s to scrape. Rspress
|
|
62
|
+
* renders separate left and right nav `<ul>`s; the ciderpress topbar is
|
|
63
|
+
* right-aligned, so we read the right menu and only fall back to the
|
|
64
|
+
* unscoped selector when it isn't present.
|
|
65
|
+
*
|
|
66
|
+
* @private
|
|
67
|
+
* @returns Top-level nav item elements.
|
|
68
|
+
*/
|
|
69
|
+
function navMenuRoots(): readonly HTMLElement[] {
|
|
70
|
+
const right = document.querySelectorAll<HTMLElement>('.rp-nav-menu--right > .rp-nav-menu__item')
|
|
71
|
+
if (right.length > 0) {
|
|
72
|
+
return [...right]
|
|
73
|
+
}
|
|
74
|
+
return [...document.querySelectorAll<HTMLElement>('.rp-nav-menu > .rp-nav-menu__item')]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Project a single top-level `.rp-nav-menu__item` element into a nav
|
|
79
|
+
* item, recursing one level into its `.rp-hover-group` dropdown when
|
|
80
|
+
* present.
|
|
81
|
+
*
|
|
82
|
+
* @private
|
|
83
|
+
* @param root - Top-level nav `<li>` element.
|
|
84
|
+
* @returns Parsed nav item, or `null` when unusable.
|
|
85
|
+
*/
|
|
86
|
+
function scrapeNavItem(root: HTMLElement): CiderpressNavMenuItem | null {
|
|
87
|
+
const container = root.querySelector(':scope > .rp-nav-menu__item__container')
|
|
88
|
+
if (container === null) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
const text = readElementText(container)
|
|
92
|
+
if (text === '') {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
const group = root.querySelector(':scope > .rp-hover-group')
|
|
96
|
+
if (group !== null) {
|
|
97
|
+
const items = scrapeGroupItems(group)
|
|
98
|
+
if (items.length === 0) {
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
return { text, items }
|
|
102
|
+
}
|
|
103
|
+
const href = container.getAttribute('href')
|
|
104
|
+
if (href === null || href === '') {
|
|
105
|
+
return null
|
|
106
|
+
}
|
|
107
|
+
// Un-base the scraped href so `<Link>` re-applies the site `base` once
|
|
108
|
+
// rather than doubling the mount prefix on subpath deploys.
|
|
109
|
+
return { text, link: removeBase(href) }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Read the child links out of a Rspress `.rp-hover-group` dropdown.
|
|
114
|
+
*
|
|
115
|
+
* @private
|
|
116
|
+
* @param group - The `.rp-hover-group` element.
|
|
117
|
+
* @returns Child nav items with text + link (empties dropped).
|
|
118
|
+
*/
|
|
119
|
+
function scrapeGroupItems(group: Element): readonly CiderpressNavMenuItem[] {
|
|
120
|
+
const anchors = group.querySelectorAll<HTMLAnchorElement>('.rp-hover-group__item__link')
|
|
53
121
|
return [...anchors]
|
|
54
122
|
.map((anchor) => ({
|
|
55
|
-
text:
|
|
56
|
-
|
|
123
|
+
text: readElementText(anchor),
|
|
124
|
+
// Rspress's rendered hrefs already include the site `base`; strip it so
|
|
125
|
+
// the consuming `<Link>` re-applies it once instead of doubling the mount
|
|
126
|
+
// prefix on subpath deploys (the `/examples/<slug>/examples/<slug>/…` 404).
|
|
127
|
+
link: removeBase(anchor.getAttribute('href') ?? ''),
|
|
57
128
|
}))
|
|
58
129
|
.filter((item) => item.text !== '' && item.link !== '')
|
|
59
130
|
}
|
|
60
131
|
|
|
61
132
|
/**
|
|
62
|
-
* Pull the trimmed text content from an
|
|
133
|
+
* Pull the trimmed text content from an element. Returns an empty
|
|
63
134
|
* string when `textContent` is missing — callers treat empty as "skip
|
|
64
|
-
* this
|
|
135
|
+
* this element".
|
|
65
136
|
*
|
|
66
137
|
* @private
|
|
67
|
-
* @param
|
|
138
|
+
* @param element - Element to read.
|
|
68
139
|
* @returns Trimmed inner text, or empty string when absent.
|
|
69
140
|
*/
|
|
70
|
-
function
|
|
71
|
-
const text =
|
|
141
|
+
function readElementText(element: Element): string {
|
|
142
|
+
const text = element.textContent
|
|
72
143
|
if (text === null) {
|
|
73
144
|
return ''
|
|
74
145
|
}
|
package/dist/theme/index.tsx
CHANGED
|
@@ -19,7 +19,6 @@ import '@rspress/core/dist/theme/components/Sidebar/SidebarDivider.css'
|
|
|
19
19
|
import '@rspress/core/dist/theme/components/Sidebar/SidebarGroup.css'
|
|
20
20
|
import '@rspress/core/dist/theme/components/Sidebar/SidebarItem.css'
|
|
21
21
|
import '@rspress/core/dist/theme/components/Sidebar/SidebarSectionHeader.css'
|
|
22
|
-
|
|
23
22
|
import './styles/layers.css'
|
|
24
23
|
import './styles/overrides/fonts.css'
|
|
25
24
|
import './styles/overrides/tokens.css'
|
|
@@ -39,6 +38,8 @@ import './styles/themes/arcade.css'
|
|
|
39
38
|
// (border trace, neon pulse, CRT scanlines, etc.) scoped to [data-cp-theme='arcade']
|
|
40
39
|
import './styles/themes/arcade-fx.css'
|
|
41
40
|
import './styles/overrides/details.css'
|
|
41
|
+
import './styles/overrides/lists.css'
|
|
42
|
+
import './styles/overrides/footnotes.css'
|
|
42
43
|
import './styles/overrides/scrollbar.css'
|
|
43
44
|
import './styles/overrides/rail.css'
|
|
44
45
|
import './styles/overrides/sidebar.css'
|
|
@@ -54,6 +55,7 @@ import './components/content-footer/feedback.css'
|
|
|
54
55
|
import './components/content-footer/meta-actions.css'
|
|
55
56
|
import './components/content-footer/page-pager.css'
|
|
56
57
|
import './components/sidebar/framework-picker.css'
|
|
58
|
+
import './components/sidebar/sidebar-badge.css'
|
|
57
59
|
import './components/sidebar/sidebar-promo.css'
|
|
58
60
|
import './components/sidebar/sidebar-toggle.css'
|
|
59
61
|
import './components/home/page-rail.css'
|
|
@@ -90,6 +92,8 @@ export { HomeFeature } from './components/home/feature'
|
|
|
90
92
|
export { HomeLayout } from './components/home/layout'
|
|
91
93
|
/** @internal Sidebar override — multi-scope filtering for standalone sections */
|
|
92
94
|
export { Sidebar } from './components/sidebar/sidebar-scope'
|
|
95
|
+
/** @internal Tag override — renders ciderpress sidebar badges, delegates other tags to Rspress */
|
|
96
|
+
export { Tag } from './components/sidebar/sidebar-badge'
|
|
93
97
|
|
|
94
98
|
export { FeatureCard, FeatureGrid } from './components/home/feature-card'
|
|
95
99
|
export type { FeatureCardProps, FeatureItem } from './components/home/feature-card'
|
|
@@ -150,8 +154,6 @@ export type { StepsProps, StepProps } from './components/shared/steps'
|
|
|
150
154
|
export { Field, FieldGroup } from './components/shared/field'
|
|
151
155
|
export type { FieldProps, FieldGroupProps } from './components/shared/field'
|
|
152
156
|
|
|
153
|
-
export { CopyMarkdownButton } from './components/openapi'
|
|
154
|
-
export type { CopyMarkdownButtonProps } from './components/openapi'
|
|
155
157
|
export { OpenAPIOperation } from './components/openapi'
|
|
156
158
|
export type { OpenAPIOperationProps } from './components/openapi'
|
|
157
159
|
export { OpenAPIOverview } from './components/openapi'
|
|
@@ -2,8 +2,9 @@ import type { CiderpressSocialLink } from '../components/nav/ciderpress-nav-soci
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Pull social-link entries (GitHub, npm, etc) from Rspress's `site`
|
|
5
|
-
* shape. Lives on `site.themeConfig.socialLinks` after
|
|
6
|
-
*
|
|
5
|
+
* shape. Lives on `site.themeConfig.socialLinks` after the
|
|
6
|
+
* `packages/ui/src/config.ts` serialiser writes the unified `socials`
|
|
7
|
+
* config into the runtime theme config.
|
|
7
8
|
*
|
|
8
9
|
* @param site - Rspress site data (typically the result of `useSite().site`)
|
|
9
10
|
* @returns Array of social-link entries (empty array when not configured)
|
|
@@ -22,6 +23,6 @@ export function readSocialLinks(site: unknown): readonly CiderpressSocialLink[]
|
|
|
22
23
|
typeof item === 'object' &&
|
|
23
24
|
item !== null &&
|
|
24
25
|
typeof (item as { icon?: unknown }).icon === 'string' &&
|
|
25
|
-
typeof (item as {
|
|
26
|
+
typeof (item as { url?: unknown }).url === 'string'
|
|
26
27
|
)
|
|
27
28
|
}
|
|
@@ -107,7 +107,7 @@ function buildIconDataUri(brand: string, shadow: string, surface: string): strin
|
|
|
107
107
|
/**
|
|
108
108
|
* Locate the existing `<link rel="icon">` element, creating one if the
|
|
109
109
|
* document head doesn't have one yet (a defensive fallback — Rspress
|
|
110
|
-
* normally renders one from `
|
|
110
|
+
* normally renders one from `brand.favicon`).
|
|
111
111
|
*
|
|
112
112
|
* @private
|
|
113
113
|
* @returns Mutable link element
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { withBase } from '@rspress/core/runtime'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prefix `value` with the configured Rspress `base` only when it's an
|
|
5
|
+
* absolute path that would otherwise escape the mount. Pass-through for
|
|
6
|
+
* data URIs, fully-qualified URLs, hash anchors, and relative paths —
|
|
7
|
+
* applying `withBase()` to those produces broken results (the kitchen-
|
|
8
|
+
* sink data-URI logo regression that the examples e2e caught).
|
|
9
|
+
*
|
|
10
|
+
* Use this wrapper at every render site that takes a user-supplied asset
|
|
11
|
+
* URL from the ciderpress config (`logo`, `heroDemo.src`, brand chips
|
|
12
|
+
* etc.), instead of calling `withBase()` directly.
|
|
13
|
+
*
|
|
14
|
+
* @param value - Asset URL or path from user config (may be `undefined`)
|
|
15
|
+
* @returns The base-prefixed URL when applicable, otherwise `value` as-is
|
|
16
|
+
*/
|
|
17
|
+
export function withMountBase(value: string | undefined): string {
|
|
18
|
+
if (value === undefined) {
|
|
19
|
+
return ''
|
|
20
|
+
}
|
|
21
|
+
if (!value.startsWith('/')) {
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
// Data URIs (`data:image/svg+xml;...`) and protocol-relative or
|
|
25
|
+
// explicit-protocol URLs are absolute in their own namespace — never
|
|
26
|
+
// prefix.
|
|
27
|
+
if (value.startsWith('//') || /^[a-z][a-z0-9+\-.]*:/i.test(value)) {
|
|
28
|
+
return value
|
|
29
|
+
}
|
|
30
|
+
return withBase(value)
|
|
31
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GFM footnote section styling — deliberately UNLAYERED.
|
|
3
|
+
*
|
|
4
|
+
* `remark-gfm` appends a `<section class="footnotes">` with an `<ol>` of
|
|
5
|
+
* definitions and a `#footnote-label` heading. Rspress tags that heading
|
|
6
|
+
* `sr-only` (visually hidden, kept for a11y + the on-this-page nav), but the
|
|
7
|
+
* theme never defined `.sr-only`, so it rendered as a full 1.75rem section
|
|
8
|
+
* heading and the definitions sat at body size with no separation from the
|
|
9
|
+
* content above — nothing like how GitHub and most renderers present footnotes.
|
|
10
|
+
*
|
|
11
|
+
* These rules are NOT in `ciderpress.overrides`: Rspress sizes headings with an
|
|
12
|
+
* unlayered, low-specificity `.rp-doc :not(:where(...)):where(h2)` rule
|
|
13
|
+
* (specificity 0,1,0), and layer-scoped rules always lose to unlayered ones
|
|
14
|
+
* regardless of specificity. Match Rspress's approach — unlayered, class-scoped
|
|
15
|
+
* under `.rp-doc` (specificity 0,2,0+ > 0,1,0) — so the label restyle actually
|
|
16
|
+
* wins. Restores the expected treatment: a top-border separator, a small muted
|
|
17
|
+
* label, and compact muted definition text set apart from the body.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
.rp-doc .footnotes {
|
|
21
|
+
margin-block-start: 2.5rem;
|
|
22
|
+
padding-block-start: 1.25rem;
|
|
23
|
+
border-block-start: 1px solid var(--cp-c-border);
|
|
24
|
+
font-size: 0.85em;
|
|
25
|
+
color: var(--cp-c-text-2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.rp-doc .footnotes > h2 {
|
|
29
|
+
margin: 0 0 0.75rem;
|
|
30
|
+
padding: 0;
|
|
31
|
+
border: 0;
|
|
32
|
+
font-size: 0.75rem;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
letter-spacing: 0.05em;
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
color: var(--cp-c-text-3);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.rp-doc .footnotes ol {
|
|
40
|
+
margin: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.rp-doc .footnotes li {
|
|
44
|
+
margin-block: 0.35rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.rp-doc .footnotes li p {
|
|
48
|
+
margin: 0.25rem 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.rp-doc .data-footnote-backref {
|
|
52
|
+
margin-inline-start: 0.35em;
|
|
53
|
+
text-decoration: none;
|
|
54
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content list markers — fixed-width marker gutter.
|
|
3
|
+
*
|
|
4
|
+
* Rspress renders list markers `outside`, which right-aligns them to the text
|
|
5
|
+
* edge: a wide decimal marker (`10.`, `99.`) hangs further left than a bullet,
|
|
6
|
+
* so the marker column differs between `ol` and `ul` and numbers drift into the
|
|
7
|
+
* body-text margin. Text lines up, but the markers do not.
|
|
8
|
+
*
|
|
9
|
+
* Replace the native markers with a fixed-width gutter shared by `ol` and `ul`,
|
|
10
|
+
* sized for two digits (`99.`). Every marker occupies the same column, decimal
|
|
11
|
+
* points align, and list text starts at the same offset regardless of marker
|
|
12
|
+
* width. `ol` numbering comes from the automatic `list-item` counter (still
|
|
13
|
+
* incremented under `list-style: none`), so nested lists and `start` offsets
|
|
14
|
+
* keep working. The marker is absolutely positioned in the `li` inline padding
|
|
15
|
+
* so it aligns to the first line whether the item holds inline text (tight
|
|
16
|
+
* lists) or a block `<p>` (loose lists, e.g. footnote definitions); wrapped and
|
|
17
|
+
* following lines hang under the text.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately UNLAYERED: Rspress sets base list padding with an unlayered,
|
|
20
|
+
* low-specificity `.rp-doc :not(:where(...)):where(ol|ul)` rule (specificity
|
|
21
|
+
* 0,1,0). Layer-scoped rules always lose to unlayered ones regardless of
|
|
22
|
+
* specificity, so these match Rspress's approach — unlayered, class-scoped under
|
|
23
|
+
* `.rp-doc` (specificity 0,2,0+ > 0,1,0).
|
|
24
|
+
*
|
|
25
|
+
* Task lists (GFM `- [ ]`) are excluded — they render a checkbox instead of a
|
|
26
|
+
* marker; see the dedicated rules below.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
.rp-doc :where(ol, ul):not(.contains-task-list) {
|
|
30
|
+
list-style: none;
|
|
31
|
+
padding-inline-start: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.rp-doc :where(ol, ul):not(.contains-task-list) > li {
|
|
35
|
+
position: relative;
|
|
36
|
+
padding-inline-start: 2.1em;
|
|
37
|
+
margin-block: 0.35rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.rp-doc :where(ol, ul):not(.contains-task-list) > li::before {
|
|
41
|
+
position: absolute;
|
|
42
|
+
inset-inline-start: 0;
|
|
43
|
+
width: 1.6em;
|
|
44
|
+
text-align: end;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.rp-doc ol:not(.contains-task-list) > li::before {
|
|
48
|
+
content: counter(list-item) '.';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.rp-doc ul:not(.contains-task-list) > li::before {
|
|
52
|
+
content: '•';
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
padding-inline-end: 0.22em;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* Task-list normalization — deliberately UNLAYERED (same specificity reasoning
|
|
59
|
+
* as above). GFM leaves a `disc` bullet on `- [ ]` items next to the checkbox;
|
|
60
|
+
* hide it, then seat the item in the SAME marker gutter as ordered/unordered
|
|
61
|
+
* lists so task text lines up with every other list. The checkbox is absolutely
|
|
62
|
+
* positioned in the gutter (right edge at the marker column, centered on the
|
|
63
|
+
* first line), so text starts at the shared 2.5em offset regardless of the
|
|
64
|
+
* checkbox's intrinsic size.
|
|
65
|
+
*/
|
|
66
|
+
.rp-doc .contains-task-list {
|
|
67
|
+
list-style: none;
|
|
68
|
+
padding-inline-start: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.rp-doc .task-list-item {
|
|
72
|
+
position: relative;
|
|
73
|
+
list-style: none;
|
|
74
|
+
padding-inline-start: 2.1em;
|
|
75
|
+
margin-block: 0.35rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.rp-doc .task-list-item > input[type='checkbox'] {
|
|
79
|
+
position: absolute;
|
|
80
|
+
inset-inline-start: 0.8em;
|
|
81
|
+
inset-block-start: 0.45em;
|
|
82
|
+
margin: 0;
|
|
83
|
+
}
|
|
@@ -145,18 +145,22 @@ html .rp-doc-layout__sidebar > :first-child:not(.cp-sidebar-top) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
html .cp-sidebar-bottom {
|
|
148
|
-
/*
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
/* Size to content and sit directly under the nav tree. `flex: 0 0 auto`
|
|
149
|
+
(no grow) is deliberate: growing to fill (`1 0 auto`) stretched the
|
|
150
|
+
band across all leftover sidebar height, and a short nav tree left a
|
|
151
|
+
tall empty band with its content (promo + below-links) stranded at the
|
|
152
|
+
bottom. With no grow the band is exactly as tall as its content and
|
|
153
|
+
the promo/links hug the last nav item. */
|
|
154
|
+
flex: 0 0 auto;
|
|
153
155
|
display: flex;
|
|
154
156
|
flex-direction: column;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
/* When the nav tree overflows the viewport the sidebar scrolls; this
|
|
158
|
+
band stays glued to the viewport bottom (`sticky bottom: 0`) so the
|
|
159
|
+
promo/links remain visible while the tree scrolls behind them. With a
|
|
160
|
+
short (non-scrolling) nav tree the sticky offset is never engaged, so
|
|
161
|
+
the band simply flows under the last nav item. `z-index: 3` paints it
|
|
162
|
+
above the thin overlay scrollbar thumb so it doesn't appear to "go
|
|
163
|
+
through" the band region. */
|
|
160
164
|
position: sticky;
|
|
161
165
|
bottom: 0;
|
|
162
166
|
z-index: 3;
|
|
@@ -189,6 +193,19 @@ html .rp-outline__title {
|
|
|
189
193
|
display: none;
|
|
190
194
|
}
|
|
191
195
|
|
|
196
|
+
/* "On this page" outline — truncate long headings to a single line with
|
|
197
|
+
an ellipsis instead of wrapping. Rspress already sets a native `title`
|
|
198
|
+
on each `.rp-toc-item` anchor, so hovering a truncated heading reveals
|
|
199
|
+
the full text. Targets `.rp-toc-item__text`, shared by the desktop
|
|
200
|
+
rail and the mobile outline dropdown. */
|
|
201
|
+
html .rp-toc-item__text {
|
|
202
|
+
display: block;
|
|
203
|
+
min-width: 0;
|
|
204
|
+
overflow: hidden;
|
|
205
|
+
text-overflow: ellipsis;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
}
|
|
208
|
+
|
|
192
209
|
html .rp-doc-layout__doc {
|
|
193
210
|
background: var(--cp-c-bg);
|
|
194
211
|
}
|
|
@@ -31,6 +31,15 @@
|
|
|
31
31
|
gap: 10px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/* Page badges pinned to the right of the card title row. */
|
|
35
|
+
.cp-section-card__badges {
|
|
36
|
+
margin-left: auto;
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 4px;
|
|
40
|
+
flex: 0 0 auto;
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
.cp-section-card:hover {
|
|
35
44
|
border-color: var(--cp-c-brand-1);
|
|
36
45
|
background: var(--cp-c-bg-elv);
|
|
@@ -24,6 +24,28 @@
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/* ── Long titles — single-line ellipsis, badge stays pinned ──── */
|
|
28
|
+
/* The item's left slot holds the label; constrain it so the label
|
|
29
|
+
truncates instead of pushing the badge (right slot) out of view.
|
|
30
|
+
A `title` attribute is added by the Sidebar effect on overflow. */
|
|
31
|
+
html .rp-sidebar-item__left {
|
|
32
|
+
min-width: 0;
|
|
33
|
+
flex: 1 1 auto;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
html .rp-sidebar-item__left > .rp-doc {
|
|
38
|
+
min-width: 0;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
text-overflow: ellipsis;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
html .rp-sidebar-item__right {
|
|
45
|
+
flex: 0 0 auto;
|
|
46
|
+
margin-left: 6px;
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
/* ── Group section headers — mono ALL CAPS (mockup pattern) ──── */
|
|
28
50
|
html .rp-sidebar-section-header {
|
|
29
51
|
font-family: var(--cp-font-family-mono);
|