Skip to content

Commit 88d398f

Browse files
committed
Social Image: render definition docs
1 parent 1e08f05 commit 88d398f

File tree

5 files changed

+429
-72
lines changed

5 files changed

+429
-72
lines changed

netlify/edge-functions/common/definition.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,52 @@
1-
type SyntaxSegment = { annotation?: { tag: string }; segment: string };
2-
type DocElement = { annotation?: { tag: string }; segment: string };
1+
type SyntaxSegment = {
2+
annotation?: { tag: string };
3+
segment: string;
4+
};
5+
6+
type DocSpecial =
7+
| { tag: "Source"; contents: Array<SyntaxSegment> }
8+
| { tag: "Link"; contents: Array<SyntaxSegment> }
9+
| { tag: "Example"; contents: Array<SyntaxSegment> }
10+
| { tag: "ExampleBlock"; contents: Array<SyntaxSegment> }
11+
| { tag: "Signature"; contents: Array<Array<SyntaxSegment>> }
12+
| { tag: "SignatureInline"; contents: Array<SyntaxSegment> }
13+
| { tag: "Eval"; contents: [Array<SyntaxSegment>, Array<SyntaxSegment>] }
14+
| {
15+
tag: "EvalInline";
16+
contents: [Array<SyntaxSegment>, Array<SyntaxSegment>];
17+
}
18+
| { tag: "Embed"; contents: Array<SyntaxSegment> }
19+
| { tag: "EmbedInline"; contents: Array<SyntaxSegment> };
20+
21+
type DocElement =
22+
| { tag: "Word"; contents: string }
23+
| { tag: "NamedLink"; contents: [DocElement, DocElement] }
24+
| { tag: "Paragraph"; contents: Array<DocElement> }
25+
| { tag: "Special"; contents: DocSpecial }
26+
| { tag: "Span"; contents: Array<DocElement> }
27+
| { tag: "UntitledSection"; contents: Array<DocElement> }
28+
| { tag: "Section"; contents: [DocElement, Array<DocElement>] }
29+
| { tag: "BulletedList"; contents: Array<DocElement> }
30+
| { tag: "NumberedList"; contents: [Number, Array<DocElement>] }
31+
| { tag: "Code"; contents: DocElement }
32+
| { tag: "CodeBlock"; contents: [string, DocElement] }
33+
| { tag: "Group"; contents: DocElement }
34+
| { tag: "Join"; contents: Array<DocElement> }
35+
| { tag: "Column"; contents: Array<DocElement> }
36+
| { tag: "Image"; contents: [DocElement, DocElement, DocElement?] }
37+
| { tag: "Folded"; contents: [DocElement, DocElement] }
38+
| { tag: "Callout"; contents: [DocElement | null, DocElement] }
39+
| { tag: "Aside"; contents: DocElement }
40+
| { tag: "Tooltip"; contents: [DocElement, DocElement] }
41+
| { tag: "SectionBreak"; contents: [] }
42+
| { tag: "Blankline"; contents: [] }
43+
| { tag: "Anchor"; contents: [string, DocElement] }
44+
| { tag: "Style"; contents: [string, DocElement] }
45+
| { tag: "Blockqoute"; contents: DocElement }
46+
| { tag: "Italic"; contents: DocElement }
47+
| { tag: "Bold"; contents: DocElement }
48+
| { tag: "Strikethrough"; contents: DocElement }
49+
| { tag: "Table"; contents: Array<Array<DocElement>> };
350

451
type DefinitionSyntax = {
552
contents: Array<SyntaxSegment>;
@@ -11,7 +58,7 @@ type APITerm = {
1158
defnTermTag: string;
1259
signature: Array<SyntaxSegment>;
1360
termDefinition: DefinitionSyntax;
14-
termDocs: Array<DocElement>;
61+
termDocs: Array<[string, string, DocElement]>;
1562
};
1663

1764
type APIType = {
@@ -33,5 +80,6 @@ export {
3380
APITerm,
3481
APIType,
3582
APIDefinitions,
83+
DocSpecial,
3684
DocElement,
3785
};
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*
2+
Due to this bug: https://github.com/vercel/satori/issues/484
3+
We just render the doc mostly on a single line and, hide the overflow with a fade.
4+
*/
5+
import React from "https://esm.sh/react@18.2.0";
6+
import { DocSpecial, DocElement } from "../common/definition.ts";
7+
import * as Sizing from "../common/sizing.ts";
8+
import Colors from "../common/colors.ts";
9+
import { intersperse } from "../common/utils.ts";
10+
import { InlineSyntax, Syntax } from "./syntax.tsx";
11+
import * as Fonts from "../common/fonts.ts";
12+
13+
function Docs(props: { docRoot: DocElement }) {
14+
function renderSectionContent(e: DocElement) {
15+
switch (e.tag) {
16+
case "Span":
17+
return <p style={STYLES.docBlock}>{go(e)}</p>;
18+
case "Paragraph":
19+
return <p style={STYLES.docBlock}>{go(e)}</p>;
20+
default:
21+
return go(e);
22+
}
23+
}
24+
25+
function go(e: DocElement) {
26+
switch (e.tag) {
27+
case "Special":
28+
const special = e.contents;
29+
switch (special.tag) {
30+
case "Source":
31+
return (
32+
<span style={STYLES.docCode}>
33+
<Syntax syntax={special.contents} />
34+
</span>
35+
);
36+
case "Link":
37+
return (
38+
<span style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
39+
<InlineSyntax syntax={special.contents} />
40+
</span>
41+
);
42+
case "Example":
43+
return (
44+
<span style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
45+
<InlineSyntax syntax={special.contents} />
46+
</span>
47+
);
48+
case "ExampleBlock":
49+
return (
50+
<span style={STYLES.docCode}>
51+
<Syntax syntax={special.contents} />
52+
</span>
53+
);
54+
case "EmbedInline":
55+
return (
56+
<span style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
57+
<InlineSyntax syntax={special.contents} />
58+
</span>
59+
);
60+
case "Embed":
61+
return (
62+
<span style={STYLES.docCode}>
63+
<Syntax syntax={special.contents} />
64+
</span>
65+
);
66+
case "SignatureInline":
67+
return (
68+
<span style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
69+
<InlineSyntax syntax={special.contents} />
70+
</span>
71+
);
72+
case "Signature":
73+
return (
74+
<span style={STYLES.docCode}>
75+
{special.contents.map((s) => (
76+
<Syntax syntax={s} />
77+
))}
78+
</span>
79+
);
80+
case "Eval":
81+
return (
82+
<span style={{ ...STYLES.docCode }}>
83+
<Syntax syntax={special.contents[0]} />
84+
<Syntax syntax={special.contents[1]} />
85+
</span>
86+
);
87+
case "EvalInline":
88+
return (
89+
<span style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
90+
<InlineSyntax syntax={special.contents[0]} />
91+
<InlineSyntax syntax={special.contents[1]} />
92+
</span>
93+
);
94+
95+
default:
96+
return <></>;
97+
}
98+
case "NamedLink":
99+
return <a style={STYLES.docLink}>{go(e.contents[0])}</a>;
100+
case "Word":
101+
return <span>{e.contents}</span>;
102+
case "Paragraph":
103+
return (
104+
<span style={STYLES.docInline}>
105+
{intersperse(
106+
e.contents.map(go),
107+
<span style={STYLES.docSpace}> </span>
108+
)}
109+
</span>
110+
);
111+
case "Span":
112+
return (
113+
<span style={STYLES.docInline}>
114+
{intersperse(
115+
e.contents.map(go),
116+
<span style={STYLES.docSpace}> </span>
117+
)}
118+
</span>
119+
);
120+
case "UntitledSection":
121+
return (
122+
<section style={STYLES.docBlock}>
123+
{e.contents.map(renderSectionContent)}
124+
</section>
125+
);
126+
case "Section":
127+
const [title, sectionContent] = e.contents as [
128+
DocElement,
129+
Array<DocElement>
130+
];
131+
132+
return (
133+
<section style={STYLES.docBlock}>
134+
<h1 style={STYLES.docBlock}>{go(title)}</h1>
135+
<div style={STYLES.docBlock}>
136+
{sectionContent.map(renderSectionContent)}
137+
</div>
138+
</section>
139+
);
140+
case "BulletedList":
141+
return (
142+
<ul style={STYLES.docList}>
143+
{e.contents.map((e) => (
144+
<li style={STYLES.docListItem}>
145+
<span style={STYLES.docListItemBullet}></span> {go(e)}
146+
</li>
147+
))}
148+
</ul>
149+
);
150+
case "NumberedList":
151+
const [start, els] = e.contents;
152+
return (
153+
<ol start={start} style={STYLES.docList}>
154+
{els.map((e, i) => (
155+
<li style={STYLES.docListItem}>
156+
<span style={STYLES.docListItemBullet}>{start + i}</span>{" "}
157+
{go(e)}
158+
</li>
159+
))}
160+
</ol>
161+
);
162+
case "Code":
163+
return (
164+
<code style={{ ...STYLES.docCode, ...STYLES.docCodeInline }}>
165+
{go(e.contents)}
166+
</code>
167+
);
168+
case "Join":
169+
return <span style={STYLES.docInline}>{e.contents}</span>;
170+
case "Group":
171+
return <span style={STYLES.docInline}>{go(e.contents)}</span>;
172+
default:
173+
return <></>;
174+
}
175+
}
176+
177+
const docs = go(props.docRoot);
178+
179+
return (
180+
<section style={STYLES.docs}>
181+
{docs}
182+
<span style={STYLES.docsFade}></span>
183+
</section>
184+
);
185+
}
186+
187+
const STYLES = {
188+
docs: {
189+
display: "flex",
190+
paddingTop: Sizing.toPx(2),
191+
height: Sizing.toPx(5),
192+
maxHeight: Sizing.toPx(5),
193+
overflow: "hidden",
194+
borderTop: `2px solid ${Colors.gray.lighten40}`,
195+
fontSize: Sizing.toPx(2.25),
196+
gap: 0,
197+
position: "relative",
198+
fontWeight: Fonts.Weights.semiBold,
199+
border: "1px solid blue",
200+
},
201+
docsFade: {
202+
position: "absolute",
203+
top: -10,
204+
right: `-${Sizing.toPx(2.5)}px`,
205+
bottom: -10,
206+
width: 400,
207+
height: Sizing.toPx(6),
208+
background: `linear-gradient(to right, rgba(255, 255, 255, 0), ${Colors.gray.lighten100}, ${Colors.gray.lighten100})`,
209+
},
210+
docBlock: {
211+
display: "flex",
212+
flexWrap: "wrap",
213+
gap: 0,
214+
marginTop: 0,
215+
width: "100%",
216+
},
217+
docLink: {
218+
color: Colors.blue1,
219+
},
220+
docList: {
221+
display: "flex",
222+
flexWrap: "wrap",
223+
gap: Sizing.toPx(0.5),
224+
marginTop: 0,
225+
marginLeft: Sizing.toPx(1),
226+
width: "100%",
227+
},
228+
docListItem: {
229+
display: "flex",
230+
flexWrap: "wrap",
231+
gap: 0,
232+
marginTop: 0,
233+
width: "100%",
234+
listStyleType: "disc",
235+
},
236+
docListItemBullet: {
237+
marginRight: Sizing.toPx(1),
238+
},
239+
docInline: {},
240+
docSpace: {
241+
width: "8ch",
242+
},
243+
docCode: {
244+
fontFamily: "monospace",
245+
},
246+
docCodeInline: {},
247+
};
248+
249+
export default Docs;

0 commit comments

Comments
 (0)