@ciderpress/ui 1.0.0-rc.10 → 1.0.0-rc.11
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/loader-apple.css +1 -1
- package/dist/head/css/loader-dots.css +1 -1
- package/dist/head/css/themes/amber.css +1 -1
- package/dist/head/css/themes/arcade.css +1 -1
- package/dist/head/css/themes/grannysmith.css +1 -1
- package/dist/head/css/themes/honeycrisp.css +1 -1
- package/dist/head/css/themes/midnight.css +1 -1
- package/dist/head/css/themes/mulled.css +1 -1
- package/dist/node.mjs +408 -15
- package/dist/theme/components/home/cta.css +15 -0
- package/dist/theme/components/home/cta.tsx +10 -2
- package/dist/theme/components/home/feature-card.css +1 -1
- package/dist/theme/components/home/feature-card.tsx +3 -2
- package/dist/theme/components/home/feature.tsx +34 -31
- package/dist/theme/components/home/hero-demo.css +11 -0
- package/dist/theme/components/home/home-visual.tsx +226 -0
- package/dist/theme/components/home/layout.tsx +350 -171
- package/dist/theme/components/home/split.css +46 -19
- package/dist/theme/components/home/split.tsx +28 -8
- package/dist/theme/components/home/tabs.css +299 -0
- package/dist/theme/components/home/tabs.tsx +351 -0
- package/dist/theme/components/home/trust-strip.css +41 -0
- package/dist/theme/components/home/trust-strip.tsx +168 -11
- package/dist/theme/components/home/workspaces.tsx +171 -69
- package/dist/theme/components/workspaces/card.css +4 -0
- package/dist/theme/components/workspaces/card.tsx +3 -3
- package/dist/theme/components/workspaces/grid.tsx +18 -6
- package/dist/theme/index.tsx +5 -0
- package/dist/theme/lib/rich-text-parse.ts +740 -0
- package/dist/theme/lib/rich-text.test.tsx +342 -0
- package/dist/theme/lib/rich-text.tsx +86 -0
- package/dist/theme/styles/overrides/fonts.css +7 -4
- package/dist/theme/styles/overrides/home.css +11 -2
- package/dist/theme/styles/overrides/rspress.css +10 -5
- package/dist/theme/styles/overrides/tokens.css +41 -23
- package/dist/theme/styles/rich-text.css +106 -0
- package/dist/theme/styles/themes/amber.css +32 -2
- package/dist/theme/styles/themes/arcade.css +16 -1
- package/dist/theme/styles/themes/grannysmith.css +32 -2
- package/dist/theme/styles/themes/honeycrisp.css +37 -7
- package/dist/theme/styles/themes/midnight.css +16 -1
- package/dist/theme/styles/themes/mulled.css +54 -9
- package/package.json +3 -3
- package/src/theme/components/home/cta.css +15 -0
- package/src/theme/components/home/cta.tsx +10 -2
- package/src/theme/components/home/feature-card.css +1 -1
- package/src/theme/components/home/feature-card.tsx +3 -2
- package/src/theme/components/home/feature.tsx +34 -31
- package/src/theme/components/home/hero-demo.css +11 -0
- package/src/theme/components/home/home-visual.tsx +226 -0
- package/src/theme/components/home/layout.tsx +350 -171
- package/src/theme/components/home/split.css +46 -19
- package/src/theme/components/home/split.tsx +28 -8
- package/src/theme/components/home/tabs.css +299 -0
- package/src/theme/components/home/tabs.tsx +351 -0
- package/src/theme/components/home/trust-strip.css +41 -0
- package/src/theme/components/home/trust-strip.tsx +168 -11
- package/src/theme/components/home/workspaces.tsx +171 -69
- package/src/theme/components/workspaces/card.css +4 -0
- package/src/theme/components/workspaces/card.tsx +3 -3
- package/src/theme/components/workspaces/grid.tsx +18 -6
- package/src/theme/index.tsx +5 -0
- package/src/theme/lib/rich-text-parse.ts +740 -0
- package/src/theme/lib/rich-text.test.tsx +342 -0
- package/src/theme/lib/rich-text.tsx +86 -0
- package/src/theme/styles/overrides/fonts.css +7 -4
- package/src/theme/styles/overrides/home.css +11 -2
- package/src/theme/styles/overrides/rspress.css +10 -5
- package/src/theme/styles/overrides/tokens.css +41 -23
- package/src/theme/styles/rich-text.css +106 -0
- package/src/theme/styles/themes/amber.css +32 -2
- package/src/theme/styles/themes/arcade.css +16 -1
- package/src/theme/styles/themes/grannysmith.css +32 -2
- package/src/theme/styles/themes/honeycrisp.css +37 -7
- package/src/theme/styles/themes/midnight.css +16 -1
- package/src/theme/styles/themes/mulled.css +54 -9
- package/dist/theme/components/home/hero-demo-custom.tsx +0 -116
- package/dist/theme/components/home/split-visual-custom.tsx +0 -26
- package/src/theme/components/home/hero-demo-custom.tsx +0 -116
- package/src/theme/components/home/split-visual-custom.tsx +0 -26
|
@@ -1,69 +1,181 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { useFrontmatter } from '@rspress/core/runtime'
|
|
1
|
+
import type { TruncateConfig } from '@ciderpress/config'
|
|
3
2
|
import { match, P } from 'massaman/match'
|
|
4
3
|
import type React from 'react'
|
|
5
4
|
|
|
6
5
|
import { useCiderpress } from '../../hooks/use-ciderpress'
|
|
7
|
-
import type { WorkspaceGroupData } from '../../hooks/use-ciderpress'
|
|
6
|
+
import type { WorkspaceCardData, WorkspaceGroupData } from '../../hooks/use-ciderpress'
|
|
7
|
+
import { renderRichText } from '../../lib/rich-text.tsx'
|
|
8
8
|
import { WorkspaceCard } from '../workspaces/card'
|
|
9
9
|
import { WorkspaceGrid } from '../workspaces/grid'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* A single card compiled from `showcase.source` — the same shape as a
|
|
13
|
+
* workspace card, minus the grouping.
|
|
14
|
+
*/
|
|
15
|
+
export type ShowcaseCard = WorkspaceCardData
|
|
16
|
+
|
|
11
17
|
interface FrontmatterWorkspacesHeading {
|
|
12
18
|
readonly label?: string
|
|
13
19
|
readonly title?: string
|
|
14
|
-
readonly
|
|
20
|
+
readonly body?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface HomeWorkspacesProps {
|
|
24
|
+
readonly heading?: FrontmatterWorkspacesHeading
|
|
25
|
+
/**
|
|
26
|
+
* Cards compiled from an explicit `showcase.source` path list. When
|
|
27
|
+
* present they render as one ungrouped grid; otherwise the grouped
|
|
28
|
+
* workspace data from the theme config is used.
|
|
29
|
+
*/
|
|
30
|
+
readonly cards?: readonly ShowcaseCard[]
|
|
31
|
+
readonly columns?: 1 | 2 | 3 | 4
|
|
32
|
+
readonly truncate?: TruncateConfig
|
|
15
33
|
}
|
|
16
34
|
|
|
35
|
+
const DEFAULT_COLUMNS: 1 | 2 | 3 | 4 = 2
|
|
36
|
+
|
|
17
37
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* when `home.showcase.heading` is configured. The sync engine writes
|
|
23
|
-
* it into frontmatter as `workspacesHeading`.
|
|
38
|
+
* Showcase grid block. Renders either the cards compiled from an
|
|
39
|
+
* explicit `showcase.source` path list (one ungrouped grid) or the
|
|
40
|
+
* grouped workspace data serialized from the repo at build time. Grid
|
|
41
|
+
* layout — heading, columns, truncation — comes from props.
|
|
24
42
|
*
|
|
25
|
-
* @
|
|
43
|
+
* @param props - Optional heading, cards, column count, and truncation limits.
|
|
44
|
+
* @returns React element with the showcase grid, or null when empty.
|
|
26
45
|
*/
|
|
27
|
-
export function HomeWorkspaces(): React.ReactElement | null {
|
|
28
|
-
const { workspaces
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| undefined
|
|
46
|
+
export function HomeWorkspaces(props: HomeWorkspacesProps): React.ReactElement | null {
|
|
47
|
+
const { workspaces } = useCiderpress()
|
|
48
|
+
const { heading, cards, columns, truncate } = props
|
|
49
|
+
const resolvedColumns = match(columns)
|
|
50
|
+
.with(P.nonNullable, (c) => c)
|
|
51
|
+
.otherwise(() => DEFAULT_COLUMNS)
|
|
34
52
|
|
|
35
|
-
|
|
53
|
+
// A `cards` key means the block declared an explicit `showcase.source`
|
|
54
|
+
// — an empty array is "that source resolved to nothing", not "fall back
|
|
55
|
+
// to the workspace deck". Only an absent key takes the fallback.
|
|
56
|
+
const body = match(cards)
|
|
36
57
|
.with(
|
|
37
|
-
P.when((
|
|
38
|
-
(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{match(h.title)
|
|
51
|
-
.with(undefined, () => null)
|
|
52
|
-
.otherwise((t) => (
|
|
53
|
-
<h2 className="cp-feature-section-head__title">{t}</h2>
|
|
54
|
-
))}
|
|
55
|
-
{match(h.subtitle)
|
|
56
|
-
.with(undefined, () => null)
|
|
57
|
-
.otherwise((s) => (
|
|
58
|
-
<p className="cp-feature-section-head__sub">{s}</p>
|
|
59
|
-
))}
|
|
60
|
-
</div>
|
|
61
|
-
))}
|
|
62
|
-
{groups.map((group) => renderGroup(group, gridConfig))}
|
|
63
|
-
</div>
|
|
64
|
-
)
|
|
58
|
+
P.when((c): c is readonly ShowcaseCard[] => Array.isArray(c)),
|
|
59
|
+
(list) =>
|
|
60
|
+
match(list.length === 0)
|
|
61
|
+
.with(true, () => null)
|
|
62
|
+
.otherwise(() => [renderCardGrid(list, resolvedColumns, truncate)])
|
|
63
|
+
)
|
|
64
|
+
.otherwise(() =>
|
|
65
|
+
match(workspaces)
|
|
66
|
+
.with(
|
|
67
|
+
P.when((w): w is readonly WorkspaceGroupData[] => Array.isArray(w) && w.length > 0),
|
|
68
|
+
(groups) => groups.map((group, i) => renderGroup(group, resolvedColumns, truncate, i))
|
|
69
|
+
)
|
|
70
|
+
.otherwise(() => null)
|
|
65
71
|
)
|
|
66
|
-
|
|
72
|
+
|
|
73
|
+
return match(body)
|
|
74
|
+
.with(null, () => null)
|
|
75
|
+
.otherwise((content) => (
|
|
76
|
+
<div className="cp-workspace-section">
|
|
77
|
+
{/* The rule separates this band from the heading above it, so a
|
|
78
|
+
block with no heading of its own would render it as an orphan
|
|
79
|
+
line floating over the cards. */}
|
|
80
|
+
{match(renderHeading(heading))
|
|
81
|
+
.with(P.nullish, () => null)
|
|
82
|
+
.otherwise((head) => (
|
|
83
|
+
<>
|
|
84
|
+
<hr className="cp-divider" />
|
|
85
|
+
{head}
|
|
86
|
+
</>
|
|
87
|
+
))}
|
|
88
|
+
{content}
|
|
89
|
+
</div>
|
|
90
|
+
))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Render the block heading, or nothing when every field is absent.
|
|
95
|
+
*
|
|
96
|
+
* @private
|
|
97
|
+
* @param heading - Flat heading trio from the block
|
|
98
|
+
* @returns Heading element, or null
|
|
99
|
+
*/
|
|
100
|
+
function renderHeading(
|
|
101
|
+
heading: FrontmatterWorkspacesHeading | undefined
|
|
102
|
+
): React.ReactElement | null {
|
|
103
|
+
if (heading === undefined) {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
const { label, title, body } = heading
|
|
107
|
+
const hasContent = [label, title, body].some((value) => typeof value === 'string')
|
|
108
|
+
if (!hasContent) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
return (
|
|
112
|
+
<div className="cp-feature-section-head">
|
|
113
|
+
{match(label)
|
|
114
|
+
.with(P.string, (e) => (
|
|
115
|
+
<div className="cp-feature-section-head__eyebrow">{renderRichText(e)}</div>
|
|
116
|
+
))
|
|
117
|
+
.otherwise(() => null)}
|
|
118
|
+
{match(title)
|
|
119
|
+
.with(P.string, (t) => (
|
|
120
|
+
<h2 className="cp-feature-section-head__title">{renderRichText(t)}</h2>
|
|
121
|
+
))
|
|
122
|
+
.otherwise(() => null)}
|
|
123
|
+
{match(body)
|
|
124
|
+
.with(P.string, (s) => <p className="cp-feature-section-head__sub">{renderRichText(s)}</p>)
|
|
125
|
+
.otherwise(() => null)}
|
|
126
|
+
</div>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Render an ungrouped grid of cards compiled from `showcase.source`.
|
|
132
|
+
*
|
|
133
|
+
* @private
|
|
134
|
+
* @param cards - Resolved showcase cards
|
|
135
|
+
* @param columns - Column count for the grid
|
|
136
|
+
* @param truncate - Optional line-clamp limits for card text
|
|
137
|
+
* @returns Ungrouped workspace grid element
|
|
138
|
+
*/
|
|
139
|
+
function renderCardGrid(
|
|
140
|
+
cards: readonly ShowcaseCard[],
|
|
141
|
+
columns: 1 | 2 | 3 | 4,
|
|
142
|
+
truncate: TruncateConfig | undefined
|
|
143
|
+
): React.ReactElement {
|
|
144
|
+
return (
|
|
145
|
+
<WorkspaceGrid key="showcase-source" columns={columns}>
|
|
146
|
+
{cards.map((card, i) => renderCard(card, i, truncate))}
|
|
147
|
+
</WorkspaceGrid>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Render a single showcase / workspace card.
|
|
153
|
+
*
|
|
154
|
+
* @private
|
|
155
|
+
* @param card - Card data
|
|
156
|
+
* @param index - Array index for key generation
|
|
157
|
+
* @param truncate - Optional line-clamp limits for card text
|
|
158
|
+
* @returns Workspace card element
|
|
159
|
+
*/
|
|
160
|
+
function renderCard(
|
|
161
|
+
card: WorkspaceCardData,
|
|
162
|
+
index: number,
|
|
163
|
+
truncate: TruncateConfig | undefined
|
|
164
|
+
): React.ReactElement {
|
|
165
|
+
return (
|
|
166
|
+
<WorkspaceCard
|
|
167
|
+
key={`${card.title}-${index}`}
|
|
168
|
+
title={card.title}
|
|
169
|
+
href={card.href}
|
|
170
|
+
icon={card.icon}
|
|
171
|
+
scope={card.scope}
|
|
172
|
+
description={card.description}
|
|
173
|
+
tags={card.tags}
|
|
174
|
+
badge={card.badge}
|
|
175
|
+
titleLines={truncate && truncate.title}
|
|
176
|
+
descriptionLines={truncate && truncate.description}
|
|
177
|
+
/>
|
|
178
|
+
)
|
|
67
179
|
}
|
|
68
180
|
|
|
69
181
|
/**
|
|
@@ -71,37 +183,27 @@ export function HomeWorkspaces(): React.ReactElement | null {
|
|
|
71
183
|
*
|
|
72
184
|
* @private
|
|
73
185
|
* @param group - Workspace group data with heading, description, and cards
|
|
74
|
-
* @param
|
|
186
|
+
* @param columns - Column count for the grid
|
|
187
|
+
* @param truncate - Optional line-clamp limits for card text
|
|
188
|
+
* @param index - Position in the group list, to disambiguate the key
|
|
75
189
|
* @returns Workspace grid element
|
|
76
190
|
*/
|
|
77
191
|
function renderGroup(
|
|
78
192
|
group: WorkspaceGroupData,
|
|
79
|
-
|
|
193
|
+
columns: 1 | 2 | 3 | 4,
|
|
194
|
+
truncate: TruncateConfig | undefined,
|
|
195
|
+
index: number
|
|
80
196
|
): React.ReactElement {
|
|
81
|
-
const titleLines = gridConfig && gridConfig.truncate && gridConfig.truncate.title
|
|
82
|
-
const descLines = gridConfig && gridConfig.truncate && gridConfig.truncate.description
|
|
83
|
-
|
|
84
197
|
return (
|
|
198
|
+
// Heading alone is not unique — two configured workspace groups may
|
|
199
|
+
// share a title, which collided into one React key.
|
|
85
200
|
<WorkspaceGrid
|
|
86
|
-
key={group.heading}
|
|
201
|
+
key={`${group.heading}-${index}`}
|
|
87
202
|
heading={group.heading}
|
|
88
203
|
description={group.description}
|
|
89
|
-
columns={
|
|
204
|
+
columns={columns}
|
|
90
205
|
>
|
|
91
|
-
{group.cards.map((card, i) => (
|
|
92
|
-
<WorkspaceCard
|
|
93
|
-
key={`${card.title}-${i}`}
|
|
94
|
-
title={card.title}
|
|
95
|
-
href={card.href}
|
|
96
|
-
icon={card.icon}
|
|
97
|
-
scope={card.scope}
|
|
98
|
-
description={card.description}
|
|
99
|
-
tags={card.tags}
|
|
100
|
-
badge={card.badge}
|
|
101
|
-
titleLines={titleLines}
|
|
102
|
-
descriptionLines={descLines}
|
|
103
|
-
/>
|
|
104
|
-
))}
|
|
206
|
+
{group.cards.map((card, i) => renderCard(card, i, truncate))}
|
|
105
207
|
</WorkspaceGrid>
|
|
106
208
|
)
|
|
107
209
|
}
|
|
@@ -69,6 +69,10 @@
|
|
|
69
69
|
font-size: 15px;
|
|
70
70
|
font-weight: 700;
|
|
71
71
|
color: var(--cp-c-text-1);
|
|
72
|
+
/* Presentational lowercase. Done here rather than in JS because the
|
|
73
|
+
title may carry inline markup — lowercasing the source would also
|
|
74
|
+
lowercase link targets and class names inside it. */
|
|
75
|
+
text-transform: lowercase;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
.cp-workspace-card__desc {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { match, P } from 'massaman/match'
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
|
|
4
|
+
import { renderRichText } from '../../lib/rich-text.tsx'
|
|
4
5
|
import { Card } from '../shared/card'
|
|
5
6
|
import { CardIcon } from '../shared/card-icon'
|
|
6
7
|
import type { CardIconInput } from '../shared/resolve-card-icon'
|
|
@@ -65,7 +66,6 @@ export function WorkspaceCard({
|
|
|
65
66
|
titleLines,
|
|
66
67
|
descriptionLines,
|
|
67
68
|
}: WorkspaceCardProps): React.ReactElement {
|
|
68
|
-
const name = title.toLowerCase()
|
|
69
69
|
const resolved = resolveCardIcon(icon)
|
|
70
70
|
|
|
71
71
|
const iconEl = match(resolved)
|
|
@@ -93,7 +93,7 @@ export function WorkspaceCard({
|
|
|
93
93
|
className={clampClass('cp-workspace-card__desc', descriptionLines)}
|
|
94
94
|
style={clampStyle(descriptionLines)}
|
|
95
95
|
>
|
|
96
|
-
{d}
|
|
96
|
+
{renderRichText(d)}
|
|
97
97
|
</span>
|
|
98
98
|
))
|
|
99
99
|
.otherwise(() => null)
|
|
@@ -122,7 +122,7 @@ export function WorkspaceCard({
|
|
|
122
122
|
className={clampClass('cp-workspace-card__name', titleLines)}
|
|
123
123
|
style={clampStyle(titleLines)}
|
|
124
124
|
>
|
|
125
|
-
{
|
|
125
|
+
{renderRichText(title)}
|
|
126
126
|
</span>
|
|
127
127
|
</div>
|
|
128
128
|
</div>
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import { match, P } from 'massaman/match'
|
|
1
2
|
import type React from 'react'
|
|
2
3
|
|
|
4
|
+
import { renderRichText } from '../../lib/rich-text.tsx'
|
|
5
|
+
|
|
3
6
|
import './card.css'
|
|
4
7
|
|
|
5
8
|
export interface WorkspaceGridProps {
|
|
6
9
|
/**
|
|
7
|
-
* Section heading (e.g. "Apps", "Packages").
|
|
10
|
+
* Section heading (e.g. "Apps", "Packages"). Omitted for an ungrouped
|
|
11
|
+
* grid, such as a showcase block with an explicit `source` list.
|
|
8
12
|
*/
|
|
9
|
-
readonly heading
|
|
13
|
+
readonly heading?: string
|
|
10
14
|
/**
|
|
11
|
-
* Brief description rendered below the heading.
|
|
15
|
+
* Brief description rendered below the heading. Omitted for an
|
|
16
|
+
* ungrouped grid.
|
|
12
17
|
*/
|
|
13
|
-
readonly description
|
|
18
|
+
readonly description?: string
|
|
14
19
|
/**
|
|
15
20
|
* Number of grid columns.
|
|
16
21
|
*/
|
|
@@ -38,8 +43,15 @@ export function WorkspaceGrid({
|
|
|
38
43
|
|
|
39
44
|
return (
|
|
40
45
|
<>
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
{/* Rich text, matching the card titles and descriptions this grid
|
|
47
|
+
wraps — a group heading was the one string in the block that
|
|
48
|
+
showed its `**markers**` literally. */}
|
|
49
|
+
{match(heading)
|
|
50
|
+
.with(P.string, (h) => <h2>{renderRichText(h)}</h2>)
|
|
51
|
+
.otherwise(() => null)}
|
|
52
|
+
{match(description)
|
|
53
|
+
.with(P.string, (d) => <p className="cp-workspace-section__desc">{renderRichText(d)}</p>)
|
|
54
|
+
.otherwise(() => null)}
|
|
43
55
|
<div className="cp-workspace-grid" style={style}>
|
|
44
56
|
{children}
|
|
45
57
|
</div>
|
package/dist/theme/index.tsx
CHANGED
|
@@ -23,6 +23,7 @@ import './styles/layers.css'
|
|
|
23
23
|
import './styles/overrides/fonts.css'
|
|
24
24
|
import './styles/overrides/tokens.css'
|
|
25
25
|
import './styles/overrides/rspress.css'
|
|
26
|
+
import './styles/rich-text.css'
|
|
26
27
|
// Theme color palettes — scoped via [data-cp-theme][data-cp-variant] selectors.
|
|
27
28
|
// `mulled.css` ships first because it is `DEFAULT_THEME_NAME` — its rules emit
|
|
28
29
|
// the `:root { ... }` FOUC fallback that paints before JS hydrates the
|
|
@@ -106,6 +107,10 @@ export { TrustStrip } from './components/home/trust-strip'
|
|
|
106
107
|
export type { TrustStripProps } from './components/home/trust-strip'
|
|
107
108
|
export { HomeSplit } from './components/home/split'
|
|
108
109
|
export type { SplitProps, SplitAction } from './components/home/split'
|
|
110
|
+
export { HomeTabs } from './components/home/tabs'
|
|
111
|
+
export { RichText, renderRichText } from './lib/rich-text.tsx'
|
|
112
|
+
export { hasAccentMarker, toPlainText } from './lib/rich-text-parse.ts'
|
|
113
|
+
export type { HomeTabsProps, HomeTabEntry, TabsAction } from './components/home/tabs'
|
|
109
114
|
export { CTA } from './components/home/cta'
|
|
110
115
|
export type { CTAProps } from './components/home/cta'
|
|
111
116
|
export { WorkspaceCard } from './components/workspaces/card'
|