@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
package/dist/node.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import path_0 from "path";
|
|
|
12
12
|
import { Project, SyntaxKind as external_ts_morph_SyntaxKind } from "ts-morph";
|
|
13
13
|
import node_process from "node:process";
|
|
14
14
|
import katex from "katex";
|
|
15
|
+
import { unfold } from "massaman/array";
|
|
15
16
|
import react from "react";
|
|
16
17
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
17
18
|
import { dirname as __rspack_dirname } from "node:path";
|
|
@@ -17358,7 +17359,7 @@ function buildLabelCss(label) {
|
|
|
17358
17359
|
margin-top: -14px;
|
|
17359
17360
|
text-align: left;
|
|
17360
17361
|
color: var(--cp-c-text-2, #888);
|
|
17361
|
-
font-family: 'Geist Pixel Square', ui-sans-serif, system-ui, sans-serif;
|
|
17362
|
+
font-family: var(--cp-ff-display, 'Geist Pixel Square', ui-sans-serif, system-ui, sans-serif);
|
|
17362
17363
|
font-size: 28px;
|
|
17363
17364
|
font-weight: 400;
|
|
17364
17365
|
line-height: 1;
|
|
@@ -17451,6 +17452,408 @@ function mermaidPlugin(options = {}) {
|
|
|
17451
17452
|
builderConfig: remarkMermaid.builderConfig
|
|
17452
17453
|
};
|
|
17453
17454
|
}
|
|
17455
|
+
const SAFE_PROTOCOLS = new Set([
|
|
17456
|
+
'http:',
|
|
17457
|
+
'https:',
|
|
17458
|
+
'mailto:',
|
|
17459
|
+
'tel:'
|
|
17460
|
+
]);
|
|
17461
|
+
const PROTOCOL_PATTERN = /^[a-z][a-z0-9+.-]*:/i;
|
|
17462
|
+
const FORBIDDEN_CHARS_PATTERN = /[\u0000-\u0020\u007F]/u;
|
|
17463
|
+
function safeUrl(href) {
|
|
17464
|
+
const trimmed = href.trim();
|
|
17465
|
+
if (FORBIDDEN_CHARS_PATTERN.test(trimmed)) return null;
|
|
17466
|
+
return match_match(trimmed).with('', ()=>null).with(P.string.startsWith('#'), ()=>trimmed).with(P.string.startsWith('/'), ()=>trimmed).with(P.string.startsWith('./'), ()=>trimmed).with(P.string.startsWith('../'), ()=>trimmed).otherwise(()=>{
|
|
17467
|
+
const match_ = trimmed.match(PROTOCOL_PATTERN);
|
|
17468
|
+
if (!match_) return trimmed;
|
|
17469
|
+
const scheme = match_[0].toLowerCase();
|
|
17470
|
+
if (SAFE_PROTOCOLS.has(scheme)) return trimmed;
|
|
17471
|
+
return null;
|
|
17472
|
+
});
|
|
17473
|
+
}
|
|
17474
|
+
const ALLOWED_TAGS = new Set([
|
|
17475
|
+
'b',
|
|
17476
|
+
'strong',
|
|
17477
|
+
'i',
|
|
17478
|
+
'em',
|
|
17479
|
+
'code',
|
|
17480
|
+
'kbd',
|
|
17481
|
+
'mark',
|
|
17482
|
+
'sup',
|
|
17483
|
+
'sub',
|
|
17484
|
+
'span',
|
|
17485
|
+
'small',
|
|
17486
|
+
'u',
|
|
17487
|
+
's',
|
|
17488
|
+
'del',
|
|
17489
|
+
'ins'
|
|
17490
|
+
]);
|
|
17491
|
+
const STRIPPED_TAGS = new Set([
|
|
17492
|
+
"script",
|
|
17493
|
+
'style',
|
|
17494
|
+
'iframe',
|
|
17495
|
+
'object',
|
|
17496
|
+
'embed',
|
|
17497
|
+
'template',
|
|
17498
|
+
"noscript"
|
|
17499
|
+
]);
|
|
17500
|
+
const ACCENT_CLASS = 'cp-accent';
|
|
17501
|
+
const MARK_CLASS = 'cp-mark';
|
|
17502
|
+
const TOKEN_PATTERN = /(?:\\([*=`[\]<\\]))|(?:`([^`]+)`)|(?:\*\*([\s\S]+?)\*\*)|(?:==([\s\S]+?)==)|(?:(?<!\*)\*(?=[^\s*])([^*\n]+?)(?<=[^\s*])\*(?!\*))|(?:\[([^\]]*)\]\(((?:[^()\s]|\([^()\s]*\))+)\))|(?:<(\/?)\s*([a-zA-Z][a-zA-Z0-9-]*)(?=[\s/>])((?:"[^"]*"|'[^']*'|[^>"'])*)>)/;
|
|
17503
|
+
const ATTR_PATTERN = /([a-zA-Z-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
17504
|
+
const CLOSE_TAG_BOUNDARY = /[\s>]/;
|
|
17505
|
+
function parseRichText(text) {
|
|
17506
|
+
if ('string' != typeof text || 0 === text.length) return [];
|
|
17507
|
+
return parseInline({
|
|
17508
|
+
input: text,
|
|
17509
|
+
inLink: false
|
|
17510
|
+
});
|
|
17511
|
+
}
|
|
17512
|
+
function toPlainText(text) {
|
|
17513
|
+
return flattenNodes(parseRichText(text));
|
|
17514
|
+
}
|
|
17515
|
+
function parseInline({ input, inLink }) {
|
|
17516
|
+
return unfold((remaining)=>{
|
|
17517
|
+
if (0 === remaining.length) return false;
|
|
17518
|
+
const found = TOKEN_PATTERN.exec(remaining);
|
|
17519
|
+
if (null === found) return [
|
|
17520
|
+
[
|
|
17521
|
+
{
|
|
17522
|
+
kind: 'text',
|
|
17523
|
+
value: remaining
|
|
17524
|
+
}
|
|
17525
|
+
],
|
|
17526
|
+
''
|
|
17527
|
+
];
|
|
17528
|
+
const lead = remaining.slice(0, found.index);
|
|
17529
|
+
const after = remaining.slice(found.index + found[0].length);
|
|
17530
|
+
const step = consumeToken({
|
|
17531
|
+
found,
|
|
17532
|
+
after,
|
|
17533
|
+
inLink
|
|
17534
|
+
});
|
|
17535
|
+
return [
|
|
17536
|
+
[
|
|
17537
|
+
...textNodes(lead),
|
|
17538
|
+
...step.nodes
|
|
17539
|
+
],
|
|
17540
|
+
step.rest
|
|
17541
|
+
];
|
|
17542
|
+
}, input).flat();
|
|
17543
|
+
}
|
|
17544
|
+
function textNodes(lead) {
|
|
17545
|
+
if (0 === lead.length) return [];
|
|
17546
|
+
return [
|
|
17547
|
+
{
|
|
17548
|
+
kind: 'text',
|
|
17549
|
+
value: lead
|
|
17550
|
+
}
|
|
17551
|
+
];
|
|
17552
|
+
}
|
|
17553
|
+
function consumeToken({ found, after, inLink }) {
|
|
17554
|
+
const [, escaped, code, bold, highlight, italic, linkText, linkHref, closing, tagName, rawAttrs] = found;
|
|
17555
|
+
if (void 0 !== escaped) return {
|
|
17556
|
+
nodes: [
|
|
17557
|
+
{
|
|
17558
|
+
kind: 'text',
|
|
17559
|
+
value: escaped
|
|
17560
|
+
}
|
|
17561
|
+
],
|
|
17562
|
+
rest: after
|
|
17563
|
+
};
|
|
17564
|
+
if (void 0 !== code) return {
|
|
17565
|
+
nodes: [
|
|
17566
|
+
codeNode(code)
|
|
17567
|
+
],
|
|
17568
|
+
rest: after
|
|
17569
|
+
};
|
|
17570
|
+
if (void 0 !== bold) return {
|
|
17571
|
+
nodes: [
|
|
17572
|
+
accentNode(parseInline({
|
|
17573
|
+
input: bold,
|
|
17574
|
+
inLink
|
|
17575
|
+
}))
|
|
17576
|
+
],
|
|
17577
|
+
rest: after
|
|
17578
|
+
};
|
|
17579
|
+
if (void 0 !== highlight) return {
|
|
17580
|
+
nodes: [
|
|
17581
|
+
markNode(parseInline({
|
|
17582
|
+
input: highlight,
|
|
17583
|
+
inLink
|
|
17584
|
+
}))
|
|
17585
|
+
],
|
|
17586
|
+
rest: after
|
|
17587
|
+
};
|
|
17588
|
+
if (void 0 !== italic) return {
|
|
17589
|
+
nodes: [
|
|
17590
|
+
rich_text_parse_element({
|
|
17591
|
+
tag: 'em',
|
|
17592
|
+
children: parseInline({
|
|
17593
|
+
input: italic,
|
|
17594
|
+
inLink
|
|
17595
|
+
})
|
|
17596
|
+
})
|
|
17597
|
+
],
|
|
17598
|
+
rest: after
|
|
17599
|
+
};
|
|
17600
|
+
if (void 0 !== linkHref) return {
|
|
17601
|
+
nodes: linkNodes({
|
|
17602
|
+
text: linkText ?? '',
|
|
17603
|
+
href: linkHref,
|
|
17604
|
+
inLink
|
|
17605
|
+
}),
|
|
17606
|
+
rest: after
|
|
17607
|
+
};
|
|
17608
|
+
if (void 0 !== tagName) {
|
|
17609
|
+
const tail = (rawAttrs ?? '').trimEnd();
|
|
17610
|
+
const isSelfClosing = tail.endsWith('/');
|
|
17611
|
+
return consumeTag({
|
|
17612
|
+
tagName: tagName.toLowerCase(),
|
|
17613
|
+
attrs: match_match(isSelfClosing).with(true, ()=>tail.slice(0, -1)).otherwise(()=>tail),
|
|
17614
|
+
isClosing: '/' === closing,
|
|
17615
|
+
isSelfClosing,
|
|
17616
|
+
after,
|
|
17617
|
+
inLink
|
|
17618
|
+
});
|
|
17619
|
+
}
|
|
17620
|
+
return {
|
|
17621
|
+
nodes: [
|
|
17622
|
+
{
|
|
17623
|
+
kind: 'text',
|
|
17624
|
+
value: found[0]
|
|
17625
|
+
}
|
|
17626
|
+
],
|
|
17627
|
+
rest: after
|
|
17628
|
+
};
|
|
17629
|
+
}
|
|
17630
|
+
function consumeTag(params) {
|
|
17631
|
+
const { tagName, attrs, isClosing, isSelfClosing, after, inLink } = params;
|
|
17632
|
+
if (isClosing) return {
|
|
17633
|
+
nodes: [],
|
|
17634
|
+
rest: after
|
|
17635
|
+
};
|
|
17636
|
+
if ('br' === tagName) return {
|
|
17637
|
+
nodes: [
|
|
17638
|
+
{
|
|
17639
|
+
kind: 'break'
|
|
17640
|
+
}
|
|
17641
|
+
],
|
|
17642
|
+
rest: after
|
|
17643
|
+
};
|
|
17644
|
+
if (isSelfClosing) return {
|
|
17645
|
+
nodes: selfClosingNodes({
|
|
17646
|
+
tagName,
|
|
17647
|
+
attrs
|
|
17648
|
+
}),
|
|
17649
|
+
rest: after
|
|
17650
|
+
};
|
|
17651
|
+
const close = findClosingTag({
|
|
17652
|
+
text: after,
|
|
17653
|
+
tagName
|
|
17654
|
+
});
|
|
17655
|
+
if (null === close) {
|
|
17656
|
+
if (STRIPPED_TAGS.has(tagName)) return {
|
|
17657
|
+
nodes: [],
|
|
17658
|
+
rest: ''
|
|
17659
|
+
};
|
|
17660
|
+
return {
|
|
17661
|
+
nodes: [],
|
|
17662
|
+
rest: after
|
|
17663
|
+
};
|
|
17664
|
+
}
|
|
17665
|
+
const inner = after.slice(0, close.start);
|
|
17666
|
+
const rest = after.slice(close.end);
|
|
17667
|
+
if (STRIPPED_TAGS.has(tagName)) return {
|
|
17668
|
+
nodes: [],
|
|
17669
|
+
rest
|
|
17670
|
+
};
|
|
17671
|
+
if ('a' === tagName) return {
|
|
17672
|
+
nodes: anchorNodes({
|
|
17673
|
+
attrs,
|
|
17674
|
+
inner,
|
|
17675
|
+
inLink
|
|
17676
|
+
}),
|
|
17677
|
+
rest
|
|
17678
|
+
};
|
|
17679
|
+
if (ALLOWED_TAGS.has(tagName)) return {
|
|
17680
|
+
nodes: [
|
|
17681
|
+
taggedElement({
|
|
17682
|
+
tag: tagName,
|
|
17683
|
+
attrs,
|
|
17684
|
+
children: parseInline({
|
|
17685
|
+
input: inner,
|
|
17686
|
+
inLink
|
|
17687
|
+
})
|
|
17688
|
+
})
|
|
17689
|
+
],
|
|
17690
|
+
rest
|
|
17691
|
+
};
|
|
17692
|
+
return {
|
|
17693
|
+
nodes: parseInline({
|
|
17694
|
+
input: inner,
|
|
17695
|
+
inLink
|
|
17696
|
+
}),
|
|
17697
|
+
rest
|
|
17698
|
+
};
|
|
17699
|
+
}
|
|
17700
|
+
function selfClosingNodes({ tagName, attrs }) {
|
|
17701
|
+
if (!ALLOWED_TAGS.has(tagName)) return [];
|
|
17702
|
+
return [
|
|
17703
|
+
taggedElement({
|
|
17704
|
+
tag: tagName,
|
|
17705
|
+
attrs,
|
|
17706
|
+
children: []
|
|
17707
|
+
})
|
|
17708
|
+
];
|
|
17709
|
+
}
|
|
17710
|
+
function findClosingTag({ text, tagName }) {
|
|
17711
|
+
return scanForClose({
|
|
17712
|
+
lower: text.toLowerCase(),
|
|
17713
|
+
tagName,
|
|
17714
|
+
from: 0
|
|
17715
|
+
});
|
|
17716
|
+
}
|
|
17717
|
+
function scanForClose({ lower, tagName, from }) {
|
|
17718
|
+
const needle = `</${tagName}`;
|
|
17719
|
+
const candidates = unfold((offset)=>{
|
|
17720
|
+
const at = lower.indexOf(needle, offset);
|
|
17721
|
+
if (-1 === at) return false;
|
|
17722
|
+
return [
|
|
17723
|
+
at,
|
|
17724
|
+
at + needle.length
|
|
17725
|
+
];
|
|
17726
|
+
}, from);
|
|
17727
|
+
const start = candidates.find((at)=>CLOSE_TAG_BOUNDARY.test(lower.charAt(at + needle.length)));
|
|
17728
|
+
if (void 0 === start) return null;
|
|
17729
|
+
const gt = lower.indexOf('>', start);
|
|
17730
|
+
if (-1 === gt) return null;
|
|
17731
|
+
return {
|
|
17732
|
+
start,
|
|
17733
|
+
end: gt + 1
|
|
17734
|
+
};
|
|
17735
|
+
}
|
|
17736
|
+
function parseAttrs(attrs) {
|
|
17737
|
+
const entries = [
|
|
17738
|
+
...attrs.matchAll(ATTR_PATTERN)
|
|
17739
|
+
];
|
|
17740
|
+
return entries.reduce((acc, entry)=>{
|
|
17741
|
+
const name = entry[1].toLowerCase();
|
|
17742
|
+
const value = entry[2] ?? entry[3] ?? '';
|
|
17743
|
+
return match_match(name).with('class', ()=>({
|
|
17744
|
+
...acc,
|
|
17745
|
+
className: value
|
|
17746
|
+
})).with('classname', ()=>({
|
|
17747
|
+
...acc,
|
|
17748
|
+
className: value
|
|
17749
|
+
})).with('title', ()=>({
|
|
17750
|
+
...acc,
|
|
17751
|
+
title: value
|
|
17752
|
+
})).with('href', ()=>({
|
|
17753
|
+
...acc,
|
|
17754
|
+
href: value
|
|
17755
|
+
})).otherwise(()=>acc);
|
|
17756
|
+
}, {});
|
|
17757
|
+
}
|
|
17758
|
+
function taggedElement({ tag, attrs, children }) {
|
|
17759
|
+
const parsed = parseAttrs(attrs);
|
|
17760
|
+
return {
|
|
17761
|
+
kind: 'element',
|
|
17762
|
+
tag,
|
|
17763
|
+
className: parsed.className,
|
|
17764
|
+
title: parsed.title,
|
|
17765
|
+
children
|
|
17766
|
+
};
|
|
17767
|
+
}
|
|
17768
|
+
function anchorNodes({ attrs, inner, inLink }) {
|
|
17769
|
+
const { href } = parseAttrs(attrs);
|
|
17770
|
+
if (void 0 === href || inLink) return parseInline({
|
|
17771
|
+
input: inner,
|
|
17772
|
+
inLink
|
|
17773
|
+
});
|
|
17774
|
+
const safe = safeUrl(href);
|
|
17775
|
+
if (null === safe) return parseInline({
|
|
17776
|
+
input: inner,
|
|
17777
|
+
inLink
|
|
17778
|
+
});
|
|
17779
|
+
const children = parseInline({
|
|
17780
|
+
input: inner,
|
|
17781
|
+
inLink: true
|
|
17782
|
+
});
|
|
17783
|
+
return linkOrChildren({
|
|
17784
|
+
href: safe,
|
|
17785
|
+
children
|
|
17786
|
+
});
|
|
17787
|
+
}
|
|
17788
|
+
function linkNodes({ text, href, inLink }) {
|
|
17789
|
+
const safe = safeUrl(href);
|
|
17790
|
+
if (null === safe || inLink) return parseInline({
|
|
17791
|
+
input: text,
|
|
17792
|
+
inLink
|
|
17793
|
+
});
|
|
17794
|
+
return linkOrChildren({
|
|
17795
|
+
href: safe,
|
|
17796
|
+
children: parseInline({
|
|
17797
|
+
input: text,
|
|
17798
|
+
inLink: true
|
|
17799
|
+
})
|
|
17800
|
+
});
|
|
17801
|
+
}
|
|
17802
|
+
function linkOrChildren({ href, children }) {
|
|
17803
|
+
if (0 === children.length) return [];
|
|
17804
|
+
return [
|
|
17805
|
+
{
|
|
17806
|
+
kind: 'link',
|
|
17807
|
+
href,
|
|
17808
|
+
children
|
|
17809
|
+
}
|
|
17810
|
+
];
|
|
17811
|
+
}
|
|
17812
|
+
function codeNode(value) {
|
|
17813
|
+
return {
|
|
17814
|
+
kind: 'code',
|
|
17815
|
+
value
|
|
17816
|
+
};
|
|
17817
|
+
}
|
|
17818
|
+
function accentNode(children) {
|
|
17819
|
+
return {
|
|
17820
|
+
kind: 'element',
|
|
17821
|
+
tag: 'strong',
|
|
17822
|
+
className: ACCENT_CLASS,
|
|
17823
|
+
children
|
|
17824
|
+
};
|
|
17825
|
+
}
|
|
17826
|
+
function markNode(children) {
|
|
17827
|
+
return {
|
|
17828
|
+
kind: 'element',
|
|
17829
|
+
tag: 'mark',
|
|
17830
|
+
className: MARK_CLASS,
|
|
17831
|
+
children
|
|
17832
|
+
};
|
|
17833
|
+
}
|
|
17834
|
+
function rich_text_parse_element({ tag, children }) {
|
|
17835
|
+
return {
|
|
17836
|
+
kind: 'element',
|
|
17837
|
+
tag,
|
|
17838
|
+
children
|
|
17839
|
+
};
|
|
17840
|
+
}
|
|
17841
|
+
function flattenNodes(nodes) {
|
|
17842
|
+
return nodes.map(flattenNode).join('');
|
|
17843
|
+
}
|
|
17844
|
+
function flattenNode(node) {
|
|
17845
|
+
return match_match(node).with({
|
|
17846
|
+
kind: 'text'
|
|
17847
|
+
}, (n)=>n.value).with({
|
|
17848
|
+
kind: 'code'
|
|
17849
|
+
}, (n)=>n.value).with({
|
|
17850
|
+
kind: 'break'
|
|
17851
|
+
}, ()=>' ').with({
|
|
17852
|
+
kind: 'link'
|
|
17853
|
+
}, (n)=>flattenNodes(n.children)).with({
|
|
17854
|
+
kind: 'element'
|
|
17855
|
+
}, (n)=>flattenNodes(n.children)).exhaustive();
|
|
17856
|
+
}
|
|
17454
17857
|
const DEFAULT_LOADER_MIN_MS = 150;
|
|
17455
17858
|
const DEFAULT_LOADER_MAX_MS = 5000;
|
|
17456
17859
|
const VSCODE_SET_JS = "document.documentElement.dataset.ciderpressEnv='vscode'";
|
|
@@ -17546,8 +17949,8 @@ function createRspressConfig(options) {
|
|
|
17546
17949
|
cleanUrls: true
|
|
17547
17950
|
},
|
|
17548
17951
|
llms: true,
|
|
17549
|
-
title: config.title ?? 'ciderpress',
|
|
17550
|
-
description: config.description ?? 'Documentation',
|
|
17952
|
+
title: toPlainText(config.title ?? 'ciderpress'),
|
|
17953
|
+
description: toPlainText(config.description ?? 'Documentation'),
|
|
17551
17954
|
icon: resolveFaviconPath(favicon),
|
|
17552
17955
|
logo: match_match(logoRaw).with(P.string, (l)=>l).otherwise(()=>''),
|
|
17553
17956
|
logoText: '',
|
|
@@ -17860,20 +18263,10 @@ function buildSiteBlock(params) {
|
|
|
17860
18263
|
};
|
|
17861
18264
|
}
|
|
17862
18265
|
function resolveHomeConfig(home) {
|
|
17863
|
-
const fallbackShowcase = {
|
|
17864
|
-
columns: 2
|
|
17865
|
-
};
|
|
17866
18266
|
if (void 0 === home) return {
|
|
17867
|
-
hero: {}
|
|
17868
|
-
showcase: fallbackShowcase
|
|
17869
|
-
};
|
|
17870
|
-
return {
|
|
17871
|
-
...home,
|
|
17872
|
-
showcase: {
|
|
17873
|
-
columns: 2,
|
|
17874
|
-
...home.showcase
|
|
17875
|
-
}
|
|
18267
|
+
hero: {}
|
|
17876
18268
|
};
|
|
18269
|
+
return home;
|
|
17877
18270
|
}
|
|
17878
18271
|
function buildHeadScriptBody(options) {
|
|
17879
18272
|
const minimalRegistry = options.registry.map((entry)=>({
|
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
padding: 0 32px;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
.cp-cta__eyebrow {
|
|
13
|
+
font-family: var(--cp-font-family-mono);
|
|
14
|
+
font-size: var(--cp-fs-eyebrow);
|
|
15
|
+
text-transform: uppercase;
|
|
16
|
+
letter-spacing: var(--cp-letter-eyebrow);
|
|
17
|
+
/* Same shade pair as `.cp-accent` — `--cp-c-brand-1` is a fill colour and
|
|
18
|
+
misses 4.5:1 at this size on several themes. See styles/rich-text.css. */
|
|
19
|
+
color: var(--cp-c-brand-2, var(--cp-c-brand-1));
|
|
20
|
+
margin-bottom: var(--cp-s-12);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
html[data-cp-variant='dark'] .cp-cta__eyebrow {
|
|
24
|
+
color: var(--cp-c-brand-light, var(--cp-c-brand-1));
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
.cp-cta__title {
|
|
13
28
|
font-size: clamp(32px, 5vw, 56px);
|
|
14
29
|
font-weight: 600;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { match } from 'massaman/match'
|
|
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 { RouteLink } from '../../lib/route-link.tsx'
|
|
5
6
|
import { safeUrl } from '../../lib/safe-url.ts'
|
|
6
7
|
import type { HeroAction } from './hero'
|
|
@@ -8,6 +9,10 @@ import type { HeroAction } from './hero'
|
|
|
8
9
|
import './cta.css'
|
|
9
10
|
|
|
10
11
|
export interface CTAProps {
|
|
12
|
+
/**
|
|
13
|
+
* Small uppercase kicker rendered above the headline.
|
|
14
|
+
*/
|
|
15
|
+
readonly eyebrow?: string
|
|
11
16
|
/**
|
|
12
17
|
* Headline.
|
|
13
18
|
*/
|
|
@@ -30,12 +35,15 @@ export interface CTAProps {
|
|
|
30
35
|
* @returns React element.
|
|
31
36
|
*/
|
|
32
37
|
export function CTA(props: CTAProps): React.ReactElement {
|
|
33
|
-
const { title, subtitle, actions } = props
|
|
38
|
+
const { eyebrow, title, subtitle, actions } = props
|
|
34
39
|
const list = actions ?? []
|
|
35
40
|
|
|
36
41
|
return (
|
|
37
42
|
<section className="cp-cta">
|
|
38
43
|
<div className="cp-cta__inner">
|
|
44
|
+
{match(eyebrow)
|
|
45
|
+
.with(P.string, (e) => <div className="cp-cta__eyebrow">{renderRichText(e)}</div>)
|
|
46
|
+
.otherwise(() => null)}
|
|
39
47
|
<h2 className="cp-cta__title">{title}</h2>
|
|
40
48
|
{match(subtitle)
|
|
41
49
|
.with(undefined, () => null)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.cp-feature-card__title {
|
|
21
|
-
font-family:
|
|
21
|
+
font-family: var(--cp-ff-display);
|
|
22
22
|
font-size: var(--cp-fs-feature-title);
|
|
23
23
|
font-weight: var(--cp-fw-bold);
|
|
24
24
|
color: var(--rp-c-text-1, var(--cp-c-text-1));
|
|
@@ -2,6 +2,7 @@ import { match, P } from 'massaman/match'
|
|
|
2
2
|
import type React from 'react'
|
|
3
3
|
|
|
4
4
|
import './feature-card.css'
|
|
5
|
+
import { renderRichText } from '../../lib/rich-text.tsx'
|
|
5
6
|
import { Card } from '../shared/card'
|
|
6
7
|
import { CardIcon } from '../shared/card-icon'
|
|
7
8
|
import type { CardIconInput } from '../shared/resolve-card-icon'
|
|
@@ -64,14 +65,14 @@ export function FeatureCard({
|
|
|
64
65
|
className={clampClass('cp-feature-card__title', titleLines)}
|
|
65
66
|
style={clampStyle(titleLines)}
|
|
66
67
|
>
|
|
67
|
-
{title}
|
|
68
|
+
{renderRichText(title)}
|
|
68
69
|
</span>
|
|
69
70
|
</div>
|
|
70
71
|
<span
|
|
71
72
|
className={clampClass('cp-feature-card__desc', descriptionLines)}
|
|
72
73
|
style={clampStyle(descriptionLines)}
|
|
73
74
|
>
|
|
74
|
-
{description}
|
|
75
|
+
{renderRichText(description)}
|
|
75
76
|
</span>
|
|
76
77
|
{linkTail}
|
|
77
78
|
</Card>
|
|
@@ -1,16 +1,22 @@
|
|
|
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
|
-
import {
|
|
5
|
+
import { renderRichText } from '../../lib/rich-text.tsx'
|
|
7
6
|
import { FeatureCard } from './feature-card'
|
|
8
7
|
import type { FeatureItem } from './feature-card'
|
|
9
8
|
|
|
10
9
|
interface FrontmatterFeaturesHeading {
|
|
11
10
|
readonly label?: string
|
|
12
11
|
readonly title?: string
|
|
13
|
-
readonly
|
|
12
|
+
readonly body?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface HomeFeatureProps {
|
|
16
|
+
readonly items?: readonly FeatureItem[]
|
|
17
|
+
readonly heading?: FrontmatterFeaturesHeading
|
|
18
|
+
readonly columns?: 1 | 2 | 3 | 4
|
|
19
|
+
readonly truncate?: TruncateConfig
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
const DEFAULT_HEADING_EYEBROW = 'Features'
|
|
@@ -19,23 +25,15 @@ const DEFAULT_HEADING_SUBTITLE =
|
|
|
19
25
|
"Everything you need, nothing you don't. Configured in TypeScript, validated at boot."
|
|
20
26
|
|
|
21
27
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
28
|
+
* Features grid block. Renders the supplied feature cards under a heading,
|
|
29
|
+
* or nothing when no cards are present.
|
|
24
30
|
*
|
|
25
|
-
* @
|
|
31
|
+
* @param props - Resolved feature cards, optional heading, and truncation limits.
|
|
32
|
+
* @returns React element with the feature grid, or null.
|
|
26
33
|
*/
|
|
27
|
-
export function HomeFeature(): React.ReactElement | null {
|
|
28
|
-
const {
|
|
29
|
-
|
|
30
|
-
const gridConfig = home && home.features
|
|
31
|
-
|
|
32
|
-
// Rspress types frontmatter as its own FrontMatterMeta shape which does not
|
|
33
|
-
// include ciderpress-specific `features`. The double cast is necessary because
|
|
34
|
-
// no shared Zod schema exists for frontmatter validation at runtime.
|
|
35
|
-
const fm = frontmatter as Record<string, unknown>
|
|
36
|
-
const features = fm.features as readonly FeatureItem[] | undefined
|
|
37
|
-
const heading = fm.featuresHeading as FrontmatterFeaturesHeading | undefined
|
|
38
|
-
// Frontmatter is unvalidated user content — a `featuresHeading.title: {}`
|
|
34
|
+
export function HomeFeature(props: HomeFeatureProps): React.ReactElement | null {
|
|
35
|
+
const { items, heading, columns, truncate } = props
|
|
36
|
+
// Frontmatter is unvalidated user content — a `heading.title: {}`
|
|
39
37
|
// would otherwise render as `[object Object]` in the H2. Treat any
|
|
40
38
|
// non-string value as missing and fall through to the framework default.
|
|
41
39
|
const headingEyebrow = match(heading && heading.label)
|
|
@@ -44,22 +42,27 @@ export function HomeFeature(): React.ReactElement | null {
|
|
|
44
42
|
const headingTitle = match(heading && heading.title)
|
|
45
43
|
.with(P.string, (s) => s)
|
|
46
44
|
.otherwise(() => DEFAULT_HEADING_TITLE)
|
|
47
|
-
const headingSubtitle = match(heading && heading.
|
|
45
|
+
const headingSubtitle = match(heading && heading.body)
|
|
48
46
|
.with(P.string, (s) => s)
|
|
49
47
|
.otherwise(() => DEFAULT_HEADING_SUBTITLE)
|
|
48
|
+
// Column count rides on a custom property so the grid keeps its CSS
|
|
49
|
+
// media queries — a narrow viewport still collapses to one column.
|
|
50
|
+
const gridStyle = match(columns)
|
|
51
|
+
.with(P.number, (c) => ({ '--cp-feature-cols': String(c) }) as React.CSSProperties)
|
|
52
|
+
.otherwise(() => undefined)
|
|
50
53
|
|
|
51
|
-
return match(
|
|
54
|
+
return match(items)
|
|
52
55
|
.with(
|
|
53
56
|
P.when((f): f is readonly FeatureItem[] => Array.isArray(f) && f.length > 0),
|
|
54
|
-
(
|
|
57
|
+
(list) => (
|
|
55
58
|
<div className="cp-feature-section">
|
|
56
59
|
<div className="cp-feature-section-head">
|
|
57
|
-
<div className="cp-feature-section-head__eyebrow">{headingEyebrow}</div>
|
|
58
|
-
<h2 className="cp-feature-section-head__title">{headingTitle}</h2>
|
|
59
|
-
<p className="cp-feature-section-head__sub">{headingSubtitle}</p>
|
|
60
|
+
<div className="cp-feature-section-head__eyebrow">{renderRichText(headingEyebrow)}</div>
|
|
61
|
+
<h2 className="cp-feature-section-head__title">{renderRichText(headingTitle)}</h2>
|
|
62
|
+
<p className="cp-feature-section-head__sub">{renderRichText(headingSubtitle)}</p>
|
|
60
63
|
</div>
|
|
61
|
-
<div className="cp-feature-grid">
|
|
62
|
-
{
|
|
64
|
+
<div className="cp-feature-grid" style={gridStyle}>
|
|
65
|
+
{list.map((f, i) => renderFeature(f, i, truncate))}
|
|
63
66
|
</div>
|
|
64
67
|
</div>
|
|
65
68
|
)
|
|
@@ -74,16 +77,16 @@ export function HomeFeature(): React.ReactElement | null {
|
|
|
74
77
|
* @private
|
|
75
78
|
* @param feature - Feature item data
|
|
76
79
|
* @param index - Array index for key generation
|
|
77
|
-
* @param
|
|
80
|
+
* @param truncate - Optional line-clamp limits for card text
|
|
78
81
|
* @returns Feature card element
|
|
79
82
|
*/
|
|
80
83
|
function renderFeature(
|
|
81
84
|
feature: FeatureItem,
|
|
82
85
|
index: number,
|
|
83
|
-
|
|
86
|
+
truncate: TruncateConfig | undefined
|
|
84
87
|
): React.ReactElement {
|
|
85
|
-
const titleLines =
|
|
86
|
-
const descLines =
|
|
88
|
+
const titleLines = truncate && truncate.title
|
|
89
|
+
const descLines = truncate && truncate.description
|
|
87
90
|
|
|
88
91
|
return (
|
|
89
92
|
<FeatureCard
|
|
@@ -72,6 +72,17 @@
|
|
|
72
72
|
padding: 0;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/* Code-form hero demo — the frame owns the chrome, so strip the native
|
|
76
|
+
Rspress code block's own margin and rounded corners. */
|
|
77
|
+
.cp-hero-demo--code .rp-codeblock {
|
|
78
|
+
margin: 0;
|
|
79
|
+
border-radius: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.cp-hero-demo--code .rp-codeblock pre {
|
|
83
|
+
overflow-x: auto;
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
.cp-hero-demo__img {
|
|
76
87
|
display: block;
|
|
77
88
|
width: 100%;
|